I've simplified my issues down to a simple test case where I use spawn() to run a proc that completes a future. It feels like it ought to be straightforward:
import asyncdispatch
import threadpool
let future = newFuture[string]()
let complete_future = proc (future_to_complete:Future[string]) =
future_to_complete.complete("woah")
spawn complete_future(future)
let str = waitFor future
echo str
But on the waitFor line I get the following exception:
Error: unhandled exception: No handles or timers registered in dispatcher. [ValueError]
I'm a total newbie so I'm sure I'm missing something key here. But what is it?
See https://github.com/nim-lang/Nim/issues/14564.
TL;DR: As the error suggests, you don't have any IO operations or timers in progress so the event loop has nothing to do and so you get this error (if you didn't get this error your program would busy wait, leading to 100% cpu usage)
What is your bigger program? What does it do? If you're not doing any IO operations then you might not need to be using futures at all.
Ah, that does make sense. But I'm curious what other way I might achieve what I'm trying to do. In short, I'm planning on interfacing with a C library, a JavaScript runtime. It requires that it be used on the same thread at all times, so I'm using the channels and threads modules to enable that. My idea was that I'd have an outgoing channel and an incoming channel and pass references to a future through them. A very simplified version here:
import asyncdispatch
import threadpool
type ChannelMsg = object
future: ref Future[string]
var from_thread: Channel[ChannelMsg]
var to_thread: Channel[ChannelMsg]
from_thread.open()
to_thread.open()
proc run_on_thread() {.thread.} =
while true:
let incoming = to_thread.recv()
echo "Received on thread, sending back"
from_thread.send(ChannelMsg(future: incoming.future))
var thread: Thread[void]
createThread(thread, run_on_thread)
proc run_outside_thread() =
while true:
let outgoing = from_thread.recv()
echo "Received off thread"
outgoing.future[].complete("DONE")
spawn run_outside_thread()
var future: ref Future[string] = new(ref Future[string])
future[] = newFuture[string]()
echo "Sending to thread"
to_thread.send(ChannelMsg(future: future))
let result = waitFor future[]
echo result
And it very nearly works! The output is:
Sending to thread
Received on thread, sending back
Received off thread
[...]
Error: unhandled exception: No handles or timers registered in dispatcher. [ValueError]
...which makes total sense given what you're saying about IO and timers. Is there an entirely different approach I should be taking here?