/** Poller main loop */ @Override public void run() { for (; ; ) { CompletionStatus info; try { info = GetQueuedCompletionStatus(port); } catch (WindowsException x) { // this should not happen x.printStackTrace(); return; } // wakeup if (info.completionKey() == WAKEUP_COMPLETION_KEY) { boolean shutdown = processRequests(); if (shutdown) { return; } continue; } // map completionKey to get WatchKey WindowsWatchKey key = ck2key.get((int) info.completionKey()); if (key == null) { // We get here when a registration is changed. In that case // the directory is closed which causes an event with the // old completion key. continue; } boolean criticalError = false; int errorCode = info.error(); int messageSize = info.bytesTransferred(); if (errorCode == ERROR_NOTIFY_ENUM_DIR) { // buffer overflow key.signalEvent(StandardWatchEventKinds.OVERFLOW, null); } else if (errorCode != 0 && errorCode != ERROR_MORE_DATA) { // ReadDirectoryChangesW failed criticalError = true; } else { // ERROR_MORE_DATA is a warning about incomplete // data transfer over TCP/UDP stack. For the case // [messageSize] is zero in the most of cases. if (messageSize > 0) { // process non-empty events. processEvents(key, messageSize); } else if (errorCode == 0) { // insufficient buffer size // not described, but can happen. key.signalEvent(StandardWatchEventKinds.OVERFLOW, null); } // start read for next batch of changes try { ReadDirectoryChangesW( key.handle(), key.buffer().address(), CHANGES_BUFFER_SIZE, key.watchSubtree(), ALL_FILE_NOTIFY_EVENTS, key.countAddress(), key.overlappedAddress()); } catch (WindowsException x) { // no choice but to cancel key criticalError = true; } } if (criticalError) { implCancelKey(key); key.signal(); } } }
/** * Register a directory for changes as follows: * * <p>1. Open directory 2. Read its attributes (and check it really is a directory) 3. Assign * completion key and associated handle with completion port 4. Call ReadDirectoryChangesW to * start (async) read of changes 5. Create or return existing key representing registration */ @Override Object implRegister( Path obj, Set<? extends WatchEvent.Kind<?>> events, WatchEvent.Modifier... modifiers) { WindowsPath dir = (WindowsPath) obj; boolean watchSubtree = false; // FILE_TREE modifier allowed for (WatchEvent.Modifier modifier : modifiers) { if (modifier == ExtendedWatchEventModifier.FILE_TREE) { watchSubtree = true; } else { if (modifier == null) return new NullPointerException(); if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier) continue; // ignore return new UnsupportedOperationException("Modifier not supported"); } } // open directory long handle; try { handle = CreateFile( dir.getPathForWin32Calls(), FILE_LIST_DIRECTORY, (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED); } catch (WindowsException x) { return x.asIOException(dir); } boolean registered = false; try { // read attributes and check file is a directory WindowsFileAttributes attrs; try { attrs = WindowsFileAttributes.readAttributes(handle); } catch (WindowsException x) { return x.asIOException(dir); } if (!attrs.isDirectory()) { return new NotDirectoryException(dir.getPathForExceptionMessage()); } // check if this directory is already registered FileKey fk = new FileKey(attrs.volSerialNumber(), attrs.fileIndexHigh(), attrs.fileIndexLow()); WindowsWatchKey existing = fk2key.get(fk); // if already registered and we're not changing the subtree // modifier then simply update the event and return the key. if (existing != null && watchSubtree == existing.watchSubtree()) { existing.setEvents(events); return existing; } // Can overflow the int type capacity. // Skip WAKEUP_COMPLETION_KEY value. int completionKey = ++lastCompletionKey; if (completionKey == WAKEUP_COMPLETION_KEY) completionKey = ++lastCompletionKey; // associate handle with completion port try { CreateIoCompletionPort(handle, port, completionKey); } catch (WindowsException x) { return new IOException(x.getMessage()); } // allocate memory for events, including space for other structures // needed to do overlapped I/O int size = CHANGES_BUFFER_SIZE + SIZEOF_DWORD + SIZEOF_OVERLAPPED; NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); 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; if (existing == null) { // not registered so create new watch key watchKey = new WindowsWatchKey(dir, watcher, fk) .init( handle, events, watchSubtree, buffer, countAddress, overlappedAddress, completionKey); // map file key to watch key fk2key.put(fk, watchKey); } else { // 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); registered = true; return watchKey; } finally { if (!registered) CloseHandle(handle); } }