Home | History | Annotate | Download | only in fs
      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<Request>();
     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<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      */
     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 }
    291