8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.nio.fs; 27 28 import java.nio.file.*; 29 import java.io.IOException; 30 import java.util.*; 31 import com.sun.nio.file.ExtendedWatchEventModifier; 32 import sun.misc.Unsafe; 33 34 import static sun.nio.fs.WindowsNativeDispatcher.*; 35 import static sun.nio.fs.WindowsConstants.*; 36 37 /* 38 * Win32 implementation of WatchService based on ReadDirectoryChangesW. 39 */ 40 41 class WindowsWatchService 42 extends AbstractWatchService 43 { 44 private final static int WAKEUP_COMPLETION_KEY = 0; 45 private final Unsafe unsafe = Unsafe.getUnsafe(); 46 47 // background thread to service I/O completion port 48 private final Poller poller; 49 50 /** 51 * Creates an I/O completion port and a daemon thread to service it 52 */ 53 WindowsWatchService(WindowsFileSystem fs) throws IOException { 54 // create I/O completion port 55 long port = 0L; 56 try { 57 port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0); 58 } catch (WindowsException x) { 59 throw new IOException(x.getMessage()); 60 } 61 62 this.poller = new Poller(fs, this, port); 63 this.poller.start(); 64 } 65 66 @Override 67 WatchKey register(Path path, 68 WatchEvent.Kind<?>[] events, 69 WatchEvent.Modifier... modifiers) 70 throws IOException 71 { 72 // delegate to poller 73 return poller.register(path, events, modifiers); 74 } 75 76 @Override 77 void implClose() throws IOException { 78 // delegate to poller 79 poller.close(); 80 } 81 82 /** 83 * Windows implementation of WatchKey. 84 */ 85 private class WindowsWatchKey extends AbstractWatchKey { 86 // file key (used to detect existing registrations) 87 private final FileKey fileKey; 88 89 // handle to directory 90 private volatile long handle = INVALID_HANDLE_VALUE; 91 92 // interest events 93 private Set<? extends WatchEvent.Kind<?>> events; 94 95 // subtree 96 private boolean watchSubtree; 97 98 // buffer for change events 99 private NativeBuffer buffer; 100 101 // pointer to bytes returned (in buffer) 102 private long countAddress; 103 104 // pointer to overlapped structure (in buffer) 105 private long overlappedAddress; 152 NativeBuffer buffer() { 153 return buffer; 154 } 155 156 long countAddress() { 157 return countAddress; 158 } 159 160 long overlappedAddress() { 161 return overlappedAddress; 162 } 163 164 FileKey fileKey() { 165 return fileKey; 166 } 167 168 int completionKey() { 169 return completionKey; 170 } 171 172 // close directory and release buffer 173 void releaseResources() { 174 CloseHandle(handle); 175 buffer.cleaner().clean(); 176 } 177 178 // Invalidate key by closing directory and releasing buffer 179 void invalidate() { 180 releaseResources(); 181 handle = INVALID_HANDLE_VALUE; 182 buffer = null; 183 countAddress = 0; 184 overlappedAddress = 0; 185 } 186 187 @Override 188 public boolean isValid() { 189 return handle != INVALID_HANDLE_VALUE; 190 } 191 192 @Override 193 public void cancel() { 194 if (isValid()) { 195 // delegate to poller 196 poller.cancel(this); 197 } 198 } 199 } 200 201 // file key to unique identify (open) directory 202 private static class FileKey { 203 private final int volSerialNumber; 204 private final int fileIndexHigh; 205 private final int fileIndexLow; 206 207 FileKey(int volSerialNumber, int fileIndexHigh, int fileIndexLow) { 208 this.volSerialNumber = volSerialNumber; 209 this.fileIndexHigh = fileIndexHigh; 210 this.fileIndexLow = fileIndexLow; 211 } 212 213 @Override 214 public int hashCode() { 215 return volSerialNumber ^ fileIndexHigh ^ fileIndexLow; 216 } 224 FileKey other = (FileKey)obj; 225 if (this.volSerialNumber != other.volSerialNumber) return false; 226 if (this.fileIndexHigh != other.fileIndexHigh) return false; 227 return this.fileIndexLow == other.fileIndexLow; 228 } 229 } 230 231 // all change events 232 private static final int ALL_FILE_NOTIFY_EVENTS = 233 FILE_NOTIFY_CHANGE_FILE_NAME | 234 FILE_NOTIFY_CHANGE_DIR_NAME | 235 FILE_NOTIFY_CHANGE_ATTRIBUTES | 236 FILE_NOTIFY_CHANGE_SIZE | 237 FILE_NOTIFY_CHANGE_LAST_WRITE | 238 FILE_NOTIFY_CHANGE_CREATION | 239 FILE_NOTIFY_CHANGE_SECURITY; 240 241 /** 242 * Background thread to service I/O completion port. 243 */ 244 private class Poller extends AbstractPoller { 245 /* 246 * typedef struct _OVERLAPPED { 247 * DWORD Internal; 248 * DWORD InternalHigh; 249 * DWORD Offset; 250 * DWORD OffsetHigh; 251 * HANDLE hEvent; 252 * } OVERLAPPED; 253 */ 254 private static final short SIZEOF_DWORD = 4; 255 private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit 256 257 /* 258 * typedef struct _FILE_NOTIFY_INFORMATION { 259 * DWORD NextEntryOffset; 260 * DWORD Action; 261 * DWORD FileNameLength; 262 * WCHAR FileName[1]; 263 * } FileNameLength; 264 */ 265 private static final short OFFSETOF_NEXTENTRYOFFSET = 0; 266 private static final short OFFSETOF_ACTION = 4; 267 private static final short OFFSETOF_FILENAMELENGTH = 8; 268 private static final short OFFSETOF_FILENAME = 12; 269 270 // size of per-directory buffer for events (FIXME - make this configurable) 271 // Need to be less than 4*16384 = 65536. DWORD align. 272 private static final int CHANGES_BUFFER_SIZE = 16 * 1024; 273 274 private final WindowsFileSystem fs; 275 private final WindowsWatchService watcher; 276 private final long port; 277 278 // maps completion key to WatchKey 279 private final Map<Integer,WindowsWatchKey> ck2key; 280 281 // maps file key to WatchKey 282 private final Map<FileKey,WindowsWatchKey> fk2key; 283 284 // unique completion key for each directory 285 // native completion key capacity is 64 bits on Win64. 286 private int lastCompletionKey; 287 288 Poller(WindowsFileSystem fs, WindowsWatchService watcher, long port) { 289 this.fs = fs; 290 this.watcher = watcher; 291 this.port = port; 292 this.ck2key = new HashMap<>(); 293 this.fk2key = new HashMap<>(); 294 this.lastCompletionKey = 0; 295 } 296 297 @Override 298 void wakeup() throws IOException { 299 try { 300 PostQueuedCompletionStatus(port, WAKEUP_COMPLETION_KEY); 301 } catch (WindowsException x) { 302 throw new IOException(x.getMessage()); 376 int completionKey = ++lastCompletionKey; 377 if (completionKey == WAKEUP_COMPLETION_KEY) 378 completionKey = ++lastCompletionKey; 379 380 // associate handle with completion port 381 try { 382 CreateIoCompletionPort(handle, port, completionKey); 383 } catch (WindowsException x) { 384 return new IOException(x.getMessage()); 385 } 386 387 // allocate memory for events, including space for other structures 388 // needed to do overlapped I/O 389 int size = CHANGES_BUFFER_SIZE + SIZEOF_DWORD + SIZEOF_OVERLAPPED; 390 NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); 391 392 long bufferAddress = buffer.address(); 393 long overlappedAddress = bufferAddress + size - SIZEOF_OVERLAPPED; 394 long countAddress = overlappedAddress - SIZEOF_DWORD; 395 396 // start async read of changes to directory 397 try { 398 ReadDirectoryChangesW(handle, 399 bufferAddress, 400 CHANGES_BUFFER_SIZE, 401 watchSubtree, 402 ALL_FILE_NOTIFY_EVENTS, 403 countAddress, 404 overlappedAddress); 405 } catch (WindowsException x) { 406 buffer.release(); 407 return new IOException(x.getMessage()); 408 } 409 410 WindowsWatchKey watchKey; 411 if (existing == null) { 412 // not registered so create new watch key 413 watchKey = new WindowsWatchKey(dir, watcher, fk) 414 .init(handle, events, watchSubtree, buffer, countAddress, 415 overlappedAddress, completionKey); 416 // map file key to watch key 417 fk2key.put(fk, watchKey); 418 } else { 419 // directory already registered so need to: 420 // 1. remove mapping from old completion key to existing watch key 421 // 2. release existing key's resources (handle/buffer) 422 // 3. re-initialize key with new handle/buffer 423 ck2key.remove(existing.completionKey()); 424 existing.releaseResources(); 425 watchKey = existing.init(handle, events, watchSubtree, buffer, 426 countAddress, overlappedAddress, completionKey); 427 } 428 // map completion map to watch key 429 ck2key.put(completionKey, watchKey); 430 431 registered = true; 432 return watchKey; 433 434 } finally { 435 if (!registered) CloseHandle(handle); 436 } 437 } 438 439 // cancel single key 440 @Override 441 void implCancelKey(WatchKey obj) { 442 WindowsWatchKey key = (WindowsWatchKey)obj; 443 if (key.isValid()) { 444 fk2key.remove(key.fileKey()); 445 ck2key.remove(key.completionKey()); 446 key.invalidate(); 447 } 448 } 449 450 // close watch service 451 @Override 452 void implCloseAll() { 453 // cancel all keys 454 for (Map.Entry<Integer, WindowsWatchKey> entry: ck2key.entrySet()) { 455 entry.getValue().invalidate(); 456 } 457 fk2key.clear(); 458 ck2key.clear(); 459 460 // close I/O completion port 461 CloseHandle(port); 462 } 463 464 // Translate file change action into watch event 465 private WatchEvent.Kind<?> translateActionToEvent(int action) 466 { 467 switch (action) { 468 case FILE_ACTION_MODIFIED : 469 return StandardWatchEventKinds.ENTRY_MODIFY; 470 471 case FILE_ACTION_ADDED : 472 case FILE_ACTION_RENAMED_NEW_NAME : 473 return StandardWatchEventKinds.ENTRY_CREATE; 474 475 case FILE_ACTION_REMOVED : 476 case FILE_ACTION_RENAMED_OLD_NAME : 477 return StandardWatchEventKinds.ENTRY_DELETE; 478 479 default : 480 return null; // action not recognized 481 } 482 } 483 484 // process events (list of FILE_NOTIFY_INFORMATION structures) 485 private void processEvents(WindowsWatchKey key, int size) { 486 long address = key.buffer().address(); 487 488 int nextOffset; 489 do { 490 int action = unsafe.getInt(address + OFFSETOF_ACTION); 491 492 // map action to event 493 WatchEvent.Kind<?> kind = translateActionToEvent(action); 494 if (key.events().contains(kind)) { 495 // copy the name 496 int nameLengthInBytes = unsafe.getInt(address + OFFSETOF_FILENAMELENGTH); 497 if ((nameLengthInBytes % 2) != 0) { 498 throw new AssertionError("FileNameLength.FileNameLength is not a multiple of 2"); 499 } 500 char[] nameAsArray = new char[nameLengthInBytes/2]; 501 unsafe.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray, 502 Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes); 503 504 // create FileName and queue event 505 WindowsPath name = WindowsPath 506 .createFromNormalizedPath(fs, new String(nameAsArray)); 507 key.signalEvent(kind, name); 508 } 509 510 // next event 511 nextOffset = unsafe.getInt(address + OFFSETOF_NEXTENTRYOFFSET); 512 address += (long)nextOffset; 513 } while (nextOffset != 0); 514 } 515 516 /** 517 * Poller main loop 518 */ 519 @Override 520 public void run() { 521 for (;;) { 522 CompletionStatus info; 523 try { 524 info = GetQueuedCompletionStatus(port); 525 } catch (WindowsException x) { 526 // this should not happen 527 x.printStackTrace(); 528 return; 529 } 530 531 // wakeup | 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.nio.fs; 27 28 import java.io.IOException; 29 import java.nio.file.NotDirectoryException; 30 import java.nio.file.Path; 31 import java.nio.file.StandardWatchEventKinds; 32 import java.nio.file.WatchEvent; 33 import java.nio.file.WatchKey; 34 import java.util.HashMap; 35 import java.util.Map; 36 import java.util.Set; 37 38 import com.sun.nio.file.ExtendedWatchEventModifier; 39 import sun.misc.Unsafe; 40 41 import static sun.nio.fs.WindowsNativeDispatcher.*; 42 import static sun.nio.fs.WindowsConstants.*; 43 44 /* 45 * Win32 implementation of WatchService based on ReadDirectoryChangesW. 46 */ 47 48 class WindowsWatchService 49 extends AbstractWatchService 50 { 51 private final static int WAKEUP_COMPLETION_KEY = 0; 52 53 // background thread to service I/O completion port 54 private final Poller poller; 55 56 /** 57 * Creates an I/O completion port and a daemon thread to service it 58 */ 59 WindowsWatchService(WindowsFileSystem fs) throws IOException { 60 // create I/O completion port 61 long port = 0L; 62 try { 63 port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0); 64 } catch (WindowsException x) { 65 throw new IOException(x.getMessage()); 66 } 67 68 this.poller = new Poller(fs, this, port); 69 this.poller.start(); 70 } 71 72 @Override 73 WatchKey register(Path path, 74 WatchEvent.Kind<?>[] events, 75 WatchEvent.Modifier... modifiers) 76 throws IOException 77 { 78 // delegate to poller 79 return poller.register(path, events, modifiers); 80 } 81 82 @Override 83 void implClose() throws IOException { 84 // delegate to poller 85 poller.close(); 86 } 87 88 /** 89 * Windows implementation of WatchKey. 90 */ 91 private static class WindowsWatchKey extends AbstractWatchKey { 92 // file key (used to detect existing registrations) 93 private final FileKey fileKey; 94 95 // handle to directory 96 private volatile long handle = INVALID_HANDLE_VALUE; 97 98 // interest events 99 private Set<? extends WatchEvent.Kind<?>> events; 100 101 // subtree 102 private boolean watchSubtree; 103 104 // buffer for change events 105 private NativeBuffer buffer; 106 107 // pointer to bytes returned (in buffer) 108 private long countAddress; 109 110 // pointer to overlapped structure (in buffer) 111 private long overlappedAddress; 158 NativeBuffer buffer() { 159 return buffer; 160 } 161 162 long countAddress() { 163 return countAddress; 164 } 165 166 long overlappedAddress() { 167 return overlappedAddress; 168 } 169 170 FileKey fileKey() { 171 return fileKey; 172 } 173 174 int completionKey() { 175 return completionKey; 176 } 177 178 // Invalidate the key, assumes that resources have been released 179 void invalidate() { 180 ((WindowsWatchService)watcher()).poller.releaseResources(this); 181 handle = INVALID_HANDLE_VALUE; 182 buffer = null; 183 countAddress = 0; 184 overlappedAddress = 0; 185 } 186 187 @Override 188 public boolean isValid() { 189 return handle != INVALID_HANDLE_VALUE; 190 } 191 192 @Override 193 public void cancel() { 194 if (isValid()) { 195 // delegate to poller 196 ((WindowsWatchService)watcher()).poller.cancel(this); 197 } 198 } 199 } 200 201 // file key to unique identify (open) directory 202 private static class FileKey { 203 private final int volSerialNumber; 204 private final int fileIndexHigh; 205 private final int fileIndexLow; 206 207 FileKey(int volSerialNumber, int fileIndexHigh, int fileIndexLow) { 208 this.volSerialNumber = volSerialNumber; 209 this.fileIndexHigh = fileIndexHigh; 210 this.fileIndexLow = fileIndexLow; 211 } 212 213 @Override 214 public int hashCode() { 215 return volSerialNumber ^ fileIndexHigh ^ fileIndexLow; 216 } 224 FileKey other = (FileKey)obj; 225 if (this.volSerialNumber != other.volSerialNumber) return false; 226 if (this.fileIndexHigh != other.fileIndexHigh) return false; 227 return this.fileIndexLow == other.fileIndexLow; 228 } 229 } 230 231 // all change events 232 private static final int ALL_FILE_NOTIFY_EVENTS = 233 FILE_NOTIFY_CHANGE_FILE_NAME | 234 FILE_NOTIFY_CHANGE_DIR_NAME | 235 FILE_NOTIFY_CHANGE_ATTRIBUTES | 236 FILE_NOTIFY_CHANGE_SIZE | 237 FILE_NOTIFY_CHANGE_LAST_WRITE | 238 FILE_NOTIFY_CHANGE_CREATION | 239 FILE_NOTIFY_CHANGE_SECURITY; 240 241 /** 242 * Background thread to service I/O completion port. 243 */ 244 private static class Poller extends AbstractPoller { 245 private final static Unsafe UNSAFE = Unsafe.getUnsafe(); 246 247 /* 248 * typedef struct _OVERLAPPED { 249 * ULONG_PTR Internal; 250 * ULONG_PTR InternalHigh; 251 * union { 252 * struct { DWORD Offset; DWORD OffsetHigh; }; 253 * PVOID Pointer; 254 * }; 255 * HANDLE hEvent; 256 * } OVERLAPPED; 257 */ 258 private static final short SIZEOF_DWORD = 4; 259 private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit 260 private static final short OFFSETOF_HEVENT = 261 (UNSAFE.addressSize() == 4) ? (short) 16 : 24; 262 263 264 /* 265 * typedef struct _FILE_NOTIFY_INFORMATION { 266 * DWORD NextEntryOffset; 267 * DWORD Action; 268 * DWORD FileNameLength; 269 * WCHAR FileName[1]; 270 * } FileNameLength; 271 */ 272 private static final short OFFSETOF_NEXTENTRYOFFSET = 0; 273 private static final short OFFSETOF_ACTION = 4; 274 private static final short OFFSETOF_FILENAMELENGTH = 8; 275 private static final short OFFSETOF_FILENAME = 12; 276 277 // size of per-directory buffer for events (FIXME - make this configurable) 278 // Need to be less than 4*16384 = 65536. DWORD align. 279 private static final int CHANGES_BUFFER_SIZE = 16 * 1024; 280 281 private final WindowsFileSystem fs; 282 private final WindowsWatchService watcher; 283 private final long port; 284 285 // maps completion key to WatchKey 286 private final Map<Integer, WindowsWatchKey> ck2key; 287 288 // maps file key to WatchKey 289 private final Map<FileKey, WindowsWatchKey> fk2key; 290 291 // unique completion key for each directory 292 // native completion key capacity is 64 bits on Win64. 293 private int lastCompletionKey; 294 295 Poller(WindowsFileSystem fs, WindowsWatchService watcher, long port) { 296 this.fs = fs; 297 this.watcher = watcher; 298 this.port = port; 299 this.ck2key = new HashMap<>(); 300 this.fk2key = new HashMap<>(); 301 this.lastCompletionKey = 0; 302 } 303 304 @Override 305 void wakeup() throws IOException { 306 try { 307 PostQueuedCompletionStatus(port, WAKEUP_COMPLETION_KEY); 308 } catch (WindowsException x) { 309 throw new IOException(x.getMessage()); 383 int completionKey = ++lastCompletionKey; 384 if (completionKey == WAKEUP_COMPLETION_KEY) 385 completionKey = ++lastCompletionKey; 386 387 // associate handle with completion port 388 try { 389 CreateIoCompletionPort(handle, port, completionKey); 390 } catch (WindowsException x) { 391 return new IOException(x.getMessage()); 392 } 393 394 // allocate memory for events, including space for other structures 395 // needed to do overlapped I/O 396 int size = CHANGES_BUFFER_SIZE + SIZEOF_DWORD + SIZEOF_OVERLAPPED; 397 NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); 398 399 long bufferAddress = buffer.address(); 400 long overlappedAddress = bufferAddress + size - SIZEOF_OVERLAPPED; 401 long countAddress = overlappedAddress - SIZEOF_DWORD; 402 403 // zero the overlapped structure 404 UNSAFE.setMemory(overlappedAddress, SIZEOF_OVERLAPPED, (byte)0); 405 406 // start async read of changes to directory 407 try { 408 createAndAttachEvent(overlappedAddress); 409 410 ReadDirectoryChangesW(handle, 411 bufferAddress, 412 CHANGES_BUFFER_SIZE, 413 watchSubtree, 414 ALL_FILE_NOTIFY_EVENTS, 415 countAddress, 416 overlappedAddress); 417 } catch (WindowsException x) { 418 closeAttachedEvent(overlappedAddress); 419 buffer.release(); 420 return new IOException(x.getMessage()); 421 } 422 423 WindowsWatchKey watchKey; 424 if (existing == null) { 425 // not registered so create new watch key 426 watchKey = new WindowsWatchKey(dir, watcher, fk) 427 .init(handle, events, watchSubtree, buffer, countAddress, 428 overlappedAddress, completionKey); 429 // map file key to watch key 430 fk2key.put(fk, watchKey); 431 } else { 432 // directory already registered so need to: 433 // 1. remove mapping from old completion key to existing watch key 434 // 2. release existing key's resources (handle/buffer) 435 // 3. re-initialize key with new handle/buffer 436 ck2key.remove(existing.completionKey()); 437 releaseResources(existing); 438 watchKey = existing.init(handle, events, watchSubtree, buffer, 439 countAddress, overlappedAddress, completionKey); 440 } 441 // map completion map to watch key 442 ck2key.put(completionKey, watchKey); 443 444 registered = true; 445 return watchKey; 446 447 } finally { 448 if (!registered) CloseHandle(handle); 449 } 450 } 451 452 /** 453 * Cancels the outstanding I/O operation on the directory 454 * associated with the given key and releases the associated 455 * resources. 456 */ 457 private void releaseResources(WindowsWatchKey key) { 458 try { 459 CancelIo(key.handle()); 460 GetOverlappedResult(key.handle(), key.overlappedAddress()); 461 } catch (WindowsException expected) { 462 // expected as I/O operation has been cancelled 463 } 464 CloseHandle(key.handle()); 465 closeAttachedEvent(key.overlappedAddress()); 466 key.buffer().cleaner().clean(); 467 } 468 469 /** 470 * Creates an unnamed event and set it as the hEvent field 471 * in the given OVERLAPPED structure 472 */ 473 private void createAndAttachEvent(long ov) throws WindowsException { 474 long hEvent = CreateEvent(false, false); 475 UNSAFE.putAddress(ov + OFFSETOF_HEVENT, hEvent); 476 } 477 478 /** 479 * Closes the event attached to the given OVERLAPPED structure. A 480 * no-op if there isn't an event attached. 481 */ 482 private void closeAttachedEvent(long ov) { 483 long hEvent = UNSAFE.getAddress(ov + OFFSETOF_HEVENT); 484 if (hEvent != 0 && hEvent != INVALID_HANDLE_VALUE) 485 CloseHandle(hEvent); 486 } 487 488 // cancel single key 489 @Override 490 void implCancelKey(WatchKey obj) { 491 WindowsWatchKey key = (WindowsWatchKey)obj; 492 if (key.isValid()) { 493 fk2key.remove(key.fileKey()); 494 ck2key.remove(key.completionKey()); 495 key.invalidate(); 496 } 497 } 498 499 // close watch service 500 @Override 501 void implCloseAll() { 502 // cancel all keys 503 ck2key.values().forEach(WindowsWatchKey::invalidate); 504 505 fk2key.clear(); 506 ck2key.clear(); 507 508 // close I/O completion port 509 CloseHandle(port); 510 } 511 512 // Translate file change action into watch event 513 private WatchEvent.Kind<?> translateActionToEvent(int action) { 514 switch (action) { 515 case FILE_ACTION_MODIFIED : 516 return StandardWatchEventKinds.ENTRY_MODIFY; 517 518 case FILE_ACTION_ADDED : 519 case FILE_ACTION_RENAMED_NEW_NAME : 520 return StandardWatchEventKinds.ENTRY_CREATE; 521 522 case FILE_ACTION_REMOVED : 523 case FILE_ACTION_RENAMED_OLD_NAME : 524 return StandardWatchEventKinds.ENTRY_DELETE; 525 526 default : 527 return null; // action not recognized 528 } 529 } 530 531 // process events (list of FILE_NOTIFY_INFORMATION structures) 532 private void processEvents(WindowsWatchKey key, int size) { 533 long address = key.buffer().address(); 534 535 int nextOffset; 536 do { 537 int action = UNSAFE.getInt(address + OFFSETOF_ACTION); 538 539 // map action to event 540 WatchEvent.Kind<?> kind = translateActionToEvent(action); 541 if (key.events().contains(kind)) { 542 // copy the name 543 int nameLengthInBytes = UNSAFE.getInt(address + OFFSETOF_FILENAMELENGTH); 544 if ((nameLengthInBytes % 2) != 0) { 545 throw new AssertionError("FileNameLength is not a multiple of 2"); 546 } 547 char[] nameAsArray = new char[nameLengthInBytes/2]; 548 UNSAFE.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray, 549 Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes); 550 551 // create FileName and queue event 552 WindowsPath name = WindowsPath 553 .createFromNormalizedPath(fs, new String(nameAsArray)); 554 key.signalEvent(kind, name); 555 } 556 557 // next event 558 nextOffset = UNSAFE.getInt(address + OFFSETOF_NEXTENTRYOFFSET); 559 address += (long)nextOffset; 560 } while (nextOffset != 0); 561 } 562 563 /** 564 * Poller main loop 565 */ 566 @Override 567 public void run() { 568 for (;;) { 569 CompletionStatus info; 570 try { 571 info = GetQueuedCompletionStatus(port); 572 } catch (WindowsException x) { 573 // this should not happen 574 x.printStackTrace(); 575 return; 576 } 577 578 // wakeup |