Skip to content

Commit 396a781

Browse files
Scala Native 0.5.x (#47)
* Scala Native 0.5.x * Upgrade Scala Native version to 0.5.3 * Remove Scala 2.11 support * Fix event loop, use dedicated WorkStealing API instead of unsafe and incorrect reverse-engineering of class structure * Execute CI on stable Ubuntu 22 * nativeConfig ~= {_.withMultithreading(false)} * Replace manual handle memory management with GC allocated space (#2) --------- Co-authored-by: Wojciech Mazur <wmazur@virtuslab.com> Co-authored-by: Wojciech Mazur <wojciech.mazur95@gmail.com>
1 parent 2af53bc commit 396a781

File tree

9 files changed

+51
-69
lines changed

9 files changed

+51
-69
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ on:
1212

1313
jobs:
1414
test:
15-
runs-on: ubuntu-latest
15+
runs-on: ubuntu-22.04
1616
steps:
1717
- name: Checkout
18-
uses: actions/checkout@v2
18+
uses: actions/checkout@v4
1919
- name: Setup JDK
20-
uses: actions/setup-java@v2
20+
uses: actions/setup-java@v4
2121
with:
2222
distribution: temurin
2323
java-version: 8

build.sbt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
val scala3 = "3.1.1"
2-
val scala213 = "2.13.4"
3-
val scala212 = "2.12.13"
4-
val scala211 = "2.11.12"
1+
val scala3 = "3.3.3"
2+
val scala213 = "2.13.14"
3+
val scala212 = "2.12.19"
54

65
inThisBuild(
76
Seq(
87
organization := "com.github.lolgab",
98
version := "0.2.1",
109
scalaVersion := scala213,
11-
crossScalaVersions := Seq(scala3, scala213, scala212, scala211),
10+
crossScalaVersions := Seq(scala3, scala213, scala212)
1211
)
1312
)
1413

@@ -39,7 +38,7 @@ lazy val commonSettings = Seq(
3938
"-Xfatal-warnings",
4039
// "-Wunused:imports"
4140
),
42-
libraryDependencies += "com.lihaoyi" %%% "utest" % "0.7.11" % Test,
41+
libraryDependencies += "com.lihaoyi" %%% "utest" % "0.8.3" % Test,
4342
testFrameworks += new TestFramework("utest.runner.Framework"),
4443
)
4544

core/src/main/scala/scala/scalanative/loop/Eventloop.scala

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,22 @@ import scala.scalanative.unsafe._
33
import scala.scalanative.runtime._
44
import scala.scalanative.runtime.Intrinsics._
55
import scala.collection.mutable
6+
import scala.scalanative.concurrent.NativeExecutionContext
67

78
object EventLoop {
89
import LibUV._, LibUVConstants._
910

1011
val loop: LibUV.Loop = uv_default_loop()
1112

1213
// Schedule loop execution after main ends
13-
scalanative.runtime.ExecutionContext.global.execute(
14-
new Runnable {
15-
def run(): Unit = EventLoop.run()
16-
}
17-
)
18-
19-
// Reference to the private queue of scala.scalanative.runtime.ExecutionContext
20-
private val queue: mutable.ListBuffer[Runnable] = {
21-
val executionContextPtr =
22-
fromRawPtr[Byte](castObjectToRawPtr(ExecutionContext))
23-
val queuePtr = !((executionContextPtr + 8).asInstanceOf[Ptr[Ptr[Byte]]])
24-
castRawPtrToObject(toRawPtr(queuePtr))
25-
.asInstanceOf[mutable.ListBuffer[Runnable]]
26-
}
14+
NativeExecutionContext.queue.execute { () => EventLoop.run() }
2715

2816
def run(): Unit = {
17+
// scala.scalanative package private queue containing WorkStealing API
18+
val queue = NativeExecutionContext.queueInternal
2919
while (uv_loop_alive(loop) != 0 || queue.nonEmpty) {
3020
while (queue.nonEmpty) {
31-
val runnable = queue.remove(0)
32-
try {
33-
runnable.run()
34-
} catch {
35-
case t: Throwable =>
36-
ExecutionContext.global.reportFailure(t)
37-
}
21+
queue.stealWork(1)
3822
uv_run(loop, UV_RUN_NOWAIT)
3923
}
4024
uv_run(loop, UV_RUN_ONCE)

core/src/main/scala/scala/scalanative/loop/Poll.scala

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,40 @@
11
package scala.scalanative.loop
22

3-
import scala.scalanative.libc.stdlib
43
import LibUV._, LibUVConstants._
5-
import scala.scalanative.unsafe.Ptr
4+
import scala.scalanative.unsafe.{Ptr, sizeOf}
5+
import scala.scalanative.runtime.BlobArray
66
import internals.HandleUtils
77

88
class RWResult(val result: Int, val readable: Boolean, val writable: Boolean)
9-
@inline class Poll(val ptr: Ptr[Byte]) extends AnyVal {
9+
@inline class Poll(private val data: BlobArray) extends AnyVal {
10+
private def handle: Ptr[Byte] = data.atUnsafe(0)
11+
1012
def start(in: Boolean, out: Boolean)(callback: RWResult => Unit): Unit = {
11-
HandleUtils.setData(ptr, callback)
13+
HandleUtils.setData(handle, callback)
1214
var events = 0
1315
if (out) events |= UV_WRITABLE
1416
if (in) events |= UV_READABLE
15-
uv_poll_start(ptr, events, Poll.pollReadWriteCB)
17+
uv_poll_start(handle, events, Poll.pollReadWriteCB)
1618
}
1719

1820
def startReadWrite(callback: RWResult => Unit): Unit = {
19-
HandleUtils.setData(ptr, callback)
20-
uv_poll_start(ptr, UV_READABLE | UV_WRITABLE, Poll.pollReadWriteCB)
21+
HandleUtils.setData(handle, callback)
22+
uv_poll_start(handle, UV_READABLE | UV_WRITABLE, Poll.pollReadWriteCB)
2123
}
2224

2325
def startRead(callback: Int => Unit): Unit = {
24-
HandleUtils.setData(ptr, callback)
25-
uv_poll_start(ptr, UV_READABLE, Poll.pollReadCB)
26+
HandleUtils.setData(handle, callback)
27+
uv_poll_start(handle, UV_READABLE, Poll.pollReadCB)
2628
}
2729

2830
def startWrite(callback: Int => Unit): Unit = {
29-
HandleUtils.setData(ptr, callback)
30-
uv_poll_start(ptr, UV_WRITABLE, Poll.pollWriteCB)
31+
HandleUtils.setData(handle, callback)
32+
uv_poll_start(handle, UV_WRITABLE, Poll.pollWriteCB)
3133
}
3234

3335
def stop(): Unit = {
34-
uv_poll_stop(ptr)
35-
HandleUtils.close(ptr)
36+
uv_poll_stop(handle)
37+
HandleUtils.close(handle)
3638
}
3739
}
3840

@@ -72,8 +74,10 @@ object Poll {
7274
private lazy val size = uv_handle_size(UV_POLL_T)
7375

7476
def apply(fd: Int): Poll = {
75-
val pollHandle = stdlib.malloc(size)
76-
uv_poll_init(EventLoop.loop, pollHandle, fd)
77-
new Poll(pollHandle)
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)
7882
}
7983
}

core/src/main/scala/scala/scalanative/loop/Timer.scala

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package scala.scalanative.loop
22

3-
import scala.scalanative.libc.stdlib
43
import scala.concurrent.{Future, Promise}
54
import scala.concurrent.duration._
65
import LibUV._, LibUVConstants._
7-
import scala.scalanative.unsafe.Ptr
6+
import scala.scalanative.unsafe.{Ptr, sizeOf}
7+
import scala.scalanative.runtime.BlobArray
88
import internals.HandleUtils
99

10-
@inline final class Timer private (private val ptr: Ptr[Byte]) extends AnyVal {
10+
@inline final class Timer private (private val data: BlobArray) extends AnyVal {
11+
private def ptr = data.atUnsafe(0)
1112
def clear(): Unit = {
1213
uv_timer_stop(ptr)
1314
HandleUtils.close(ptr)
@@ -29,17 +30,21 @@ object Timer {
2930
repeat: Long,
3031
callback: () => Unit
3132
): Timer = {
32-
val timerHandle = stdlib.malloc(uv_handle_size(UV_TIMER_T))
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)
3338
uv_timer_init(EventLoop.loop, timerHandle)
34-
HandleUtils.setData(timerHandle, callback)
35-
val timer = new Timer(timerHandle)
39+
val timer = new Timer(data)
3640
val withClearIfTimeout: () => Unit =
3741
if (repeat == 0L) { () =>
3842
{
3943
callback()
4044
timer.clear()
4145
}
4246
} else callback
47+
HandleUtils.setData(timerHandle, withClearIfTimeout)
4348
uv_timer_start(timerHandle, timeoutCB, timeout, repeat)
4449
timer
4550
}

core/src/main/scala/scala/scalanative/loop/internals/HandleUtils.scala

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,9 @@ package internals
44
import scala.scalanative.runtime._
55
import scala.scalanative.runtime.Intrinsics._
66
import scala.scalanative.unsafe.Ptr
7-
import scala.scalanative.libc.stdlib
87
import LibUV._
98

109
private[loop] object HandleUtils {
11-
private val references = new java.util.IdentityHashMap[Object, Int]()
12-
1310
@inline def getData[T <: Object](handle: Ptr[Byte]): T = {
1411
// data is the first member of uv_loop_t
1512
val ptrOfPtr = handle.asInstanceOf[Ptr[Ptr[Byte]]]
@@ -24,23 +21,16 @@ private[loop] object HandleUtils {
2421
// data is the first member of uv_loop_t
2522
val ptrOfPtr = handle.asInstanceOf[Ptr[Ptr[Byte]]]
2623
if (obj != null) {
27-
references.put(obj, references.get(obj) + 1)
2824
val rawptr = castObjectToRawPtr(obj)
2925
!ptrOfPtr = fromRawPtr[Byte](rawptr)
3026
} else {
3127
!ptrOfPtr = null
3228
}
3329
}
34-
private val onCloseCB: CloseCB = (handle: UVHandle) => {
35-
stdlib.free(handle)
36-
}
3730
@inline def close(handle: Ptr[Byte]): Unit = {
38-
if (getData(handle) != null) {
39-
uv_close(handle, onCloseCB)
40-
val data = getData[Object](handle)
41-
val current = references.get(data)
42-
if (current > 1) references.put(data, current - 1)
43-
else references.remove(data)
31+
val data = getData[Object](handle)
32+
if (data != null) {
33+
uv_close(handle, null)
4434
setData(handle, null)
4535
}
4636
}

core/src/test/scala/scala/scalanative/loop/PollTests.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@ object PollTests extends LoopTestSuite {
3232
throw new Exception("Poll result != 0")
3333
}
3434
val buf = stackalloc[Byte]()
35-
val bytesRead = read(r, buf, 1L.toULong)
35+
val bytesRead = read(r, buf, 1L.toCSize)
3636
assert(bytesRead == 1)
3737
assert(buf(0) == byte)
3838
promise.success(())
3939
poll.stop()
4040
}
4141
val buf = stackalloc[Byte]()
4242
buf(0) = byte
43-
val bytesWrote = write(w, buf, 1L.toULong)
43+
val bytesWrote = write(w, buf, 1L.toCSize)
4444
assert(bytesWrote == 1)
4545
promise.future
4646
}

project/build.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sbt.version=1.6.2
1+
sbt.version=1.10.0

project/plugins.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.4")
1+
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.3")
22
addSbtPlugin("com.eed3si9n" % "sbt-dirty-money" % "0.2.0")
33
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.1")
44
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.4")

0 commit comments

Comments
 (0)