Home | History | Annotate | Download | only in fs
      1 /*
      2  * Copyright (c) 2008, 2009, 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 sun.misc.Unsafe;
     29 import java.util.concurrent.ExecutionException;
     30 
     31 /**
     32  * Base implementation of a task (typically native) that polls a memory location
     33  * during execution so that it may be aborted/cancelled before completion. The
     34  * task is executed by invoking the {@link runInterruptibly} method defined
     35  * here and cancelled by invoking Thread.interrupt.
     36  */
     37 
     38 abstract class Cancellable implements Runnable {
     39     private static final Unsafe unsafe = Unsafe.getUnsafe();
     40 
     41     private final long pollingAddress;
     42     private final Object lock = new Object();
     43 
     44     // the following require lock when examining or changing
     45     private boolean completed;
     46     private Throwable exception;
     47 
     48     protected Cancellable() {
     49         pollingAddress = unsafe.allocateMemory(4);
     50         unsafe.putIntVolatile(null, pollingAddress, 0);
     51     }
     52 
     53     /**
     54      * Returns the memory address of a 4-byte int that should be polled to
     55      * detect cancellation.
     56      */
     57     protected long addressToPollForCancel() {
     58         return pollingAddress;
     59     }
     60 
     61     /**
     62      * The value to write to the polled memory location to indicate that the
     63      * task has been cancelled. If this method is not overridden then it
     64      * defaults to MAX_VALUE.
     65      */
     66     protected int cancelValue() {
     67         return Integer.MAX_VALUE;
     68     }
     69 
     70     /**
     71      * "cancels" the task by writing bits into memory location that it polled
     72      * by the task.
     73      */
     74     final void cancel() {
     75         synchronized (lock) {
     76             if (!completed) {
     77                 unsafe.putIntVolatile(null, pollingAddress, cancelValue());
     78             }
     79         }
     80     }
     81 
     82     /**
     83      * Returns the exception thrown by the task or null if the task completed
     84      * successfully.
     85      */
     86     private Throwable exception() {
     87         synchronized (lock) {
     88             return exception;
     89         }
     90     }
     91 
     92     @Override
     93     public final void run() {
     94         try {
     95             implRun();
     96         } catch (Throwable t) {
     97             synchronized (lock) {
     98                 exception = t;
     99             }
    100         } finally {
    101             synchronized (lock) {
    102                 completed = true;
    103                 unsafe.freeMemory(pollingAddress);
    104             }
    105         }
    106     }
    107 
    108     /**
    109      * The task body. This should periodically poll the memory location
    110      * to check for cancellation.
    111      */
    112     abstract void implRun() throws Throwable;
    113 
    114     /**
    115      * Invokes the given task in its own thread. If this (meaning the current)
    116      * thread is interrupted then an attempt is make to cancel the background
    117      * thread by writing into the memory location that it polls cooperatively.
    118      */
    119     static void runInterruptibly(Cancellable task) throws ExecutionException {
    120         Thread t = new Thread(task);
    121         t.start();
    122         boolean cancelledByInterrupt = false;
    123         while (t.isAlive()) {
    124             try {
    125                 t.join();
    126             } catch (InterruptedException e) {
    127                 cancelledByInterrupt = true;
    128                 task.cancel();
    129             }
    130         }
    131         if (cancelledByInterrupt)
    132             Thread.currentThread().interrupt();
    133         Throwable exc = task.exception();
    134         if (exc != null)
    135             throw new ExecutionException(exc);
    136     }
    137 }
    138