Path: blob/master/src/java.base/share/classes/sun/nio/fs/AbstractPoller.java
41159 views
/*1* Copyright (c) 2008, 2021, 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.nio.file.*;28import java.security.AccessController;29import java.security.PrivilegedAction;30import java.io.IOException;31import java.util.*;3233/**34* Base implementation of background poller thread used in watch service35* implementations. A poller thread waits on events from the file system and36* also services "requests" from clients to register for new events or cancel37* existing registrations.38*/3940abstract class AbstractPoller implements Runnable {4142// list of requests pending to the poller thread43private final LinkedList<Request> requestList;4445// set to true when shutdown46private boolean shutdown;4748protected AbstractPoller() {49this.requestList = new LinkedList<>();50this.shutdown = false;51}5253/**54* Starts the poller thread55*/56@SuppressWarnings("removal")57public void start() {58final Runnable thisRunnable = this;59AccessController.doPrivileged(new PrivilegedAction<>() {60@Override61public Object run() {62Thread thr = new Thread(null,63thisRunnable,64"FileSystemWatchService",650,66false);67thr.setDaemon(true);68thr.start();69return null;70}71});72}7374/**75* Wakeup poller thread so that it can service pending requests76*/77abstract void wakeup() throws IOException;7879/**80* Executed by poller thread to register directory for changes81*/82abstract Object implRegister(Path path,83Set<? extends WatchEvent.Kind<?>> events,84WatchEvent.Modifier... modifiers);8586/**87* Executed by poller thread to cancel key88*/89abstract void implCancelKey(WatchKey key);9091/**92* Executed by poller thread to shutdown and cancel all keys93*/94abstract void implCloseAll();9596/**97* Requests, and waits on, poller thread to register given file.98*/99final WatchKey register(Path dir,100WatchEvent.Kind<?>[] events,101WatchEvent.Modifier... modifiers)102throws IOException103{104// validate arguments before request to poller105if (dir == null)106throw new NullPointerException();107Set<WatchEvent.Kind<?>> eventSet = new HashSet<>(events.length);108for (WatchEvent.Kind<?> event: events) {109// standard events110if (event == StandardWatchEventKinds.ENTRY_CREATE ||111event == StandardWatchEventKinds.ENTRY_MODIFY ||112event == StandardWatchEventKinds.ENTRY_DELETE)113{114eventSet.add(event);115continue;116}117118// OVERFLOW is ignored119if (event == StandardWatchEventKinds.OVERFLOW)120continue;121122// null/unsupported123if (event == null)124throw new NullPointerException("An element in event set is 'null'");125throw new UnsupportedOperationException(event.name());126}127if (eventSet.isEmpty())128throw new IllegalArgumentException("No events to register");129return (WatchKey)invoke(RequestType.REGISTER, dir, eventSet, modifiers);130}131132/**133* Cancels, and waits on, poller thread to cancel given key.134*/135final void cancel(WatchKey key) {136try {137invoke(RequestType.CANCEL, key);138} catch (IOException x) {139// should not happen140throw new AssertionError(x.getMessage());141}142}143144/**145* Shutdown poller thread146*/147final void close() throws IOException {148invoke(RequestType.CLOSE);149}150151/**152* Types of request that the poller thread must handle153*/154private static enum RequestType {155REGISTER,156CANCEL,157CLOSE;158}159160/**161* Encapsulates a request (command) to the poller thread.162*/163private static class Request {164private final RequestType type;165private final Object[] params;166167private boolean completed = false;168private Object result = null;169170Request(RequestType type, Object... params) {171this.type = type;172this.params = params;173}174175RequestType type() {176return type;177}178179Object[] parameters() {180return params;181}182183void release(Object result) {184synchronized (this) {185this.completed = true;186this.result = result;187notifyAll();188}189}190191/**192* Await completion of the request. The return value is the result of193* the request.194*/195Object awaitResult() {196boolean interrupted = false;197synchronized (this) {198while (!completed) {199try {200wait();201} catch (InterruptedException x) {202interrupted = true;203}204}205if (interrupted)206Thread.currentThread().interrupt();207return result;208}209}210}211212/**213* Enqueues request to poller thread and waits for result214*/215private Object invoke(RequestType type, Object... params) throws IOException {216// submit request217Request req = new Request(type, params);218synchronized (requestList) {219if (shutdown) {220throw new ClosedWatchServiceException();221}222requestList.add(req);223224// wakeup thread225wakeup();226}227228// wait for result229Object result = req.awaitResult();230231if (result instanceof RuntimeException)232throw (RuntimeException)result;233if (result instanceof IOException )234throw (IOException)result;235return result;236}237238/**239* Invoked by poller thread to process all pending requests240*241* @return true if poller thread should shutdown242*/243@SuppressWarnings("unchecked")244boolean processRequests() {245synchronized (requestList) {246Request req;247while ((req = requestList.poll()) != null) {248// if in process of shutdown then reject request249if (shutdown) {250req.release(new ClosedWatchServiceException());251continue;252}253254switch (req.type()) {255/**256* Register directory257*/258case REGISTER: {259Object[] params = req.parameters();260Path path = (Path)params[0];261Set<? extends WatchEvent.Kind<?>> events =262(Set<? extends WatchEvent.Kind<?>>)params[1];263WatchEvent.Modifier[] modifiers =264(WatchEvent.Modifier[])params[2];265req.release(implRegister(path, events, modifiers));266break;267}268/**269* Cancel existing key270*/271case CANCEL : {272Object[] params = req.parameters();273WatchKey key = (WatchKey)params[0];274implCancelKey(key);275req.release(null);276break;277}278/**279* Close watch service280*/281case CLOSE: {282implCloseAll();283req.release(null);284shutdown = true;285break;286}287288default:289req.release(new IOException("request not recognized"));290}291}292}293return shutdown;294}295}296297298