diff --git a/tail.go b/tail.go index 1bae11c..b2b1bc2 100644 --- a/tail.go +++ b/tail.go @@ -4,6 +4,7 @@ package tail import ( "bufio" + "errors" "fmt" "io" "io/ioutil" @@ -156,6 +157,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) tail.colseFile() @@ -236,6 +245,7 @@ func (tail *Tail) tailFileSync() { var offset int64 = 0 var err error + // Read line by line. for { // do not seek in named pipes @@ -265,8 +275,7 @@ func (tail *Tail) tailFileSync() { case <-tail.Dying(): return } - err = tail.seekEnd() - if err != nil { + if err := tail.seekEnd(); err != nil { tail.Kill(err) return } @@ -307,6 +316,9 @@ func (tail *Tail) tailFileSync() { select { case <-tail.Dying(): + if tail.Err() == errStopAtEOF { + continue + } return default: } diff --git a/tail_test.go b/tail_test.go index b6d0676..28fb989 100644 --- a/tail_test.go +++ b/tail_test.go @@ -55,6 +55,25 @@ func TestStop(t *testing.T) { tail.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 + tail.Cleanup() +} + func MaxLineSizeT(_t *testing.T, follow bool, fileContent string, expected []string) { t := NewTailTest("maxlinesize", _t) t.CreateFile("test.txt", fileContent) 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 58efc10..76870d6 100644 --- a/watch/inotify.go +++ b/watch/inotify.go @@ -66,7 +66,6 @@ func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChange go func() { defer RemoveWatch(fw.Filename) - defer changes.Close() events := Events(fw.Filename) diff --git a/watch/polling.go b/watch/polling.go index 33b8b4d..f595992 100644 --- a/watch/polling.go +++ b/watch/polling.go @@ -56,8 +56,6 @@ func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChange fw.Size = pos go func() { - defer changes.Close() - prevSize := fw.Size for { select {