`(*Instance).Close()` hangs if there is a concurrent `(*Instance).ReadLine` call
slingamn opened this issue · comments
I'm using the following test case with readline v1.5.1, go version go1.20 linux/amd64
:
package main
import (
"fmt"
"log"
"os"
"strings"
"time"
"github.com/chzyer/readline"
)
func main() {
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
rl, err := readline.New("> ")
if err != nil {
log.Fatal(err)
}
defer rl.Close()
log.SetOutput(rl.Stderr())
input := make(chan string)
go func() {
closed := false
for {
line, err := rl.Readline()
if err != nil {
fmt.Fprintln(os.Stderr, "error: failed to read new input line:", err.Error())
return
}
if !closed {
input <- line
}
if !closed && strings.ToLower(strings.TrimSpace(line)) == "quit" {
log.Printf("quit received\n")
closed = true
close(input)
}
}
}()
for {
line, ok := <- input
if !ok {
log.Printf("quit acknowledged, sleeping")
time.Sleep(time.Second)
log.Printf("done sleeping, exiting")
return
}
fmt.Fprintf(rl, "received %s\n", strings.TrimRight(line, "\r\n"))
}
}
If I enter quit
at the prompt and wait, I get the following output:
> quit
received quit
2023/02/06 01:19:18.052671 quit received
> 2023/02/06 01:19:18.053079 quit acknowledged, sleeping
2023/02/06 01:19:19.053285 done sleeping, exiting
but then the program hangs without exiting. SIGQUIT shows that the main goroutine is blocked in (*Instance).Close()
:
https://gist.github.com/slingamn/eb5bea5e623e848cd0903c29b155a0b3
It seems that this Wait()
call:
Line 228 in 7f93d88
is blocked by the failure of this select
to terminate:
Lines 120 to 125 in 7f93d88
My diagnosis is that FillableStdin
fails to pass the Close()
call through to CancelableStdin
, which would stop the select. This patch fixes the issue, but may introduce other issues:
Thanks very much for your time.