This is a fork of the since-deleted golang.org/x/exp/inotify
package. It breaks the API in an attempt to fix some reliability
problems:
-
inotify watches are attached to inodes rather than file paths. A single inode could have multiple file paths, or change its file path between when it is watched and when events are generated.
-
AddWatch
now returns a*Watch
representing the watch descriptor. Watches can be compared by pointer equality, as only one watch struct will be used for each watch descriptor. For convenience, the watch stores the path it was created for. -
There is no attempt to automatically use
IN_MASK_ADD
. The previous behaviour was unreliable, so you'll need to specify it manually if desired. -
Event
now includes aWatch
field for its associated watch (or nil for events not associated with a watch likeIN_Q_OVERFLOW
). TheName
field is no longer prefixed with the watch's path. -
The
Watcher.Error
channel has been removed. Errors are now reported whenClose
is called. -
The Watcher's mutex is now used to guard all access to the watches map.
-
When
RemoveWatch
is called, we don't completely forget about the watch until we receive an associatedIN_IGNORED
event. This way any queued events for the old watch can be correctly decoded. -
Reads from the inotify instance are performed through an
*os.File
. This should take advantage of the Go runtime's IO polling system and stop thereadEvents
goroutine from tieing up an OS thread.
There is still a potential concurrency issue with the approach I've taken. Imagine this sequence of events:
- user calls
RemoveWatch
for watch descriptor N - the
Read
call in thereadEvents
goroutine returns with events for watch descriptor N - user calls
AddWatch
, which happens to reuse watch descriptor N - the
readEvents
goroutine acquires the lock to decode the events it received
With this sequence of events, it would be unclear which watch the
event belonged to. According to this LKML post, watch descriptors
are allocated sequentially, and can only be reused after they wrap at
INT_MAX
. So this is unlikely to be a problem in practice.