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 Cancellable()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 */ addressToPollForCancel()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 */ cancelValue()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 */ cancel()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 */ exception()86 private Throwable exception() { 87 synchronized (lock) { 88 return exception; 89 } 90 } 91 92 @Override run()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 */ implRun()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 */ runInterruptibly(Cancellable task)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