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:
commit
88298d7cf8
9
tail.go
9
tail.go
|
@ -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 {
|
||||||
|
if err != tomb.ErrDying {
|
||||||
tail.Kill(err)
|
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 {
|
||||||
|
|
10
tail_test.go
10
tail_test.go
|
@ -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")
|
||||||
|
|
|
@ -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,7 +56,7 @@ 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()
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue