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

Print this page




   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