1 /*
   2  * Copyright (c) 2008, 2018, 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 
  59     protected AbstractWatchService() {
  60     }
  61 
  62     /**
  63      * Register the given object with this watch service
  64      */
  65     abstract WatchKey register(Path path,
  66                                WatchEvent.Kind<?>[] events,
  67                                WatchEvent.Modifier... modifiers)
  68         throws IOException;
  69 
  70     // used by AbstractWatchKey to enqueue key
  71     final void enqueueKey(WatchKey key) {
  72         pendingKeys.offer(key);
  73     }
  74 
  75     /**
  76      * Throws ClosedWatchServiceException if watch service is closed
  77      */
  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      */
  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
  96     public final WatchKey poll() {
  97         checkOpen();
  98         WatchKey key = pendingKeys.poll();
  99         checkKey(key);
 100         return key;
 101     }
 102 
 103     @Override
 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
 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      */
 126     final boolean isOpen() {
 127         return !closed;
 128     }
 129 
 130     /**
 131      * Retrieves the object upon which the close method synchronizes.
 132      */
 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      */
 141     abstract void implClose() throws IOException;
 142 
 143     @Override
 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 }