diff options
author | Luke Shumaker <lukeshu@sbcglobal.net> | 2015-09-12 09:14:40 -0600 |
---|---|---|
committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2015-09-12 09:14:40 -0600 |
commit | 8941e9a90b9560e6b433c68725255f827922a5a8 (patch) | |
tree | e8978ecfa32a6822bfb91700d330e69da01fe242 /inotify | |
parent | b4ba0d061c1ef2f8069a568992eb98b7c060f5cf (diff) |
Add an inotify watcher utility using channels; use it.
The interface of inotify/inutil.Watcher more resembles
golang.org/x/exp/inotify; with it using channels instead of repeated calls
to Read().
In my use-case, this is useful because it allows implementing a "read"
(select, really) that doesn't block Close(); which is required to handle
the TERM signal correctly.
Diffstat (limited to 'inotify')
-rw-r--r-- | inotify/inutil/inotify_util.go | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/inotify/inutil/inotify_util.go b/inotify/inutil/inotify_util.go new file mode 100644 index 0000000..46146f5 --- /dev/null +++ b/inotify/inutil/inotify_util.go @@ -0,0 +1,78 @@ +package inutil + +import ( + "inotify" + "os" + "syscall" +) + +const ( + // Flags for the parameter of InotifyInit1(). + // These, oddly, appear to be 24-bit numbers. + IN_CLOEXEC = inotify.IN_CLOEXEC +) + +type Watcher struct { + Events <-chan inotify.Event + events chan<- inotify.Event + Errors <-chan error + errors chan<- error + in *inotify.Inotify +} + +func WatcherInit() (*Watcher, error) { + in, err := inotify.InotifyInit() + return newWatcher(in, err) +} + +func WatcherInit1(flags int) (*Watcher, error) { + in, err := inotify.InotifyInit1(flags&^inotify.IN_NONBLOCK) + return newWatcher(in, err) +} + +func newWatcher(in *inotify.Inotify, err error) (*Watcher, error) { + events := make(chan inotify.Event, 1) + errors := make(chan error, 1) + o := &Watcher{ + Events: events, + events: events, + Errors: errors, + errors: errors, + in: in, + } + go o.worker() + return o, err +} + +func (o *Watcher) AddWatch(path string, mask inotify.Mask) (inotify.Wd, error) { + return o.in.AddWatch(path, mask); +} + +func (o *Watcher) RmWatch(wd inotify.Wd) error { + return o.in.RmWatch(wd); +} + +func (o *Watcher) Close() { + func() { + defer recover() + close(o.events) + close(o.errors) + }() + go o.in.Close() +} + +func (o *Watcher) worker() { + defer recover() + for { + ev, err := o.in.Read(); + if ev.Wd >= 0 { + o.events <- ev + } + if err != nil { + if err.(*os.SyscallError).Err == syscall.EBADF { + o.Close() + } + o.errors <- err + } + } +} |