1 /* 2 * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 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.security.AccessController; 30 import java.security.PrivilegedAction; 31 import java.io.IOException; 32 import java.util.*; 33 import sun.misc.ManagedLocalsThread; 34 35 /** 36 * Base implementation of background poller thread used in watch service 37 * implementations. A poller thread waits on events from the file system and 38 * also services "requests" from clients to register for new events or cancel 39 * existing registrations. 40 */ 41 42 abstract class AbstractPoller implements Runnable { 43 44 // list of requests pending to the poller thread 45 private final LinkedList<Request> requestList; 46 47 // set to true when shutdown 48 private boolean shutdown; 49 50 protected AbstractPoller() { 51 this.requestList = new LinkedList<>(); 52 this.shutdown = false; 53 } 54 55 /** 56 * Starts the poller thread 57 */ 58 public void start() { 59 final Runnable thisRunnable = this; 60 AccessController.doPrivileged(new PrivilegedAction<>() { 61 @Override 62 public Object run() { 63 Thread thr = new ManagedLocalsThread(thisRunnable); 64 thr.setDaemon(true); 65 thr.start(); 66 return null; 67 } 68 }); 69 } 70 71 /** 72 * Wakeup poller thread so that it can service pending requests 73 */ 74 abstract void wakeup() throws IOException; 75 76 /** 77 * Executed by poller thread to register directory for changes 78 */ 79 abstract Object implRegister(Path path, 80 Set<? extends WatchEvent.Kind<?>> events, 81 WatchEvent.Modifier... modifiers); 82 83 /** 84 * Executed by poller thread to cancel key 85 */ 86 abstract void implCancelKey(WatchKey key); 87 88 /** 89 * Executed by poller thread to shutdown and cancel all keys 90 */ 91 abstract void implCloseAll(); 92 93 /** 94 * Requests, and waits on, poller thread to register given file. 95 */ 96 final WatchKey register(Path dir, 97 WatchEvent.Kind<?>[] events, 98 WatchEvent.Modifier... modifiers) 99 throws IOException 100 { 101 // validate arguments before request to poller 102 if (dir == null) 103 throw new NullPointerException(); 104 Set<WatchEvent.Kind<?>> eventSet = new HashSet<>(events.length); 105 for (WatchEvent.Kind<?> event: events) { 106 // standard events 107 if (event == StandardWatchEventKinds.ENTRY_CREATE || 108 event == StandardWatchEventKinds.ENTRY_MODIFY || 109 event == StandardWatchEventKinds.ENTRY_DELETE) 110 { 111 eventSet.add(event); 112 continue; 113 } 114 115 // OVERFLOW is ignored 116 if (event == StandardWatchEventKinds.OVERFLOW) 117 continue; 118 119 // null/unsupported 120 if (event == null) 121 throw new NullPointerException("An element in event set is 'null'"); 122 throw new UnsupportedOperationException(event.name()); 123 } 124 if (eventSet.isEmpty()) 125 throw new IllegalArgumentException("No events to register"); 126 return (WatchKey)invoke(RequestType.REGISTER, dir, eventSet, modifiers); 127 } 128 129 /** 130 * Cancels, and waits on, poller thread to cancel given key. 131 */ 132 final void cancel(WatchKey key) { 133 try { 134 invoke(RequestType.CANCEL, key); 135 } catch (IOException x) { 136 // should not happen 137 throw new AssertionError(x.getMessage()); 138 } 139 } 140 141 /** 142 * Shutdown poller thread 143 */ 144 final void close() throws IOException { 145 invoke(RequestType.CLOSE); 146 } 147 148 /** 149 * Types of request that the poller thread must handle 150 */ 151 private static enum RequestType { 152 REGISTER, 153 CANCEL, 154 CLOSE; 155 } 156 157 /** 158 * Encapsulates a request (command) to the poller thread. 159 */ 160 private static class Request { 161 private final RequestType type; 162 private final Object[] params; 163 164 private boolean completed = false; 165 private Object result = null; 166 167 Request(RequestType type, Object... params) { 168 this.type = type; 169 this.params = params; 170 } 171 172 RequestType type() { 173 return type; 174 } 175 176 Object[] parameters() { 177 return params; 178 } 179 180 void release(Object result) { 181 synchronized (this) { 182 this.completed = true; 183 this.result = result; 184 notifyAll(); 185 } 186 } 187 188 /** 189 * Await completion of the request. The return value is the result of 190 * the request. 191 */ 192 Object awaitResult() { 193 boolean interrupted = false; 194 synchronized (this) { 195 while (!completed) { 196 try { 197 wait(); 198 } catch (InterruptedException x) { 199 interrupted = true; 200 } 201 } 202 if (interrupted) 203 Thread.currentThread().interrupt(); 204 return result; 205 } 206 } 207 } 208 209 /** 210 * Enqueues request to poller thread and waits for result 211 */ 212 private Object invoke(RequestType type, Object... params) throws IOException { 213 // submit request 214 Request req = new Request(type, params); 215 synchronized (requestList) { 216 if (shutdown) { 217 throw new ClosedWatchServiceException(); 218 } 219 requestList.add(req); 220 } 221 222 // wakeup thread 223 wakeup(); 224 225 // wait for result 226 Object result = req.awaitResult(); 227 228 if (result instanceof RuntimeException) 229 throw (RuntimeException)result; 230 if (result instanceof IOException ) 231 throw (IOException)result; 232 return result; 233 } 234 235 /** 236 * Invoked by poller thread to process all pending requests 237 * 238 * @return true if poller thread should shutdown 239 */ 240 @SuppressWarnings("unchecked") 241 boolean processRequests() { 242 synchronized (requestList) { 243 Request req; 244 while ((req = requestList.poll()) != null) { 245 // if in process of shutdown then reject request 246 if (shutdown) { 247 req.release(new ClosedWatchServiceException()); 248 } 249 250 switch (req.type()) { 251 /** 252 * Register directory 253 */ 254 case REGISTER: { 255 Object[] params = req.parameters(); 256 Path path = (Path)params[0]; 257 Set<? extends WatchEvent.Kind<?>> events = 258 (Set<? extends WatchEvent.Kind<?>>)params[1]; 259 WatchEvent.Modifier[] modifiers = 260 (WatchEvent.Modifier[])params[2]; 261 req.release(implRegister(path, events, modifiers)); 262 break; 263 } 264 /** 265 * Cancel existing key 266 */ 267 case CANCEL : { 268 Object[] params = req.parameters(); 269 WatchKey key = (WatchKey)params[0]; 270 implCancelKey(key); 271 req.release(null); 272 break; 273 } 274 /** 275 * Close watch service 276 */ 277 case CLOSE: { 278 implCloseAll(); 279 req.release(null); 280 shutdown = true; 281 break; 282 } 283 284 default: 285 req.release(new IOException("request not recognized")); 286 } 287 } 288 } 289 return shutdown; 290 } 291 }