Tweaks to reader

This commit is contained in:
Daisuke Maki 2015-03-15 17:07:25 +09:00
parent 105e067172
commit 580fc48264
3 changed files with 46 additions and 22 deletions

2
ctx.go
View file

@ -276,7 +276,7 @@ func (c *Ctx) DrawPrompt() {
}
func (c *Ctx) NewBufferReader(r io.ReadCloser) *BufferReader {
return &BufferReader{c, r, make(chan struct{})}
return &BufferReader{c, r, make(chan struct{}, 1)}
}
func (c *Ctx) NewView() *View {

View file

@ -3,7 +3,6 @@
package peco
import (
"fmt"
"log"
"os"
"runtime"
@ -12,12 +11,19 @@ import (
)
const debug = true
var tracer *log.Logger
var mutexTracer *log.Logger
func init() {
if v, err := strconv.ParseBool(os.Getenv("PECO_TRACE")); err == nil && v {
tracer = log.New(os.Stderr, "peco: ", log.LstdFlags)
tracer.Printf("==== INITIALIZED tracer ====")
}
if v, err := strconv.ParseBool(os.Getenv("PECO_LOCK_TRACE")); err == nil && v {
mutexTracer = log.New(os.Stderr, "mutex: ", log.LstdFlags)
mutexTracer.Printf("==== INITIALIZED mutext tracer ====")
}
}
func trace(f string, args ...interface{}) {
@ -27,6 +33,13 @@ func trace(f string, args ...interface{}) {
tracer.Printf(f, args...)
}
func mutexTrace(f string, args ...interface{}) {
if mutexTracer == nil {
return
}
mutexTracer.Printf(f, args...)
}
func newMutex() sync.Locker {
return &loggingMutex{&sync.Mutex{}}
}
@ -38,13 +51,13 @@ type loggingMutex struct {
func (m *loggingMutex) Lock() {
buf := make([]byte, 8092)
l := runtime.Stack(buf, false)
fmt.Printf("LOCK %s\n", buf[:l])
mutexTrace("LOCK %s\n", buf[:l])
m.Mutex.Lock()
}
func (m *loggingMutex) Unlock() {
buf := make([]byte, 8092)
l := runtime.Stack(buf, false)
fmt.Printf("UNLOCK %s\n", buf[:l])
mutexTrace("UNLOCK %s\n", buf[:l])
m.Mutex.Unlock()
}

View file

@ -8,6 +8,8 @@ import (
"time"
)
// BufferReader reads from either stdin or a file. In case of stdin,
// it also handles possible infinite source.
type BufferReader struct {
*Ctx
input io.ReadCloser
@ -25,11 +27,13 @@ func (b *BufferReader) Loop() {
defer b.ReleaseWaitGroup()
defer func() { recover() }() // ignore errors
defer func() { close(b.inputReadyCh) }() // Make sure to close notifier
defer b.input.Close()
ch := make(chan string, 10)
// scanner.Scan() blocks until the next read or error. But we want to
// exit immediately, so we move it out to its own goroutine
// scanner.Scan() blocks until the next read or error. But we want our
// main loop to be able to exit without blocking, so we move this out
// to its own goroutine
go func() {
defer func() { recover() }()
defer func() { close(ch) }()
@ -43,8 +47,27 @@ func (b *BufferReader) Loop() {
once := &sync.Once{}
var refresh *time.Timer
loop := true
for loop {
doDelayedDraw := func() {
m.Lock()
defer m.Unlock()
trace("doDelayedDraw")
if refresh != nil {
return
}
refresh = time.AfterFunc(100*time.Millisecond, func() {
if !b.ExecQuery() {
b.SendDraw()
}
m.Lock()
defer m.Unlock()
refresh = nil
})
}
for loop := true; loop; {
select {
case <-b.LoopCh():
loop = false
@ -56,6 +79,7 @@ func (b *BufferReader) Loop() {
if line != "" {
// Notify once that we have received something from the file/stdin
// This is the cue to start initializing the terminal
once.Do(func() { b.inputReadyCh <- struct{}{} })
// Make sure we lock access to b.lines
@ -64,23 +88,10 @@ func (b *BufferReader) Loop() {
m.Unlock()
}
m.Lock()
if refresh == nil {
refresh = time.AfterFunc(100*time.Millisecond, func() {
if !b.ExecQuery() {
b.SendDraw()
}
m.Lock()
refresh = nil
m.Unlock()
})
}
m.Unlock()
doDelayedDraw()
}
}
b.input.Close()
// Out of the reader loop. If at this point we have no buffer,
// that means we have no buffer, so we should quit.
if b.GetRawLineBufferSize() == 0 {