mirror of https://github.com/chzyer/readline.git
Bugfix/datarace prompt (#81)
* fix data race in PromptLen * add lock for all operation in RuneBuffer * add race test * update travis
This commit is contained in:
parent
820d6f2766
commit
283f5429f7
|
@ -1,10 +1,9 @@
|
||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.5
|
- 1.5
|
||||||
before_install:
|
- 1.7
|
||||||
- go get golang.org/x/crypto/ssh/terminal
|
|
||||||
script:
|
script:
|
||||||
- GOOS=windows go install github.com/chzyer/readline/example/...
|
- GOOS=windows go install github.com/chzyer/readline/example/...
|
||||||
- GOOS=linux go install github.com/chzyer/readline/example/...
|
- GOOS=linux go install github.com/chzyer/readline/example/...
|
||||||
- GOOS=darwin go install github.com/chzyer/readline/example/...
|
- GOOS=darwin go install github.com/chzyer/readline/example/...
|
||||||
- go test -v
|
- go test -race -v
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package readline
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRace(t *testing.T) {
|
||||||
|
rl, err := NewEx(&Config{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for range time.Tick(time.Millisecond) {
|
||||||
|
rl.SetPrompt("hello")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
rl.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
rl.Readline()
|
||||||
|
}
|
39
runebuf.go
39
runebuf.go
|
@ -33,11 +33,15 @@ type RuneBuffer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) OnWidthChange(newWidth int) {
|
func (r *RuneBuffer) OnWidthChange(newWidth int) {
|
||||||
|
r.Lock()
|
||||||
r.width = newWidth
|
r.width = newWidth
|
||||||
|
r.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) Backup() {
|
func (r *RuneBuffer) Backup() {
|
||||||
|
r.Lock()
|
||||||
r.bck = &runeBufferBck{r.buf, r.idx}
|
r.bck = &runeBufferBck{r.buf, r.idx}
|
||||||
|
r.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) Restore() {
|
func (r *RuneBuffer) Restore() {
|
||||||
|
@ -62,15 +66,21 @@ func NewRuneBuffer(w io.Writer, prompt string, cfg *Config, width int) *RuneBuff
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) SetConfig(cfg *Config) {
|
func (r *RuneBuffer) SetConfig(cfg *Config) {
|
||||||
|
r.Lock()
|
||||||
r.cfg = cfg
|
r.cfg = cfg
|
||||||
r.interactive = cfg.useInteractive()
|
r.interactive = cfg.useInteractive()
|
||||||
|
r.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) SetMask(m rune) {
|
func (r *RuneBuffer) SetMask(m rune) {
|
||||||
|
r.Lock()
|
||||||
r.cfg.MaskRune = m
|
r.cfg.MaskRune = m
|
||||||
|
r.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) CurrentWidth(x int) int {
|
func (r *RuneBuffer) CurrentWidth(x int) int {
|
||||||
|
r.Lock()
|
||||||
|
defer r.Unlock()
|
||||||
return runes.WidthAll(r.buf[:x])
|
return runes.WidthAll(r.buf[:x])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +96,9 @@ func (r *RuneBuffer) promptLen() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) RuneSlice(i int) []rune {
|
func (r *RuneBuffer) RuneSlice(i int) []rune {
|
||||||
|
r.Lock()
|
||||||
|
defer r.Unlock()
|
||||||
|
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
rs := make([]rune, i)
|
rs := make([]rune, i)
|
||||||
copy(rs, r.buf[r.idx:r.idx+i])
|
copy(rs, r.buf[r.idx:r.idx+i])
|
||||||
|
@ -97,16 +110,22 @@ func (r *RuneBuffer) RuneSlice(i int) []rune {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) Runes() []rune {
|
func (r *RuneBuffer) Runes() []rune {
|
||||||
|
r.Lock()
|
||||||
newr := make([]rune, len(r.buf))
|
newr := make([]rune, len(r.buf))
|
||||||
copy(newr, r.buf)
|
copy(newr, r.buf)
|
||||||
|
r.Unlock()
|
||||||
return newr
|
return newr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) Pos() int {
|
func (r *RuneBuffer) Pos() int {
|
||||||
|
r.Lock()
|
||||||
|
defer r.Unlock()
|
||||||
return r.idx
|
return r.idx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) Len() int {
|
func (r *RuneBuffer) Len() int {
|
||||||
|
r.Lock()
|
||||||
|
defer r.Unlock()
|
||||||
return len(r.buf)
|
return len(r.buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,6 +173,8 @@ func (r *RuneBuffer) MoveForward() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) IsCursorInEnd() bool {
|
func (r *RuneBuffer) IsCursorInEnd() bool {
|
||||||
|
r.Lock()
|
||||||
|
defer r.Unlock()
|
||||||
return r.idx == len(r.buf)
|
return r.idx == len(r.buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,6 +403,12 @@ func (r *RuneBuffer) getSplitByLine(rs []rune) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) IdxLine(width int) int {
|
func (r *RuneBuffer) IdxLine(width int) int {
|
||||||
|
r.Lock()
|
||||||
|
defer r.Unlock()
|
||||||
|
return r.idxLine(width)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RuneBuffer) idxLine(width int) int {
|
||||||
if width == 0 {
|
if width == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -404,7 +431,7 @@ func (r *RuneBuffer) Refresh(f func()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Clean()
|
r.clean()
|
||||||
if f != nil {
|
if f != nil {
|
||||||
f()
|
f()
|
||||||
}
|
}
|
||||||
|
@ -527,10 +554,16 @@ func (r *RuneBuffer) cleanOutput(w io.Writer, idxLine int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) Clean() {
|
func (r *RuneBuffer) Clean() {
|
||||||
r.clean(r.IdxLine(r.width))
|
r.Lock()
|
||||||
|
r.clean()
|
||||||
|
r.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) clean(idxLine int) {
|
func (r *RuneBuffer) clean() {
|
||||||
|
r.cleanWithIdxLine(r.idxLine(r.width))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RuneBuffer) cleanWithIdxLine(idxLine int) {
|
||||||
if r.hadClean || !r.interactive {
|
if r.hadClean || !r.interactive {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue