OK, I've got a WebSocket-based client running on a single thread with async/await and asyncdispatch. Now I want this to interact with other threads — for example, I'd like another thread to be able to post a message for the WebSocket to send.
The sticking point is how to wake up the async dispatcher on its own thread. It seems like I need to have an async message queue, and a loop on my existing thread that pulls Futures from the channel and awaits them, much like the loop that reads from the WebSocket. I know how to build that ... but the Future is going to be completed on another thread, which means its callback will run on that other thread via callSoon, and it'll be handled by the wrong dispatcher.
It looks like AsyncEvent is the tool to handle this ... ? I can create one, add it to my client thread's dispatcher, and then trigger it from arbitrary threads after I add something to my queue, and my callback will run on the client thread and can process the queue.
I've seen the open issues with creating large numbers of AsyncEvents (they consume file descriptors) but that shouldn't be a problem for now because I believe I only need one. Even if I start doing async/await stuff on multiple threads, I should only need one per thread.
Does this make sense? Or is there a better mechanism? (I'm not considering Weave, for now, because it looks like it's oriented toward finer-grained parallelism and is probably more complex than I need.)
I'm not considering Weave, for now, because it looks like it's oriented toward finer-grained parallelism and is probably more complex than I need.
Weave is indeed focused on CPU-bound workloads and not IO workloads, i.e. it optimize for throughput and not latency.
Right now, the most straight forward way to achieve this is by manually creating an asyncEvent that you can pass to the other thread. That secondary thread can then use the asyncEvent to trigger a wakeup on the event loop.
@see here for an example: https://github.com/nim-lang/Nim/issues/11564#issuecomment-539317277
Note: example is about reading files, but the same pattern can easily be adapted to other use cases.
Thanks for the example! I'm doing it slightly differently but using your basic technique.
Next roadblock is that FlowVar doesn't seem compatible with ORC (which I need so I can pass object graphs across threads):
nim-1.2.2/lib/pure/concurrency/threadpool.nim(214, 6) Error: cannot bind another '=destroy' to: FlowVarObj; previous declaration was constructed here implicitly: nim-1.2.2/lib/pure/concurrency/threadpool.nim(211, 48)
Feedback is more than welcome.