Guava's ListenableFuture
library provides a mechanism for adding callbacks to future tasks. This is done as follows:
ListenableFuture<MyClass> future = myExecutor.submit(myCallable);
Futures.addCallback(future, new FutureCallback<MyClass>() {
@Override
public void onSuccess(@Nullable MyClass myClass) {
doSomething(myClass);
}
@Override
public void onFailure(Throwable t) {
printWarning(t);
}}, myCallbackExecutor);
}
You can wait for a ListenableFuture
to complete by calling its get
function. For instance:
MyClass myClass = future.get();
My question is, are all callbacks for a certain future guaranteed to run before the get
terminates. I.e. if there is a future with many callbacks on many callback executors registered, will all the callbacks complete before get
returns?
Edit
My use case is, I pass a builder around to many classes. Each class populates one field of the builder. I want all fields to be populated asynchronously because each field requires an external query to generate the data for the field. I want the user who calls my asyncPopulateBuilder
to receive a Future
on which she can call get
and be assured that all the fields have been populated. The way I thought to do it is as follows:
final Builder b;
ListenableFuture<MyClass> future = myExecutor.submit(myCallable);
Futures.addCallback(future, new FutureCallback<MyClass>() {
@Override
public void onSuccess(@Nullable MyClass myClass) {
b.setMyClass(myClass);
}
@Override
public void onFailure(Throwable t) {
printWarning(t);
}}, myCallbackExecutor);
}
// Do the same thing for all other fields.
What is the recommended way to block until all fields are populated in such a case?
Callbacks are not guaranteed to run before get
returns. More on that below.
As for how to address this use case, I would suggest turning the query for each field's data into a separate Future
, combining them with allAsList
+transform
, and taking action on that. (We may someday provide a shortcut for the "combine" step.)
ListenableFuture<MyClass> future = myExecutor.submit(myCallable);
final ListenableFuture<Foo> foo =
Futures.transform(
future,
new Function<MyClass, Foo>() { ... },
myCallbackExecutor);
final ListenableFuture<Bar> bar = ...;
final ListenableFuture<Baz> baz = ...;
ListenableFuture<?> allAvailable = Futures.allAsList(foo, bar, baz);
ListenableFuture<?> allSet = Futures.transform(
allAvailable,
new Function<Object, Object>() {
@Override
public Object apply(Object ignored) {
// Use getUnchecked, since we know they already succeeded:
builder.setFoo(Futures.getUnchecked(foo));
builder.setFoo(Futures.getUnchecked(bar));
builder.setFoo(Futures.getUnchecked(baz));
return null;
}
}
};
Now the user can call allSet.get()
to await population.
(Or maybe you want for allSet
to be a Future<Builder>
so that the user is handed a reference to the builder. Or maybe you don't need a full-on Future
at all, only a CountDownLatch
, in which you could use addCallback
instead of transform
and count down the latch at the end of the callback.)
This approach may also simplify error handling.
RE: "Do callbacks run before get
?"
First, I am pretty sure that we don't guarantee this anywhere in the spec, so thanks for asking rather than just going for it :) If you do end up wanting to rely on some behavior of the current implementation, please file an issue so that we can add documentation and tests.
Second, if I take your question very literally, what you're asking for isn't possible: If get()
waits for all listeners to complete, then any listener that calls get()
will hang!
A slightly more lenient version of your question is "Will all the listeners at least start before get()
returns?" This turns out to be impossible, too: Suppose that I attach two listeners to the same Future
to be run with directExecutor()
. Both listeners simply call get()
and return. One of the listeners has to run first. When it calls get()
, it will hang, since the second listener hasn't started yet -- nor can it until the first listener is done. (More generally, it can be dangerous to rely on any given Executor
to execute a task promptly.)
A still more lenient version is "Will the Future
at least call submit()
for each of the listeners before get()
returns?" But this ends up with a problem in the same scenario as I just described: Calling submit(firstListener)
on a directExecutor()
runs the task and calls get()
, which can't complete until the second listener is started, which can't happen until the first listener completes.
If anything, it's starting to sound much more likely that get()
will return before any listeners execute. But thanks to the unpredictability of thread scheduling, we can't rely on that, either. (And again: It's not documented, so please don't rely on it unless you ask for it to be documented!)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With