operation: fix SetConfig races (#131)

This commit is contained in:
Eugene Apollonsky 2017-10-03 17:59:50 +03:00 committed by chzyer
parent 9cc74fe5ad
commit 6a4bc7b4fe
1 changed files with 32 additions and 17 deletions

View File

@ -3,6 +3,7 @@ package readline
import ( import (
"errors" "errors"
"io" "io"
"sync"
) )
var ( var (
@ -18,6 +19,7 @@ func (*InterruptError) Error() string {
} }
type Operation struct { type Operation struct {
m sync.Mutex
cfg *Config cfg *Config
t *Terminal t *Terminal
buf *RuneBuffer buf *RuneBuffer
@ -95,14 +97,21 @@ func (o *Operation) SetMaskRune(r rune) {
o.buf.SetMask(r) o.buf.SetMask(r)
} }
func (o *Operation) GetConfig() *Config {
o.m.Lock()
cfg := *o.cfg
o.m.Unlock()
return &cfg
}
func (o *Operation) ioloop() { func (o *Operation) ioloop() {
for { for {
keepInSearchMode := false keepInSearchMode := false
keepInCompleteMode := false keepInCompleteMode := false
r := o.t.ReadRune() r := o.t.ReadRune()
if o.cfg.FuncFilterInputRune != nil { if o.GetConfig().FuncFilterInputRune != nil {
var process bool var process bool
r, process = o.cfg.FuncFilterInputRune(r) r, process = o.GetConfig().FuncFilterInputRune(r)
if !process { if !process {
o.buf.Refresh(nil) // to refresh the line o.buf.Refresh(nil) // to refresh the line
continue // ignore this rune continue // ignore this rune
@ -162,7 +171,7 @@ func (o *Operation) ioloop() {
o.buf.Refresh(nil) o.buf.Refresh(nil)
} }
case CharTab: case CharTab:
if o.cfg.AutoComplete == nil { if o.GetConfig().AutoComplete == nil {
o.t.Bell() o.t.Bell()
break break
} }
@ -234,7 +243,7 @@ func (o *Operation) ioloop() {
} }
o.buf.MoveToLineEnd() o.buf.MoveToLineEnd()
var data []rune var data []rune
if !o.cfg.UniqueEditLine { if !o.GetConfig().UniqueEditLine {
o.buf.WriteRune('\n') o.buf.WriteRune('\n')
data = o.buf.Reset() data = o.buf.Reset()
data = data[:len(data)-1] // trim \n data = data[:len(data)-1] // trim \n
@ -243,7 +252,7 @@ func (o *Operation) ioloop() {
data = o.buf.Reset() data = o.buf.Reset()
} }
o.outchan <- data o.outchan <- data
if !o.cfg.DisableAutoSaveHistory { if !o.GetConfig().DisableAutoSaveHistory {
// ignore IO error // ignore IO error
_ = o.history.New(data) _ = o.history.New(data)
} else { } else {
@ -277,14 +286,14 @@ func (o *Operation) ioloop() {
} }
// treat as EOF // treat as EOF
if !o.cfg.UniqueEditLine { if !o.GetConfig().UniqueEditLine {
o.buf.WriteString(o.cfg.EOFPrompt + "\n") o.buf.WriteString(o.GetConfig().EOFPrompt + "\n")
} }
o.buf.Reset() o.buf.Reset()
isUpdateHistory = false isUpdateHistory = false
o.history.Revert() o.history.Revert()
o.errchan <- io.EOF o.errchan <- io.EOF
if o.cfg.UniqueEditLine { if o.GetConfig().UniqueEditLine {
o.buf.Clean() o.buf.Clean()
} }
case CharInterrupt: case CharInterrupt:
@ -301,12 +310,12 @@ func (o *Operation) ioloop() {
} }
o.buf.MoveToLineEnd() o.buf.MoveToLineEnd()
o.buf.Refresh(nil) o.buf.Refresh(nil)
hint := o.cfg.InterruptPrompt + "\n" hint := o.GetConfig().InterruptPrompt + "\n"
if !o.cfg.UniqueEditLine { if !o.GetConfig().UniqueEditLine {
o.buf.WriteString(hint) o.buf.WriteString(hint)
} }
remain := o.buf.Reset() remain := o.buf.Reset()
if !o.cfg.UniqueEditLine { if !o.GetConfig().UniqueEditLine {
remain = remain[:len(remain)-len([]rune(hint))] remain = remain[:len(remain)-len([]rune(hint))]
} }
isUpdateHistory = false isUpdateHistory = false
@ -325,13 +334,15 @@ func (o *Operation) ioloop() {
} }
} }
if o.cfg.Listener != nil { listener := o.GetConfig().Listener
newLine, newPos, ok := o.cfg.Listener.OnChange(o.buf.Runes(), o.buf.Pos(), r) if listener != nil {
newLine, newPos, ok := listener.OnChange(o.buf.Runes(), o.buf.Pos(), r)
if ok { if ok {
o.buf.SetWithIdx(newPos, newLine) o.buf.SetWithIdx(newPos, newLine)
} }
} }
o.m.Lock()
if !keepInSearchMode && o.IsSearchMode() { if !keepInSearchMode && o.IsSearchMode() {
o.ExitSearchMode(false) o.ExitSearchMode(false)
o.buf.Refresh(nil) o.buf.Refresh(nil)
@ -348,15 +359,16 @@ func (o *Operation) ioloop() {
// it will cause null history // it will cause null history
o.history.Update(o.buf.Runes(), false) o.history.Update(o.buf.Runes(), false)
} }
o.m.Unlock()
} }
} }
func (o *Operation) Stderr() io.Writer { func (o *Operation) Stderr() io.Writer {
return &wrapWriter{target: o.cfg.Stderr, r: o, t: o.t} return &wrapWriter{target: o.GetConfig().Stderr, r: o, t: o.t}
} }
func (o *Operation) Stdout() io.Writer { func (o *Operation) Stdout() io.Writer {
return &wrapWriter{target: o.cfg.Stdout, r: o, t: o.t} return &wrapWriter{target: o.GetConfig().Stdout, r: o, t: o.t}
} }
func (o *Operation) String() (string, error) { func (o *Operation) String() (string, error) {
@ -368,8 +380,9 @@ func (o *Operation) Runes() ([]rune, error) {
o.t.EnterRawMode() o.t.EnterRawMode()
defer o.t.ExitRawMode() defer o.t.ExitRawMode()
if o.cfg.Listener != nil { listener := o.GetConfig().Listener
o.cfg.Listener.OnChange(nil, 0, 0) if listener != nil {
listener.OnChange(nil, 0, 0)
} }
o.buf.Refresh(nil) // print prompt o.buf.Refresh(nil) // print prompt
@ -437,6 +450,8 @@ func (o *Operation) IsNormalMode() bool {
} }
func (op *Operation) SetConfig(cfg *Config) (*Config, error) { func (op *Operation) SetConfig(cfg *Config) (*Config, error) {
op.m.Lock()
defer op.m.Unlock()
if op.cfg == cfg { if op.cfg == cfg {
return op.cfg, nil return op.cfg, nil
} }