tail/watch/polling.go

112 lines
2.0 KiB
Go
Raw Normal View History

// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
package watch
import (
"launchpad.net/tomb"
"os"
"sync"
"time"
"fmt"
)
// PollingFileWatcher polls the file for changes.
type PollingFileWatcher struct {
Filename string
Size int64
}
func NewPollingFileWatcher(filename string) *PollingFileWatcher {
fw := &PollingFileWatcher{filename, 0}
return fw
}
var POLL_DURATION time.Duration
func (fw *PollingFileWatcher) BlockUntilExists(t tomb.Tomb) error {
for {
if _, err := os.Stat(fw.Filename); err == nil {
return nil
} else if !os.IsNotExist(err) {
return err
}
select {
case <-time.After(POLL_DURATION):
continue
case <-t.Dying():
return fmt.Errorf("Tomb dying")
}
}
panic("unreachable")
}
func (fw *PollingFileWatcher) ChangeEvents(origFi os.FileInfo) chan bool {
ch := make(chan bool)
stop := make(chan bool)
var once sync.Once
var prevModTime time.Time
// XXX: use tomb.Tomb to cleanly manage these goroutines. replace
// the panic (below) with tomb's Kill.
stopAndClose := func() {
go func() {
close(ch)
stop <- true
}()
}
fw.Size = origFi.Size()
go func() {
prevSize := fw.Size
for {
select {
case <-stop:
return
default:
}
time.Sleep(POLL_DURATION)
fi, err := os.Stat(fw.Filename)
if err != nil {
if os.IsNotExist(err) {
once.Do(stopAndClose)
continue
}
/// XXX: do not panic here.
panic(err)
}
// File got moved/rename within POLL_DURATION?
if !os.SameFile(origFi, fi) {
once.Do(stopAndClose)
continue
}
// Was the file truncated?
fw.Size = fi.Size()
if prevSize > 0 && prevSize > fw.Size {
once.Do(stopAndClose)
continue
}
// If the file was changed since last check, notify.
modTime := fi.ModTime()
if modTime != prevModTime {
prevModTime = modTime
select {
case ch <- true:
default:
}
}
}
}()
return ch
}
func init() {
POLL_DURATION = 250 * time.Millisecond
}