From 42e77e2a699ab0eb2f27fe8cde6f4b7f6eef225a Mon Sep 17 00:00:00 2001 From: Patrick Devine Date: Wed, 14 Feb 2024 21:28:35 -0800 Subject: [PATCH] handle race condition while setting raw mode in windows (#2509) --- readline/readline.go | 36 ++++++++++++++++++++++++++++-------- readline/readline_unix.go | 5 +++-- readline/readline_windows.go | 2 +- readline/term.go | 5 +++-- readline/term_windows.go | 5 +++-- 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/readline/readline.go b/readline/readline.go index 202d9fa7..8ba7d89c 100644 --- a/readline/readline.go +++ b/readline/readline.go @@ -32,6 +32,8 @@ func (p *Prompt) placeholder() string { type Terminal struct { outchan chan rune + rawmode bool + termios any } type Instance struct { @@ -60,6 +62,16 @@ func New(prompt Prompt) (*Instance, error) { } func (i *Instance) Readline() (string, error) { + if !i.Terminal.rawmode { + fd := int(syscall.Stdin) + termios, err := SetRawMode(fd) + if err != nil { + return "", err + } + i.Terminal.rawmode = true + i.Terminal.termios = termios + } + prompt := i.Prompt.prompt() if i.Pasting { // force alt prompt when pasting @@ -67,13 +79,12 @@ func (i *Instance) Readline() (string, error) { } fmt.Print(prompt) - fd := int(syscall.Stdin) - termios, err := SetRawMode(fd) - if err != nil { - return "", err - } - // nolint: errcheck - defer UnsetRawMode(fd, termios) + defer func() { + fd := int(syscall.Stdin) + // nolint: errcheck + UnsetRawMode(fd, i.Terminal.termios) + i.Terminal.rawmode = false + }() buf, _ := NewBuffer(i.Prompt) @@ -205,7 +216,8 @@ func (i *Instance) Readline() (string, error) { case CharCtrlW: buf.DeleteWord() case CharCtrlZ: - return handleCharCtrlZ(fd, termios) + fd := int(syscall.Stdin) + return handleCharCtrlZ(fd, i.Terminal.termios) case CharEnter: output := buf.String() if output != "" { @@ -236,8 +248,16 @@ func (i *Instance) HistoryDisable() { } func NewTerminal() (*Terminal, error) { + fd := int(syscall.Stdin) + termios, err := SetRawMode(fd) + if err != nil { + return nil, err + } + t := &Terminal{ outchan: make(chan rune), + rawmode: true, + termios: termios, } go t.ioloop() diff --git a/readline/readline_unix.go b/readline/readline_unix.go index 73930c3d..76cff8c8 100644 --- a/readline/readline_unix.go +++ b/readline/readline_unix.go @@ -6,8 +6,9 @@ import ( "syscall" ) -func handleCharCtrlZ(fd int, termios *Termios) (string, error) { - if err := UnsetRawMode(fd, termios); err != nil { +func handleCharCtrlZ(fd int, termios any) (string, error) { + t := termios.(*Termios) + if err := UnsetRawMode(fd, t); err != nil { return "", err } diff --git a/readline/readline_windows.go b/readline/readline_windows.go index c8178903..b4e96b25 100644 --- a/readline/readline_windows.go +++ b/readline/readline_windows.go @@ -1,6 +1,6 @@ package readline -func handleCharCtrlZ(fd int, state *State) (string, error) { +func handleCharCtrlZ(fd int, state any) (string, error) { // not supported return "", nil } diff --git a/readline/term.go b/readline/term.go index 45757e6a..9d747162 100644 --- a/readline/term.go +++ b/readline/term.go @@ -25,8 +25,9 @@ func SetRawMode(fd int) (*Termios, error) { return termios, setTermios(fd, &newTermios) } -func UnsetRawMode(fd int, termios *Termios) error { - return setTermios(fd, termios) +func UnsetRawMode(fd int, termios any) error { + t := termios.(*Termios) + return setTermios(fd, t) } // IsTerminal returns true if the given file descriptor is a terminal. diff --git a/readline/term_windows.go b/readline/term_windows.go index 3d1c80e1..a40fbaa3 100644 --- a/readline/term_windows.go +++ b/readline/term_windows.go @@ -56,7 +56,8 @@ func SetRawMode(fd int) (*State, error) { return &State{st}, nil } -func UnsetRawMode(fd int, state *State) error { - _, _, err := syscall.SyscallN(procSetConsoleMode.Addr(), uintptr(fd), uintptr(state.mode), 0) +func UnsetRawMode(fd int, state any) error { + s := state.(*State) + _, _, err := syscall.SyscallN(procSetConsoleMode.Addr(), uintptr(fd), uintptr(s.mode), 0) return err }