As an example, I have a supplier that might take time to run:
Supplier<Integer> numLoader = sneaky(() -> {
Thread.sleep(10000);
System.out.println("5 Calculated!");
return 5;
});
* sneaky
is just a utility to convert to runtime exception.
I'd like to be able to do something like this:
Future<Integer> future = createFutureValueOnTimeout(-1, numLoader);
// numLoader takes 10 seconds to complete so -1 is returned.
int num = future.get(1000, TimeUnit.MILLISECONDS);
if (resourcesAreLow()) {
future.cancel(true);
}
doSomethingWithTheValue(num);
I also have a partial implementation for createFutureValueOnTimeout
:
private static <V> Future<V> createFutureValueOnTimeout(V v, Supplier<V> supplier) {
CompletableFuture<V> completableFuture = CompletableFuture.supplyAsync(supplier);
return new FutureDecorator<V>(completableFuture) {
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException {
return completableFuture.completeOnTimeout(v, timeout, unit).get();
}
};
}
The problem is that when calling cancel
, the sleep
isn't interrupted.
- How can I get the cancel to work?
- Is there an easier way to return a value on timeout?
Complete test:
public class TimeoutTest {
@SneakyThrows
@Test
public void testTimeout() {
int loadTimeMillis = 10000;
Supplier<Integer> numLoader = () -> {
try {
// Simulate long operation
Thread.sleep(loadTimeMillis);
} catch (InterruptedException e) {
System.out.println("Interrupted! message: " + e.getMessage());
throw Lombok.sneakyThrow(e);
}
System.out.println("5 Calculated!");
return 5;
};
Future<Integer> future = createFutureValueOnTimeout(-1, numLoader);
long start = System.currentTimeMillis();
// numLoader takes 10 seconds to complete so -1 is returned.
int num = future.get(1000, TimeUnit.MILLISECONDS);
System.out.println("Got: num: " + num + ". time: " + (System.currentTimeMillis() - start));
if (resourcesAreLow()) {
future.cancel(true);
}
// Don't stop the test. Give time for the cancel to kick in.
Thread.sleep(loadTimeMillis);
System.out.println("Finished. Time: " + (System.currentTimeMillis() - start));
}
private boolean resourcesAreLow() {
return true;
}
private static <V> Future<V> createFutureValueOnTimeout(V v, Supplier<V> supplier) {
CompletableFuture<V> completableFuture = CompletableFuture.supplyAsync(supplier);
return new FutureDecorator<V>(completableFuture) {
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException {
return completableFuture.completeOnTimeout(v, timeout, unit).get();
}
};
}
private static class FutureDecorator<V> implements Future<V> {
private final Future<V> inner;
private FutureDecorator(Future<V> inner) {this.inner = inner;}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return inner.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return inner.isCancelled();
}
@Override
public boolean isDone() {
return inner.isDone();
}
@Override
public V get() throws InterruptedException, ExecutionException {
return inner.get();
}
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return inner.get(timeout, unit);
}
}
}
Output: (Notice the lack of the Interrupted!
message)
Got: num: -1. time: 1007
5 Calculated!
Finished. Time: 11021