Skip to content

Commit b337be6

Browse files
cpovirkGoogle Java Core Libraries
authored and
Google Java Core Libraries
committedOct 21, 2022
Move interruptTask() call to immediately before afterDone() call.
Once this CL is submitted, users who override `interruptTask()` will be able to migrate to overriding `afterDone()` with no further change to behavior. RELNOTES=`util.concurrent`: Changed `AbstractFuture` to run `interruptTask()` just before `afterDone()`. Until this change, it ran slightly earlier than that: We used to run it before unblocking any pending `get()` calls, and now we run it after. PiperOrigin-RevId: 482836593
1 parent f9d336f commit b337be6

File tree

2 files changed

+60
-22
lines changed

2 files changed

+60
-22
lines changed
 

‎android/guava/src/com/google/common/util/concurrent/AbstractFuture.java

+30-11
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,13 @@ public void run() {
356356
}
357357
Object valueToSet = getFutureValue(future);
358358
if (ATOMIC_HELPER.casValue(owner, this, valueToSet)) {
359-
complete(owner);
359+
complete(
360+
owner,
361+
/*
362+
* Interruption doesn't propagate through a SetFuture chain (see getFutureValue), so
363+
* don't invoke interruptTask.
364+
*/
365+
false);
360366
}
361367
}
362368
}
@@ -656,12 +662,7 @@ mayInterruptIfRunning, new CancellationException("Future.cancel() was called."))
656662
while (true) {
657663
if (ATOMIC_HELPER.casValue(abstractFuture, localValue, valueToSet)) {
658664
rValue = true;
659-
// We call interruptTask before calling complete(), which is consistent with
660-
// FutureTask
661-
if (mayInterruptIfRunning) {
662-
abstractFuture.interruptTask();
663-
}
664-
complete(abstractFuture);
665+
complete(abstractFuture, mayInterruptIfRunning);
665666
if (localValue instanceof SetFuture) {
666667
// propagate cancellation to the future set in setfuture, this is racy, and we don't
667668
// care if we are successful or not.
@@ -779,7 +780,7 @@ public void addListener(Runnable listener, Executor executor) {
779780
protected boolean set(@ParametricNullness V value) {
780781
Object valueToSet = value == null ? NULL : value;
781782
if (ATOMIC_HELPER.casValue(this, null, valueToSet)) {
782-
complete(this);
783+
complete(this, /*callInterruptTask=*/ false);
783784
return true;
784785
}
785786
return false;
@@ -804,7 +805,7 @@ protected boolean set(@ParametricNullness V value) {
804805
protected boolean setException(Throwable throwable) {
805806
Object valueToSet = new Failure(checkNotNull(throwable));
806807
if (ATOMIC_HELPER.casValue(this, null, valueToSet)) {
807-
complete(this);
808+
complete(this, /*callInterruptTask=*/ false);
808809
return true;
809810
}
810811
return false;
@@ -847,7 +848,13 @@ protected boolean setFuture(ListenableFuture<? extends V> future) {
847848
if (future.isDone()) {
848849
Object value = getFutureValue(future);
849850
if (ATOMIC_HELPER.casValue(this, null, value)) {
850-
complete(this);
851+
complete(
852+
this,
853+
/*
854+
* Interruption doesn't propagate through a SetFuture chain (see getFutureValue), so
855+
* don't invoke interruptTask.
856+
*/
857+
false);
851858
return true;
852859
}
853860
return false;
@@ -989,14 +996,26 @@ private static Object getFutureValue(ListenableFuture<?> future) {
989996
}
990997

991998
/** Unblocks all threads and runs all listeners. */
992-
private static void complete(AbstractFuture<?> param) {
999+
private static void complete(AbstractFuture<?> param, boolean callInterruptTask) {
9931000
// Declare a "true" local variable so that the Checker Framework will infer nullness.
9941001
AbstractFuture<?> future = param;
9951002

9961003
Listener next = null;
9971004
outer:
9981005
while (true) {
9991006
future.releaseWaiters();
1007+
/*
1008+
* We call interruptTask() immediately before afterDone() so that migrating between the two
1009+
* can be a no-op.
1010+
*/
1011+
if (callInterruptTask) {
1012+
future.interruptTask();
1013+
/*
1014+
* Interruption doesn't propagate through a SetFuture chain (see getFutureValue), so don't
1015+
* invoke interruptTask on any subsequent futures.
1016+
*/
1017+
callInterruptTask = false;
1018+
}
10001019
// We call this before the listeners in order to avoid needing to manage a separate stack data
10011020
// structure for them. Also, some implementations rely on this running prior to listeners
10021021
// so that the cleanup work is visible to listeners.

‎guava/src/com/google/common/util/concurrent/AbstractFuture.java

+30-11
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,13 @@ public void run() {
356356
}
357357
Object valueToSet = getFutureValue(future);
358358
if (ATOMIC_HELPER.casValue(owner, this, valueToSet)) {
359-
complete(owner);
359+
complete(
360+
owner,
361+
/*
362+
* Interruption doesn't propagate through a SetFuture chain (see getFutureValue), so
363+
* don't invoke interruptTask.
364+
*/
365+
false);
360366
}
361367
}
362368
}
@@ -656,12 +662,7 @@ mayInterruptIfRunning, new CancellationException("Future.cancel() was called."))
656662
while (true) {
657663
if (ATOMIC_HELPER.casValue(abstractFuture, localValue, valueToSet)) {
658664
rValue = true;
659-
// We call interruptTask before calling complete(), which is consistent with
660-
// FutureTask
661-
if (mayInterruptIfRunning) {
662-
abstractFuture.interruptTask();
663-
}
664-
complete(abstractFuture);
665+
complete(abstractFuture, mayInterruptIfRunning);
665666
if (localValue instanceof SetFuture) {
666667
// propagate cancellation to the future set in setfuture, this is racy, and we don't
667668
// care if we are successful or not.
@@ -779,7 +780,7 @@ public void addListener(Runnable listener, Executor executor) {
779780
protected boolean set(@ParametricNullness V value) {
780781
Object valueToSet = value == null ? NULL : value;
781782
if (ATOMIC_HELPER.casValue(this, null, valueToSet)) {
782-
complete(this);
783+
complete(this, /*callInterruptTask=*/ false);
783784
return true;
784785
}
785786
return false;
@@ -804,7 +805,7 @@ protected boolean set(@ParametricNullness V value) {
804805
protected boolean setException(Throwable throwable) {
805806
Object valueToSet = new Failure(checkNotNull(throwable));
806807
if (ATOMIC_HELPER.casValue(this, null, valueToSet)) {
807-
complete(this);
808+
complete(this, /*callInterruptTask=*/ false);
808809
return true;
809810
}
810811
return false;
@@ -847,7 +848,13 @@ protected boolean setFuture(ListenableFuture<? extends V> future) {
847848
if (future.isDone()) {
848849
Object value = getFutureValue(future);
849850
if (ATOMIC_HELPER.casValue(this, null, value)) {
850-
complete(this);
851+
complete(
852+
this,
853+
/*
854+
* Interruption doesn't propagate through a SetFuture chain (see getFutureValue), so
855+
* don't invoke interruptTask.
856+
*/
857+
false);
851858
return true;
852859
}
853860
return false;
@@ -989,14 +996,26 @@ private static Object getFutureValue(ListenableFuture<?> future) {
989996
}
990997

991998
/** Unblocks all threads and runs all listeners. */
992-
private static void complete(AbstractFuture<?> param) {
999+
private static void complete(AbstractFuture<?> param, boolean callInterruptTask) {
9931000
// Declare a "true" local variable so that the Checker Framework will infer nullness.
9941001
AbstractFuture<?> future = param;
9951002

9961003
Listener next = null;
9971004
outer:
9981005
while (true) {
9991006
future.releaseWaiters();
1007+
/*
1008+
* We call interruptTask() immediately before afterDone() so that migrating between the two
1009+
* can be a no-op.
1010+
*/
1011+
if (callInterruptTask) {
1012+
future.interruptTask();
1013+
/*
1014+
* Interruption doesn't propagate through a SetFuture chain (see getFutureValue), so don't
1015+
* invoke interruptTask on any subsequent futures.
1016+
*/
1017+
callInterruptTask = false;
1018+
}
10001019
// We call this before the listeners in order to avoid needing to manage a separate stack data
10011020
// structure for them. Also, some implementations rely on this running prior to listeners
10021021
// so that the cleanup work is visible to listeners.

0 commit comments

Comments
 (0)
Please sign in to comment.