1
1
mirror of https://github.com/walles/moar.git synced 2024-11-22 03:14:56 +03:00

Fix a hang

Before this change, if select() returned EINTR we would hang.

This change:
* Makes us ignore EINTRs and try again
* Makes sure to not hang, even if the twin main loop would fail for some
  other reason.
This commit is contained in:
Johan Walles 2024-07-15 20:30:47 +02:00
parent 27817741fc
commit f4fed36bfd
4 changed files with 35 additions and 3 deletions

View File

@ -28,4 +28,4 @@ fi
go build -trimpath -ldflags="-s -w -X main.versionString=${VERSION}" -o "${BINARY}" go build -trimpath -ldflags="-s -w -X main.versionString=${VERSION}" -o "${BINARY}"
# Alternative build line, if you want to attach to the running process in the Go debugger: # Alternative build line, if you want to attach to the running process in the Go debugger:
# go build -ldflags="-X main.versionString=${VERSION}" -gcflags='-N -l' -o "${BINARY}" # go build -ldflags="-X main.versionString=${VERSION}" -gcflags="all=-N -l" -o "${BINARY}"

View File

@ -55,6 +55,10 @@ func (r *interruptableReaderImpl) Interrupt() {
r.shutdownRequested.Store(true) r.shutdownRequested.Store(true)
} }
func (r *interruptableReaderImpl) Close() error {
return nil
}
func newInterruptableReader(base *os.File) (interruptableReader, error) { func newInterruptableReader(base *os.File) (interruptableReader, error) {
return &interruptableReaderImpl{base: base}, nil return &interruptableReaderImpl{base: base}, nil
} }

View File

@ -24,6 +24,19 @@ type interruptableReaderImpl struct {
} }
func (r *interruptableReaderImpl) Read(p []byte) (n int, err error) { func (r *interruptableReaderImpl) Read(p []byte) (n int, err error) {
for {
n, err = r.read(p)
if err == syscall.EINTR {
// Not really a problem, we can get this on window resizes for
// example, just try again.
continue
}
return
}
}
func (r *interruptableReaderImpl) read(p []byte) (n int, err error) {
// "This argument should be set to the highest-numbered file descriptor in // "This argument should be set to the highest-numbered file descriptor in
// any of the three sets, plus 1. The indicated file descriptors in each set // any of the three sets, plus 1. The indicated file descriptors in each set
// are checked, up to this limit" // are checked, up to this limit"
@ -55,7 +68,7 @@ func (r *interruptableReaderImpl) Read(p []byte) (n int, err error) {
err = io.EOF err = io.EOF
// Let Interrupt() know we're done // Let Interrupt() know we're done
r.interruptionComplete <- struct{}{} r.Close()
return return
} }
@ -77,10 +90,18 @@ func (r *interruptableReaderImpl) Interrupt() {
<-r.interruptionComplete <-r.interruptionComplete
} }
func (r *interruptableReaderImpl) Close() error {
select {
case r.interruptionComplete <- struct{}{}:
default:
}
return nil
}
func newInterruptableReader(base *os.File) (interruptableReader, error) { func newInterruptableReader(base *os.File) (interruptableReader, error) {
reader := interruptableReaderImpl{ reader := interruptableReaderImpl{
base: base, base: base,
interruptionComplete: make(chan struct{}), interruptionComplete: make(chan struct{}, 1),
} }
pr, pw, err := os.Pipe() pr, pw, err := os.Pipe()
if err != nil { if err != nil {

View File

@ -74,6 +74,12 @@ type interruptableReader interface {
// Interrupt unblocks the read call, either now or eventually. // Interrupt unblocks the read call, either now or eventually.
Interrupt() Interrupt()
// Close() should be called after you are done with the interruptableReader.
//
// It will not close the underlying reader, but it will prevent Interrupt()
// from hanging if called after a failure in the screen mainLoop().
Close() error
} }
type UnixScreen struct { type UnixScreen struct {
@ -366,6 +372,7 @@ func (screen *UnixScreen) ShowCursorAt(column int, row int) {
func (screen *UnixScreen) mainLoop() { func (screen *UnixScreen) mainLoop() {
defer func() { defer func() {
screen.ttyInReader.Close()
log.Debug("Twin screen main loop done") log.Debug("Twin screen main loop done")
}() }()