├── 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 |
--------------------------------------------------------------------------------