// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.sdk.util;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.chromium.sdk.RelayOk;
import org.chromium.sdk.SyncCallback;
import org.chromium.sdk.util.AsyncFuture.Callback;
/**
* A class that provides an {@link AsyncFuture} for a group operation: one that consists of any
* number of sub-operations and completes once all of them have completed.
* The class does not care about nature of sub-operations, it only keeps their number. User
* registers new sub-operations ({@link #addSubOperation()}) and reports on their completion
* ({@link #subOperationDone} and ({@link #subOperationDoneSync}). One sub-operation is registered
* by default. Once all sub-operations completed,
* the main operation is considered done and the future returns the group result. Groups result is
* a list of sub-operation results.
*
* @param <T> type of sub-operation result
*/
public class AsyncFutureMerger<T> {
private final AtomicReference<AsyncFuture<List<T>>> futureRef;
private AsyncFuture.Callback<List<T>> callback;
private SyncCallback syncCallback;
private final List<T> resultBuffer = new ArrayList<T>();
private int subOperationCounter = 1;
private int subOperationSyncCounter = 1;
private RuntimeException savedException = null;
public AsyncFutureMerger() {
futureRef = new AtomicReference<AsyncFuture<List<T>>>();
AsyncFuture.initializeReference(futureRef, new AsyncFuture.Operation<List<T>>() {
@Override
public RelayOk start(Callback<List<T>> callback, SyncCallback syncCallback) {
AsyncFutureMerger.this.callback = callback;
AsyncFutureMerger.this.syncCallback = syncCallback;
return SOMEONE_CARES_ABOUT_RELAY_OK;
}
});
}
/**
* Registers a new sub-operation.
* This method is not thread-safe. User must synchronize access himself.
*/
public void addSubOperation() {
assert !futureRef.get().isDone();
subOperationCounter++;
subOperationSyncCounter++;
}
/**
* Registers a sub-operation completion and saves its result.
* This call is not mandatory and user may fail to make it; this won't cause waiting thread
* to block forever.
* This method is not thread-safe. User must synchronize access himself.
*/
public void subOperationDone(T result) {
resultBuffer.add(result);
subOperationCounter--;
if (subOperationCounter == 0) {
callback.done(resultBuffer);
}
}
/**
* Additionally registers a sub-operation completion. This method should be called only after
* {@link #subOperationDone} and this call is mandatory and must be made even in case of
* sub-operation failure; this method is typically called form finally section of
* the top-level procedure.
* Other threads may block infinitely unless this method is being called a proper number of times.
* This method is not thread-safe. User must synchronize access himself.
* @param exception optional value of a failure that prevented corresponding call
* to {@link #subOperationDone}
*/
public void subOperationDoneSync(RuntimeException exception) {
if (exception != null && savedException == null) {
savedException = exception;
}
subOperationSyncCounter--;
if (subOperationSyncCounter == 0) {
syncCallback.callbackDone(savedException);
}
}
/**
* Returns {@link AsyncFuture} that provides the group result.
* This method is thread-safe.
*/
public AsyncFuture<List<T>> getFuture() {
return futureRef.get();
}
private static final RelayOk SOMEONE_CARES_ABOUT_RELAY_OK = new RelayOk() {};
}