Skip to content

Commit

Permalink
watch: properly reset terminal after idle timeout
Browse files Browse the repository at this point in the history
Previous the terminal would not be reset because the defer was in the
redo handler goroutine. This goroutine accepts a context, but because it
blocks on reading from stdin, it does not exit when the context is
cancelled.

To fix the problem, the defer is moved into the main goroutine.
  • Loading branch information
dnephin committed Nov 21, 2020
1 parent 3a094ca commit 98ce04d
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 20 deletions.
45 changes: 38 additions & 7 deletions internal/filewatcher/term_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@ import (
"gotest.tools/gotestsum/log"
)

type redoHandler struct {
prevPath string
ch chan string
reset func()
}

func newRedoHandler() *redoHandler {
fd := int(os.Stdin.Fd())
reset, err := enableNonBlockingRead(fd)
if err != nil {
log.Warnf("failed to put terminal (fd %d) into raw mode: %v", fd, err)
return nil
}
return &redoHandler{ch: make(chan string), reset: reset}
}

func enableNonBlockingRead(fd int) (func(), error) {
term, err := unix.IoctlGetTermios(fd, tcGet)
if err != nil {
Expand All @@ -35,15 +51,10 @@ func enableNonBlockingRead(fd int) (func(), error) {
return reset, nil
}

func (r *redoHandler) run(ctx context.Context) {
fd := int(os.Stdin.Fd())
reset, err := enableNonBlockingRead(fd)
if err != nil {
log.Debugf("failed to put terminal (fd %d) into raw mode: %v", fd, err)
func (r *redoHandler) Run(ctx context.Context) {
if r == nil {
return
}
defer reset()

in := bufio.NewReader(os.Stdin)
for {
if ctx.Err() != nil {
Expand All @@ -65,3 +76,23 @@ func (r *redoHandler) run(ctx context.Context) {
}
}
}

func (r *redoHandler) Ch() <-chan string {
if r == nil {
return nil
}
return r.ch
}

func (r *redoHandler) Reset() {
if r != nil {
r.reset()
}
}

func (r *redoHandler) Save(path string) {
if r == nil {
return
}
r.prevPath = path
}
16 changes: 14 additions & 2 deletions internal/filewatcher/term_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@ package filewatcher

import "context"

func (r *redoHandler) run(_ context.Context) {
return
type redoHandler struct{}

func newRedoHandler() *redoHandler {
return nil
}

func (r *redoHandler) Run(_ context.Context) {}

func (r *redoHandler) Ch() <-chan string {
return nil
}

func (r *redoHandler) Reset() {}

func (r *redoHandler) Save(_ string) {}
21 changes: 10 additions & 11 deletions internal/filewatcher/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,20 @@ func Watch(dirs []string, run func(pkg string) error) error {
}
}

timer := time.NewTimer(time.Hour)
timer := time.NewTimer(maxIdleTime)
defer timer.Stop()

redo := &redoHandler{ch: make(chan string)}
go redo.run(ctx)
redo := newRedoHandler()
defer redo.Reset()
go redo.Run(ctx)

h := &handler{last: time.Now(), fn: run}
for {
select {
case <-timer.C:
return fmt.Errorf("exceeded idle timeout while watching files")

case path := <-redo.ch:
case path := <-redo.Ch():
resetTimer(timer)
if err := h.runTests(path); err != nil {
return fmt.Errorf("failed to rerun tests for %v: %v", path, err)
Expand All @@ -61,19 +63,21 @@ func Watch(dirs []string, run func(pkg string) error) error {
if err := h.handleEvent(event); err != nil {
return fmt.Errorf("failed to run tests for %v: %v", event.Name, err)
}
redo.prevPath = event.Name
redo.Save(event.Name)

case err := <-watcher.Errors:
return fmt.Errorf("failed while watching files: %v", err)
}
}
}

const maxIdleTime = time.Hour

func resetTimer(timer *time.Timer) {
if !timer.Stop() {
<-timer.C
}
timer.Reset(time.Hour)
timer.Reset(maxIdleTime)
}

func findAllDirs(dirs []string, maxDepth int) []string {
Expand Down Expand Up @@ -215,8 +219,3 @@ func (h *handler) runTests(path string) error {
h.last = time.Now()
return nil
}

type redoHandler struct {
prevPath string
ch chan string
}

0 comments on commit 98ce04d

Please sign in to comment.