diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0546516..c480b08 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,12 +12,12 @@ on: jobs: test: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup JDK - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: distribution: temurin java-version: 8 diff --git a/build.sbt b/build.sbt index 8fc66a7..214bd19 100644 --- a/build.sbt +++ b/build.sbt @@ -1,14 +1,13 @@ -val scala3 = "3.1.1" -val scala213 = "2.13.4" -val scala212 = "2.12.13" -val scala211 = "2.11.12" +val scala3 = "3.3.3" +val scala213 = "2.13.14" +val scala212 = "2.12.19" inThisBuild( Seq( organization := "com.github.lolgab", version := "0.2.1", scalaVersion := scala213, - crossScalaVersions := Seq(scala3, scala213, scala212, scala211), + crossScalaVersions := Seq(scala3, scala213, scala212) ) ) @@ -39,7 +38,7 @@ lazy val commonSettings = Seq( "-Xfatal-warnings", // "-Wunused:imports" ), - libraryDependencies += "com.lihaoyi" %%% "utest" % "0.7.11" % Test, + libraryDependencies += "com.lihaoyi" %%% "utest" % "0.8.3" % Test, testFrameworks += new TestFramework("utest.runner.Framework"), ) diff --git a/core/src/main/scala/scala/scalanative/loop/Eventloop.scala b/core/src/main/scala/scala/scalanative/loop/Eventloop.scala index cde110b..c60b710 100644 --- a/core/src/main/scala/scala/scalanative/loop/Eventloop.scala +++ b/core/src/main/scala/scala/scalanative/loop/Eventloop.scala @@ -3,6 +3,7 @@ import scala.scalanative.unsafe._ import scala.scalanative.runtime._ import scala.scalanative.runtime.Intrinsics._ import scala.collection.mutable +import scala.scalanative.concurrent.NativeExecutionContext object EventLoop { import LibUV._, LibUVConstants._ @@ -10,31 +11,14 @@ object EventLoop { val loop: LibUV.Loop = uv_default_loop() // Schedule loop execution after main ends - scalanative.runtime.ExecutionContext.global.execute( - new Runnable { - def run(): Unit = EventLoop.run() - } - ) - - // Reference to the private queue of scala.scalanative.runtime.ExecutionContext - private val queue: mutable.ListBuffer[Runnable] = { - val executionContextPtr = - fromRawPtr[Byte](castObjectToRawPtr(ExecutionContext)) - val queuePtr = !((executionContextPtr + 8).asInstanceOf[Ptr[Ptr[Byte]]]) - castRawPtrToObject(toRawPtr(queuePtr)) - .asInstanceOf[mutable.ListBuffer[Runnable]] - } + NativeExecutionContext.queue.execute { () => EventLoop.run() } def run(): Unit = { + // scala.scalanative package private queue containing WorkStealing API + val queue = NativeExecutionContext.queueInternal while (uv_loop_alive(loop) != 0 || queue.nonEmpty) { while (queue.nonEmpty) { - val runnable = queue.remove(0) - try { - runnable.run() - } catch { - case t: Throwable => - ExecutionContext.global.reportFailure(t) - } + queue.stealWork(1) uv_run(loop, UV_RUN_NOWAIT) } uv_run(loop, UV_RUN_ONCE) diff --git a/core/src/main/scala/scala/scalanative/loop/Poll.scala b/core/src/main/scala/scala/scalanative/loop/Poll.scala index 5546334..be50ad3 100644 --- a/core/src/main/scala/scala/scalanative/loop/Poll.scala +++ b/core/src/main/scala/scala/scalanative/loop/Poll.scala @@ -1,38 +1,40 @@ package scala.scalanative.loop -import scala.scalanative.libc.stdlib import LibUV._, LibUVConstants._ -import scala.scalanative.unsafe.Ptr +import scala.scalanative.unsafe.{Ptr, sizeOf} +import scala.scalanative.runtime.BlobArray import internals.HandleUtils class RWResult(val result: Int, val readable: Boolean, val writable: Boolean) -@inline class Poll(val ptr: Ptr[Byte]) extends AnyVal { +@inline class Poll(private val data: BlobArray) extends AnyVal { + private def handle: Ptr[Byte] = data.atUnsafe(0) + def start(in: Boolean, out: Boolean)(callback: RWResult => Unit): Unit = { - HandleUtils.setData(ptr, callback) + HandleUtils.setData(handle, callback) var events = 0 if (out) events |= UV_WRITABLE if (in) events |= UV_READABLE - uv_poll_start(ptr, events, Poll.pollReadWriteCB) + uv_poll_start(handle, events, Poll.pollReadWriteCB) } def startReadWrite(callback: RWResult => Unit): Unit = { - HandleUtils.setData(ptr, callback) - uv_poll_start(ptr, UV_READABLE | UV_WRITABLE, Poll.pollReadWriteCB) + HandleUtils.setData(handle, callback) + uv_poll_start(handle, UV_READABLE | UV_WRITABLE, Poll.pollReadWriteCB) } def startRead(callback: Int => Unit): Unit = { - HandleUtils.setData(ptr, callback) - uv_poll_start(ptr, UV_READABLE, Poll.pollReadCB) + HandleUtils.setData(handle, callback) + uv_poll_start(handle, UV_READABLE, Poll.pollReadCB) } def startWrite(callback: Int => Unit): Unit = { - HandleUtils.setData(ptr, callback) - uv_poll_start(ptr, UV_WRITABLE, Poll.pollWriteCB) + HandleUtils.setData(handle, callback) + uv_poll_start(handle, UV_WRITABLE, Poll.pollWriteCB) } def stop(): Unit = { - uv_poll_stop(ptr) - HandleUtils.close(ptr) + uv_poll_stop(handle) + HandleUtils.close(handle) } } @@ -72,8 +74,10 @@ object Poll { private lazy val size = uv_handle_size(UV_POLL_T) def apply(fd: Int): Poll = { - val pollHandle = stdlib.malloc(size) - uv_poll_init(EventLoop.loop, pollHandle, fd) - new Poll(pollHandle) + // GC managed memory, but scans only user data + val data = BlobArray.alloc(size.toInt) + data.setScannableLimitUnsafe(sizeOf[Ptr[_]]) + uv_poll_init(EventLoop.loop, data.atUnsafe(0), fd) + new Poll(data) } } diff --git a/core/src/main/scala/scala/scalanative/loop/Timer.scala b/core/src/main/scala/scala/scalanative/loop/Timer.scala index eec5fba..18ae23c 100644 --- a/core/src/main/scala/scala/scalanative/loop/Timer.scala +++ b/core/src/main/scala/scala/scalanative/loop/Timer.scala @@ -1,13 +1,14 @@ package scala.scalanative.loop -import scala.scalanative.libc.stdlib import scala.concurrent.{Future, Promise} import scala.concurrent.duration._ import LibUV._, LibUVConstants._ -import scala.scalanative.unsafe.Ptr +import scala.scalanative.unsafe.{Ptr, sizeOf} +import scala.scalanative.runtime.BlobArray import internals.HandleUtils -@inline final class Timer private (private val ptr: Ptr[Byte]) extends AnyVal { +@inline final class Timer private (private val data: BlobArray) extends AnyVal { + private def ptr = data.atUnsafe(0) def clear(): Unit = { uv_timer_stop(ptr) HandleUtils.close(ptr) @@ -29,10 +30,13 @@ object Timer { repeat: Long, callback: () => Unit ): Timer = { - val timerHandle = stdlib.malloc(uv_handle_size(UV_TIMER_T)) + // GC managed memory, but scans only user data + val data = BlobArray.alloc(uv_handle_size(UV_TIMER_T).toInt) + data.setScannableLimitUnsafe(sizeOf[Ptr[_]]) + + val timerHandle = data.atUnsafe(0) uv_timer_init(EventLoop.loop, timerHandle) - HandleUtils.setData(timerHandle, callback) - val timer = new Timer(timerHandle) + val timer = new Timer(data) val withClearIfTimeout: () => Unit = if (repeat == 0L) { () => { @@ -40,6 +44,7 @@ object Timer { timer.clear() } } else callback + HandleUtils.setData(timerHandle, withClearIfTimeout) uv_timer_start(timerHandle, timeoutCB, timeout, repeat) timer } diff --git a/core/src/main/scala/scala/scalanative/loop/internals/HandleUtils.scala b/core/src/main/scala/scala/scalanative/loop/internals/HandleUtils.scala index 11dc680..d6345ab 100644 --- a/core/src/main/scala/scala/scalanative/loop/internals/HandleUtils.scala +++ b/core/src/main/scala/scala/scalanative/loop/internals/HandleUtils.scala @@ -4,12 +4,9 @@ package internals import scala.scalanative.runtime._ import scala.scalanative.runtime.Intrinsics._ import scala.scalanative.unsafe.Ptr -import scala.scalanative.libc.stdlib import LibUV._ private[loop] object HandleUtils { - private val references = new java.util.IdentityHashMap[Object, Int]() - @inline def getData[T <: Object](handle: Ptr[Byte]): T = { // data is the first member of uv_loop_t val ptrOfPtr = handle.asInstanceOf[Ptr[Ptr[Byte]]] @@ -24,23 +21,16 @@ private[loop] object HandleUtils { // data is the first member of uv_loop_t val ptrOfPtr = handle.asInstanceOf[Ptr[Ptr[Byte]]] if (obj != null) { - references.put(obj, references.get(obj) + 1) val rawptr = castObjectToRawPtr(obj) !ptrOfPtr = fromRawPtr[Byte](rawptr) } else { !ptrOfPtr = null } } - private val onCloseCB: CloseCB = (handle: UVHandle) => { - stdlib.free(handle) - } @inline def close(handle: Ptr[Byte]): Unit = { - if (getData(handle) != null) { - uv_close(handle, onCloseCB) - val data = getData[Object](handle) - val current = references.get(data) - if (current > 1) references.put(data, current - 1) - else references.remove(data) + val data = getData[Object](handle) + if (data != null) { + uv_close(handle, null) setData(handle, null) } } diff --git a/core/src/test/scala/scala/scalanative/loop/PollTests.scala b/core/src/test/scala/scala/scalanative/loop/PollTests.scala index 395dc11..9c7f675 100644 --- a/core/src/test/scala/scala/scalanative/loop/PollTests.scala +++ b/core/src/test/scala/scala/scalanative/loop/PollTests.scala @@ -32,7 +32,7 @@ object PollTests extends LoopTestSuite { throw new Exception("Poll result != 0") } val buf = stackalloc[Byte]() - val bytesRead = read(r, buf, 1L.toULong) + val bytesRead = read(r, buf, 1L.toCSize) assert(bytesRead == 1) assert(buf(0) == byte) promise.success(()) @@ -40,7 +40,7 @@ object PollTests extends LoopTestSuite { } val buf = stackalloc[Byte]() buf(0) = byte - val bytesWrote = write(w, buf, 1L.toULong) + val bytesWrote = write(w, buf, 1L.toCSize) assert(bytesWrote == 1) promise.future } diff --git a/project/build.properties b/project/build.properties index c8fcab5..081fdbb 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.6.2 +sbt.version=1.10.0 diff --git a/project/plugins.sbt b/project/plugins.sbt index bec6aea..8097e76 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,4 @@ -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.4") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.3") addSbtPlugin("com.eed3si9n" % "sbt-dirty-money" % "0.2.0") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.1") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.4")