Nim Version
Nim Compiler Version 2.3.1 [MacOSX: arm64] (devel, also reproduces with 2.2.10)
Description
A PDispatcher's selector owns an OS resource: an epoll fd (Linux), a kqueue
fd plus a helper socket (macOS/BSD), or an IO completion port handle
(Windows). There is no way to release it:
asyncdispatch exposes no close/destroy for PDispatcher
(std/selectors has a working close(), but it is unreachable through
the asyncdispatch API)
- no destructor/finalizer is attached, so a garbage-collected dispatcher
leaks the fd
- nothing closes the thread-local dispatcher when a thread terminates
Every dropped dispatcher therefore leaks at least one fd. The practical
worst case is thread pools: any thread that touches async (e.g. a
waitFor in a worker) permanently leaks one fd when it exits, so
long-running servers with short-lived worker threads exhaust the fd limit.
import std/[asyncdispatch, os]
proc openFds(): int =
for _ in walkDir("/proc/self/fd"): inc result
echo "fds at start: ", openFds()
for i in 0..<10:
let d = newDispatcher()
setGlobalDispatcher(d)
waitFor sleepAsync(1) # force selector creation/use
setGlobalDispatcher(nil)
GC_fullCollect()
echo "fds after 10 dispatcher cycles: ", openFds()
Compiled and run on Linux with:
nim c --mm:orc -d:useMalloc --debugger:native displeak.nim
valgrind --track-fds=yes ./displeak
Output (program + valgrind fd tracking):
fds at start: 27 # baseline includes valgrind's own fds
fds after 10 dispatcher cycles: 37
==440== FILE DESCRIPTORS: 29 open (3 std) at exit.
==440== Open file descriptor 28:
==440== at 0x4973487: epoll_create1 (syscall-template.S:120)
==440== by 0x1200A6: selectors::newSelector (ioselectors_epoll.nim:86)
==440== by 0x123ECC: asyncdispatch::newDispatcher (asyncdispatch.nim:1209)
==440== by 0x127488: NimMainModule (displeak.nim:8)
==440== by 0x127599: NimMainInner (system.nim:959)
==440== by 0x1275AC: NimMain (system.nim:970)
==440== by 0x1275CE: main (system.nim:978)
[... 9 more identical entries, fds 19-27, one per loop iteration ...]
Current Output
Expected Output
Known Workarounds
No response
Additional Information
No response
Nim Version
Nim Compiler Version 2.3.1 [MacOSX: arm64] (devel, also reproduces with 2.2.10)
Description
A
PDispatcher's selector owns an OS resource: an epoll fd (Linux), a kqueuefd plus a helper socket (macOS/BSD), or an IO completion port handle
(Windows). There is no way to release it:
asyncdispatchexposes noclose/destroyforPDispatcher(
std/selectorshas a workingclose(), but it is unreachable throughthe asyncdispatch API)
leaks the fd
Every dropped dispatcher therefore leaks at least one fd. The practical
worst case is thread pools: any thread that touches async (e.g. a
waitForin a worker) permanently leaks one fd when it exits, solong-running servers with short-lived worker threads exhaust the fd limit.
Compiled and run on Linux with:
Output (program + valgrind fd tracking):
Current Output
Expected Output
Known Workarounds
No response
Additional Information
No response