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
49 protected AbstractPoller() {
50 this.requestList = new LinkedList<>();
51 this.shutdown = false;
52 }
53
54 /**
55 * Starts the poller thread
56 */
57 public void start() {
58 final Runnable thisRunnable = this;
59 AccessController.doPrivileged(new PrivilegedAction<>() {
60 @Override
61 public Object run() {
62 Thread thr = new Thread(null, thisRunnable, "FileSystemWatchService", 0, false);
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 */
73 abstract void wakeup() throws IOException;
74
75 /**
76 * Executed by poller thread to register directory for changes
77 */
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 */
85 abstract void implCancelKey(WatchKey key);
86
87 /**
88 * Executed by poller thread to shutdown and cancel all keys
89 */
90 abstract void implCloseAll();
91
92 /**
93 * Requests, and waits on, poller thread to register given file.
94 */
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 */
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 */
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
166 Request(RequestType type, Object... params) {
167 this.type = type;
168 this.params = params;
169 }
170
171 RequestType type() {
172 return type;
173 }
174
175 Object[] parameters() {
176 return params;
177 }
178
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 */
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 */
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")
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 }
--- EOF ---