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