I used asyncdispatch/asyncnet to implement my networking (currently).
I've got a problem "closing the server socket".
Initially I wrote something like this:
proc initCluster(port: Port, address: string) {.async.} =
## Initiates the cluster networking.
echo("Opening server socket...", getThreadId())
var server = newAsyncSocket()
server.setSockOpt(OptReuseAddr, true)
server.bindAddr(port, address)
server.listen()
while true:
let peer = await server.accept()
asyncCheck processPeer(peer)
Idk if it worked, because I haven't tried it yet, but it ran without error.
Then I decided to add code to gracefully terminate the process, like this:
var server: AsyncSocket
## The server socket
proc initCluster(port: Port, address: string) {.async.} =
## Initiates the cluster networking.
echo("Opening server socket...")
server = newAsyncSocket()
server.setSockOpt(OptReuseAddr, true)
server.bindAddr(port, address)
server.listen()
while (server != nil):
let peer = await server.accept()
asyncCheck processPeer(peer)
proc deinitCluster() {.async.} =
## De-initiates the cluster networking.
if server != nil:
echo("Closing server socket...")
try:
server.close()
except:
discard
server = nil
else:
echo("Server socket not open.")
But was told by the compiler that "initCluster()" is now not GC-safe, as I use a global ref to a GCed object.
So my thought was, well, with asyncdispatch, "everything" is meant to run in the same thread (which I was obviously wrong about), somehow without blocking, so I can just change "server" to a "{.threadvar.}", to make the compiler happy. If I do, the compiler is happy, but "deinitCluster()" does nothing, because it doesn't actually run in the same thread as "initCluster()" (verified by using getThreadId() in the output; "initCluster()" runs in thread #2940, and "deinitCluster()" runs in thread #4860).
So, what is the correct way to close my async server socket?
And, more generally, does that mean I still need to use shared-heap to pass messages/signals across async tasks? Is there a way to say "run this async task in the same thread as that other async task"?
In your snippet you don't have to split up this in 2 different procs. And even if you do, you can pass server as a parameter.
And, more generally, does that mean I still need to use shared-heap to pass messages/signals across async tasks?
I think you can also use (async) queues instead.
Is there a way to say "run this async task in the same thread as that other async task"?
Yes, you can access the global in a section like
proc foo =
...
{.gcsafe.}:
myglobal.add(stuff)
@Araq I think my example didn't clearly show that the issue was that the decision to close the server socket happens "independently" from the code that accepts incoming connections. I've now move the "close code" in the same proc as the "open code" (as you suggested), and use regular polling on a volatile flag to tell the proc to terminate.
I don't remeber seeing that last "{.gcsafe.}:" syntax before. I think that might solve some of my problems. :)