src/java.base/windows/classes/sun/nio/fs/WindowsWatchService.java

Print this page

        

*** 23,35 **** * questions. */ package sun.nio.fs; - import java.nio.file.*; import java.io.IOException; ! import java.util.*; import com.sun.nio.file.ExtendedWatchEventModifier; import sun.misc.Unsafe; import static sun.nio.fs.WindowsNativeDispatcher.*; import static sun.nio.fs.WindowsConstants.*; --- 23,42 ---- * questions. */ package sun.nio.fs; import java.io.IOException; ! import java.nio.file.NotDirectoryException; ! import java.nio.file.Path; ! import java.nio.file.StandardWatchEventKinds; ! import java.nio.file.WatchEvent; ! import java.nio.file.WatchKey; ! import java.util.HashMap; ! import java.util.Map; ! import java.util.Set; ! import com.sun.nio.file.ExtendedWatchEventModifier; import sun.misc.Unsafe; import static sun.nio.fs.WindowsNativeDispatcher.*; import static sun.nio.fs.WindowsConstants.*;
*** 40,50 **** class WindowsWatchService extends AbstractWatchService { private final static int WAKEUP_COMPLETION_KEY = 0; - private final Unsafe unsafe = Unsafe.getUnsafe(); // background thread to service I/O completion port private final Poller poller; /** --- 47,56 ----
*** 80,90 **** } /** * Windows implementation of WatchKey. */ ! private class WindowsWatchKey extends AbstractWatchKey { // file key (used to detect existing registrations) private final FileKey fileKey; // handle to directory private volatile long handle = INVALID_HANDLE_VALUE; --- 86,96 ---- } /** * Windows implementation of WatchKey. */ ! private static class WindowsWatchKey extends AbstractWatchKey { // file key (used to detect existing registrations) private final FileKey fileKey; // handle to directory private volatile long handle = INVALID_HANDLE_VALUE;
*** 167,185 **** int completionKey() { return completionKey; } ! // close directory and release buffer ! void releaseResources() { ! CloseHandle(handle); ! buffer.cleaner().clean(); ! } ! ! // Invalidate key by closing directory and releasing buffer void invalidate() { ! releaseResources(); handle = INVALID_HANDLE_VALUE; buffer = null; countAddress = 0; overlappedAddress = 0; } --- 173,185 ---- int completionKey() { return completionKey; } ! // Invalidate the key, assumes that resources have been released void invalidate() { ! ((WindowsWatchService)watcher()).poller.releaseResources(this); handle = INVALID_HANDLE_VALUE; buffer = null; countAddress = 0; overlappedAddress = 0; }
*** 191,201 **** @Override public void cancel() { if (isValid()) { // delegate to poller ! poller.cancel(this); } } } // file key to unique identify (open) directory --- 191,201 ---- @Override public void cancel() { if (isValid()) { // delegate to poller ! ((WindowsWatchService)watcher()).poller.cancel(this); } } } // file key to unique identify (open) directory
*** 239,260 **** FILE_NOTIFY_CHANGE_SECURITY; /** * Background thread to service I/O completion port. */ ! private class Poller extends AbstractPoller { /* * typedef struct _OVERLAPPED { ! * DWORD Internal; ! * DWORD InternalHigh; ! * DWORD Offset; ! * DWORD OffsetHigh; * HANDLE hEvent; * } OVERLAPPED; */ private static final short SIZEOF_DWORD = 4; private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit /* * typedef struct _FILE_NOTIFY_INFORMATION { * DWORD NextEntryOffset; * DWORD Action; --- 239,267 ---- FILE_NOTIFY_CHANGE_SECURITY; /** * Background thread to service I/O completion port. */ ! private static class Poller extends AbstractPoller { ! private final static Unsafe UNSAFE = Unsafe.getUnsafe(); ! /* * typedef struct _OVERLAPPED { ! * ULONG_PTR Internal; ! * ULONG_PTR InternalHigh; ! * union { ! * struct { DWORD Offset; DWORD OffsetHigh; }; ! * PVOID Pointer; ! * }; * HANDLE hEvent; * } OVERLAPPED; */ private static final short SIZEOF_DWORD = 4; private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit + private static final short OFFSETOF_HEVENT = + (UNSAFE.addressSize() == 4) ? (short) 16 : 24; + /* * typedef struct _FILE_NOTIFY_INFORMATION { * DWORD NextEntryOffset; * DWORD Action;
*** 274,287 **** private final WindowsFileSystem fs; private final WindowsWatchService watcher; private final long port; // maps completion key to WatchKey ! private final Map<Integer,WindowsWatchKey> ck2key; // maps file key to WatchKey ! private final Map<FileKey,WindowsWatchKey> fk2key; // unique completion key for each directory // native completion key capacity is 64 bits on Win64. private int lastCompletionKey; --- 281,294 ---- private final WindowsFileSystem fs; private final WindowsWatchService watcher; private final long port; // maps completion key to WatchKey ! private final Map<Integer, WindowsWatchKey> ck2key; // maps file key to WatchKey ! private final Map<FileKey, WindowsWatchKey> fk2key; // unique completion key for each directory // native completion key capacity is 64 bits on Win64. private int lastCompletionKey;
*** 391,410 **** --- 398,423 ---- long bufferAddress = buffer.address(); long overlappedAddress = bufferAddress + size - SIZEOF_OVERLAPPED; long countAddress = overlappedAddress - SIZEOF_DWORD; + // zero the overlapped structure + UNSAFE.setMemory(overlappedAddress, SIZEOF_OVERLAPPED, (byte)0); + // start async read of changes to directory try { + createAndAttachEvent(overlappedAddress); + ReadDirectoryChangesW(handle, bufferAddress, CHANGES_BUFFER_SIZE, watchSubtree, ALL_FILE_NOTIFY_EVENTS, countAddress, overlappedAddress); } catch (WindowsException x) { + closeAttachedEvent(overlappedAddress); buffer.release(); return new IOException(x.getMessage()); } WindowsWatchKey watchKey;
*** 419,429 **** // directory already registered so need to: // 1. remove mapping from old completion key to existing watch key // 2. release existing key's resources (handle/buffer) // 3. re-initialize key with new handle/buffer ck2key.remove(existing.completionKey()); ! existing.releaseResources(); watchKey = existing.init(handle, events, watchSubtree, buffer, countAddress, overlappedAddress, completionKey); } // map completion map to watch key ck2key.put(completionKey, watchKey); --- 432,442 ---- // directory already registered so need to: // 1. remove mapping from old completion key to existing watch key // 2. release existing key's resources (handle/buffer) // 3. re-initialize key with new handle/buffer ck2key.remove(existing.completionKey()); ! releaseResources(existing); watchKey = existing.init(handle, events, watchSubtree, buffer, countAddress, overlappedAddress, completionKey); } // map completion map to watch key ck2key.put(completionKey, watchKey);
*** 434,443 **** --- 447,492 ---- } finally { if (!registered) CloseHandle(handle); } } + /** + * Cancels the outstanding I/O operation on the directory + * associated with the given key and releases the associated + * resources. + */ + private void releaseResources(WindowsWatchKey key) { + try { + CancelIo(key.handle()); + GetOverlappedResult(key.handle(), key.overlappedAddress()); + } catch (WindowsException expected) { + // expected as I/O operation has been cancelled + } + CloseHandle(key.handle()); + closeAttachedEvent(key.overlappedAddress()); + key.buffer().cleaner().clean(); + } + + /** + * Creates an unnamed event and set it as the hEvent field + * in the given OVERLAPPED structure + */ + private void createAndAttachEvent(long ov) throws WindowsException { + long hEvent = CreateEvent(false, false); + UNSAFE.putAddress(ov + OFFSETOF_HEVENT, hEvent); + } + + /** + * Closes the event attached to the given OVERLAPPED structure. A + * no-op if there isn't an event attached. + */ + private void closeAttachedEvent(long ov) { + long hEvent = UNSAFE.getAddress(ov + OFFSETOF_HEVENT); + if (hEvent != 0 && hEvent != INVALID_HANDLE_VALUE) + CloseHandle(hEvent); + } + // cancel single key @Override void implCancelKey(WatchKey obj) { WindowsWatchKey key = (WindowsWatchKey)obj; if (key.isValid()) {
*** 449,471 **** // close watch service @Override void implCloseAll() { // cancel all keys ! for (Map.Entry<Integer, WindowsWatchKey> entry: ck2key.entrySet()) { ! entry.getValue().invalidate(); ! } fk2key.clear(); ck2key.clear(); // close I/O completion port CloseHandle(port); } // Translate file change action into watch event ! private WatchEvent.Kind<?> translateActionToEvent(int action) ! { switch (action) { case FILE_ACTION_MODIFIED : return StandardWatchEventKinds.ENTRY_MODIFY; case FILE_ACTION_ADDED : --- 498,518 ---- // close watch service @Override void implCloseAll() { // cancel all keys ! ck2key.values().forEach(WindowsWatchKey::invalidate); ! fk2key.clear(); ck2key.clear(); // close I/O completion port CloseHandle(port); } // Translate file change action into watch event ! private WatchEvent.Kind<?> translateActionToEvent(int action) { switch (action) { case FILE_ACTION_MODIFIED : return StandardWatchEventKinds.ENTRY_MODIFY; case FILE_ACTION_ADDED :
*** 485,516 **** private void processEvents(WindowsWatchKey key, int size) { long address = key.buffer().address(); int nextOffset; do { ! int action = unsafe.getInt(address + OFFSETOF_ACTION); // map action to event WatchEvent.Kind<?> kind = translateActionToEvent(action); if (key.events().contains(kind)) { // copy the name ! int nameLengthInBytes = unsafe.getInt(address + OFFSETOF_FILENAMELENGTH); if ((nameLengthInBytes % 2) != 0) { ! throw new AssertionError("FileNameLength.FileNameLength is not a multiple of 2"); } char[] nameAsArray = new char[nameLengthInBytes/2]; ! unsafe.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray, Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes); // create FileName and queue event WindowsPath name = WindowsPath .createFromNormalizedPath(fs, new String(nameAsArray)); key.signalEvent(kind, name); } // next event ! nextOffset = unsafe.getInt(address + OFFSETOF_NEXTENTRYOFFSET); address += (long)nextOffset; } while (nextOffset != 0); } /** --- 532,563 ---- private void processEvents(WindowsWatchKey key, int size) { long address = key.buffer().address(); int nextOffset; do { ! int action = UNSAFE.getInt(address + OFFSETOF_ACTION); // map action to event WatchEvent.Kind<?> kind = translateActionToEvent(action); if (key.events().contains(kind)) { // copy the name ! int nameLengthInBytes = UNSAFE.getInt(address + OFFSETOF_FILENAMELENGTH); if ((nameLengthInBytes % 2) != 0) { ! throw new AssertionError("FileNameLength is not a multiple of 2"); } char[] nameAsArray = new char[nameLengthInBytes/2]; ! UNSAFE.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray, Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes); // create FileName and queue event WindowsPath name = WindowsPath .createFromNormalizedPath(fs, new String(nameAsArray)); key.signalEvent(kind, name); } // next event ! nextOffset = UNSAFE.getInt(address + OFFSETOF_NEXTENTRYOFFSET); address += (long)nextOffset; } while (nextOffset != 0); } /**