Merge pull request #18 from miraclesu/fix_stop_hang

Fix a hang in `Stop` when tailing a non-existent file

Reproducible on linux, but not on mac.
This commit is contained in:
Sridhar Ratnakumar 2013-09-23 18:19:18 -07:00
commit 88298d7cf8
5 changed files with 29 additions and 15 deletions

11
tail.go
View File

@ -130,7 +130,10 @@ func (tail *Tail) reopen() error {
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
log.Printf("Waiting for %s to appear...", tail.Filename) log.Printf("Waiting for %s to appear...", tail.Filename)
if err := tail.watcher.BlockUntilExists(tail.Tomb); err != nil { if err := tail.watcher.BlockUntilExists(&tail.Tomb); err != nil {
if err == tomb.ErrDying {
return err
}
return fmt.Errorf("Failed to detect creation of %s: %s", tail.Filename, err) return fmt.Errorf("Failed to detect creation of %s: %s", tail.Filename, err)
} }
continue continue
@ -155,7 +158,9 @@ func (tail *Tail) tailFileSync() {
// deferred first open. // deferred first open.
err := tail.reopen() err := tail.reopen()
if err != nil { if err != nil {
tail.Kill(err) if err != tomb.ErrDying {
tail.Kill(err)
}
return return
} }
} }
@ -236,7 +241,7 @@ func (tail *Tail) waitForChanges() error {
if err != nil { if err != nil {
return err return err
} }
tail.changes = tail.watcher.ChangeEvents(tail.Tomb, st) tail.changes = tail.watcher.ChangeEvents(&tail.Tomb, st)
} }
select { select {

View File

@ -40,6 +40,16 @@ func TestMustExist(t *testing.T) {
tail.Stop() tail.Stop()
} }
func TestStop(t *testing.T) {
tail, err := TailFile("_no_such_file", Config{Follow: true, MustExist: false})
if err != nil {
t.Error("MustExist:false is violated")
}
if tail.Stop() != nil {
t.Error("Should be stoped successfully")
}
}
func TestMaxLineSize(_t *testing.T) { func TestMaxLineSize(_t *testing.T) {
t := NewTailTest("maxlinesize", _t) t := NewTailTest("maxlinesize", _t)
t.CreateFile("test.txt", "hello\nworld\nfin\nhe") t.CreateFile("test.txt", "hello\nworld\nfin\nhe")

View File

@ -4,9 +4,9 @@ package watch
import ( import (
"github.com/howeyc/fsnotify" "github.com/howeyc/fsnotify"
"launchpad.net/tomb"
"os" "os"
"path/filepath" "path/filepath"
"launchpad.net/tomb"
) )
// InotifyFileWatcher uses inotify to monitor file changes. // InotifyFileWatcher uses inotify to monitor file changes.
@ -20,7 +20,7 @@ func NewInotifyFileWatcher(filename string) *InotifyFileWatcher {
return fw return fw
} }
func (fw *InotifyFileWatcher) BlockUntilExists(t tomb.Tomb) error { func (fw *InotifyFileWatcher) BlockUntilExists(t *tomb.Tomb) error {
w, err := fsnotify.NewWatcher() w, err := fsnotify.NewWatcher()
if err != nil { if err != nil {
return err return err
@ -56,9 +56,9 @@ func (fw *InotifyFileWatcher) BlockUntilExists(t tomb.Tomb) error {
panic("unreachable") panic("unreachable")
} }
func (fw *InotifyFileWatcher) ChangeEvents(t tomb.Tomb, fi os.FileInfo) *FileChanges { func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, fi os.FileInfo) *FileChanges {
changes := NewFileChanges() changes := NewFileChanges()
w, err := fsnotify.NewWatcher() w, err := fsnotify.NewWatcher()
if err != nil { if err != nil {
panic(err) panic(err)
@ -108,7 +108,7 @@ func (fw *InotifyFileWatcher) ChangeEvents(t tomb.Tomb, fi os.FileInfo) *FileCha
if prevSize > 0 && prevSize > fw.Size { if prevSize > 0 && prevSize > fw.Size {
changes.NotifyTruncated() changes.NotifyTruncated()
}else{ } else {
changes.NotifyModified() changes.NotifyModified()
} }
prevSize = fw.Size prevSize = fw.Size

View File

@ -21,7 +21,7 @@ func NewPollingFileWatcher(filename string) *PollingFileWatcher {
var POLL_DURATION time.Duration var POLL_DURATION time.Duration
func (fw *PollingFileWatcher) BlockUntilExists(t tomb.Tomb) error { func (fw *PollingFileWatcher) BlockUntilExists(t *tomb.Tomb) error {
for { for {
if _, err := os.Stat(fw.Filename); err == nil { if _, err := os.Stat(fw.Filename); err == nil {
return nil return nil
@ -38,7 +38,7 @@ func (fw *PollingFileWatcher) BlockUntilExists(t tomb.Tomb) error {
panic("unreachable") panic("unreachable")
} }
func (fw *PollingFileWatcher) ChangeEvents(t tomb.Tomb, origFi os.FileInfo) *FileChanges { func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, origFi os.FileInfo) *FileChanges {
changes := NewFileChanges() changes := NewFileChanges()
var prevModTime time.Time var prevModTime time.Time
@ -49,7 +49,7 @@ func (fw *PollingFileWatcher) ChangeEvents(t tomb.Tomb, origFi os.FileInfo) *Fil
go func() { go func() {
defer changes.Close() defer changes.Close()
prevSize := fw.Size prevSize := fw.Size
for { for {
select { select {

View File

@ -3,19 +3,18 @@
package watch package watch
import ( import (
"os"
"launchpad.net/tomb" "launchpad.net/tomb"
"os"
) )
// FileWatcher monitors file-level events. // FileWatcher monitors file-level events.
type FileWatcher interface { type FileWatcher interface {
// BlockUntilExists blocks until the file comes into existence. // BlockUntilExists blocks until the file comes into existence.
BlockUntilExists(tomb.Tomb) error BlockUntilExists(*tomb.Tomb) error
// ChangeEvents reports on changes to a file, be it modification, // ChangeEvents reports on changes to a file, be it modification,
// deletion, renames or truncations. Returned FileChanges group of // deletion, renames or truncations. Returned FileChanges group of
// channels will be closed, thus become unusable, after a deletion // channels will be closed, thus become unusable, after a deletion
// or truncation event. // or truncation event.
ChangeEvents(tomb.Tomb, os.FileInfo) *FileChanges ChangeEvents(*tomb.Tomb, os.FileInfo) *FileChanges
} }