Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -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
30 changes: 25 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -62,50 +66,66 @@ 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)

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)
.dependsOn(core)

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)
62 changes: 30 additions & 32 deletions core/src/main/scala/scala/scalanative/loop/Eventloop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
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
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 = {
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package scala.scalanative.loop

import utest._

abstract class LoopTestSuite extends TestSuite {
override def utestAfterEach(path: Seq[String]): Unit = {
EventLoop.run()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.3.10
sbt.version=1.3.13