Cancel the event reader before closing the descriptor
`deinit` closed the inotify descriptor while its dispatch source was still active. The kernel drops the epoll registration on close, but libdispatch keeps its own; an instance created afterwards that reuses the descriptor number could inherit that stale state and never receive events. Roughly one test run in three lost a single event this way. The reader is now cancelled in `deinit` and the descriptor closed in its cancel handler, as libdispatch requires.
This commit is contained in:
@@ -5,7 +5,7 @@ public actor Inotify {
|
||||
private let fd: CInt
|
||||
private var excludedItemNames: Set<String> = []
|
||||
private var watches = InotifyWatchManager()
|
||||
private var eventReader: any DispatchSourceRead
|
||||
private nonisolated(unsafe) let eventReader: any DispatchSourceRead
|
||||
private nonisolated let eventStream: AsyncStream<RawInotifyEvent>
|
||||
public nonisolated var events: AsyncCompactMapSequence<AsyncStream<RawInotifyEvent>, InotifyEvent> {
|
||||
self.eventStream.compactMap(self.transform(_:))
|
||||
@@ -73,7 +73,11 @@ public actor Inotify {
|
||||
}
|
||||
|
||||
deinit {
|
||||
cinotify_deinit(self.fd)
|
||||
// The file descriptor is closed by the reader's cancel handler once
|
||||
// libdispatch has unregistered it. Closing it here would leave a
|
||||
// registration behind that a later instance reusing the descriptor
|
||||
// number could inherit, silently losing its events.
|
||||
self.eventReader.cancel()
|
||||
}
|
||||
|
||||
private func transform(_ rawEvent: RawInotifyEvent) async -> InotifyEvent? {
|
||||
@@ -112,6 +116,7 @@ public actor Inotify {
|
||||
}
|
||||
}
|
||||
reader.setCancelHandler {
|
||||
cinotify_deinit(fd)
|
||||
continuation.finish()
|
||||
}
|
||||
reader.activate()
|
||||
|
||||
Reference in New Issue
Block a user