Skip to content

Commit

Permalink
BlockUntilExists should return immediately if the file already exists
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Sridhar Ratnakumar committed May 28, 2013
1 parent fb37e0b commit 644891e
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 11 deletions.
8 changes: 2 additions & 6 deletions tail.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions tail_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

Expand Down
23 changes: 18 additions & 5 deletions watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 644891e

Please sign in to comment.