Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(*Instance).Close() hangs if there is a concurrent (*Instance).ReadLine call #217

Open
slingamn opened this issue Feb 6, 2023 · 0 comments

Comments

@slingamn
Copy link
Collaborator

slingamn commented Feb 6, 2023

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:

t.wg.Wait()

is blocked by the failure of this select to terminate:

readline/std.go

Lines 120 to 125 in 7f93d88

select {
case <-c.notify:
return c.read, c.err
case <-c.stop:
return 0, io.EOF
}

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:

slingamn@4c5bb20

Thanks very much for your time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant