├── project ├── build.properties └── plugins.sbt ├── .gitignore ├── .scalafmt.conf ├── core └── src │ ├── test │ └── scala │ │ └── scala │ │ └── scalanative │ │ └── loop │ │ ├── LoopTestSuite.scala │ │ ├── PollTests.scala │ │ └── TimerTests.scala │ └── main │ └── scala │ └── scala │ └── scalanative │ └── loop │ ├── internals │ └── HandleUtils.scala │ ├── Timer.scala │ ├── Poll.scala │ └── Eventloop.scala ├── README.md ├── .github └── workflows │ └── ci.yml └── scalajs-compat ├── RawTimers.scala └── package.scala /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.10.0 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | */target/ 3 | project/project 4 | examples/*/target 5 | lowered.hnir 6 | .metals 7 | .bloop 8 | .bsp 9 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = "2.4.2" 2 | style = defaultWithAlign 3 | docstrings = JavaDoc 4 | assumeStandardLibraryStripMargin = true 5 | project.excludeFilters = [ 6 | scalalib/ 7 | ] 8 | project.git = true 9 | runner.dialect = scala212 10 | -------------------------------------------------------------------------------- /core/src/test/scala/scala/scalanative/loop/LoopTestSuite.scala: -------------------------------------------------------------------------------- 1 | package scala.scalanative.loop 2 | 3 | import utest._ 4 | 5 | abstract class LoopTestSuite extends TestSuite { 6 | override def utestAfterEach(path: Seq[String]): Unit = { 7 | EventLoop.run() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.3") 2 | addSbtPlugin("com.eed3si9n" % "sbt-dirty-money" % "0.2.0") 3 | addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.1") 4 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.10.0") 5 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.3.4") 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scala-native-loop 2 | 3 | Async IO and event loop for Scala Native 4 | 5 | ## What is it? 6 | 7 | scala-native-loop provides asynchronous utilities for Scala Native. 8 | It's backed by libuv, the same C library that the Node.js ecosystem runs on. 9 | It currently offers: 10 | 11 | - `scala.scalanative.loop.Timer`: to schedule callbacks to execute after a timeout 12 | - `scala.scalanative.loop.Poll`: to schedule callbacks when data is read/written on a file descriptor 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - '*' 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | test: 15 | runs-on: ubuntu-22.04 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | - name: Setup JDK 20 | uses: actions/setup-java@v4 21 | with: 22 | distribution: temurin 23 | java-version: 8 24 | - name: Install libuv 25 | run: sudo apt-get update && sudo apt-get install -y libuv1-dev 26 | - name: Build and Test 27 | run: sbt -v +test 28 | -------------------------------------------------------------------------------- /core/src/main/scala/scala/scalanative/loop/internals/HandleUtils.scala: -------------------------------------------------------------------------------- 1 | package scala.scalanative.loop 2 | package internals 3 | 4 | import scala.scalanative.runtime._ 5 | import scala.scalanative.runtime.Intrinsics._ 6 | import scala.scalanative.unsafe.Ptr 7 | import LibUV._ 8 | 9 | private[loop] object HandleUtils { 10 | @inline def getData[T <: Object](handle: Ptr[Byte]): T = { 11 | // data is the first member of uv_loop_t 12 | val ptrOfPtr = handle.asInstanceOf[Ptr[Ptr[Byte]]] 13 | val dataPtr = !ptrOfPtr 14 | if (dataPtr == null) null.asInstanceOf[T] 15 | else { 16 | val rawptr = toRawPtr(dataPtr) 17 | castRawPtrToObject(rawptr).asInstanceOf[T] 18 | } 19 | } 20 | @inline def setData(handle: Ptr[Byte], obj: Object): Unit = { 21 | // data is the first member of uv_loop_t 22 | val ptrOfPtr = handle.asInstanceOf[Ptr[Ptr[Byte]]] 23 | if (obj != null) { 24 | val rawptr = castObjectToRawPtr(obj) 25 | !ptrOfPtr = fromRawPtr[Byte](rawptr) 26 | } else { 27 | !ptrOfPtr = null 28 | } 29 | } 30 | @inline def close(handle: Ptr[Byte]): Unit = { 31 | val data = getData[Object](handle) 32 | if (data != null) { 33 | uv_close(handle, null) 34 | setData(handle, null) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/test/scala/scala/scalanative/loop/PollTests.scala: -------------------------------------------------------------------------------- 1 | package scala.scalanative.loop 2 | 3 | import utest._ 4 | import scala.scalanative.unsafe._ 5 | import scala.scalanative.unsigned._ 6 | import scala.scalanative.posix.unistd._ 7 | import scala.concurrent.ExecutionContext.Implicits.global 8 | import scala.concurrent.{Future, Promise} 9 | 10 | object PollTests extends LoopTestSuite { 11 | def usingPipe(f: (Int, Int) => Future[Unit]): Future[Unit] = { 12 | val fildes = stackalloc[CInt](2) 13 | if (pipe(fildes) != 0) { 14 | throw new Exception("Failed to create pipe") 15 | } 16 | val future = f(fildes(0), fildes(1)) 17 | future.onComplete { _ => 18 | close(fildes(0)) 19 | close(fildes(1)) 20 | } 21 | future 22 | } 23 | 24 | val tests = Tests { 25 | test("startRead") { 26 | usingPipe { (r, w) => 27 | val promise = Promise[Unit]() 28 | val byte = 10.toByte 29 | val poll = Poll(r) 30 | poll.startRead { i => 31 | if (i != 0) { 32 | throw new Exception("Poll result != 0") 33 | } 34 | val buf = stackalloc[Byte]() 35 | val bytesRead = read(r, buf, 1L.toCSize) 36 | assert(bytesRead == 1) 37 | assert(buf(0) == byte) 38 | promise.success(()) 39 | poll.stop() 40 | } 41 | val buf = stackalloc[Byte]() 42 | buf(0) = byte 43 | val bytesWrote = write(w, buf, 1L.toCSize) 44 | assert(bytesWrote == 1) 45 | promise.future 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /scalajs-compat/RawTimers.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Scala.js (https://www.scala-js.org/) 3 | * 4 | * Copyright EPFL. 5 | * 6 | * Licensed under Apache License 2.0 7 | * (https://www.apache.org/licenses/LICENSE-2.0). 8 | * 9 | * See the NOTICE file distributed with this work for 10 | * additional information regarding copyright ownership. 11 | */ 12 | 13 | package scala.scalajs.js.timers 14 | 15 | import scalanative.loop.Timer 16 | import scala.concurrent.duration._ 17 | 18 | /** 19 | * Non-Standard 20 | * Raw JavaScript timer methods. 21 | * 22 | * The methods on this object expose the raw JavaScript methods for timers. In 23 | * general it is more advisable to use the methods directly defined on 24 | * [[timers]] as they are more Scala-like. 25 | */ 26 | object RawTimers { 27 | 28 | /** Schedule `handler` for execution in `interval` milliseconds. 29 | * 30 | * @param handler the function to call after `interval` has passed 31 | * @param interval duration in milliseconds to wait 32 | * @return A handle that can be used to cancel the timeout by passing it 33 | * to [[clearTimeout]]. 34 | */ 35 | @inline def setTimeout( 36 | handler: () => Unit, 37 | interval: Double 38 | ): SetTimeoutHandle = 39 | Timer.timeout(interval.millis)(handler) 40 | 41 | /** Schedule `handler` for repeated execution every `interval` 42 | * milliseconds. 43 | * 44 | * @param handler the function to call after each `interval` 45 | * @param interval duration in milliseconds between executions 46 | * @return A handle that can be used to cancel the interval by passing it 47 | * to [[clearInterval]]. 48 | */ 49 | @inline def setInterval( 50 | handler: () => Unit, 51 | interval: Double 52 | ): SetIntervalHandle = 53 | Timer.repeat(interval.millis)(handler) 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/scala/scala/scalanative/loop/Timer.scala: -------------------------------------------------------------------------------- 1 | package scala.scalanative.loop 2 | 3 | import scala.concurrent.{Future, Promise} 4 | import scala.concurrent.duration._ 5 | import LibUV._, LibUVConstants._ 6 | import scala.scalanative.unsafe.{Ptr, sizeOf} 7 | import scala.scalanative.runtime.BlobArray 8 | import internals.HandleUtils 9 | 10 | @inline final class Timer private (private val data: BlobArray) extends AnyVal { 11 | private def ptr = data.atUnsafe(0) 12 | def clear(): Unit = { 13 | uv_timer_stop(ptr) 14 | HandleUtils.close(ptr) 15 | } 16 | } 17 | 18 | object Timer { 19 | private val timeoutCB: TimerCB = (handle: TimerHandle) => { 20 | val callback = HandleUtils.getData[() => Unit](handle) 21 | callback.apply() 22 | } 23 | private val repeatCB: TimerCB = (handle: TimerHandle) => { 24 | val callback = HandleUtils.getData[() => Unit](handle) 25 | callback.apply() 26 | } 27 | @inline 28 | private def startTimer( 29 | timeout: Long, 30 | repeat: Long, 31 | callback: () => Unit 32 | ): Timer = { 33 | // GC managed memory, but scans only user data 34 | val data = BlobArray.alloc(uv_handle_size(UV_TIMER_T).toInt) 35 | data.setScannableLimitUnsafe(sizeOf[Ptr[_]]) 36 | 37 | val timerHandle = data.atUnsafe(0) 38 | uv_timer_init(EventLoop.loop, timerHandle) 39 | val timer = new Timer(data) 40 | val withClearIfTimeout: () => Unit = 41 | if (repeat == 0L) { () => 42 | { 43 | callback() 44 | timer.clear() 45 | } 46 | } else callback 47 | HandleUtils.setData(timerHandle, withClearIfTimeout) 48 | uv_timer_start(timerHandle, timeoutCB, timeout, repeat) 49 | timer 50 | } 51 | 52 | def delay(duration: FiniteDuration): Future[Unit] = { 53 | val promise = Promise[Unit]() 54 | timeout(duration)(() => promise.success(())) 55 | promise.future 56 | } 57 | 58 | def timeout(duration: FiniteDuration)(callback: () => Unit): Timer = { 59 | startTimer(duration.toMillis, 0L, callback) 60 | } 61 | 62 | def repeat(duration: FiniteDuration)(callback: () => Unit): Timer = { 63 | val millis = duration.toMillis 64 | startTimer(millis, millis, callback) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /core/src/test/scala/scala/scalanative/loop/TimerTests.scala: -------------------------------------------------------------------------------- 1 | package scala.scalanative.loop 2 | 3 | import utest._ 4 | import scala.concurrent.duration._ 5 | import scala.concurrent.ExecutionContext.Implicits.global 6 | import scala.concurrent.{Future, Promise} 7 | 8 | object TimerTests extends LoopTestSuite { 9 | val tests = Tests { 10 | def now(): Duration = System.currentTimeMillis().millis 11 | val d = 200.millis 12 | test("delay") { 13 | var i = 0 14 | val startTime = now() 15 | def checkDelay(time: Int) = { 16 | i += 1 17 | assert(i == time) 18 | assert(now() - startTime >= d * time) 19 | } 20 | for { 21 | () <- Timer.delay(d) 22 | _ = checkDelay(1) 23 | () <- Timer.delay(d) 24 | _ = checkDelay(2) 25 | } yield () 26 | } 27 | test("repeat") { 28 | var i = 0 29 | val startTime = now() 30 | val times = 3 31 | val p = Promise[Unit]() 32 | var timer: Timer = null.asInstanceOf[Timer] 33 | timer = Timer.repeat(d) { () => 34 | if (i == times) { 35 | p.success(()) 36 | timer.clear() 37 | } else i += 1 38 | } 39 | p.future.map { _ => 40 | assert(i == times) 41 | val took = now() - startTime 42 | assert(took >= d * 3) 43 | } 44 | } 45 | test("clear timeout") { 46 | val handle = Timer.timeout(d) { () => 47 | throw new Exception("This timeout should have not triggered") 48 | } 49 | handle.clear() 50 | for { 51 | () <- Timer.delay(d * 2) 52 | } yield () 53 | } 54 | test("close multiple times") { 55 | val p = Promise[Unit]() 56 | val timer = Timer.timeout(10.millis)(() => {}) 57 | timer.clear() 58 | timer.clear() 59 | global.execute(new Runnable { def run(): Unit = timer.clear() }) 60 | Timer.timeout(50.millis) { () => 61 | timer.clear() 62 | timer.clear() 63 | p.success(()) 64 | } 65 | p.future 66 | } 67 | test("deadlock when futures need event loop run to unlock") { 68 | var completed = false 69 | def recursive(): Future[Unit] = 70 | if (!completed) Future(recursive()) else Future.successful(()) 71 | val r = recursive() 72 | Timer.timeout(10.millis)(() => completed = true) 73 | r 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /core/src/main/scala/scala/scalanative/loop/Poll.scala: -------------------------------------------------------------------------------- 1 | package scala.scalanative.loop 2 | 3 | import LibUV._, LibUVConstants._ 4 | import scala.scalanative.unsafe.{Ptr, sizeOf} 5 | import scala.scalanative.runtime.BlobArray 6 | import internals.HandleUtils 7 | 8 | class RWResult(val result: Int, val readable: Boolean, val writable: Boolean) 9 | @inline class Poll(private val data: BlobArray) extends AnyVal { 10 | private def handle: Ptr[Byte] = data.atUnsafe(0) 11 | 12 | def start(in: Boolean, out: Boolean)(callback: RWResult => Unit): Unit = { 13 | HandleUtils.setData(handle, callback) 14 | var events = 0 15 | if (out) events |= UV_WRITABLE 16 | if (in) events |= UV_READABLE 17 | uv_poll_start(handle, events, Poll.pollReadWriteCB) 18 | } 19 | 20 | def startReadWrite(callback: RWResult => Unit): Unit = { 21 | HandleUtils.setData(handle, callback) 22 | uv_poll_start(handle, UV_READABLE | UV_WRITABLE, Poll.pollReadWriteCB) 23 | } 24 | 25 | def startRead(callback: Int => Unit): Unit = { 26 | HandleUtils.setData(handle, callback) 27 | uv_poll_start(handle, UV_READABLE, Poll.pollReadCB) 28 | } 29 | 30 | def startWrite(callback: Int => Unit): Unit = { 31 | HandleUtils.setData(handle, callback) 32 | uv_poll_start(handle, UV_WRITABLE, Poll.pollWriteCB) 33 | } 34 | 35 | def stop(): Unit = { 36 | uv_poll_stop(handle) 37 | HandleUtils.close(handle) 38 | } 39 | } 40 | 41 | object Poll { 42 | private val pollReadWriteCB: PollCB = ( 43 | handle: PollHandle, 44 | status: Int, 45 | events: Int 46 | ) => { 47 | val callback = 48 | HandleUtils.getData[RWResult => Unit](handle) 49 | callback.apply( 50 | new RWResult( 51 | result = status, 52 | readable = (events & UV_READABLE) != 0, 53 | writable = (events & UV_WRITABLE) != 0 54 | ) 55 | ) 56 | } 57 | private val pollReadCB: PollCB = ( 58 | handle: PollHandle, 59 | status: Int, 60 | events: Int 61 | ) => { 62 | val callback = HandleUtils.getData[Int => Unit](handle) 63 | if ((events & UV_READABLE) != 0) callback.apply(status) 64 | } 65 | private val pollWriteCB: PollCB = ( 66 | handle: PollHandle, 67 | status: Int, 68 | events: Int 69 | ) => { 70 | val callback = HandleUtils.getData[Int => Unit](handle) 71 | if ((events & UV_WRITABLE) != 0) callback.apply(status) 72 | } 73 | 74 | private lazy val size = uv_handle_size(UV_POLL_T) 75 | 76 | def apply(fd: Int): Poll = { 77 | // GC managed memory, but scans only user data 78 | val data = BlobArray.alloc(size.toInt) 79 | data.setScannableLimitUnsafe(sizeOf[Ptr[_]]) 80 | uv_poll_init(EventLoop.loop, data.atUnsafe(0), fd) 81 | new Poll(data) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /scalajs-compat/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Scala.js (https://www.scala-js.org/) 3 | * 4 | * Copyright EPFL. 5 | * 6 | * Licensed under Apache License 2.0 7 | * (https://www.apache.org/licenses/LICENSE-2.0). 8 | * 9 | * See the NOTICE file distributed with this work for 10 | * additional information regarding copyright ownership. 11 | */ 12 | 13 | package scala.scalajs.js 14 | 15 | import scala.concurrent.duration.FiniteDuration 16 | 17 | /** 18 | * Non-Standard 19 | * Non-standard, but in general well supported methods to schedule asynchronous 20 | * execution. 21 | * 22 | * The methods in this package work in all JavaScript virtual machines 23 | * supporting `setTimeout` and `setInterval`. 24 | */ 25 | package object timers { 26 | 27 | import scala.scalanative.loop.Timer 28 | 29 | type SetTimeoutHandle = Timer 30 | type SetIntervalHandle = Timer 31 | 32 | /** Schedule something for execution in `interval` milliseconds. 33 | * 34 | * @param interval duration in milliseconds to wait 35 | * @param body code to execute after `interval` has passed 36 | * @return A handle that can be used to cancel the timeout by passing it 37 | * to [[clearTimeout]]. 38 | * @note Uses JavaScript's non-standard `setTimeout` 39 | */ 40 | def setTimeout(interval: Double)(body: => Unit): SetTimeoutHandle = { 41 | RawTimers.setTimeout(() => body, interval) 42 | } 43 | 44 | /** Schedule something for execution after a duration. 45 | * 46 | * @param interval duration to wait 47 | * @param body code to execute after `interval` has passed 48 | * @return A handle that can be used to cancel the timeout by passing it 49 | * to [[clearTimeout]]. 50 | * @note Uses JavaScript's non-standard `setTimeout` 51 | */ 52 | def setTimeout(interval: FiniteDuration)(body: => Unit): SetTimeoutHandle = { 53 | RawTimers.setTimeout(() => body, interval.toMillis.toDouble) 54 | } 55 | 56 | /** Cancel a timeout execution 57 | * @param handle The handle returned by 58 | * [[setTimeout(interval:scala\.concurrent\.duration\.FiniteDuration)* setTimeout]]. 59 | * @note Uses JavaScript's non-standard `clearTimeout` 60 | */ 61 | def clearTimeout(handle: SetTimeoutHandle): Unit = handle.clear() 62 | 63 | /** Schedule something for repeated execution every `interval` milliseconds. 64 | * 65 | * @param interval duration in milliseconds between executions 66 | * @param body code to execute after each `interval` 67 | * @return A handle that can be used to cancel the interval by passing it 68 | * to [[clearInterval]]. 69 | * @note Uses JavaScript's non-standard `setInterval` 70 | */ 71 | def setInterval(interval: Double)(body: => Unit): SetIntervalHandle = 72 | RawTimers.setInterval(() => body, interval) 73 | 74 | /** Schedule something for repeated execution every duration. 75 | * 76 | * @param interval duration between executions 77 | * @param body code to execute after each `interval` 78 | * @return A handle that can be used to cancel the interval by passing it 79 | * to [[clearInterval]]. 80 | * @note Uses JavaScript's non-standard `setInterval` 81 | */ 82 | def setInterval(interval: FiniteDuration)(body: => Unit): SetIntervalHandle = 83 | RawTimers.setInterval(() => body, interval.toMillis.toDouble) 84 | 85 | /** Cancel an interval execution 86 | * @param handle The handle returned by 87 | * [[setInterval(interval:scala\.concurrent\.duration\.FiniteDuration)* setInterval]]. 88 | * @note Uses JavaScript's non-standard `clearInterval` 89 | */ 90 | def clearInterval(handle: SetIntervalHandle): Unit = 91 | handle.clear() 92 | 93 | } 94 | -------------------------------------------------------------------------------- /core/src/main/scala/scala/scalanative/loop/Eventloop.scala: -------------------------------------------------------------------------------- 1 | package scala.scalanative.loop 2 | import scala.scalanative.unsafe._ 3 | import scala.scalanative.runtime._ 4 | import scala.scalanative.runtime.Intrinsics._ 5 | import scala.collection.mutable 6 | import scala.scalanative.concurrent.NativeExecutionContext 7 | 8 | object EventLoop { 9 | import LibUV._, LibUVConstants._ 10 | 11 | val loop: LibUV.Loop = uv_default_loop() 12 | 13 | // Schedule loop execution after main ends 14 | NativeExecutionContext.queue.execute { () => EventLoop.run() } 15 | 16 | def run(): Unit = { 17 | // scala.scalanative package private queue containing WorkStealing API 18 | val queue = NativeExecutionContext.queueInternal 19 | while (uv_loop_alive(loop) != 0 || queue.nonEmpty) { 20 | while (queue.nonEmpty) { 21 | queue.stealWork(1) 22 | uv_run(loop, UV_RUN_NOWAIT) 23 | } 24 | uv_run(loop, UV_RUN_ONCE) 25 | } 26 | } 27 | } 28 | 29 | @link("uv") 30 | @extern 31 | object LibUV { 32 | type UVHandle = Ptr[Byte] 33 | type PipeHandle = Ptr[Byte] 34 | type PollHandle = Ptr[Byte] 35 | type TCPHandle = Ptr[Byte] 36 | type PrepareHandle = Ptr[Byte] 37 | type TimerHandle = Ptr[Byte] 38 | 39 | type TTYHandle = Ptr[Byte] 40 | type Loop = Ptr[Byte] 41 | type Buffer = CStruct2[Ptr[Byte], CSize] 42 | type WriteReq = Ptr[Ptr[Byte]] 43 | type FSReq = Ptr[Ptr[Byte]] 44 | type ShutdownReq = Ptr[Ptr[Byte]] 45 | type Connection = Ptr[Byte] 46 | type ConnectionCB = CFuncPtr2[TCPHandle, Int, Unit] 47 | type AllocCB = CFuncPtr3[TCPHandle, CSize, Ptr[Buffer], Unit] 48 | type ReadCB = CFuncPtr3[TCPHandle, CSSize, Ptr[Buffer], Unit] 49 | type WriteCB = CFuncPtr2[WriteReq, Int, Unit] 50 | type PrepareCB = CFuncPtr1[PrepareHandle, Unit] 51 | type ShutdownCB = CFuncPtr2[ShutdownReq, Int, Unit] 52 | type CloseCB = CFuncPtr1[UVHandle, Unit] 53 | type PollCB = CFuncPtr3[PollHandle, Int, Int, Unit] 54 | type TimerCB = CFuncPtr1[TimerHandle, Unit] 55 | type FSCB = CFuncPtr1[FSReq, Unit] 56 | 57 | def uv_default_loop(): Loop = extern 58 | def uv_loop_size(): CSize = extern 59 | def uv_loop_alive(loop: Loop): CInt = extern 60 | def uv_loop_close(loop: Loop): CInt = extern 61 | def uv_is_active(handle: Ptr[Byte]): Int = extern 62 | def uv_handle_size(h_type: Int): CSize = extern 63 | def uv_req_size(r_type: Int): CSize = extern 64 | def uv_prepare_init(loop: Loop, handle: PrepareHandle): Int = extern 65 | def uv_prepare_start(handle: PrepareHandle, cb: PrepareCB): Int = extern 66 | def uv_prepare_stop(handle: PrepareHandle): Unit = extern 67 | 68 | def uv_tty_init(loop: Loop, handle: TTYHandle, fd: Int, readable: Int): Int = 69 | extern 70 | 71 | def uv_tcp_init(loop: Loop, tcp_handle: TCPHandle): Int = extern 72 | def uv_tcp_bind(tcp_handle: TCPHandle, address: Ptr[Byte], flags: Int): Int = 73 | extern 74 | 75 | def uv_ip4_addr(address: CString, port: Int, out_addr: Ptr[Byte]): Int = 76 | extern 77 | def uv_ip4_name(address: Ptr[Byte], s: CString, size: Int): Int = extern 78 | 79 | def uv_pipe_init(loop: Loop, handle: PipeHandle, ipc: Int): Int = extern 80 | def uv_pipe_open(handle: PipeHandle, fd: Int): Int = extern 81 | def uv_pipe_bind(handle: PipeHandle, socketName: CString): Int = extern 82 | 83 | def uv_poll_init(loop: Loop, handle: PollHandle, fd: Int): Int = 84 | extern 85 | def uv_poll_init_socket( 86 | loop: Loop, 87 | handle: PollHandle, 88 | socket: Ptr[Byte] 89 | ): Int = extern 90 | def uv_poll_start(handle: PollHandle, events: Int, cb: PollCB): Int = extern 91 | def uv_poll_stop(handle: PollHandle): Int = extern 92 | 93 | def uv_timer_init(loop: Loop, handle: TimerHandle): Int = extern 94 | def uv_timer_start( 95 | handle: TimerHandle, 96 | cb: TimerCB, 97 | timeout: Long, 98 | repeat: Long 99 | ): Int = extern 100 | def uv_timer_stop(handle: TimerHandle): Int = extern 101 | 102 | def uv_listen(handle: PipeHandle, backlog: Int, callback: ConnectionCB): Int = 103 | extern 104 | def uv_accept(server: PipeHandle, client: PipeHandle): Int = extern 105 | def uv_read_start(client: PipeHandle, allocCB: AllocCB, readCB: ReadCB): Int = 106 | extern 107 | def uv_write( 108 | writeReq: WriteReq, 109 | client: PipeHandle, 110 | bufs: Ptr[Buffer], 111 | numBufs: Int, 112 | writeCB: WriteCB 113 | ): Int = extern 114 | def uv_read_stop(client: PipeHandle): Int = extern 115 | def uv_shutdown( 116 | shutdownReq: ShutdownReq, 117 | client: PipeHandle, 118 | shutdownCB: ShutdownCB 119 | ): Int = extern 120 | def uv_close(handle: PipeHandle, closeCB: CloseCB): Unit = extern 121 | def uv_is_closing(handle: PipeHandle): Int = extern 122 | def uv_run(loop: Loop, runMode: Int): Int = extern 123 | 124 | def uv_strerror(err: Int): CString = extern 125 | def uv_err_name(err: Int): CString = extern 126 | 127 | def uv_fileno(handle: TTYHandle, fileno: Ptr[Int]): Int = extern 128 | def uv_handle_type_name(handle: TTYHandle): Int = extern 129 | def uv_guess_handle(fd: Int): Int = extern 130 | 131 | def uv_fs_open( 132 | loop: Loop, 133 | req: FSReq, 134 | path: CString, 135 | flags: Int, 136 | mode: Int, 137 | cb: FSCB 138 | ): Int = extern 139 | def uv_fs_read( 140 | loop: Loop, 141 | req: FSReq, 142 | fd: Int, 143 | bufs: Ptr[Buffer], 144 | numBufs: Int, 145 | offset: Long, 146 | fsCB: FSCB 147 | ): Int = extern 148 | def uv_fs_write( 149 | loop: Loop, 150 | req: FSReq, 151 | fd: Int, 152 | bufs: Ptr[Buffer], 153 | numBufs: Int, 154 | offset: Long, 155 | fsCB: FSCB 156 | ): Int = extern 157 | def uv_fs_close(loop: Loop, req: FSReq, fd: Int, fsCB: FSCB): Int = extern 158 | def uv_req_cleanup(req: FSReq): Unit = extern 159 | def uv_fs_get_result(req: FSReq): Int = extern 160 | def uv_fs_get_ptr(req: FSReq): Ptr[Byte] = extern 161 | } 162 | 163 | object LibUVConstants { 164 | import LibUV._ 165 | 166 | // uv_run_mode 167 | val UV_RUN_DEFAULT = 0 168 | val UV_RUN_ONCE = 1 169 | val UV_RUN_NOWAIT = 2 170 | 171 | // UV_HANDLE_T 172 | val UV_PIPE_T = 7 173 | val UV_POLL_T = 8 174 | val UV_PREPARE_T = 9 175 | val UV_PROCESS_T = 10 176 | val UV_TCP_T = 12 177 | val UV_TIMER_T = 13 178 | val UV_TTY_T = 14 179 | val UV_UDP_T = 15 180 | 181 | // UV_REQ_T 182 | val UV_WRITE_REQ_T = 3 183 | val UV_FS_REQ_T = 6 184 | 185 | val UV_READABLE = 1 186 | val UV_WRITABLE = 2 187 | val UV_DISCONNECT = 4 188 | val UV_PRIORITIZED = 8 189 | 190 | val O_RDWR = 2 191 | val O_CREAT = sys.props("os.name") match { 192 | case "Mac OS X" => 512 193 | case _ => 64 194 | } 195 | val default_permissions = 420 // octal 0644 196 | 197 | def check(v: Int, label: String): Int = { 198 | if (v == 0) { 199 | v 200 | } else { 201 | val error = fromCString(uv_err_name(v)) 202 | val message = fromCString(uv_strerror(v)) 203 | println(s"ERROR: $label returned $v: $error: $message") 204 | v 205 | } 206 | } 207 | } 208 | --------------------------------------------------------------------------------