Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/classes/sun/nio/fs/WindowsWatchService.java
32288 views
/*1* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.nio.fs;2627import java.io.IOException;28import java.nio.file.NotDirectoryException;29import java.nio.file.Path;30import java.nio.file.StandardWatchEventKinds;31import java.nio.file.WatchEvent;32import java.nio.file.WatchKey;33import java.util.HashMap;34import java.util.Map;35import java.util.Set;3637import com.sun.nio.file.ExtendedWatchEventModifier;38import sun.misc.Unsafe;3940import static sun.nio.fs.WindowsNativeDispatcher.*;41import static sun.nio.fs.WindowsConstants.*;4243/*44* Win32 implementation of WatchService based on ReadDirectoryChangesW.45*/4647class WindowsWatchService48extends AbstractWatchService49{50private final static int WAKEUP_COMPLETION_KEY = 0;5152// background thread to service I/O completion port53private final Poller poller;5455/**56* Creates an I/O completion port and a daemon thread to service it57*/58WindowsWatchService(WindowsFileSystem fs) throws IOException {59// create I/O completion port60long port = 0L;61try {62port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0);63} catch (WindowsException x) {64throw new IOException(x.getMessage());65}6667this.poller = new Poller(fs, this, port);68this.poller.start();69}7071@Override72WatchKey register(Path path,73WatchEvent.Kind<?>[] events,74WatchEvent.Modifier... modifiers)75throws IOException76{77// delegate to poller78return poller.register(path, events, modifiers);79}8081@Override82void implClose() throws IOException {83// delegate to poller84poller.close();85}8687/**88* Windows implementation of WatchKey.89*/90private static class WindowsWatchKey extends AbstractWatchKey {91// file key (used to detect existing registrations)92private final FileKey fileKey;9394// handle to directory95private volatile long handle = INVALID_HANDLE_VALUE;9697// interest events98private Set<? extends WatchEvent.Kind<?>> events;99100// subtree101private boolean watchSubtree;102103// buffer for change events104private NativeBuffer buffer;105106// pointer to bytes returned (in buffer)107private long countAddress;108109// pointer to overlapped structure (in buffer)110private long overlappedAddress;111112// completion key (used to map I/O completion to WatchKey)113private int completionKey;114115// flag indicates that ReadDirectoryChangesW failed116// and overlapped I/O operation wasn't started117private boolean errorStartingOverlapped;118119WindowsWatchKey(Path dir,120AbstractWatchService watcher,121FileKey fileKey)122{123super(dir, watcher);124this.fileKey = fileKey;125}126127WindowsWatchKey init(long handle,128Set<? extends WatchEvent.Kind<?>> events,129boolean watchSubtree,130NativeBuffer buffer,131long countAddress,132long overlappedAddress,133int completionKey)134{135this.handle = handle;136this.events = events;137this.watchSubtree = watchSubtree;138this.buffer = buffer;139this.countAddress = countAddress;140this.overlappedAddress = overlappedAddress;141this.completionKey = completionKey;142return this;143}144145long handle() {146return handle;147}148149Set<? extends WatchEvent.Kind<?>> events() {150return events;151}152153void setEvents(Set<? extends WatchEvent.Kind<?>> events) {154this.events = events;155}156157boolean watchSubtree() {158return watchSubtree;159}160161NativeBuffer buffer() {162return buffer;163}164165long countAddress() {166return countAddress;167}168169long overlappedAddress() {170return overlappedAddress;171}172173FileKey fileKey() {174return fileKey;175}176177int completionKey() {178return completionKey;179}180181void setErrorStartingOverlapped(boolean value) {182errorStartingOverlapped = value;183}184185boolean isErrorStartingOverlapped() {186return errorStartingOverlapped;187}188189// Invalidate the key, assumes that resources have been released190void invalidate() {191((WindowsWatchService)watcher()).poller.releaseResources(this);192handle = INVALID_HANDLE_VALUE;193buffer = null;194countAddress = 0;195overlappedAddress = 0;196errorStartingOverlapped = false;197}198199@Override200public boolean isValid() {201return handle != INVALID_HANDLE_VALUE;202}203204@Override205public void cancel() {206if (isValid()) {207// delegate to poller208((WindowsWatchService)watcher()).poller.cancel(this);209}210}211}212213// file key to unique identify (open) directory214private static class FileKey {215private final int volSerialNumber;216private final int fileIndexHigh;217private final int fileIndexLow;218219FileKey(int volSerialNumber, int fileIndexHigh, int fileIndexLow) {220this.volSerialNumber = volSerialNumber;221this.fileIndexHigh = fileIndexHigh;222this.fileIndexLow = fileIndexLow;223}224225@Override226public int hashCode() {227return volSerialNumber ^ fileIndexHigh ^ fileIndexLow;228}229230@Override231public boolean equals(Object obj) {232if (obj == this)233return true;234if (!(obj instanceof FileKey))235return false;236FileKey other = (FileKey)obj;237if (this.volSerialNumber != other.volSerialNumber) return false;238if (this.fileIndexHigh != other.fileIndexHigh) return false;239return this.fileIndexLow == other.fileIndexLow;240}241}242243// all change events244private static final int ALL_FILE_NOTIFY_EVENTS =245FILE_NOTIFY_CHANGE_FILE_NAME |246FILE_NOTIFY_CHANGE_DIR_NAME |247FILE_NOTIFY_CHANGE_ATTRIBUTES |248FILE_NOTIFY_CHANGE_SIZE |249FILE_NOTIFY_CHANGE_LAST_WRITE |250FILE_NOTIFY_CHANGE_CREATION |251FILE_NOTIFY_CHANGE_SECURITY;252253/**254* Background thread to service I/O completion port.255*/256private static class Poller extends AbstractPoller {257private final static Unsafe UNSAFE = Unsafe.getUnsafe();258259/*260* typedef struct _OVERLAPPED {261* ULONG_PTR Internal;262* ULONG_PTR InternalHigh;263* union {264* struct { DWORD Offset; DWORD OffsetHigh; };265* PVOID Pointer;266* };267* HANDLE hEvent;268* } OVERLAPPED;269*/270private static final short SIZEOF_DWORD = 4;271private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit272private static final short OFFSETOF_HEVENT =273(UNSAFE.addressSize() == 4) ? (short) 16 : 24;274275276/*277* typedef struct _FILE_NOTIFY_INFORMATION {278* DWORD NextEntryOffset;279* DWORD Action;280* DWORD FileNameLength;281* WCHAR FileName[1];282* } FileNameLength;283*/284private static final short OFFSETOF_NEXTENTRYOFFSET = 0;285private static final short OFFSETOF_ACTION = 4;286private static final short OFFSETOF_FILENAMELENGTH = 8;287private static final short OFFSETOF_FILENAME = 12;288289// size of per-directory buffer for events (FIXME - make this configurable)290// Need to be less than 4*16384 = 65536. DWORD align.291private static final int CHANGES_BUFFER_SIZE = 16 * 1024;292293private final WindowsFileSystem fs;294private final WindowsWatchService watcher;295private final long port;296297// maps completion key to WatchKey298private final Map<Integer, WindowsWatchKey> ck2key;299300// maps file key to WatchKey301private final Map<FileKey, WindowsWatchKey> fk2key;302303// unique completion key for each directory304// native completion key capacity is 64 bits on Win64.305private int lastCompletionKey;306307Poller(WindowsFileSystem fs, WindowsWatchService watcher, long port) {308this.fs = fs;309this.watcher = watcher;310this.port = port;311this.ck2key = new HashMap<>();312this.fk2key = new HashMap<>();313this.lastCompletionKey = 0;314}315316@Override317void wakeup() throws IOException {318try {319PostQueuedCompletionStatus(port, WAKEUP_COMPLETION_KEY);320} catch (WindowsException x) {321throw new IOException(x.getMessage());322}323}324325/**326* Register a directory for changes as follows:327*328* 1. Open directory329* 2. Read its attributes (and check it really is a directory)330* 3. Assign completion key and associated handle with completion port331* 4. Call ReadDirectoryChangesW to start (async) read of changes332* 5. Create or return existing key representing registration333*/334@Override335Object implRegister(Path obj,336Set<? extends WatchEvent.Kind<?>> events,337WatchEvent.Modifier... modifiers)338{339WindowsPath dir = (WindowsPath)obj;340boolean watchSubtree = false;341342// FILE_TREE modifier allowed343for (WatchEvent.Modifier modifier: modifiers) {344if (modifier == ExtendedWatchEventModifier.FILE_TREE) {345watchSubtree = true;346} else {347if (modifier == null)348return new NullPointerException();349if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier)350continue; // ignore351return new UnsupportedOperationException("Modifier not supported");352}353}354355// open directory356long handle;357try {358handle = CreateFile(dir.getPathForWin32Calls(),359FILE_LIST_DIRECTORY,360(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),361OPEN_EXISTING,362FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED);363} catch (WindowsException x) {364return x.asIOException(dir);365}366367boolean registered = false;368try {369// read attributes and check file is a directory370WindowsFileAttributes attrs;371try {372attrs = WindowsFileAttributes.readAttributes(handle);373} catch (WindowsException x) {374return x.asIOException(dir);375}376if (!attrs.isDirectory()) {377return new NotDirectoryException(dir.getPathForExceptionMessage());378}379380// check if this directory is already registered381FileKey fk = new FileKey(attrs.volSerialNumber(),382attrs.fileIndexHigh(),383attrs.fileIndexLow());384WindowsWatchKey existing = fk2key.get(fk);385386// if already registered and we're not changing the subtree387// modifier then simply update the event and return the key.388if (existing != null && watchSubtree == existing.watchSubtree()) {389existing.setEvents(events);390return existing;391}392393// Can overflow the int type capacity.394// Skip WAKEUP_COMPLETION_KEY value.395int completionKey = ++lastCompletionKey;396if (completionKey == WAKEUP_COMPLETION_KEY)397completionKey = ++lastCompletionKey;398399// associate handle with completion port400try {401CreateIoCompletionPort(handle, port, completionKey);402} catch (WindowsException x) {403return new IOException(x.getMessage());404}405406// allocate memory for events, including space for other structures407// needed to do overlapped I/O408int size = CHANGES_BUFFER_SIZE + SIZEOF_DWORD + SIZEOF_OVERLAPPED;409NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);410411long bufferAddress = buffer.address();412long overlappedAddress = bufferAddress + size - SIZEOF_OVERLAPPED;413long countAddress = overlappedAddress - SIZEOF_DWORD;414415// zero the overlapped structure416UNSAFE.setMemory(overlappedAddress, SIZEOF_OVERLAPPED, (byte)0);417418// start async read of changes to directory419try {420createAndAttachEvent(overlappedAddress);421422ReadDirectoryChangesW(handle,423bufferAddress,424CHANGES_BUFFER_SIZE,425watchSubtree,426ALL_FILE_NOTIFY_EVENTS,427countAddress,428overlappedAddress);429} catch (WindowsException x) {430closeAttachedEvent(overlappedAddress);431buffer.release();432return new IOException(x.getMessage());433}434435WindowsWatchKey watchKey;436if (existing == null) {437// not registered so create new watch key438watchKey = new WindowsWatchKey(dir, watcher, fk)439.init(handle, events, watchSubtree, buffer, countAddress,440overlappedAddress, completionKey);441// map file key to watch key442fk2key.put(fk, watchKey);443} else {444// directory already registered so need to:445// 1. remove mapping from old completion key to existing watch key446// 2. release existing key's resources (handle/buffer)447// 3. re-initialize key with new handle/buffer448ck2key.remove(existing.completionKey());449releaseResources(existing);450watchKey = existing.init(handle, events, watchSubtree, buffer,451countAddress, overlappedAddress, completionKey);452}453// map completion map to watch key454ck2key.put(completionKey, watchKey);455456registered = true;457return watchKey;458459} finally {460if (!registered) CloseHandle(handle);461}462}463464/**465* Cancels the outstanding I/O operation on the directory466* associated with the given key and releases the associated467* resources.468*/469private void releaseResources(WindowsWatchKey key) {470if (!key.isErrorStartingOverlapped()) {471try {472CancelIo(key.handle());473GetOverlappedResult(key.handle(), key.overlappedAddress());474} catch (WindowsException expected) {475// expected as I/O operation has been cancelled476}477}478CloseHandle(key.handle());479closeAttachedEvent(key.overlappedAddress());480key.buffer().cleaner().clean();481}482483/**484* Creates an unnamed event and set it as the hEvent field485* in the given OVERLAPPED structure486*/487private void createAndAttachEvent(long ov) throws WindowsException {488long hEvent = CreateEvent(false, false);489UNSAFE.putAddress(ov + OFFSETOF_HEVENT, hEvent);490}491492/**493* Closes the event attached to the given OVERLAPPED structure. A494* no-op if there isn't an event attached.495*/496private void closeAttachedEvent(long ov) {497long hEvent = UNSAFE.getAddress(ov + OFFSETOF_HEVENT);498if (hEvent != 0 && hEvent != INVALID_HANDLE_VALUE)499CloseHandle(hEvent);500}501502// cancel single key503@Override504void implCancelKey(WatchKey obj) {505WindowsWatchKey key = (WindowsWatchKey)obj;506if (key.isValid()) {507fk2key.remove(key.fileKey());508ck2key.remove(key.completionKey());509key.invalidate();510}511}512513// close watch service514@Override515void implCloseAll() {516// cancel all keys517ck2key.values().forEach(WindowsWatchKey::invalidate);518519fk2key.clear();520ck2key.clear();521522// close I/O completion port523CloseHandle(port);524}525526// Translate file change action into watch event527private WatchEvent.Kind<?> translateActionToEvent(int action) {528switch (action) {529case FILE_ACTION_MODIFIED :530return StandardWatchEventKinds.ENTRY_MODIFY;531532case FILE_ACTION_ADDED :533case FILE_ACTION_RENAMED_NEW_NAME :534return StandardWatchEventKinds.ENTRY_CREATE;535536case FILE_ACTION_REMOVED :537case FILE_ACTION_RENAMED_OLD_NAME :538return StandardWatchEventKinds.ENTRY_DELETE;539540default :541return null; // action not recognized542}543}544545// process events (list of FILE_NOTIFY_INFORMATION structures)546private void processEvents(WindowsWatchKey key, int size) {547long address = key.buffer().address();548549int nextOffset;550do {551int action = UNSAFE.getInt(address + OFFSETOF_ACTION);552553// map action to event554WatchEvent.Kind<?> kind = translateActionToEvent(action);555if (key.events().contains(kind)) {556// copy the name557int nameLengthInBytes = UNSAFE.getInt(address + OFFSETOF_FILENAMELENGTH);558if ((nameLengthInBytes % 2) != 0) {559throw new AssertionError("FileNameLength is not a multiple of 2");560}561char[] nameAsArray = new char[nameLengthInBytes/2];562UNSAFE.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray,563Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);564565// create FileName and queue event566WindowsPath name = WindowsPath567.createFromNormalizedPath(fs, new String(nameAsArray));568key.signalEvent(kind, name);569}570571// next event572nextOffset = UNSAFE.getInt(address + OFFSETOF_NEXTENTRYOFFSET);573address += (long)nextOffset;574} while (nextOffset != 0);575}576577/**578* Poller main loop579*/580@Override581public void run() {582for (;;) {583CompletionStatus info;584try {585info = GetQueuedCompletionStatus(port);586} catch (WindowsException x) {587// this should not happen588x.printStackTrace();589return;590}591592// wakeup593if (info.completionKey() == WAKEUP_COMPLETION_KEY) {594boolean shutdown = processRequests();595if (shutdown) {596return;597}598continue;599}600601// map completionKey to get WatchKey602WindowsWatchKey key = ck2key.get((int)info.completionKey());603if (key == null) {604// We get here when a registration is changed. In that case605// the directory is closed which causes an event with the606// old completion key.607continue;608}609610boolean criticalError = false;611int errorCode = info.error();612int messageSize = info.bytesTransferred();613if (errorCode == ERROR_NOTIFY_ENUM_DIR) {614// buffer overflow615key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);616} else if (errorCode != 0 && errorCode != ERROR_MORE_DATA) {617// ReadDirectoryChangesW failed618criticalError = true;619} else {620// ERROR_MORE_DATA is a warning about incomplite621// data transfer over TCP/UDP stack. For the case622// [messageSize] is zero in the most of cases.623624if (messageSize > 0) {625// process non-empty events.626processEvents(key, messageSize);627} else if (errorCode == 0) {628// insufficient buffer size629// not described, but can happen.630key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);631}632633// start read for next batch of changes634try {635ReadDirectoryChangesW(key.handle(),636key.buffer().address(),637CHANGES_BUFFER_SIZE,638key.watchSubtree(),639ALL_FILE_NOTIFY_EVENTS,640key.countAddress(),641key.overlappedAddress());642} catch (WindowsException x) {643// no choice but to cancel key644criticalError = true;645key.setErrorStartingOverlapped(true);646}647}648if (criticalError) {649implCancelKey(key);650key.signal();651}652}653}654}655}656657658