Skip to content

Commit

Permalink
Replace manual handle memory management with GC allocated space (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
WojciechMazur authored Jun 17, 2024
1 parent 526cb61 commit 3afc321
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 36 deletions.
1 change: 0 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ lazy val commonSettings = Seq(
),
libraryDependencies += "com.lihaoyi" %%% "utest" % "0.8.3" % Test,
testFrameworks += new TestFramework("utest.runner.Framework"),
nativeConfig ~= {_.withMultithreading(false)},
)

lazy val core = project
Expand Down
36 changes: 20 additions & 16 deletions core/src/main/scala/scala/scalanative/loop/Poll.scala
Original file line number Diff line number Diff line change
@@ -1,38 +1,40 @@
package scala.scalanative.loop

import scala.scalanative.libc.stdlib
import LibUV._, LibUVConstants._
import scala.scalanative.unsafe.Ptr
import scala.scalanative.unsafe.{Ptr, sizeOf}
import scala.scalanative.runtime.BlobArray
import internals.HandleUtils

class RWResult(val result: Int, val readable: Boolean, val writable: Boolean)
@inline class Poll(val ptr: Ptr[Byte]) extends AnyVal {
@inline class Poll(private val data: BlobArray) extends AnyVal {
private def handle: Ptr[Byte] = data.atUnsafe(0)

def start(in: Boolean, out: Boolean)(callback: RWResult => Unit): Unit = {
HandleUtils.setData(ptr, callback)
HandleUtils.setData(handle, callback)
var events = 0
if (out) events |= UV_WRITABLE
if (in) events |= UV_READABLE
uv_poll_start(ptr, events, Poll.pollReadWriteCB)
uv_poll_start(handle, events, Poll.pollReadWriteCB)
}

def startReadWrite(callback: RWResult => Unit): Unit = {
HandleUtils.setData(ptr, callback)
uv_poll_start(ptr, UV_READABLE | UV_WRITABLE, Poll.pollReadWriteCB)
HandleUtils.setData(handle, callback)
uv_poll_start(handle, UV_READABLE | UV_WRITABLE, Poll.pollReadWriteCB)
}

def startRead(callback: Int => Unit): Unit = {
HandleUtils.setData(ptr, callback)
uv_poll_start(ptr, UV_READABLE, Poll.pollReadCB)
HandleUtils.setData(handle, callback)
uv_poll_start(handle, UV_READABLE, Poll.pollReadCB)
}

def startWrite(callback: Int => Unit): Unit = {
HandleUtils.setData(ptr, callback)
uv_poll_start(ptr, UV_WRITABLE, Poll.pollWriteCB)
HandleUtils.setData(handle, callback)
uv_poll_start(handle, UV_WRITABLE, Poll.pollWriteCB)
}

def stop(): Unit = {
uv_poll_stop(ptr)
HandleUtils.close(ptr)
uv_poll_stop(handle)
HandleUtils.close(handle)
}
}

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

def apply(fd: Int): Poll = {
val pollHandle = stdlib.malloc(size)
uv_poll_init(EventLoop.loop, pollHandle, fd)
new Poll(pollHandle)
// GC managed memory, but scans only user data
val data = BlobArray.alloc(size.toInt)
data.setScannableLimitUnsafe(sizeOf[Ptr[_]])
uv_poll_init(EventLoop.loop, data.atUnsafe(0), fd)
new Poll(data)
}
}
17 changes: 11 additions & 6 deletions core/src/main/scala/scala/scalanative/loop/Timer.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package scala.scalanative.loop

import scala.scalanative.libc.stdlib
import scala.concurrent.{Future, Promise}
import scala.concurrent.duration._
import LibUV._, LibUVConstants._
import scala.scalanative.unsafe.Ptr
import scala.scalanative.unsafe.{Ptr, sizeOf}
import scala.scalanative.runtime.BlobArray
import internals.HandleUtils

@inline final class Timer private (private val ptr: Ptr[Byte]) extends AnyVal {
@inline final class Timer private (private val data: BlobArray) extends AnyVal {
private def ptr = data.atUnsafe(0)
def clear(): Unit = {
uv_timer_stop(ptr)
HandleUtils.close(ptr)
Expand All @@ -29,17 +30,21 @@ object Timer {
repeat: Long,
callback: () => Unit
): Timer = {
val timerHandle = stdlib.malloc(uv_handle_size(UV_TIMER_T))
// GC managed memory, but scans only user data
val data = BlobArray.alloc(uv_handle_size(UV_TIMER_T).toInt)
data.setScannableLimitUnsafe(sizeOf[Ptr[_]])

val timerHandle = data.atUnsafe(0)
uv_timer_init(EventLoop.loop, timerHandle)
HandleUtils.setData(timerHandle, callback)
val timer = new Timer(timerHandle)
val timer = new Timer(data)
val withClearIfTimeout: () => Unit =
if (repeat == 0L) { () =>
{
callback()
timer.clear()
}
} else callback
HandleUtils.setData(timerHandle, withClearIfTimeout)
uv_timer_start(timerHandle, timeoutCB, timeout, repeat)
timer
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ package internals
import scala.scalanative.runtime._
import scala.scalanative.runtime.Intrinsics._
import scala.scalanative.unsafe.Ptr
import scala.scalanative.libc.stdlib
import LibUV._

private[loop] object HandleUtils {
private val references = new java.util.IdentityHashMap[Object, Int]()

@inline def getData[T <: Object](handle: Ptr[Byte]): T = {
// data is the first member of uv_loop_t
val ptrOfPtr = handle.asInstanceOf[Ptr[Ptr[Byte]]]
Expand All @@ -24,23 +21,16 @@ private[loop] object HandleUtils {
// data is the first member of uv_loop_t
val ptrOfPtr = handle.asInstanceOf[Ptr[Ptr[Byte]]]
if (obj != null) {
references.put(obj, references.get(obj) + 1)
val rawptr = castObjectToRawPtr(obj)
!ptrOfPtr = fromRawPtr[Byte](rawptr)
} else {
!ptrOfPtr = null
}
}
private val onCloseCB: CloseCB = (handle: UVHandle) => {
stdlib.free(handle)
}
@inline def close(handle: Ptr[Byte]): Unit = {
if (getData(handle) != null) {
uv_close(handle, onCloseCB)
val data = getData[Object](handle)
val current = references.get(data)
if (current > 1) references.put(data, current - 1)
else references.remove(data)
val data = getData[Object](handle)
if (data != null) {
uv_close(handle, null)
setData(handle, null)
}
}
Expand Down

0 comments on commit 3afc321

Please sign in to comment.