diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6c0d469 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,21 @@ +os: linux +dist: bionic +language: scala +branches: + only: + - master +scala: + - 2.11.12 +jdk: + - openjdk8 +before_cache: + - find $HOME/.sbt -name "*.lock" -print -delete +before_install: + - sudo apt-get update + - sudo apt-get install -y libuv1-dev +script: + - sbt test +cache: + directories: + - $HOME/.sbt + - $HOME/.cache/coursier diff --git a/build.sbt b/build.sbt index 0873dee..0239016 100644 --- a/build.sbt +++ b/build.sbt @@ -44,13 +44,17 @@ lazy val commonSettings = Seq( "-Ywarn-unused-import" ), Compile / doc / scalacOptions -= "-Xfatal-warnings", - libraryDependencies += "com.lihaoyi" %%% "utest" % "0.7.5-SNAPSHOT" % Test, + libraryDependencies += "com.lihaoyi" %%% "utest" % "0.7.4" % Test, testFrameworks += new TestFramework("utest.runner.Framework"), Test / nativeLinkStubs := true, publish / skip := true, publishLocal / skip := true ) +lazy val examplesSettings = Seq( + test := {} +) + lazy val core = project .in(file("core")) .settings(name := "native-loop-core") @@ -62,18 +66,21 @@ lazy val core = project lazy val pipe = project .in(file("pipe")) .settings(commonSettings) + .settings(test := {}) .enablePlugins(ScalaNativePlugin) .dependsOn(core) lazy val client = project .in(file("client")) .settings(commonSettings) + .settings(test := {}) .enablePlugins(ScalaNativePlugin) .dependsOn(core) lazy val server = project .in(file("server")) .settings(commonSettings) + .settings(test := {}) .enablePlugins(ScalaNativePlugin) .dependsOn(core) @@ -81,6 +88,7 @@ lazy val scalaJsCompat = project .in(file("scalajs-compat")) .settings(name := "native-loop-js-compat") .settings(commonSettings) + .settings(test := {}) .settings(publish / skip := false) .settings(publishLocal / skip := false) .enablePlugins(ScalaNativePlugin) @@ -88,24 +96,36 @@ lazy val scalaJsCompat = project lazy val serverExample = project .in(file("examples/server")) - .settings(commonSettings) + .settings( + commonSettings, + examplesSettings + ) .enablePlugins(ScalaNativePlugin) .dependsOn(core, server, client) lazy val pipeExample = project .in(file("examples/pipe")) - .settings(commonSettings) + .settings( + commonSettings, + examplesSettings + ) .enablePlugins(ScalaNativePlugin) .dependsOn(core, pipe, client) lazy val curlExample = project .in(file("examples/curl")) - .settings(commonSettings) + .settings( + commonSettings, + examplesSettings + ) .enablePlugins(ScalaNativePlugin) .dependsOn(core, client) lazy val timerExample = project .in(file("examples/timer")) - .settings(commonSettings) + .settings( + commonSettings, + examplesSettings + ) .enablePlugins(ScalaNativePlugin) .dependsOn(core) diff --git a/core/src/main/scala/scala/scalanative/loop/Eventloop.scala b/core/src/main/scala/scala/scalanative/loop/Eventloop.scala index 76a2509..c0db995 100644 --- a/core/src/main/scala/scala/scalanative/loop/Eventloop.scala +++ b/core/src/main/scala/scala/scalanative/loop/Eventloop.scala @@ -10,38 +10,38 @@ object EventLoop { // Schedule loop execution after main ends scalanative.runtime.ExecutionContext.global.execute( new Runnable { - - /** - * This is the implementation of the event loop - * that integrates with libuv. The logic is the - * following: - * - First we run all Scala futures in the default - * execution context - * - Then in loop: - * - we check if they generated IO calls on - * the event loop - * - If it's the case we run libuv's event loop - * using UV_RUN_ONCE until there are callbacks - * to execute - * - We run the default execution context again - * in case the callbacks generated new Futures - */ - def run(): Unit = { - @tailrec - def runUv(): Unit = { - val res = uv_run(loop, UV_RUN_ONCE) - if (res != 0) runUv() - } - - scala.scalanative.runtime.loop() - while (uv_loop_alive(loop) != 0) { - runUv() - scala.scalanative.runtime.loop() - } - uv_loop_close(loop) - } + def run(): Unit = EventLoop.run() } ) + + /** + * This is the implementation of the event loop + * that integrates with libuv. The logic is the + * following: + * - First we run all Scala futures in the default + * execution context + * - Then in loop: + * - we check if they generated IO calls on + * the event loop + * - If it's the case we run libuv's event loop + * using UV_RUN_ONCE until there are callbacks + * to execute + * - We run the default execution context again + * in case the callbacks generated new Futures + */ + def run(): Unit = { + @tailrec + def runUv(): Unit = { + val res = uv_run(loop, UV_RUN_ONCE) + if (res != 0) runUv() + } + + scala.scalanative.runtime.loop() + while (uv_loop_alive(loop) != 0) { + runUv() + scala.scalanative.runtime.loop() + } + } } @link("uv") @@ -78,8 +78,6 @@ object LibUV { def uv_loop_close(loop: Loop): CInt = extern def uv_is_active(handle: Ptr[Byte]): Int = extern def uv_handle_size(h_type: Int): CSize = extern - def uv_handle_get_data(handle: Ptr[Byte]): Long = extern - def uv_handle_set_data(handle: Ptr[Byte], data: Long): Unit = extern def uv_req_size(r_type: Int): CSize = extern def uv_prepare_init(loop: Loop, handle: PrepareHandle): Int = extern def uv_prepare_start(handle: PrepareHandle, cb: PrepareCB): Int = extern 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 a0c8a44..63a21ad 100644 --- a/core/src/main/scala/scala/scalanative/loop/internals/HandleUtils.scala +++ b/core/src/main/scala/scala/scalanative/loop/internals/HandleUtils.scala @@ -1,6 +1,7 @@ package scala.scalanative.loop package internals +import scala.scalanative.runtime._ import scala.scalanative.runtime.Intrinsics._ import scala.scalanative.unsafe.Ptr import scala.scalanative.libc.stdlib @@ -8,19 +9,22 @@ import scala.collection.mutable import LibUV._ private[loop] object HandleUtils { - private val references = mutable.Map.empty[Long, Int] + private val references = mutable.Map.empty[Object, Int] @inline def getData[T <: Object](handle: Ptr[Byte]): T = { - val data = LibUV.uv_handle_get_data(handle) - val rawptr = castLongToRawPtr(data) + // data is the first member of uv_loop_t + val ptrOfPtr = handle.asInstanceOf[Ptr[Ptr[Byte]]] + val rawptr = toRawPtr(!ptrOfPtr) castRawPtrToObject(rawptr).asInstanceOf[T] } - @inline def setData[T <: Object](handle: Ptr[Byte], function: T): Unit = { - val rawptr = castObjectToRawPtr(function) - val data = castRawPtrToLong(rawptr) - if (references.contains(data)) references(data) += 1 - else references(data) = 1 - LibUV.uv_handle_set_data(handle, data) + @inline def setData(handle: Ptr[Byte], obj: Object): Unit = { + if (references.contains(obj)) references(obj) += 1 + else references(obj) = 1 + + // data is the first member of uv_loop_t + val ptrOfPtr = handle.asInstanceOf[Ptr[Ptr[Byte]]] + val rawptr = castObjectToRawPtr(obj) + !ptrOfPtr = fromRawPtr[Byte](rawptr) } private val onCloseCB = new CloseCB { def apply(handle: UVHandle): Unit = { @@ -29,7 +33,7 @@ private[loop] object HandleUtils { } @inline def close(handle: Ptr[Byte]): Unit = { uv_close(handle, onCloseCB) - val data = LibUV.uv_handle_get_data(handle) + val data = getData[Object](handle) val current = references(data) if (current > 1) references(data) -= 1 else references.remove(data) diff --git a/core/src/test/scala/scala/scalanative/loop/LoopTestSuite.scala b/core/src/test/scala/scala/scalanative/loop/LoopTestSuite.scala new file mode 100644 index 0000000..8a8da28 --- /dev/null +++ b/core/src/test/scala/scala/scalanative/loop/LoopTestSuite.scala @@ -0,0 +1,9 @@ +package scala.scalanative.loop + +import utest._ + +abstract class LoopTestSuite extends TestSuite { + override def utestAfterEach(path: Seq[String]): Unit = { + EventLoop.run() + } +} diff --git a/core/src/test/scala/scala/scalanative/loop/TimerTests.scala b/core/src/test/scala/scala/scalanative/loop/TimerTests.scala index 9ed7edd..3be3a24 100644 --- a/core/src/test/scala/scala/scalanative/loop/TimerTests.scala +++ b/core/src/test/scala/scala/scalanative/loop/TimerTests.scala @@ -5,7 +5,7 @@ import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Promise -object TimerTests extends TestSuite { +object TimerTests extends LoopTestSuite { val tests = Tests { def now(): Duration = System.currentTimeMillis().millis val d = 200.millis diff --git a/project/build.properties b/project/build.properties index 797e7cc..0837f7a 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.3.10 +sbt.version=1.3.13