Skip to content

Commit a754b6e

Browse files
Merge pull request #401 from ipodishima/fix/ev_vanished
Fix EV_VANISHED crash by closing DispatchIO before file descriptors
2 parents 2bd6cf3 + d63aa82 commit a754b6e

1 file changed

Lines changed: 30 additions & 14 deletions

File tree

Sources/SwiftTerm/LocalProcess.swift

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,15 @@ public class LocalProcess {
269269

270270
// Mark as running and set up I/O for reading from master fd first
271271
running = true
272-
io = DispatchIO(type: .stream, fileDescriptor: master, queue: dispatchQueue, cleanupHandler: { _ in })
272+
// Capture FD values for cleanup handler to close them safely after DispatchIO is done
273+
let masterToClose = master
274+
let slaveToClose = slave
275+
io = DispatchIO(type: .stream, fileDescriptor: master, queue: dispatchQueue, cleanupHandler: { _ in
276+
// Close file descriptors after DispatchIO has finished with them
277+
// This prevents EV_VANISHED crash by ensuring proper cleanup order
278+
close(masterToClose)
279+
close(slaveToClose)
280+
})
273281
guard let io else {
274282
return
275283
}
@@ -362,7 +370,13 @@ public class LocalProcess {
362370
running = true
363371
self.childfd = childfd
364372
self.shellPid = shellPid
365-
io = DispatchIO(type: .stream, fileDescriptor: childfd, queue: dispatchQueue, cleanupHandler: { x in })
373+
// Capture FD value for cleanup handler to close it safely after DispatchIO is done
374+
let fdToClose = childfd
375+
io = DispatchIO(type: .stream, fileDescriptor: childfd, queue: dispatchQueue, cleanupHandler: { _ in
376+
// Close file descriptor after DispatchIO has finished with it
377+
// This prevents EV_VANISHED crash by ensuring proper cleanup order
378+
close(fdToClose)
379+
})
366380
guard let io else {
367381
return
368382
}
@@ -379,22 +393,24 @@ public class LocalProcess {
379393
task.cancel()
380394
subprocessTask = nil
381395
}
382-
383-
// Close file descriptors
384-
if masterFd != -1 {
385-
close(masterFd)
386-
masterFd = -1
387-
}
388-
if slaveFd != -1 {
389-
close(slaveFd)
390-
slaveFd = -1
391-
}
396+
397+
// Set FD markers to -1 (actual FDs are closed by DispatchIO cleanup handler)
398+
masterFd = -1
399+
slaveFd = -1
392400
#endif
393-
401+
402+
// Close DispatchIO - this will trigger the cleanup handler which closes file descriptors
403+
// The cleanup handler ensures FDs are closed AFTER DispatchIO is done with them,
404+
// preventing "BUG IN CLIENT OF LIBDISPATCH: Unexpected EV_VANISHED" crash
405+
// This applies to both Subprocess and forkpty paths
406+
io?.close()
407+
io = nil
408+
childfd = -1
409+
394410
if shellPid != 0 {
395411
kill(shellPid, SIGTERM)
396412
}
397-
413+
398414
running = false
399415
}
400416

0 commit comments

Comments
 (0)