I'm porting libuv code to nim (I want to use it with asyncdispatch). The library has methods with C callbacks (I cannot use closures directly). I want to pass object of type Future[void] to callback. Future is ref of object. I can use public user field of type (void*) in libuv's handle to do it.
I know I can use global dictionary where keys are libuv's handles and values are Future[void] objects. It is safe. And probably I'll do it like this.
But this code also works:
proc sleepAsync*(loop : PLoop, ms : int64) : Future[void] =
let pTimer = cast[PTimer](alloc(sizeof(Timer)))
discard loop.timer_init(pTimer)
result = newFuture[void]("sleepAsync")
#GCref(result)
pTimer.data = addr result[]
discard pTimer.timer_start(TimerProc callback, ms, 0)
with:
proc callback(pTimer : PTimer, status : cint) {.cdecl.} =
var future = (cast[Future[void]](pTimer.data))
close(cast[PHandle](pTimer), CloseProc closeProc)
future.complete()
#GCunref(future)
I'm not an expert in Nim, but I've got a feeling this is not a safe thing to do (it is a conversion from ptr to ref type?):
var future = (cast[Future[void]](pTimer.data))
It works because it compiles to:
future = ((FutureHEX3Aobjecttype181302*) ((*ptimer).data));
and:
pTimer.data = addr result[]
compiles to:
(*ptimer).data = ((void*) (result));
But can I depend on this behaviour?
And I've got one more question. Let's assume that my code is correct. Should I also call GCref / GCunref like in commented out lines?
Thank you, Marek
Looks safe if you do the GCref/GCunref pair, but I only skimmed your code. ;-)
It's unsafe if libuv runs the callbacks on some thread pool which iirc is exactly what's happening and which is why Nim doesn't use libuv.
Hello, Marek. How is your work?
Can you give a link to your repo?
Hi Karatin,
I don't have a repo for it. I've got only two files that succeeded as PoC.
# main.nim
import libuv, times, os
import asyncdispatch except sleepAsync
import sleepAsync
let loop = default_loop()
proc main() {.async.} =
echo "Sleep Begin"
echo "My formatted time: ", format(getLocalTime(getTime()), "d MMMM yyyy HH:mm:ss")
await loop.sleepAsync(2000)
echo "Sleep Done"
echo "My formatted time: ", format(getLocalTime(getTime()), "d MMMM yyyy HH:mm:ss")
asyncCheck main()
discard loop.run(RUN_DEFAULT)
# sleepAsync.nim
import libuv, times, os
import asyncdispatch except sleepAsync
proc closeProc(pHandle : PHandle) {.cdecl.} =
dealloc(pHandle)
proc callback(pTimer : PTimer, status : cint) {.cdecl.} =
var future = (cast[Future[void]](pTimer.data))
close(cast[PHandle](pTimer), CloseProc closeProc)
future.complete()
GCunref(future)
proc sleepAsync*(loop : PLoop, ms : int64) : Future[void] =
let pTimer = cast[PTimer](alloc(sizeof(Timer)))
discard loop.timer_init(pTimer)
result = newFuture[void]("sleepAsync")
GCref(result)
pTimer.data = addr result[]
discard pTimer.timer_start(TimerProc callback, ms, 0)
You can use it as a starting point and add all other libuv methods easily. The idea is very simple. Async method returns Future[T]. And when async operation finish you call future.complete() method.
My intention is not to build complete async library right now. I will add methods as I need them. My intention is to start UI framework. I'm just experimenting.
For that I needed a good foundation of event's loop (dipatcher) with await support. But libuv is missing input events (touch/mouse/keyboard). So I'm trying to merge it with GLFW now. Unfortunately, I do not see how to do it on the same thread. Before libuv 2.0 this is possible only in Unix ( https://github.com/kkaefer/gluv ). Anyway, I would like my engine to work in retained mode with separate rendering thread for performance reasons. So I should be fine with GLFW working in another thread anyway (I do not need to use libuv there). However, I may want to redirect input events to main/control thread.
To implement retained mode rendering it would also be nice to have immutable collections (to modify controls state during rendering previous state in parallel). So I may be experimenting with them as well. Fortunately, there is source code for .NET version with all nice data structures and algorithms - it just need to be translated.