Package name.pachler.nio.file.impl

# Class LinuxPathWatchService

java.lang.Object
name.pachler.nio.file.WatchService
name.pachler.nio.file.impl.PathWatchService
name.pachler.nio.file.impl.LinuxPathWatchService

All Implemented Interfaces:
  `Closeable`, `AutoCloseable`

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():

- the close() method can be called: The expectation is that calls blocked in take() or poll() will throw a ClosedWatchServiceException() more or less immediately after close() returns.
- Another thread also calls poll() or take(), but maybe with a timeout value that would make it terminate earlier than the first thread. This thread may not be blocked by the first thread
- poll() and take() are expected to only retreive one event, but read() call on an inotify file descriptor can return multiple events at once; there is no reliable way only retrieve one event. As a consequence, the reading thread needs to ensure that other waiting threads receive the remaining events

- 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

## Field Summary

Fields
Modifier and Type
Field
Description
`static final byte`
`CMD_CLOSE`
`static final byte`
`CMD_NOTIFY`

### Fields inherited from class name.pachler.nio.file.impl.PathWatchService

`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 Summary

Constructors
Constructor
Description
`LinuxPathWatchService()`

## Method Summary

Modifier and Type
Method
Description
`void`
`close()`
`protected void`
`finalize()`
`WatchKey`
`poll()`
`WatchKey`
`poll(long timeout, TimeUnit unit)`
`PathWatchKey`
`register(Path path, WatchEvent.Kind<?>[] kinds, WatchEvent.Modifier[] modifiers)`
`boolean`
`reset(PathWatchKey pathWatchKey)`
`WatchKey`
`take()`

### Methods inherited from class name.pachler.nio.file.impl.PathWatchService

`checkAndCastToPathImpl, makeFlagMask`

### Methods inherited from class java.lang.Object

`clone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

## Field Details

### CMD_CLOSE

public static final byte CMD_CLOSE

See Also:
  - Constant Field Values

### CMD_NOTIFY

public static final byte CMD_NOTIFY

See Also:
  - Constant Field Values

## Constructor Details

### LinuxPathWatchService

public LinuxPathWatchService()

## Method Details

### finalize

protected void finalize() throws Throwable

Overrides:
  `finalize` in class `Object`
Throws:
  `Throwable`

### take

public WatchKey take() throws InterruptedException

Specified by:
  `take` in class `WatchService`
Throws:
  `InterruptedException`

### poll

public WatchKey poll() throws InterruptedException

Specified by:
  `poll` in class `WatchService`
Throws:
  `InterruptedException`

### poll

public WatchKey poll(long timeout, TimeUnit unit) throws InterruptedException, ClosedWatchServiceException

Specified by:
  `poll` in class `WatchService`
Throws:
  `InterruptedException`
  `ClosedWatchServiceException`

### close

public void close() throws IOException

Specified by:
  `close` in interface `AutoCloseable`
Specified by:
  `close` in interface `Closeable`
Specified by:
  `close` in class `WatchService`
Throws:
  `IOException`

### register

public PathWatchKey register(Path path, WatchEvent.Kind<?>[] kinds, WatchEvent.Modifier[] modifiers) throws IOException

Specified by:
  `register` in class `PathWatchService`
Throws:
  `IOException`

### reset

public boolean reset(PathWatchKey pathWatchKey)