All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.diffplug.common.util.concurrent.AggregateFutureState Maven / Gradle / Ivy

There is a newer version: 1.2.0
Show newest version
/*
 * Original Guava code is copyright (C) 2015 The Guava Authors.
 * Modifications from Guava are copyright (C) 2016 DiffPlug.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.diffplug.common.util.concurrent;

import static com.diffplug.common.collect.Sets.newConcurrentHashSet;
import static java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater;
import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater;

import java.util.Set;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

import com.diffplug.common.annotations.GwtCompatible;

/**
 * A helper which does some thread-safe operations for aggregate futures, which must be implemented
 * differently in GWT.  Namely:
 * 

Lazily initializes a set of seen exceptions *

Decrements a counter atomically */ @GwtCompatible(emulated = true) abstract class AggregateFutureState { @SuppressWarnings({"rawtypes", "unchecked"}) private static final AtomicReferenceFieldUpdater> SEEN_EXCEPTIONS_UDPATER = newUpdater( AggregateFutureState.class, (Class) Set.class, "seenExceptions"); private static final AtomicIntegerFieldUpdater REMAINING_COUNT_UPDATER = newUpdater(AggregateFutureState.class, "remaining"); // 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 private volatile Set seenExceptions = null; @SuppressWarnings("unused") private volatile int remaining; AggregateFutureState(int remainingFutures) { this.remaining = remainingFutures; } final Set getOrInitSeenExceptions() { /* * The initialization of seenExceptions has to be more complicated than we'd like. The simple * approach would be for each caller CAS it from null to a Set populated with its exception. But * there's another race: If the first thread fails with an exception and a second thread * immediately fails with the same exception: * * Thread1: calls setException(), which returns true, context switch before it can CAS * seenExceptions to its exception * * 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 * 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 seenExceptionsLocal = seenExceptions; if (seenExceptionsLocal == null) { seenExceptionsLocal = newConcurrentHashSet(); /* * Other handleException() callers may see this as soon as we publish it. We need to populate * it with the initial failure before we do, or else they may think that the initial failure * has never been seen before. */ addInitialException(seenExceptionsLocal); SEEN_EXCEPTIONS_UDPATER.compareAndSet(this, null, seenExceptionsLocal); /* * If another handleException() caller created the set, we need to use that copy in case yet * other callers have added to it. * * This read is guaranteed to get us the right value because we only set this once (here). */ seenExceptionsLocal = seenExceptions; } return seenExceptionsLocal; } /** Populates {@code seen} with the exception that was passed to {@code setException}. */ abstract void addInitialException(Set seen); final int decrementRemainingAndGet() { return REMAINING_COUNT_UPDATER.decrementAndGet(this); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy