1
1
mirror of https://github.com/walles/moar.git synced 2024-11-21 16:04:20 +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}"
# 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)
}
func (r *interruptableReaderImpl) Close() error {
return nil
}
func newInterruptableReader(base *os.File) (interruptableReader, error) {
return &interruptableReaderImpl{base: base}, nil
}

View File

@ -24,6 +24,19 @@ type interruptableReaderImpl struct {
}
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
// any of the three sets, plus 1. The indicated file descriptors in each set
// are checked, up to this limit"
@ -55,7 +68,7 @@ func (r *interruptableReaderImpl) Read(p []byte) (n int, err error) {
err = io.EOF
// Let Interrupt() know we're done
r.interruptionComplete <- struct{}{}
r.Close()
return
}
@ -77,10 +90,18 @@ func (r *interruptableReaderImpl) Interrupt() {
<-r.interruptionComplete
}
func (r *interruptableReaderImpl) Close() error {
select {
case r.interruptionComplete <- struct{}{}:
default:
}
return nil
}
func newInterruptableReader(base *os.File) (interruptableReader, error) {
reader := interruptableReaderImpl{
base: base,
interruptionComplete: make(chan struct{}),
interruptionComplete: make(chan struct{}, 1),
}
pr, pw, err := os.Pipe()
if err != nil {

View File

@ -74,6 +74,12 @@ type interruptableReader interface {
// Interrupt unblocks the read call, either now or eventually.
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 {
@ -366,6 +372,7 @@ func (screen *UnixScreen) ShowCursorAt(column int, row int) {
func (screen *UnixScreen) mainLoop() {
defer func() {
screen.ttyInReader.Close()
log.Debug("Twin screen main loop done")
}()