From 1578b7c3c33622ee2307e2173321a66eec5a7aa9 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Thu, 10 Sep 2026 17:56:15 +0900 Subject: [PATCH] Fix spurious exit while waiting for a key with --listen - getch() charged EINTR against a 100 iteration limit meant for read attempts, and the count only reset once a byte was read - So signals arriving during an idle prompt drained it, and running out returned getCharError, which became Event{Fatal} and exit status 2 - Only the cancellable path is affected, hence only --listen - select() is not restarted by SA_RESTART, unlike the blocking read on the non-cancellable path, so it is the only one that sees EINTR - waitReadable() already retries EINTR without a limit Fix #4917 --- src/tui/light.go | 1 - src/tui/light_unix.go | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tui/light.go b/src/tui/light.go index bbc50dc4..5ce64010 100644 --- a/src/tui/light.go +++ b/src/tui/light.go @@ -27,7 +27,6 @@ const ( queryTimeout = 500 * time.Millisecond maxInputBuffer = 1024 * 1024 escapeLookback = 256 - maxSelectTries = 100 ) const DefaultTtyDevice string = "/dev/tty" diff --git a/src/tui/light_unix.go b/src/tui/light_unix.go index 15e50a96..3b8b1dec 100644 --- a/src/tui/light_unix.go +++ b/src/tui/light_unix.go @@ -240,7 +240,7 @@ func (r *LightRenderer) getch(cancellable bool, nonblock bool) (int, getCharResu }() cancelFd := int(rpipe.Fd()) - for range maxSelectTries { + for { var rfds unix.FdSet limit := len(rfds.Bits) * unix.NFDBITS if fd >= limit || cancelFd >= limit { @@ -251,6 +251,8 @@ func (r *LightRenderer) getch(cancellable bool, nonblock bool) (int, getCharResu rfds.Set(cancelFd) _, err := unix.Select(max(fd, cancelFd)+1, &rfds, nil, nil, nil) if err != nil { + // An interrupted wait is not a failed read, so it must not count + // against anything. Retry until the fd is ready or the wait fails. if err == syscall.EINTR { continue } @@ -265,7 +267,6 @@ func (r *LightRenderer) getch(cancellable bool, nonblock bool) (int, getCharResu return getter() } } - return 0, getCharError } func (r *LightRenderer) Size() TermSize {