mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-10 15:04:59 +00:00
Fix ioselectors_kqueue raising wrong exceptions (#24079)
kqueue will remove pipes automatically if their read end is closed. Unfortunately this means that trying to unregister it (which is necessary to clean up resources & for consistency with other ioselectors implementations) will set an ENOENT error, which currently raises an exception. (ETA: in other words, it is currently impossible to call unregister on a pipe fd without potentially getting the selector into an invalid state on platforms with kqueue.) Avoid this issue by ignoring ENOENT errors returned from kqueue. (Tested on FreeBSD. I added a test case to the tioselectors file; the seemingly unrelated change is to fix a race condition that doesn't appear on Linux, so that it would run my code too.)
This commit is contained in:
@@ -194,7 +194,9 @@ when hasThreadSupport:
|
||||
if s.changesLength > 0:
|
||||
if kevent(s.kqFD, addr(s.changes[0]), cint(s.changesLength),
|
||||
nil, 0, nil) == -1:
|
||||
raiseIOSelectorsError(osLastError())
|
||||
let res = osLastError()
|
||||
if cint(res) != ENOENT: # ignore pipes whose read end is closed
|
||||
raiseIOSelectorsError(res)
|
||||
s.changesLength = 0
|
||||
else:
|
||||
template modifyKQueue[T](s: Selector[T], nident: uint, nfilter: cshort,
|
||||
@@ -211,7 +213,9 @@ else:
|
||||
if length > 0:
|
||||
if kevent(s.kqFD, addr(s.changes[0]), length,
|
||||
nil, 0, nil) == -1:
|
||||
raiseIOSelectorsError(osLastError())
|
||||
let res = osLastError()
|
||||
if cint(res) != ENOENT: # ignore pipes whose read end is closed
|
||||
raiseIOSelectorsError(res)
|
||||
s.changes.setLen(0)
|
||||
|
||||
proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
|
||||
|
||||
@@ -58,7 +58,11 @@ when not defined(windows):
|
||||
registerHandle(selector, client_socket, {Event.Write}, 0)
|
||||
|
||||
freeAddrInfo(aiList)
|
||||
discard selector.select(100)
|
||||
|
||||
# make sure both sockets are selected
|
||||
var nevs = 0
|
||||
while nevs < 2:
|
||||
nevs += selector.select(100).len
|
||||
|
||||
var sockAddress: SockAddr
|
||||
var addrLen = sizeof(sockAddress).Socklen
|
||||
@@ -427,6 +431,20 @@ when not defined(windows):
|
||||
doAssert(res[0].fd == dirfd and
|
||||
{Event.Vnode, Event.VnodeDelete} <= res[0].events)
|
||||
|
||||
proc pipe_test(): bool =
|
||||
# closing the read end of a pipe will result in it automatically
|
||||
# being removed from the kqueue; make sure no exception is raised
|
||||
var s = newSelector[int]()
|
||||
var fds: array[2, cint]
|
||||
discard pipe(fds)
|
||||
s.registerHandle(fds[1], {Write}, 0)
|
||||
discard close(fds[0])
|
||||
let res = s.select(-1)
|
||||
doAssert(res.len == 1)
|
||||
s.unregister(fds[1])
|
||||
discard close(fds[1])
|
||||
return true
|
||||
|
||||
when hasThreadSupport:
|
||||
|
||||
var counter = 0
|
||||
@@ -468,6 +486,7 @@ when not defined(windows):
|
||||
when defined(macosx) or defined(freebsd) or defined(openbsd) or
|
||||
defined(netbsd):
|
||||
processTest("File notification test...", vnode_test())
|
||||
processTest("Pipe test...", pipe_test())
|
||||
echo("All tests passed!")
|
||||
else:
|
||||
import nativesockets, winlean, os, osproc
|
||||
|
||||
Reference in New Issue
Block a user