void inputSucceeded(Input input) { /* cleanup... */ }
void noInputSucceeded() { /* cleanup... */ }
void runTask(Input input, SettableFuture<Void> failover) {
// Calls either failover.set(null) or inputSucceeded(input).
}
final AtomicReference<ListenableFuture<Void>> failovers =
new AtomicReference<>(immediateFuture(null));
void addInput(final Input input) {
final SettableFuture<Void> failover =
SettableFuture.create();
failovers.getAndSet(failover)
.addListener(() -> runTask(input, failover));
}
void noMoreInputs() {
failovers.getAndSet(null)
.addListener(() -> noInputSucceeded());
}
addInputs to be run concurrently, but ensures that everything in runTask (up to the failover.set call, at least) is fully synchronized. Inputs are handled on a first-come-first-served basis. If noMoreInputs is called after all inputs are added, then exactly one of inputSucceeded or noInputSucceeded will run to perform whatever cleanup is necessary.I call this the "failover trigger" pattern because it provides each task with a trigger it can use to optionally start the next task.

1 comment:
Post a Comment