@ 261102.1

260910.1 · 1578b7c3c33622ee2307e2173321a66eec5a7aa9

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

reading declarations

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"

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 {