public class LinuxPathWatchService extends PathWatchService
This Linux implementation of the WatchService interface works without the use of threads or asynchronous I/O, using Linux' inotify file system event facitily.
The implementation hinges around select() to wait for events on the inotify file descriptor that each LinuxPathWatchService. Each time a WatchKey is registered (through Path.register(), which eventually calls register() on the LinuxPathWatchService), inotify_add_watch() is called on the service's inotify file descriptor. To wait for events, the take() and poll() methods use select() to wait for the inotify FD to become readable. However, a lot of things can happen while a thread is waiting inside poll() or take() with select():
With these requirements in mind (and with the desire not to create a separate
monitoring thread per WatchService like on Windows), the solution was to
perform management on the inotify descriptor with the threads that call
into poll()/take() on a first comes first served basis:
The first thread calling into pollImpl() (called from poll()/take()) becomes
the master thread. It is the only thread at any given time that services
the inotify file descriptor by calling select() and read(). The thread
looses it's master thread status when it is done with calling select()
and read().
All other threads that call into pollImpl() simply call wait(). When the
master thread is done, it calls notify() to wake up the next thread, which
might then become the master thread.
Also note that select() waits on two file descriptors: Because select() does not return when a file descriptor closes while it is waiting for it (for reasons that elude me), a command pipe is used which receives commands from other threads.
This is how close() is implemented: Instead of closing the inotify file descriptor
directly, it writes a command byte into the command pipe. If there is a
master thread, it wakes up, consumes the command byte and executes the
command (which is to close the inotify FD). If there is no master thread,
the thread calling close() closes the inotify file descriptor.
All this is necessary because a thread calling select() can't be interrupted,
and select() does not return when one of it's file descriptors is closed
while it is waiting for it
| Modifier and Type | Field and Description |
|---|---|
static byte |
CMD_CLOSE |
static byte |
CMD_NOTIFY |
FLAG_ACCURATE, FLAG_FILTER_ENTRY_CREATE, FLAG_FILTER_ENTRY_DELETE, FLAG_FILTER_ENTRY_MODIFY, FLAG_FILTER_ENTRY_RENAME_FROM, FLAG_FILTER_ENTRY_RENAME_TO, FLAG_FILTER_KEY_INVALID, FLAG_WATCH_SUBTREE| Constructor and Description |
|---|
LinuxPathWatchService() |
| Modifier and Type | Method and Description |
|---|---|
void |
close() |
protected void |
finalize() |
WatchKey |
poll() |
WatchKey |
poll(long timeout,
java.util.concurrent.TimeUnit unit) |
PathWatchKey |
register(Path path,
WatchEvent.Kind<?>[] kinds,
WatchEvent.Modifier[] modifiers) |
boolean |
reset(PathWatchKey pathWatchKey) |
WatchKey |
take() |
checkAndCastToPathImpl, makeFlagMaskpublic static final byte CMD_CLOSE
public static final byte CMD_NOTIFY
protected void finalize()
throws java.lang.Throwable
finalize in class java.lang.Objectjava.lang.Throwablepublic WatchKey take() throws java.lang.InterruptedException
take in class WatchServicejava.lang.InterruptedExceptionpublic WatchKey poll() throws java.lang.InterruptedException
poll in class WatchServicejava.lang.InterruptedExceptionpublic WatchKey poll(long timeout, java.util.concurrent.TimeUnit unit) throws java.lang.InterruptedException, ClosedWatchServiceException
poll in class WatchServicejava.lang.InterruptedExceptionClosedWatchServiceExceptionpublic void close()
throws java.io.IOException
close in interface java.io.Closeableclose in interface java.lang.AutoCloseableclose in class WatchServicejava.io.IOExceptionpublic PathWatchKey register(Path path, WatchEvent.Kind<?>[] kinds, WatchEvent.Modifier[] modifiers) throws java.io.IOException
register in class PathWatchServicejava.io.IOExceptionpublic boolean reset(PathWatchKey pathWatchKey)