From 0587510d194a78ea17d7c9d1f2447241c6f86b80 Mon Sep 17 00:00:00 2001 From: Jonathan Rudenberg Date: Thu, 25 Dec 2014 20:59:31 -0500 Subject: [PATCH 1/2] Remove Close method from FileChanges This method will never do what we want, closing the channels will always signal receivers, which will confuse them. --- watch/filechanges.go | 6 ------ watch/inotify.go | 1 - watch/polling.go | 2 -- 3 files changed, 9 deletions(-) diff --git a/watch/filechanges.go b/watch/filechanges.go index fb0f9ef..3ce5dce 100644 --- a/watch/filechanges.go +++ b/watch/filechanges.go @@ -23,12 +23,6 @@ func (fc *FileChanges) NotifyDeleted() { sendOnlyIfEmpty(fc.Deleted) } -func (fc *FileChanges) Close() { - close(fc.Modified) - close(fc.Truncated) - close(fc.Deleted) -} - // sendOnlyIfEmpty sends on a bool channel only if the channel has no // backlog to be read by other goroutines. This concurrency pattern // can be used to notify other goroutines if and only if they are diff --git a/watch/inotify.go b/watch/inotify.go index c2f13dc..0b1b1fe 100644 --- a/watch/inotify.go +++ b/watch/inotify.go @@ -77,7 +77,6 @@ func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, fi os.FileInfo) *FileCh go func() { defer inotifyTracker.CloseWatcher(w) - defer changes.Close() for { prevSize := fw.Size diff --git a/watch/polling.go b/watch/polling.go index 10a17eb..de01c71 100644 --- a/watch/polling.go +++ b/watch/polling.go @@ -49,8 +49,6 @@ func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, origFi os.FileInfo) *Fi fw.Size = origFi.Size() go func() { - defer changes.Close() - var retry int = 0 prevSize := fw.Size From 166cd27854c7ded9afb572098562750fb3533e67 Mon Sep 17 00:00:00 2001 From: Jonathan Rudenberg Date: Wed, 24 Dec 2014 16:48:29 -0500 Subject: [PATCH 2/2] Add StopAtEOF to stop tailing when the end of the file is reached --- tail.go | 23 ++++++++++++++++++++--- tail_test.go | 19 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/tail.go b/tail.go index cedbb23..877f849 100644 --- a/tail.go +++ b/tail.go @@ -4,6 +4,7 @@ package tail import ( "bufio" + "errors" "fmt" "github.com/ActiveState/tail/ratelimiter" "github.com/ActiveState/tail/util" @@ -136,6 +137,14 @@ func (tail *Tail) Stop() error { return tail.Wait() } +// StopAtEOF stops tailing as soon as the end of the file is reached. +func (tail *Tail) StopAtEOF() error { + tail.Kill(errStopAtEOF) + return tail.Wait() +} + +var errStopAtEOF = errors.New("tail: stop at eof") + func (tail *Tail) close() { close(tail.Lines) if tail.file != nil { @@ -209,6 +218,8 @@ func (tail *Tail) tailFileSync() { tail.openReader() + var stopAtEOF bool + // Read line by line. for { line, err := tail.readLine() @@ -228,14 +239,16 @@ func (tail *Tail) tailFileSync() { case <-tail.Dying(): return } - err = tail.seekEnd() - if err != nil { + if err := tail.seekEnd(); err != nil { tail.Kill(err) return } } + if stopAtEOF && err == io.EOF { + return + } } else if err == io.EOF { - if !tail.Follow { + if stopAtEOF || !tail.Follow { return } // When EOF is reached, wait for more data to become @@ -256,6 +269,10 @@ func (tail *Tail) tailFileSync() { select { case <-tail.Dying(): + if tail.Err() == errStopAtEOF { + stopAtEOF = true + continue + } return default: } diff --git a/tail_test.go b/tail_test.go index 0b9c5f9..f7abe37 100644 --- a/tail_test.go +++ b/tail_test.go @@ -54,6 +54,25 @@ func TestStop(t *testing.T) { Cleanup() } +func TestStopAtEOF(_t *testing.T) { + t := NewTailTest("maxlinesize", _t) + t.CreateFile("test.txt", "hello\nthere\nworld\n") + tail := t.StartTail("test.txt", Config{Follow: true, Location: nil}) + + // read "hello" + <-tail.Lines + + done := make(chan struct{}) + go func() { + <-time.After(100 * time.Millisecond) + t.VerifyTailOutput(tail, []string{"there", "world"}) + close(done) + }() + tail.StopAtEOF() + <-done + Cleanup() +} + func TestMaxLineSize(_t *testing.T) { t := NewTailTest("maxlinesize", _t) t.CreateFile("test.txt", "hello\nworld\nfin\nhe")