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 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)
|
||||||
// XXX: potential race condition here, as the file
|
if err := tail.watcher.BlockUntilExists(); err != nil {
|
||||||
// could have been created right after out IsNotExist
|
|
||||||
// check above. this will lead to blocking here forever.
|
|
||||||
err := tail.watcher.BlockUntilExists()
|
|
||||||
if err != nil {
|
|
||||||
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
|
||||||
|
@ -191,7 +187,7 @@ func (tail *Tail) tailFileSync() {
|
||||||
changes = nil // XXX: how to kill changes' goroutine?
|
changes = nil // XXX: how to kill changes' goroutine?
|
||||||
|
|
||||||
log.Println("Changes channel is closed.")
|
log.Println("Changes channel is closed.")
|
||||||
// File got deleted/renamed
|
// File got deleted/renamed/truncated.
|
||||||
if tail.ReOpen {
|
if tail.ReOpen {
|
||||||
// TODO: no logging in a library?
|
// TODO: no logging in a library?
|
||||||
log.Printf("Re-opening moved/deleted/truncated file %s ...", tail.Filename)
|
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)
|
<-time.After(100 * time.Millisecond)
|
||||||
t.RemoveFile("test.txt")
|
t.RemoveFile("test.txt")
|
||||||
|
|
||||||
|
println("Stopping (REOPEN)...")
|
||||||
tail.Stop()
|
tail.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23
watch.go
23
watch.go
|
@ -14,7 +14,7 @@ import (
|
||||||
// FileWatcher monitors file-level events.
|
// FileWatcher monitors file-level events.
|
||||||
type FileWatcher interface {
|
type FileWatcher interface {
|
||||||
// BlockUntilExists blocks until the missing file comes into
|
// 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
|
BlockUntilExists() error
|
||||||
|
|
||||||
// ChangeEvents returns a channel of events corresponding to the
|
// ChangeEvents returns a channel of events corresponding to the
|
||||||
|
@ -34,19 +34,34 @@ func NewInotifyFileWatcher(filename string) *InotifyFileWatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fw *InotifyFileWatcher) BlockUntilExists() error {
|
func (fw *InotifyFileWatcher) BlockUntilExists() error {
|
||||||
|
fmt.Println("BUE(inotify): creating watcher")
|
||||||
w, err := fsnotify.NewWatcher()
|
w, err := fsnotify.NewWatcher()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer w.Close()
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer w.RemoveWatch(filepath.Dir(fw.Filename))
|
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 {
|
for {
|
||||||
evt := <-w.Event
|
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 {
|
if evt.Name == fw.Filename {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -124,8 +139,6 @@ func NewPollingFileWatcher(filename string) *PollingFileWatcher {
|
||||||
|
|
||||||
var POLL_DURATION time.Duration
|
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 {
|
func (fw *PollingFileWatcher) BlockUntilExists() error {
|
||||||
for {
|
for {
|
||||||
if _, err := os.Stat(fw.Filename); err == nil {
|
if _, err := os.Stat(fw.Filename); err == nil {
|
||||||
|
|
Loading…
Reference in New Issue