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);
}
/**