From 6842e86706dd3bfcff546fc0eaa25e390bf83af7 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Fri, 22 May 2026 08:01:52 +0200 Subject: [PATCH] Handle POLLNVAL in Terminal::wait_for_input `poll` reports `POLLNVAL` immediately when a watched fd has been closed or otherwise become invalid (for example, after a PTY teardown). The previous masks only covered `POLLIN | POLLHUP | POLLERR`, so neither branch fired and the `while (true)` loop spun at 100% CPU. Add `POLLNVAL` to the mask so the invalid fd is treated the same as a terminal/error condition - we read what we can from the interrupt pipe or surface a key-press event for the input fd, which lets the caller unwind cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/terminal.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/terminal.cxx b/src/terminal.cxx index c30a415..550bab5 100644 --- a/src/terminal.cxx +++ b/src/terminal.cxx @@ -653,7 +653,8 @@ Terminal::EVENT_TYPE Terminal::wait_for_input( int long timeout_ ) { if ( err == 0 ) { return ( EVENT_TYPE::TIMEOUT ); } - if ( fds[1].revents & ( POLLIN | POLLHUP | POLLERR ) ) { + short const ready_mask( POLLIN | POLLHUP | POLLERR | POLLNVAL ); + if ( fds[1].revents & ready_mask ) { char data( 0 ); static_cast( read( _interrupt[0], &data, 1 ) == 1 ); if ( data == 'k' ) { @@ -666,7 +667,7 @@ Terminal::EVENT_TYPE Terminal::wait_for_input( int long timeout_ ) { return ( EVENT_TYPE::RESIZE ); } } - if ( fds[0].revents & ( POLLIN | POLLHUP | POLLERR ) ) { + if ( fds[0].revents & ready_mask ) { return ( EVENT_TYPE::KEY_PRESS ); } }