BlockUntilExists should return immediately if the file already exists
this fixes a potential race condition in the use of BlockUntilExists following a file existence check (as we do in tail.go:reopen). closes issue 5
This commit is contained in:
parent
fb37e0b7ca
commit
644891ebbc
8
tail.go
8
tail.go
|
@ -102,11 +102,7 @@ func (tail *Tail) reopen() error {
|
|||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("Waiting for %s to appear...", tail.Filename)
|
||||
// XXX: potential race condition here, as the file
|
||||
// could have been created right after out IsNotExist
|
||||
// check above. this will lead to blocking here forever.
|
||||
err := tail.watcher.BlockUntilExists()
|
||||
if err != nil {
|
||||
if err := tail.watcher.BlockUntilExists(); err != nil {
|
||||
return fmt.Errorf("Failed to detect creation of %s: %s", tail.Filename, err)
|
||||
}
|
||||
continue
|
||||
|
@ -191,7 +187,7 @@ func (tail *Tail) tailFileSync() {
|
|||
changes = nil // XXX: how to kill changes' goroutine?
|
||||
|
||||
log.Println("Changes channel is closed.")
|
||||
// File got deleted/renamed
|
||||
// File got deleted/renamed/truncated.
|
||||
if tail.ReOpen {
|
||||
// TODO: no logging in a library?
|
||||
log.Printf("Re-opening moved/deleted/truncated file %s ...", tail.Filename)
|
||||
|
|
|
@ -123,6 +123,7 @@ func _TestReOpen(_t *testing.T, poll bool) {
|
|||
<-time.After(100 * time.Millisecond)
|
||||
t.RemoveFile("test.txt")
|
||||
|
||||
println("Stopping (REOPEN)...")
|
||||
tail.Stop()
|
||||
}
|
||||
|
||||
|
|
23
watch.go
23
watch.go
|
@ -14,7 +14,7 @@ import (
|
|||
// FileWatcher monitors file-level events.
|
||||
type FileWatcher interface {
|
||||
// BlockUntilExists blocks until the missing file comes into
|
||||
// existence. If the file already exists, block until it is recreated.
|
||||
// existence. If the file already exists, returns immediately.
|
||||
BlockUntilExists() error
|
||||
|
||||
// ChangeEvents returns a channel of events corresponding to the
|
||||
|
@ -34,19 +34,34 @@ func NewInotifyFileWatcher(filename string) *InotifyFileWatcher {
|
|||
}
|
||||
|
||||
func (fw *InotifyFileWatcher) BlockUntilExists() error {
|
||||
fmt.Println("BUE(inotify): creating watcher")
|
||||
w, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer w.Close()
|
||||
err = w.WatchFlags(filepath.Dir(fw.Filename), fsnotify.FSN_CREATE)
|
||||
|
||||
dirname := filepath.Dir(fw.Filename)
|
||||
|
||||
// Watch for new files to be created in the parent directory.
|
||||
err = w.WatchFlags(dirname, fsnotify.FSN_CREATE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer w.RemoveWatch(filepath.Dir(fw.Filename))
|
||||
|
||||
fmt.Println("BUE(inotify): does file exist now?")
|
||||
// Do a real check now as the file might have been created before
|
||||
// calling `WatchFlags` above.
|
||||
if _, err = os.Stat(fw.Filename); !os.IsNotExist(err) {
|
||||
// file exists, or stat returned an error.
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("BUE(inotify): checking events (last: %v)\n", err)
|
||||
for {
|
||||
evt := <-w.Event
|
||||
fmt.Printf("block until exits (inotify) evt: %v\n", evt)
|
||||
fmt.Printf("BUE(inotify): got event: %v\n", evt)
|
||||
if evt.Name == fw.Filename {
|
||||
break
|
||||
}
|
||||
|
@ -124,8 +139,6 @@ func NewPollingFileWatcher(filename string) *PollingFileWatcher {
|
|||
|
||||
var POLL_DURATION time.Duration
|
||||
|
||||
// BlockUntilExists blocks until the file comes into existence. If the
|
||||
// file already exists, then block until it is created again.
|
||||
func (fw *PollingFileWatcher) BlockUntilExists() error {
|
||||
for {
|
||||
if _, err := os.Stat(fw.Filename); err == nil {
|
||||
|
|
Loading…
Reference in New Issue