From ee6c764989fd0c29aacea8dc07f6af345989f0d8 Mon Sep 17 00:00:00 2001 From: Milind Chabbi Date: Mon, 21 Jun 2021 18:07:36 -0700 Subject: [PATCH] Fix data race in prompt and select. The cursor and screen buffers used in prompt.go and select.go are concurrently accessed. This causes a data race. Lock protecting the accesses to the Cursor and the screenbuffer eliminates the data race. --- prompt.go | 10 ++++++++++ select.go | 7 ++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/prompt.go b/prompt.go index 8e35123..8a6592a 100644 --- a/prompt.go +++ b/prompt.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "strings" + "sync" "text/template" "github.com/chzyer/readline" @@ -157,10 +158,14 @@ func (p *Prompt) Run() (string, error) { } eraseDefault := input != "" && !p.AllowEdit cur := NewCursor(input, p.Pointer, eraseDefault) + var curLock sync.Mutex listen := func(input []rune, pos int, key rune) ([]rune, int, bool) { + defer curLock.Unlock() + curLock.Lock() _, _, keepOn := cur.Listen(input, pos, key) err := validFn(cur.Get()) + var prompt []byte if err != nil { @@ -193,7 +198,10 @@ func (p *Prompt) Run() (string, error) { for { _, err = rl.Readline() + curLock.Lock() inputErr = validFn(cur.Get()) + curLock.Unlock() + if inputErr == nil { break } @@ -203,6 +211,8 @@ func (p *Prompt) Run() (string, error) { } } + defer curLock.Unlock() + curLock.Lock() if err != nil { switch err { case readline.ErrInterrupt: diff --git a/select.go b/select.go index 19b9e0c..818eec1 100644 --- a/select.go +++ b/select.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "sync" "text/template" "github.com/chzyer/readline" @@ -246,6 +247,7 @@ func (s *Select) innerRun(cursorPos, scroll int, top rune) (int, string, error) rl.Write([]byte(hideCursor)) sb := screenbuf.New(rl) + var curLock sync.Mutex cur := NewCursor("", s.Pointer, false) canSearch := s.Searcher != nil @@ -254,6 +256,8 @@ func (s *Select) innerRun(cursorPos, scroll int, top rune) (int, string, error) s.list.SetStart(scroll) c.SetListener(func(line []rune, pos int, key rune) ([]rune, int, bool) { + defer curLock.Unlock() + curLock.Lock() switch { case key == KeyEnter: return nil, 0, true @@ -372,7 +376,8 @@ func (s *Select) innerRun(cursorPos, scroll int, top rune) (int, string, error) } } - + defer curLock.Unlock() + curLock.Lock() if err != nil { if err.Error() == "Interrupt" { err = ErrInterrupt