let stdin cancelable (#72)

This commit is contained in:
chzyer 2016-09-01 18:13:06 +08:00 committed by GitHub
parent 27fdf0b285
commit 6cbc078c57
2 changed files with 62 additions and 4 deletions

52
std.go
View File

@ -7,7 +7,7 @@ import (
) )
var ( var (
Stdin io.ReadCloser = os.Stdin Stdin io.ReadCloser = NewCancelableStdin()
Stdout io.WriteCloser = os.Stdout Stdout io.WriteCloser = os.Stdout
Stderr io.WriteCloser = os.Stderr Stderr io.WriteCloser = os.Stderr
) )
@ -64,3 +64,53 @@ func Line(prompt string) (string, error) {
ins.SetPrompt(prompt) ins.SetPrompt(prompt)
return ins.Readline() return ins.Readline()
} }
type CancelableStdin struct {
mutex sync.Mutex
stop chan struct{}
notify chan struct{}
data []byte
read int
err error
}
func NewCancelableStdin() *CancelableStdin {
c := &CancelableStdin{
notify: make(chan struct{}),
stop: make(chan struct{}),
}
go c.ioloop()
return c
}
func (c *CancelableStdin) ioloop() {
loop:
for {
select {
case <-c.notify:
c.read, c.err = os.Stdin.Read(c.data)
<-c.notify
case <-c.stop:
break loop
}
}
}
func (c *CancelableStdin) Read(b []byte) (n int, err error) {
c.mutex.Lock()
defer c.mutex.Unlock()
c.data = b
c.notify <- struct{}{}
select {
case <-c.notify:
return c.read, c.err
case <-c.stop:
return 0, io.EOF
}
}
func (c *CancelableStdin) Close() error {
close(c.stop)
return nil
}

View File

@ -3,6 +3,7 @@ package readline
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"io"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -109,7 +110,11 @@ func (t *Terminal) KickRead() {
func (t *Terminal) ioloop() { func (t *Terminal) ioloop() {
t.wg.Add(1) t.wg.Add(1)
defer t.wg.Done() defer func() {
t.wg.Done()
close(t.outchan)
}()
var ( var (
isEscape bool isEscape bool
isEscapeEx bool isEscapeEx bool
@ -182,7 +187,7 @@ func (t *Terminal) ioloop() {
t.outchan <- r t.outchan <- r
} }
} }
close(t.outchan)
} }
func (t *Terminal) Bell() { func (t *Terminal) Bell() {
@ -193,7 +198,10 @@ func (t *Terminal) Close() error {
if atomic.SwapInt32(&t.closed, 1) != 0 { if atomic.SwapInt32(&t.closed, 1) != 0 {
return nil return nil
} }
t.stopChan <- struct{}{} if closer, ok := t.cfg.Stdin.(io.Closer); ok {
closer.Close()
}
close(t.stopChan)
t.wg.Wait() t.wg.Wait()
return t.ExitRawMode() return t.ExitRawMode()
} }