aboutsummaryrefslogtreecommitdiff
path: root/guava/src/com/google/common/util/concurrent/AggregateFutureState.java
diff options
context:
space:
mode:
Diffstat (limited to 'guava/src/com/google/common/util/concurrent/AggregateFutureState.java')
-rw-r--r--guava/src/com/google/common/util/concurrent/AggregateFutureState.java28
1 files changed, 23 insertions, 5 deletions
diff --git a/guava/src/com/google/common/util/concurrent/AggregateFutureState.java b/guava/src/com/google/common/util/concurrent/AggregateFutureState.java
index 040d81363..f8398d817 100644
--- a/guava/src/com/google/common/util/concurrent/AggregateFutureState.java
+++ b/guava/src/com/google/common/util/concurrent/AggregateFutureState.java
@@ -37,9 +37,9 @@ import java.util.logging.Logger;
*/
@GwtCompatible(emulated = true)
@ReflectionSupport(value = ReflectionSupport.Level.FULL)
-abstract class AggregateFutureState {
+abstract class AggregateFutureState<OutputT> extends AbstractFuture.TrustedFuture<OutputT> {
// Lazily initialized the first time we see an exception; not released until all the input futures
- // & this future completes. Released when the future releases the reference to the running state
+ // have completed and we have processed them all.
private volatile Set<Throwable> seenExceptions = null;
private volatile int remaining;
@@ -89,12 +89,27 @@ abstract class AggregateFutureState {
* Thread2: calls setException(), which returns false, CASes seenExceptions to its exception,
* and wrongly believes that its exception is new (leading it to logging it when it shouldn't)
*
- * Our solution is for threads to CAS seenExceptions from null to a Set population with _the
+ * Our solution is for threads to CAS seenExceptions from null to a Set populated with _the
* initial exception_, no matter which thread does the work. This ensures that seenExceptions
* always contains not just the current thread's exception but also the initial thread's.
*/
Set<Throwable> seenExceptionsLocal = seenExceptions;
if (seenExceptionsLocal == null) {
+ // TODO(cpovirk): Should we use a simpler (presumably cheaper) data structure?
+ /*
+ * Using weak references here could let us release exceptions earlier, but:
+ *
+ * 1. On Android, querying a WeakReference blocks if the GC is doing an otherwise-concurrent
+ * pass.
+ *
+ * 2. We would probably choose to compare exceptions using == instead of equals() (for
+ * consistency with how weak references are cleared). That's a behavior change -- arguably the
+ * removal of a feature.
+ *
+ * Fortunately, exceptions rarely contain references to expensive resources.
+ */
+
+ //
seenExceptionsLocal = newConcurrentHashSet();
/*
* Other handleException() callers may see this as soon as we publish it. We need to populate
@@ -122,6 +137,10 @@ abstract class AggregateFutureState {
return ATOMIC_HELPER.decrementAndGetRemainingCount(this);
}
+ final void clearSeenExceptions() {
+ seenExceptions = null;
+ }
+
private abstract static class AtomicHelper {
/** Atomic compare-and-set of the {@link AggregateFutureState#seenExceptions} field. */
abstract void compareAndSetSeenExceptions(
@@ -169,8 +188,7 @@ abstract class AggregateFutureState {
@Override
int decrementAndGetRemainingCount(AggregateFutureState state) {
synchronized (state) {
- state.remaining--;
- return state.remaining;
+ return --state.remaining;
}
}
}