~# ls -l /proc/18549/fd/ total 0 lr-x------ 1 root root 64 Jun 26 18:35 0 -> /dev/null lrwx------ 1 root root 64 Jun 26 18:35 1 -> 'socket:[72405]' lrwx------ 1 root root 64 Jun 26 18:38 11 -> 'anon_inode:[signalfd]' <--- l-wx------ 1 root root 64 Jun 26 18:39 12 -> 'pipe:[73815]' lr-x------ 1 root root 64 Jun 26 18:40 13 -> 'pipe:[73816]' lr-x------ 1 root root 64 Jun 26 18:40 15 -> 'pipe:[73817]' lrwx------ 1 root root 64 Jun 26 18:35 2 -> 'socket:[72405]' lrwx------ 1 root root 64 Jun 26 18:35 3 -> 'anon_inode:[eventpoll]' lrwx------ 1 root root 64 Jun 26 18:35 4 -> 'socket:[72410]' lrwx------ 1 root root 64 Jun 26 18:36 5 -> 'anon_inode:[signalfd]' <--- lrwx------ 1 root root 64 Jun 26 18:35 6 -> 'anon_inode:[signalfd]' <--- lrwx------ 1 root root 64 Jun 26 18:35 7 -> 'anon_inode:[signalfd]' <--- lrwx------ 1 root root 64 Jun 26 18:37 8 -> 'anon_inode:[signalfd]' <--- lrwx------ 1 root root 64 Jun 26 18:35 9 -> 'anon_inode:[signalfd]' <--- I don't know where to unregister them since asyncdispatch.addProcess() does not return the file descriptor of the signalfd registered by registerProcess(). If there is a way to deal with this, I would appreciate it if you could let me know.
The code to be reproduced looks like this.
import std/asyncdispatch
import std/strformat
import std/strutils
import asynctools
proc close(p: AsyncProcess) =
if not p.inputHandle.isNil:
p.inputHandle.close()
if not p.outputHandle.isNil:
p.outputHandle.close()
if not p.errorHandle.isNil:
p.errorHandle.close()
proc exec() {.async.} =
const opts = {poUsePath}
let cmdline = "sleep 30"
let cmds = cmdline.split()
let child = startProcess(cmds[0], args = cmds[1..^1], options = opts)
await sleepAsync(100)
child.terminate()
let exitcode = await child.waitForExit()
echo &"exitcode: {exitcode}"
child.close()
proc main() {.async.} =
for i in 0..<10:
await exec()
await sleepAsync(600 * 1000)
waitFor main()
Its sad but asynctools has a lot of bugs related to processes and signals, and i'm not going to maintain it. If you need asynchronous way of spawning processes please consider usage of https://github.com/status-im/nim-chronos . We just added async process module into it with a lot of fixes made.
It uses just one signalfd descriptor to handle all the processes and signals so it will not leak.
Coexistence seems difficult.
I applied the following patch to asyncdispatch and the leak seems to be fixed.
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 24c1ec3d5..3cceed1d3 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -1436,6 +1436,8 @@ else:
if (customSet * events) != {}:
isCustomEvent = true
processCustomCallbacks(p, fd)
+ if events.contains(Event.Process):
+ p.selector.unregister(fd.int)
result = true
# because state `data` can be modified in callback we need to update
I tried really hard to get asyncdispatch to work well for me, but the sad reality is that asyncdispatch fundamentally sucks. You should bite the bullet now and switch to Chronos before you further entrench yourself into the asyncdispatch ecosystem.
Jester is slow and httpbeast (the server it runs on) is apt to leak memory under ORC, as does pretty much every other thing written with asyncdispatch. Please don't keep trying to get asyncdispatch to work for you.
Hmmm. If you know of any samples of how to port a library built using existing asyncdispatch to chronos, could you please let me know?
I want to use MQTT(nmqtt) and serialport (serial).