diff --git a/src/share/classes/java/util/concurrent/FutureTask.java b/src/share/classes/java/util/concurrent/FutureTask.java
index 45c91d062ea5d0a307705b1b4443cc20d6fdd843..e9b6e053dd07ea83772a7a9113e10cf271d3eac8 100644
--- a/src/share/classes/java/util/concurrent/FutureTask.java
+++ b/src/share/classes/java/util/concurrent/FutureTask.java
@@ -34,51 +34,111 @@
*/
package java.util.concurrent;
-import java.util.concurrent.locks.*;
+import java.util.concurrent.locks.LockSupport;
/**
* A cancellable asynchronous computation. This class provides a base
* implementation of {@link Future}, with methods to start and cancel
* a computation, query to see if the computation is complete, and
* retrieve the result of the computation. The result can only be
- * retrieved when the computation has completed; the get
- * method will block if the computation has not yet completed. Once
+ * retrieved when the computation has completed; the {@code get}
+ * methods will block if the computation has not yet completed. Once
* the computation has completed, the computation cannot be restarted
- * or cancelled.
+ * or cancelled (unless the computation is invoked using
+ * {@link #runAndReset}).
*
- *
A FutureTask can be used to wrap a {@link Callable} or
- * {@link java.lang.Runnable} object. Because FutureTask
- * implements Runnable, a FutureTask can be
- * submitted to an {@link Executor} for execution.
+ *
A {@code FutureTask} can be used to wrap a {@link Callable} or
+ * {@link Runnable} object. Because {@code FutureTask} implements
+ * {@code Runnable}, a {@code FutureTask} can be submitted to an
+ * {@link Executor} for execution.
*
*
In addition to serving as a standalone class, this class provides
- * protected functionality that may be useful when creating
+ * {@code protected} functionality that may be useful when creating
* customized task classes.
*
* @since 1.5
* @author Doug Lea
- * @param The result type returned by this FutureTask's get method
+ * @param The result type returned by this FutureTask's {@code get} methods
*/
public class FutureTask implements RunnableFuture {
- /** Synchronization control for FutureTask */
- private final Sync sync;
+ /*
+ * Revision notes: This differs from previous versions of this
+ * class that relied on AbstractQueuedSynchronizer, mainly to
+ * avoid surprising users about retaining interrupt status during
+ * cancellation races. Sync control in the current design relies
+ * on a "state" field updated via CAS to track completion, along
+ * with a simple Treiber stack to hold waiting threads.
+ *
+ * Style note: As usual, we bypass overhead of using
+ * AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.
+ */
+
+ /**
+ * The run state of this task, initially NEW. The run state
+ * transitions to a terminal state only in methods set,
+ * setException, and cancel. During completion, state may take on
+ * transient values of COMPLETING (while outcome is being set) or
+ * INTERRUPTING (only while interrupting the runner to satisfy a
+ * cancel(true)). Transitions from these intermediate to final
+ * states use cheaper ordered/lazy writes because values are unique
+ * and cannot be further modified.
+ *
+ * Possible state transitions:
+ * NEW -> COMPLETING -> NORMAL
+ * NEW -> COMPLETING -> EXCEPTIONAL
+ * NEW -> CANCELLED
+ * NEW -> INTERRUPTING -> INTERRUPTED
+ */
+ private volatile int state;
+ private static final int NEW = 0;
+ private static final int COMPLETING = 1;
+ private static final int NORMAL = 2;
+ private static final int EXCEPTIONAL = 3;
+ private static final int CANCELLED = 4;
+ private static final int INTERRUPTING = 5;
+ private static final int INTERRUPTED = 6;
+
+ /** The underlying callable; nulled out after running */
+ private Callable callable;
+ /** The result to return or exception to throw from get() */
+ private Object outcome; // non-volatile, protected by state reads/writes
+ /** The thread running the callable; CASed during run() */
+ private volatile Thread runner;
+ /** Treiber stack of waiting threads */
+ private volatile WaitNode waiters;
+
+ /**
+ * Returns result or throws exception for completed task.
+ *
+ * @param s completed state value
+ */
+ @SuppressWarnings("unchecked")
+ private V report(int s) throws ExecutionException {
+ Object x = outcome;
+ if (s == NORMAL)
+ return (V)x;
+ if (s >= CANCELLED)
+ throw new CancellationException();
+ throw new ExecutionException((Throwable)x);
+ }
/**
- * Creates a FutureTask that will, upon running, execute the
- * given Callable.
+ * Creates a {@code FutureTask} that will, upon running, execute the
+ * given {@code Callable}.
*
* @param callable the callable task
- * @throws NullPointerException if callable is null
+ * @throws NullPointerException if the callable is null
*/
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
- sync = new Sync(callable);
+ this.callable = callable;
+ this.state = NEW; // ensure visibility of callable
}
/**
- * Creates a FutureTask that will, upon running, execute the
- * given Runnable, and arrange that get will return the
+ * Creates a {@code FutureTask} that will, upon running, execute the
+ * given {@code Runnable}, and arrange that {@code get} will return the
* given result on successful completion.
*
* @param runnable the runnable task
@@ -86,29 +146,46 @@ public class FutureTask implements RunnableFuture {
* you don't need a particular result, consider using
* constructions of the form:
* {@code Future> f = new FutureTask(runnable, null)}
- * @throws NullPointerException if runnable is null
+ * @throws NullPointerException if the runnable is null
*/
public FutureTask(Runnable runnable, V result) {
- sync = new Sync(Executors.callable(runnable, result));
+ this.callable = Executors.callable(runnable, result);
+ this.state = NEW; // ensure visibility of callable
}
public boolean isCancelled() {
- return sync.innerIsCancelled();
+ return state >= CANCELLED;
}
public boolean isDone() {
- return sync.innerIsDone();
+ return state != NEW;
}
public boolean cancel(boolean mayInterruptIfRunning) {
- return sync.innerCancel(mayInterruptIfRunning);
+ if (state != NEW)
+ return false;
+ if (mayInterruptIfRunning) {
+ if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING))
+ return false;
+ Thread t = runner;
+ if (t != null)
+ t.interrupt();
+ UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state
+ }
+ else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED))
+ return false;
+ finishCompletion();
+ return true;
}
/**
* @throws CancellationException {@inheritDoc}
*/
public V get() throws InterruptedException, ExecutionException {
- return sync.innerGet();
+ int s = state;
+ if (s <= COMPLETING)
+ s = awaitDone(false, 0L);
+ return report(s);
}
/**
@@ -116,12 +193,18 @@ public class FutureTask implements RunnableFuture {
*/
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
- return sync.innerGet(unit.toNanos(timeout));
+ if (unit == null)
+ throw new NullPointerException();
+ int s = state;
+ if (s <= COMPLETING &&
+ (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
+ throw new TimeoutException();
+ return report(s);
}
/**
* Protected method invoked when this task transitions to state
- * isDone (whether normally or via cancellation). The
+ * {@code isDone} (whether normally or via cancellation). The
* default implementation does nothing. Subclasses may override
* this method to invoke completion callbacks or perform
* bookkeeping. Note that you can query status inside the
@@ -131,230 +214,269 @@ public class FutureTask implements RunnableFuture {
protected void done() { }
/**
- * Sets the result of this Future to the given value unless
+ * Sets the result of this future to the given value unless
* this future has already been set or has been cancelled.
- * This method is invoked internally by the run method
+ *
+ * This method is invoked internally by the {@link #run} method
* upon successful completion of the computation.
+ *
* @param v the value
*/
protected void set(V v) {
- sync.innerSet(v);
+ if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
+ outcome = v;
+ UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
+ finishCompletion();
+ }
}
/**
- * Causes this future to report an ExecutionException
- * with the given throwable as its cause, unless this Future has
+ * Causes this future to report an {@link ExecutionException}
+ * with the given throwable as its cause, unless this future has
* already been set or has been cancelled.
- * This method is invoked internally by the run method
+ *
+ *
This method is invoked internally by the {@link #run} method
* upon failure of the computation.
+ *
* @param t the cause of failure
*/
protected void setException(Throwable t) {
- sync.innerSetException(t);
+ if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
+ outcome = t;
+ UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
+ finishCompletion();
+ }
}
- // The following (duplicated) doc comment can be removed once
- //
- // 6270645: Javadoc comments should be inherited from most derived
- // superinterface or superclass
- // is fixed.
- /**
- * Sets this Future to the result of its computation
- * unless it has been cancelled.
- */
public void run() {
- sync.innerRun();
+ if (state != NEW ||
+ !UNSAFE.compareAndSwapObject(this, runnerOffset,
+ null, Thread.currentThread()))
+ return;
+ try {
+ Callable c = callable;
+ if (c != null && state == NEW) {
+ V result;
+ boolean ran;
+ try {
+ result = c.call();
+ ran = true;
+ } catch (Throwable ex) {
+ result = null;
+ ran = false;
+ setException(ex);
+ }
+ if (ran)
+ set(result);
+ }
+ } finally {
+ // runner must be non-null until state is settled to
+ // prevent concurrent calls to run()
+ runner = null;
+ // state must be re-read after nulling runner to prevent
+ // leaked interrupts
+ int s = state;
+ if (s >= INTERRUPTING)
+ handlePossibleCancellationInterrupt(s);
+ }
}
/**
* Executes the computation without setting its result, and then
- * resets this Future to initial state, failing to do so if the
+ * resets this future to initial state, failing to do so if the
* computation encounters an exception or is cancelled. This is
* designed for use with tasks that intrinsically execute more
* than once.
+ *
* @return true if successfully run and reset
*/
protected boolean runAndReset() {
- return sync.innerRunAndReset();
+ if (state != NEW ||
+ !UNSAFE.compareAndSwapObject(this, runnerOffset,
+ null, Thread.currentThread()))
+ return false;
+ boolean ran = false;
+ int s = state;
+ try {
+ Callable c = callable;
+ if (c != null && s == NEW) {
+ try {
+ c.call(); // don't set result
+ ran = true;
+ } catch (Throwable ex) {
+ setException(ex);
+ }
+ }
+ } finally {
+ // runner must be non-null until state is settled to
+ // prevent concurrent calls to run()
+ runner = null;
+ // state must be re-read after nulling runner to prevent
+ // leaked interrupts
+ s = state;
+ if (s >= INTERRUPTING)
+ handlePossibleCancellationInterrupt(s);
+ }
+ return ran && s == NEW;
}
/**
- * Synchronization control for FutureTask. Note that this must be
- * a non-static inner class in order to invoke the protected
- * done method. For clarity, all inner class support
- * methods are same as outer, prefixed with "inner".
- *
- * Uses AQS sync state to represent run status
+ * Ensures that any interrupt from a possible cancel(true) is only
+ * delivered to a task while in run or runAndReset.
*/
- private final class Sync extends AbstractQueuedSynchronizer {
- private static final long serialVersionUID = -7828117401763700385L;
-
- /** State value representing that task is ready to run */
- private static final int READY = 0;
- /** State value representing that task is running */
- private static final int RUNNING = 1;
- /** State value representing that task ran */
- private static final int RAN = 2;
- /** State value representing that task was cancelled */
- private static final int CANCELLED = 4;
-
- /** The underlying callable */
- private final Callable callable;
- /** The result to return from get() */
- private V result;
- /** The exception to throw from get() */
- private Throwable exception;
-
- /**
- * The thread running task. When nulled after set/cancel, this
- * indicates that the results are accessible. Must be
- * volatile, to ensure visibility upon completion.
- */
- private volatile Thread runner;
-
- Sync(Callable callable) {
- this.callable = callable;
- }
-
- private boolean ranOrCancelled(int state) {
- return (state & (RAN | CANCELLED)) != 0;
- }
-
- /**
- * Implements AQS base acquire to succeed if ran or cancelled
- */
- protected int tryAcquireShared(int ignore) {
- return innerIsDone() ? 1 : -1;
- }
-
- /**
- * Implements AQS base release to always signal after setting
- * final done status by nulling runner thread.
- */
- protected boolean tryReleaseShared(int ignore) {
- runner = null;
- return true;
- }
-
- boolean innerIsCancelled() {
- return getState() == CANCELLED;
- }
-
- boolean innerIsDone() {
- return ranOrCancelled(getState()) && runner == null;
- }
-
- V innerGet() throws InterruptedException, ExecutionException {
- acquireSharedInterruptibly(0);
- if (getState() == CANCELLED)
- throw new CancellationException();
- if (exception != null)
- throw new ExecutionException(exception);
- return result;
- }
+ private void handlePossibleCancellationInterrupt(int s) {
+ // It is possible for our interrupter to stall before getting a
+ // chance to interrupt us. Let's spin-wait patiently.
+ if (s == INTERRUPTING)
+ while (state == INTERRUPTING)
+ Thread.yield(); // wait out pending interrupt
+
+ // assert state == INTERRUPTED;
+
+ // We want to clear any interrupt we may have received from
+ // cancel(true). However, it is permissible to use interrupts
+ // as an independent mechanism for a task to communicate with
+ // its caller, and there is no way to clear only the
+ // cancellation interrupt.
+ //
+ // Thread.interrupted();
+ }
- V innerGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException {
- if (!tryAcquireSharedNanos(0, nanosTimeout))
- throw new TimeoutException();
- if (getState() == CANCELLED)
- throw new CancellationException();
- if (exception != null)
- throw new ExecutionException(exception);
- return result;
- }
+ /**
+ * Simple linked list nodes to record waiting threads in a Treiber
+ * stack. See other classes such as Phaser and SynchronousQueue
+ * for more detailed explanation.
+ */
+ static final class WaitNode {
+ volatile Thread thread;
+ volatile WaitNode next;
+ WaitNode() { thread = Thread.currentThread(); }
+ }
- void innerSet(V v) {
- for (;;) {
- int s = getState();
- if (s == RAN)
- return;
- if (s == CANCELLED) {
- // aggressively release to set runner to null,
- // in case we are racing with a cancel request
- // that will try to interrupt runner
- releaseShared(0);
- return;
- }
- if (compareAndSetState(s, RAN)) {
- result = v;
- releaseShared(0);
- done();
- return;
+ /**
+ * Removes and signals all waiting threads, invokes done(), and
+ * nulls out callable.
+ */
+ private void finishCompletion() {
+ // assert state > COMPLETING;
+ for (WaitNode q; (q = waiters) != null;) {
+ if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
+ for (;;) {
+ Thread t = q.thread;
+ if (t != null) {
+ q.thread = null;
+ LockSupport.unpark(t);
+ }
+ WaitNode next = q.next;
+ if (next == null)
+ break;
+ q.next = null; // unlink to help gc
+ q = next;
}
+ break;
}
}
- void innerSetException(Throwable t) {
- for (;;) {
- int s = getState();
- if (s == RAN)
- return;
- if (s == CANCELLED) {
- // aggressively release to set runner to null,
- // in case we are racing with a cancel request
- // that will try to interrupt runner
- releaseShared(0);
- return;
- }
- if (compareAndSetState(s, RAN)) {
- exception = t;
- releaseShared(0);
- done();
- return;
- }
+ done();
+
+ callable = null; // to reduce footprint
+ }
+
+ /**
+ * Awaits completion or aborts on interrupt or timeout.
+ *
+ * @param timed true if use timed waits
+ * @param nanos time to wait, if timed
+ * @return state upon completion
+ */
+ private int awaitDone(boolean timed, long nanos)
+ throws InterruptedException {
+ final long deadline = timed ? System.nanoTime() + nanos : 0L;
+ WaitNode q = null;
+ boolean queued = false;
+ for (;;) {
+ if (Thread.interrupted()) {
+ removeWaiter(q);
+ throw new InterruptedException();
}
- }
- boolean innerCancel(boolean mayInterruptIfRunning) {
- for (;;) {
- int s = getState();
- if (ranOrCancelled(s))
- return false;
- if (compareAndSetState(s, CANCELLED))
- break;
+ int s = state;
+ if (s > COMPLETING) {
+ if (q != null)
+ q.thread = null;
+ return s;
}
- if (mayInterruptIfRunning) {
- Thread r = runner;
- if (r != null)
- r.interrupt();
+ else if (s == COMPLETING) // cannot time out yet
+ Thread.yield();
+ else if (q == null)
+ q = new WaitNode();
+ else if (!queued)
+ queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
+ q.next = waiters, q);
+ else if (timed) {
+ nanos = deadline - System.nanoTime();
+ if (nanos <= 0L) {
+ removeWaiter(q);
+ return state;
+ }
+ LockSupport.parkNanos(this, nanos);
}
- releaseShared(0);
- done();
- return true;
+ else
+ LockSupport.park(this);
}
+ }
- void innerRun() {
- if (!compareAndSetState(READY, RUNNING))
- return;
-
- runner = Thread.currentThread();
- if (getState() == RUNNING) { // recheck after setting thread
- V result;
- try {
- result = callable.call();
- } catch (Throwable ex) {
- setException(ex);
- return;
+ /**
+ * Tries to unlink a timed-out or interrupted wait node to avoid
+ * accumulating garbage. Internal nodes are simply unspliced
+ * without CAS since it is harmless if they are traversed anyway
+ * by releasers. To avoid effects of unsplicing from already
+ * removed nodes, the list is retraversed in case of an apparent
+ * race. This is slow when there are a lot of nodes, but we don't
+ * expect lists to be long enough to outweigh higher-overhead
+ * schemes.
+ */
+ private void removeWaiter(WaitNode node) {
+ if (node != null) {
+ node.thread = null;
+ retry:
+ for (;;) { // restart on removeWaiter race
+ for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
+ s = q.next;
+ if (q.thread != null)
+ pred = q;
+ else if (pred != null) {
+ pred.next = s;
+ if (pred.thread == null) // check for race
+ continue retry;
+ }
+ else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
+ q, s))
+ continue retry;
}
- set(result);
- } else {
- releaseShared(0); // cancel
+ break;
}
}
+ }
- boolean innerRunAndReset() {
- if (!compareAndSetState(READY, RUNNING))
- return false;
- try {
- runner = Thread.currentThread();
- if (getState() == RUNNING)
- callable.call(); // don't set result
- runner = null;
- return compareAndSetState(RUNNING, READY);
- } catch (Throwable ex) {
- setException(ex);
- return false;
- }
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long stateOffset;
+ private static final long runnerOffset;
+ private static final long waitersOffset;
+ static {
+ try {
+ UNSAFE = sun.misc.Unsafe.getUnsafe();
+ Class> k = FutureTask.class;
+ stateOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("state"));
+ runnerOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("runner"));
+ waitersOffset = UNSAFE.objectFieldOffset
+ (k.getDeclaredField("waiters"));
+ } catch (Exception e) {
+ throw new Error(e);
}
}
+
}
diff --git a/test/java/util/concurrent/FutureTask/DoneTimedGetLoops.java b/test/java/util/concurrent/FutureTask/DoneTimedGetLoops.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1b81c286afcdc32d0ef4a3751cecb790665502f
--- /dev/null
+++ b/test/java/util/concurrent/FutureTask/DoneTimedGetLoops.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * Written by Martin Buchholz with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/*
+ * @test
+ * @run main DoneTimedGetLoops 300
+ * @summary isDone returning true guarantees that subsequent timed get
+ * will never throw TimeoutException.
+ */
+
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+
+@SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
+public class DoneTimedGetLoops {
+ final long testDurationMillisDefault = 10L * 1000L;
+ final long testDurationMillis;
+
+ static class PublicFutureTask extends FutureTask {
+ final static Runnable noop = new Runnable() { public void run() {} };
+ PublicFutureTask() { super(noop, null); }
+ public void set(Boolean v) { super.set(v); }
+ public void setException(Throwable t) { super.setException(t); }
+ }
+
+ DoneTimedGetLoops(String[] args) {
+ testDurationMillis = (args.length > 0) ?
+ Long.valueOf(args[0]) : testDurationMillisDefault;
+ }
+
+ void test(String[] args) throws Throwable {
+ final long testDurationNanos = testDurationMillis * 1000L * 1000L;
+ final long quittingTimeNanos = System.nanoTime() + testDurationNanos;
+ final long timeoutMillis = 10L * 1000L;
+
+ final AtomicReference normalRef
+ = new AtomicReference();
+ final AtomicReference abnormalRef
+ = new AtomicReference();
+
+ final Throwable throwable = new Throwable();
+
+ abstract class CheckedThread extends Thread {
+ CheckedThread(String name) {
+ super(name);
+ setDaemon(true);
+ start();
+ }
+ /** Polls for quitting time. */
+ protected boolean quittingTime() {
+ return System.nanoTime() - quittingTimeNanos > 0;
+ }
+ /** Polls occasionally for quitting time. */
+ protected boolean quittingTime(long i) {
+ return (i % 1024) == 0 && quittingTime();
+ }
+ abstract protected void realRun() throws Exception;
+ public void run() {
+ try { realRun(); } catch (Throwable t) { unexpected(t); }
+ }
+ }
+
+ Thread setter = new CheckedThread("setter") {
+ protected void realRun() {
+ while (! quittingTime()) {
+ PublicFutureTask future = new PublicFutureTask();
+ normalRef.set(future);
+ future.set(Boolean.TRUE);
+ }}};
+
+ Thread setterException = new CheckedThread("setterException") {
+ protected void realRun() {
+ while (! quittingTime()) {
+ PublicFutureTask future = new PublicFutureTask();
+ abnormalRef.set(future);
+ future.setException(throwable);
+ }}};
+
+ Thread doneTimedGetNormal = new CheckedThread("doneTimedGetNormal") {
+ protected void realRun() throws Exception {
+ while (! quittingTime()) {
+ PublicFutureTask future = normalRef.get();
+ if (future != null) {
+ while (!future.isDone())
+ ;
+ check(future.get(0L, TimeUnit.HOURS) == Boolean.TRUE);
+ }}}};
+
+ Thread doneTimedGetAbnormal = new CheckedThread("doneTimedGetAbnormal") {
+ protected void realRun() throws Exception {
+ while (! quittingTime()) {
+ PublicFutureTask future = abnormalRef.get();
+ if (future != null) {
+ while (!future.isDone())
+ ;
+ try { future.get(0L, TimeUnit.HOURS); fail(); }
+ catch (ExecutionException t) {
+ check(t.getCause() == throwable);
+ }
+ }}}};
+
+ for (Thread thread : new Thread[] {
+ setter,
+ setterException,
+ doneTimedGetNormal,
+ doneTimedGetAbnormal }) {
+ thread.join(timeoutMillis + testDurationMillis);
+ if (thread.isAlive()) {
+ System.err.printf("Hung thread: %s%n", thread.getName());
+ failed++;
+ for (StackTraceElement e : thread.getStackTrace())
+ System.err.println(e);
+ // Kludge alert
+ thread.stop();
+ thread.join(timeoutMillis);
+ }
+ }
+ }
+
+ //--------------------- Infrastructure ---------------------------
+ volatile int passed = 0, failed = 0;
+ void pass() {passed++;}
+ void fail() {failed++; Thread.dumpStack();}
+ void fail(String msg) {System.err.println(msg); fail();}
+ void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ void check(boolean cond) {if (cond) pass(); else fail();}
+ void equal(Object x, Object y) {
+ if (x == null ? y == null : x.equals(y)) pass();
+ else fail(x + " not equal to " + y);}
+ public static void main(String[] args) throws Throwable {
+ new DoneTimedGetLoops(args).instanceMain(args);}
+ public void instanceMain(String[] args) throws Throwable {
+ try {test(args);} catch (Throwable t) {unexpected(t);}
+ System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+}
diff --git a/test/java/util/concurrent/FutureTask/ExplicitSet.java b/test/java/util/concurrent/FutureTask/ExplicitSet.java
new file mode 100644
index 0000000000000000000000000000000000000000..bd939e6c4196c73f09a2cfd9ec5b15a6a644e9a0
--- /dev/null
+++ b/test/java/util/concurrent/FutureTask/ExplicitSet.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7132378
+ * @summary Race in FutureTask if used with explicit set ( not Runnable )
+ * @author Chris Hegarty
+ */
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+
+public class ExplicitSet {
+
+ static void realMain(String[] args) throws Throwable {
+ for (int i = 1; i <= 10000; i++) {
+ //System.out.print(".");
+ test();
+ }
+ }
+
+ static void test() throws Throwable {
+ final SettableTask task = new SettableTask();
+
+ Thread thread = new Thread() { public void run() {
+ try {
+ check(task.get() != null);
+ } catch (Exception e) { unexpected(e); }
+ }};
+ thread.start();
+
+ task.set(Boolean.TRUE);
+ thread.join(5000);
+ }
+
+ static class SettableTask extends FutureTask {
+ SettableTask() {
+ super(new Callable() {
+ public Boolean call() {
+ fail ("The task should never be run!");
+ return null;
+ };
+ });
+ }
+
+ @Override
+ public void set(Boolean b) {
+ super.set(b);
+ }
+ }
+
+ //--------------------- Infrastructure ---------------------------
+ static volatile int passed = 0, failed = 0;
+ static void pass() {passed++;}
+ static void fail() {failed++; Thread.dumpStack();}
+ static void fail(String msg) {System.out.println(msg); fail();}
+ static void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ static void check(boolean cond) {if (cond) pass(); else fail();}
+ static void equal(Object x, Object y) {
+ if (x == null ? y == null : x.equals(y)) pass();
+ else fail(x + " not equal to " + y);}
+ public static void main(String[] args) throws Throwable {
+ try {realMain(args);} catch (Throwable t) {unexpected(t);}
+ System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+}