1 /*
2  * Copyright (c) 2008, 2011, 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.util.concurrent.*;
30 import java.io.IOException;
31 
32 /**
33  * Base implementation class for watch services.
34  */
35 
36 abstract class AbstractWatchService implements WatchService {
37 
38     // signaled keys waiting to be dequeued
39     private final LinkedBlockingDeque<WatchKey> pendingKeys =
40         new LinkedBlockingDeque<WatchKey>();
41 
42     // special key to indicate that watch service is closed
43     private final WatchKey CLOSE_KEY =
44         new AbstractWatchKey(null, null) {
45             @Override
46             public boolean isValid() {
47                 return true;
48             }
49 
50             @Override
51             public void cancel() {
52             }
53         };
54 
55     // used when closing watch service
56     private volatile boolean closed;
57     private final Object closeLock = new Object();
58 
AbstractWatchService()59     protected AbstractWatchService() {
60     }
61 
62     /**
63      * Register the given object with this watch service
64      */
register(Path path, WatchEvent.Kind<?>[] events, WatchEvent.Modifier... modifers)65     abstract WatchKey register(Path path,
66                                WatchEvent.Kind<?>[] events,
67                                WatchEvent.Modifier... modifers)
68         throws IOException;
69 
70     // used by AbstractWatchKey to enqueue key
enqueueKey(WatchKey key)71     final void enqueueKey(WatchKey key) {
72         pendingKeys.offer(key);
73     }
74 
75     /**
76      * Throws ClosedWatchServiceException if watch service is closed
77      */
checkOpen()78     private void checkOpen() {
79         if (closed)
80             throw new ClosedWatchServiceException();
81     }
82 
83     /**
84      * Checks the key isn't the special CLOSE_KEY used to unblock threads when
85      * the watch service is closed.
86      */
checkKey(WatchKey key)87     private void checkKey(WatchKey key) {
88         if (key == CLOSE_KEY) {
89             // re-queue in case there are other threads blocked in take/poll
90             enqueueKey(key);
91         }
92         checkOpen();
93     }
94 
95     @Override
poll()96     public final WatchKey poll() {
97         checkOpen();
98         WatchKey key = pendingKeys.poll();
99         checkKey(key);
100         return key;
101     }
102 
103     @Override
poll(long timeout, TimeUnit unit)104     public final WatchKey poll(long timeout, TimeUnit unit)
105         throws InterruptedException
106     {
107         checkOpen();
108         WatchKey key = pendingKeys.poll(timeout, unit);
109         checkKey(key);
110         return key;
111     }
112 
113     @Override
take()114     public final WatchKey take()
115         throws InterruptedException
116     {
117         checkOpen();
118         WatchKey key = pendingKeys.take();
119         checkKey(key);
120         return key;
121     }
122 
123     /**
124      * Tells whether or not this watch service is open.
125      */
isOpen()126     final boolean isOpen() {
127         return !closed;
128     }
129 
130     /**
131      * Retrieves the object upon which the close method synchronizes.
132      */
closeLock()133     final Object closeLock() {
134         return closeLock;
135     }
136 
137     /**
138      * Closes this watch service. This method is invoked by the close
139      * method to perform the actual work of closing the watch service.
140      */
implClose()141     abstract void implClose() throws IOException;
142 
143     @Override
close()144     public final void close()
145         throws IOException
146     {
147         synchronized (closeLock) {
148             // nothing to do if already closed
149             if (closed)
150                 return;
151             closed = true;
152 
153             implClose();
154 
155             // clear pending keys and queue special key to ensure that any
156             // threads blocked in take/poll wakeup
157             pendingKeys.clear();
158             pendingKeys.offer(CLOSE_KEY);
159         }
160     }
161 }
162