close the PTY master fd when the child exits naturally (T-360)

_reap() flipped _dead without releasing the master fd, and close()
short-circuits on _dead — so every naturally-exited child leaked its
fd and pty device for the life of the app. The reader isolate sends
EOF only after leaving its poll loop, so releasing the fd inside
_reap() cannot race the reader. Regression test counts /dev/ptmx
entries in /proc/self/fd across a natural exit; verified to fail
against the unfixed code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 00:18:47 +02:00
co-authored by Claude Fable 5
parent 647c22d32a
commit 466383671d
5 changed files with 53 additions and 0 deletions
+5
View File
@@ -444,6 +444,11 @@ class NativePty {
void _reap() {
if (_dead) return;
_dead = true;
// The reader isolate sends EOF only after exiting its poll loop, so
// nothing touches the master fd anymore. Release it here — close()
// short-circuits on _dead, so skipping this leaks the fd and its pty
// device for the life of the app on every natural child exit (T-360).
_nativeClose(_fd);
final s = calloc<ffi.Int32>();
_waitpid(pid, s, _kWnohang);
calloc.free(s);