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