CompletableFuture recursion for reliability - java

This is very basic recursion for CompletableFuture, I want to make reliable system so each time got exception to restart process again, I'm believing it has too much issues and want to get your feedback about it
private CompletableFuture<?> recursion() {
return CompletableFuture.runAsync(() -> {
//code here
}).handleAsync((v, th) -> {
if (th != null)
return this.recursion();
else
return v;
});
}
Edit1:
int tries =5;
private CompletableFuture<?> recursion() {
return CompletableFuture.runAsync(() -> {
//code here
}).handleAsync((v, th) -> {
if (th != null && tries-- > 0){
Thread.sleep(1000);
return this.recursion();
}else
return v;
});
}
Edit2:
cleaning code as the return CompletableFuture<?> has no need so hanged it to be returning void considering #Holger comment and using AtomicInteger for tries
AtomicInteger tries =5;
private void recursion() {
CompletableFuture.runAsync(() -> {
//code here
}).whenCompleteAsync((v, th) -> {
if (th != null && ( tries.getAndDecrement() > 0 ) ){
Thread.sleep(1000);
this.recursion();
});
}
please send me you feedback,I'm arguing but really appreciate it.

Generally, simply retrying an operation when an exception occurs, without ever dealing with the exception to analyze the reason of the failure, is far away from creating a reliable system.
Nevertheless, if you want to implement retries, your code fails to do this correctly.
Your code happens to get accepted by the compiler because you use an action which doesn’t produce a value and return a CompletableFuture<?>. This hides the problems with your code:
The bifunction passed to handleAsync is supposed to provide the result value but you are calling this.recursion() which produces a CompletableFuture<?>. The compiler doesn’t mind that in the non-exceptional case v is returned, as Void and CompletableFuture<?> have a common super type, Object, so the entire method is effectively returning a CompletableFuture<Object> which is compatible with the return type CompletableFuture<?>.
If you declared the return type as CompletableFuture<Void>, the logical error became recognizable immediately: in the exceptional case you are launching another asynchronous operation but since you are not checking it’s result but just returning a CompletableFuture<?>, which is then treated as Object, the caller will never notice whether the retry (or subsequent retries) failed. The caller will always receive a CompletableFuture<?> which reports a success, holding either (Void)null or a CompletableFuture<?> as result value.
Generally, you shouldn’t use recursion for a repetition. There is no reason to do so. Let’s demonstrate the logic with an action which returns a value:
CompletableFuture<String> performAsyncAction() {
Supplier<String> action=() -> {
if(Math.random()>0.2)
throw new IllegalStateException("simulated failure");
return "value implying success";
};
int retries=5;
return CompletableFuture.supplyAsync(() -> {
try { return action.get(); }
catch(Throwable t) {
for(int i=0; i<retries; i++) try {
Thread.sleep(1000);
return action.get();
} catch(Throwable next) { t.addSuppressed(next); }
throw t;
}
});
}
It’s easy to adapt to use a Runnable, runAsync and CompletableFuture<Void>.
Update: if you just want to schedule a retry without giving a feedback to the initiator, you may implement it without blocking a thread by waiting for the delay to elapse:
static ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor();
static void performAsyncAction(Runnable r, int tries, long delay, TimeUnit u) {
if(tries>0)
e.execute(()-> { try { r.run(); } catch(Throwable t) {
e.schedule(()->performAsyncAction(r, tries-1, delay, u), delay, u);
}});
}
This uses recursion as it settles on lambda expressions. The same would work without recursion if you use an inner class:
static ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor();
static void performAsyncAction(Runnable r, int tries, long delay, TimeUnit u) {
if(tries>0)
e.execute(new Runnable() {
int left = tries;
public void run() {
try { r.run(); } catch(Throwable t) {
if(--left > 0) e.schedule(this, delay, u);
}
}
});
}

Related

What is the best practice to stop a Function chain execution on Async execution?

I have this function here:
Function<Integer, Integer> func = (value) -> value + 5;
func = func.andThen((value) -> {
//Imagine that here some code executed and raised an exception, I'm throwing it
//manually just for the sake of this example.
throw new RuntimeException("failed");
});
func = func.andThen((value) -> {
System.out.println("Reached last function !");
return value;
});
executeFunction(func);
Now, you can see that I am throwing a Runtime exception in the first andThen method. That is because I want to prevent the second andThen to be executed. Is that the best way to do it?
Also, I notice that if I execute this function inside a different thread (async) the exception is not printed in my console, and I want to know that the exception happened.
private static void executeFunction(Function<Integer, Integer> function) {
CompletableFuture.supplyAsync(() -> function.apply(100));
}
In this case, if I want to make sure that the exception gets logged, but that the next function in the andThen chain doesn't get executed, should I log and throw? Isn't it an ati-pattern?
Instantiating and throwing lots of exceptions can become quite expensive, which is why they're supposed to be limited to exceptional circumstances. Instead, you can use Optional for control flow:
func = (value) -> Optional.of(value + 5);
func = func.andThen((optionalValue) -> {
// Instead of throwing an exception, return an empty Optional
System.out.println("Log the failure");
return Optional.empty();
});
func = func.andThen((optionalValue) -> {
optionalValue.map((value) -> { // This lambda will only execute if optionalValue is not empty
System.out.println("Reached last function !");
return value; // map wraps this in an Optional
});
});
// Finally, unwrap the value. Optional provides a number of ways to do this, depending on how you want to handle failure/empty
func = func.andThen((optional) -> optional.orElse(...));
executeFunction(func);
You can write a wrapper for functions/runnables, that logs and quits when your task fails. Like so:
class Runnables
{
public static Runnable trying(Runnable... runnables)
{
return () ->
{
int successes = 0;
try
{
for(Runnable runnable : runnables)
{
runnable.run();
successes++;
}
}
catch(Throwable t)
{
logger.error("Exception thrown from "+successes+"th runnable: ",t);
}
};
}
}
Then:
private static void executeFunction(Runnable... runnables)
{
CompletableFuture.supplyAsync(Runnables.trying(runnables));
}
You can get desired behavior by using CompletableFuture.thenApply method. For example:
public class Answer {
public static void main(String[] args) {
Function<Integer, Integer> fn0 = v -> v + 5;
Function<Integer, Integer> fn1 = v -> {
throw new RuntimeException("failed");
};
Function<Integer, Integer> fn2 = v -> {
System.out.println("Reached last function !");
return v;
};
CompletableFuture.supplyAsync(() -> fn0.apply(100))
.thenApply(fn1)
.thenApply(fn2)
.exceptionally(throwable -> {
// next line prints the exception thrown by fn1, wrapped in java.util.concurrent.CompletionException
System.out.println("Failed with error: " + throwable);
return 0; // default value, used when exception is thrown
});
}
}
Basically, CompletableFuture chain will be interrupted by exception "out of the box", so no additional handling is required.
Alternatively, if you want a bit more general approach:
public class Answer {
public static void main(String[] args) {
executeAsync(() -> stepOne(100))
.thenApply(Answer::stepTwo)
.thenApply(Answer::finalStep)
.exceptionally(Answer::handleException);
}
private static CompletableFuture<Integer> executeAsync(Supplier<Integer> task) {
return CompletableFuture.supplyAsync(task::get);
}
private static Integer stepOne(Integer value) {
return value + 5;
}
private static Integer stepTwo(Integer value) {
throw new RuntimeException("failed");
}
private static Integer finalStep(Integer value) {
System.out.println("Reached last function !");
return value;
}
private static Integer handleException(Throwable throwable) {
// next line prints the exception thrown by any step before, wrapped in java.util.concurrent.CompletionException
System.out.println("Failed with error: " + throwable);
return 0; // default value
}
Notes:
Using thenApply you can chain as many function calls as you need
In the last example methods inside same class can be replaced by methods from other classes (not necessarily static ones)

Detecting a timeout exception on a Java Future without calling get() on it

I am building a library that needs to some bluetooth operations on Android. I want to return a Future instance, so whoever is using my library can call .get() on the future returned and can handle ExecutionException, TimeoutException and InterruptedException themselves. However, I want to detect a timeout myself because I need to some cleanup logic like disconnecting from the device and so on. How can I achieve this?
You could implement a wrapper class around Future which delegates to a different one (the one returned by wherever you're getting your Future at the moment). Something like:
final class DelegatingFuture<T> implements Future<T> {
private final Future<T> delegate;
DelegatingFuture(final Future<T> delegate) {
this.delegate = Objects.requireNonNull(delegate);
}
// All other methods simply delegate to 'delegate'
#Override
public T get()
throws InterruptedException, ExecutionException {
try {
return this.delegate.get();
} catch (final Exception ex) {
// Handle cleanup...
throw ex;
}
}
// Something similar for get(long timeout, TimeUnit unit)
}
And then simply return new DelegatingFuture<>(currentFuture); wherever your handing these out.
The timeout is relevant to the caller of the get method with timeout and only to that caller. A timeout is nowhere meant to imply a cancellation. E.g., the following code is a legitimate usage of the Future API:
ExecutorService es = Executors.newSingleThreadExecutor();
Future<String> f = es.submit(() -> {
Thread.sleep(3000);
return "hello";
});
for(;;) try {
String s = f.get(500, TimeUnit.MILLISECONDS);
System.out.println("got "+s);
break;
}
catch(TimeoutException ex) {
// perhaps, do some other work
System.out.println("will wait something more");
}
catch (ExecutionException ex) {
System.out.println("failed with "+ex);
break;
}
es.shutdown();
Tying the cleanup to the methods actually intended to query the result, is not a useful approach. The timeout provided by the caller(s) of that method do not relate to the actual operation. There’s not even a guaranty that the result will be queried before the operations ends or that it gets queried at all.
The cleanup should happen when either, the operation finished or when the future gets cancelled explicitly. If the caller intends a cancellation after a timeout, the caller only needs to invoke cancel after catching a TimeoutException.
One approach, often pointed to, is to use a CompletionService, e.g.
static final ExecutorService MY__EXECUTOR = Executors.newCachedThreadPool();
static final CompletionService<String> COMPLETION_SERVICE
= new ExecutorCompletionService<>(MY__EXECUTOR);
static final Future<?> CLEANER = MY__EXECUTOR.submit(() -> {
for(;;) try {
Future<String> completed = COMPLETION_SERVICE.take();
System.out.println("cleanup "+completed);
} catch(InterruptedException ex) {
if(MY__EXECUTOR.isShutdown()) break;
}
});
public static Future<String> doSomeWork() {
return COMPLETION_SERVICE.submit(() -> {
Thread.sleep(3000);
return "hello";
});
}
You are in control over when to poll the completed futures, like in another background thread, as shown in the example, or right before commencing new jobs.
You can test it like
Future<String> f = doSomeWork();
try {
String s = f.get(500, TimeUnit.MILLISECONDS);
System.out.println("got "+s);
}
catch(TimeoutException ex) {
System.out.println("no result after 500ms");
}
catch (ExecutionException ex) {
System.out.println("failed with "+ex);
}
if(f.cancel(true)) System.out.println("canceled");
f = doSomeWork();
// never calling get() at all
But honestly, I never understood why such complicated things are actually necessary. If you want a cleanup at the right time, you can use
static final ExecutorService MY__EXECUTOR = Executors.newCachedThreadPool();
public static Future<String> doSomeWork() {
Callable<String> actualJob = () -> {
Thread.sleep(3000);
return "hello";
};
FutureTask<String> ft = new FutureTask<>(actualJob) {
#Override
protected void done() {
System.out.println("cleanup "+this);
}
};
MY__EXECUTOR.execute(ft);
return ft;
}
to achieve the same.
Or even simpler
static final ExecutorService MY__EXECUTOR = Executors.newCachedThreadPool();
public static Future<String> doSomeWork() {
Callable<String> actualJob = () -> {
Thread.sleep(3000);
return "hello";
};
return MY__EXECUTOR.submit(() -> {
try {
return actualJob.call();
}
finally {
// perform cleanup
System.out.println("cleanup");
}
});
}
In either case, the cleanup will be performed whether the job was completed successfully, failed, or got canceled. If cancel(true) was used and the actual job supports interruption, the cleanup also will be performed immediately after.

Resubmitting/scheduling task from the task itself - is it a good practice?

Consider we have a scheduled executor service:
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(...);
And for some logic we want to retry a task execution. The following approach seems to be smelling for me, but I can't understand why:
threadPool.submit(new Runnable() {
#Override
public void run() {
// ...
if (needToBeScheduled()) {
threadPool.schedule(this, delay, TimeUnit.MINUTES);
} else if (needToBeResubmitted()) {
threadPool.submit(this);
}
}
});
The one obvious problem I see is that this code is not possible to convert to lambda:
threadPool.submit(()-> {
// ...
if (needToBeScheduled()) {
threadPool.schedule(this, delay, TimeUnit.MINUTES);
} else if (needToBeResubmitted()) {
threadPool.submit(this);
}
});
^^ this won't compile, as we can not refer this from lambda. Though it can be solved by introducing a method which produces such an instance and provide it instead of this.
But this is only one disadvantage I see. Is anything else here which can cause any problems? Perhaps there is a more proper approach? Move this logic to ThreadPoolExecutor.afterExecute() (this causes type conversion though...)?
Assuming that object is stateless, i.e. there are no object variables in Runnable instance.
P.S. The logic of what to do (reschedule task or resubmit or do nothing) is based on some information retrieved from the database (or any external source). So Runnable is still stateless, but it calculates the outcome based on some results of its work.
Honestly, I don't like the approach where a task (a simple independent unit of work) decides whether it should put itself in the service or not and interacts with the ExecutorService directly. I believe // ... is the only part a task should execute.
I would convert a Runnable in a Callable<Boolean>:
Callable<Boolean> task = () -> {
// ...
return needToBeScheduled; // or sth more complex with several boolean fields
};
And I would definitely move that logic outside a task (for example, into a service method):
Future<Boolean> future = threadPool.submit(task);
try {
boolean needToBeScheduled = future.get();
if (needToBeScheduled) {
threadPool.schedule(task, delay, TimeUnit.MINUTES);
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
By something more complex I meant a class that comprises 2 boolean fields. It takes Supplier<Boolean>s to make things lazy.
final class TaskResult {
private final Supplier<Boolean> needToBeScheduled;
private final Supplier<Boolean> needToBeResubmitted;
private TaskResult(Supplier<Boolean> needToBeScheduled, Supplier<Boolean> needToBeResubmitted) {
this.needToBeScheduled = needToBeScheduled;
this.needToBeResubmitted = needToBeResubmitted;
}
public static TaskResult of(Supplier<Boolean> needToBeScheduled, Supplier<Boolean> needToBeResubmitted) {
return new TaskResult(needToBeScheduled, needToBeResubmitted);
}
public boolean needToBeScheduled() {
return needToBeScheduled != null && needToBeScheduled.get();
}
public boolean needToBeResubmitted() {
return needToBeResubmitted != null && needToBeResubmitted.get();
}
}
With a few changes to the above example, we have:
Callable<TaskResult> task = () -> {
// ...
return TaskResult.of(() -> needToBeScheduled(), () -> needToBeResubmitted());
};
final Future<TaskResult> future = threadPool.submit(task);
try {
final TaskResult result = future.get();
if (result.needToBeScheduled()) {
threadPool.schedule(task, delay, TimeUnit.MINUTES);
}
if (result.needToBeResubmitted()) {
threadPool.submit(task);
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}

RxJava - ConnectableObservable can't notify its observers more than 128 times when using observeOn and subscribeOn simultaneously

I have an application that uses a ConnectableObservable that runs for a long time. Mysteriously after some time its observer stopped getting notifications in its onNext() method.
I have written the following test that simplifies the example. It's just a ConnectableObservable with an infinite loop, with one subscriber using both observeOn and subscribeon. After 128 s.onNext(1) calls it stops notifying the observer.
#Test
public void testHotObservable() throws InterruptedException{
CountDownLatch latch = new CountDownLatch(1);
ConnectableObservable<Integer> observable = Observable.<Integer>create( (s) -> {
while(true){
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
s.onNext(1);
}
})
.observeOn(Schedulers.io())
.subscribeOn(Schedulers.io())
.publish();
Observer<Integer> observer = new Observer<Integer>() {
#Override
public void onNext(Integer i) {
System.out.println("got "+i);
}
#Override
public void onCompleted() {
System.out.println("completed");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
};
observable.subscribe(observer);
observable.connect();
latch.await();
}
This is what I've seen debugging RxJava's code I have found out the reason why it doesn't call the Observer's onNext() method but I don't understand it:
1.- s.onNext(1); is called:
2.- The execution gets to rx.internal.operators.OperatorObserveOn.ObserveOnSubscriber.pollQueue():
void pollQueue() {
int emitted = 0;
final AtomicLong localRequested = this.requested;
final AtomicLong localCounter = this.counter;
do {
localCounter.set(1);
long produced = 0;
long r = localRequested.get();
for (;;) {
...
System.out.println("R: "+r);
if (r > 0) {
Object o = queue.poll();
if (o != null) {
child.onNext(on.getValue(o));
r--;
The problem is the value of r. The first time it executes its value is always 128. After each call it decrements by 1 (r--). This means that ConnectableObservable can only notify its observers 128 times when using both observeOn and subscribeOn. If I remove subscribeOn, r's value starts over each iteration and it works.
UPDATE:
I found a solution: the problem was caused by the order of the .observerOn().subscribeOn(). If I reverse it to .subscribeOn().observeOn() it works (I can see that the value of r is always reset to 128).
Anyway I'd appreciate an explanation.
Many async operators use internal, fixed size buffers and rely on subscribers requesting requently. In your case, something doesn't request properly which I can't say what it is. I suggest trying your use case with standard components to see what could be wrong, i.e., you can replace your custom Observable with a PublishSubject + sample:
Subject<Integer, Integer> source = PublishSubject.<Integer>create().toSerialized();
ConnectableObservable<Integer> co = source.sample(
500, TimeUnit.MILLISECONDS, Schedulers.io())
.onBackpressureBuffer().publish();
co.subscribe(yourSubscriber);
co.connect();
source.onNext(1);

How to release resource in canceled CompletableFuture

Uscase
Suppose we run execution with CompletableFuture.runAsync(..) and in runnable we have try-with-resources block (we are using some resource that should be closed whatever happens), and at some point when execution is not finished in try block we cancel the completable future... altough execution is stopped the resource that should be closed is not closed the close() of AutoClosable is not called...
Question
Is that a java issue or there is a way to do that properly? without hacky workarounds like using futures (that support interruption etc..), and if its expected behaviour how should one handle similar situation when non interruptable CompletableFuture is canceled...?
The Code
public class AutoClosableResourceTest {
public static class SomeService{
public void connect(){
System.out.println("connect");
}
public Integer disconnect(){
System.out.println("disconnect");
return null;
}
}
public static class AutoClosableResource<T> implements AutoCloseable {
private final T resource;
private final Runnable closeFunction;
private AutoClosableResource(T resource, Runnable closeFunction){
this.resource = resource;
this.closeFunction = closeFunction;
}
public T get(){
return resource;
}
#Override
public void close() throws Exception {
closeFunction.run();
}
}
#Test
public void testTryWithResource() throws InterruptedException {
SomeService service = new SomeService();
CompletableFuture<Void> async = CompletableFuture.runAsync(() -> {
try (AutoClosableResource<SomeService> resource = new AutoClosableResource<>(service, service::disconnect)) {
resource.get().connect();
while (true) {
Thread.sleep(1000);
System.out.println("working...");
}
} catch (Exception e) {
e.printStackTrace();
}
});
Thread.sleep(2500);
async.cancel(true);
Thread.sleep(2500);
}
}
this will produce
connect
working...
working...
working...
working...
as you can see it does not call cancel() and leaves resource opened...
It seems you have difficulties in understanding what the purpose of CompletableFuture is. Have a look at the first sentence of its class documentation:
A Future that may be explicitly completed (setting its value and status), …
So unlike FutureTask which is completed by the thread executing its run method, a CompletableFuture can be completed by any thread which will set its value/status at an arbitrary point of time. The CompletableFuture doesn’t know which thread will complete it and it doesn’t even know whether there is a thread currently working on its completion.
Therefore the CompletableFuture can not interrupt the right thread when being canceled. That’s a fundamental part of its design.
If you want a worker thread that you can interrupt you are better off using FutureTask/ThreadPoolExecutor. The task scheduled that way may still complete a CompletableFuture at its end.
The following code will be stuck in an infinite loop. Calling async.cancel will not communicate with the following loop its desire to stop.
while (true) {
Thread.sleep(1000);
System.out.println("working...");
}
The test case exits because the thread stuck in this loop is not a daemon thread.
Replace the while loop check with the following, which checks the isCancelled flag on each iteration. Calling CompletableFuture.cancel() will mark the future as cancelled, but it does not interrupt the thread that was started via runAsync.
while (isCancelled()) {
Thread.sleep(1000);
System.out.println("working...");
}
You could use the "complete" method of the CompletableFuture to stop the thread.
Below a simple code to show the behaviour:
package com.ardevco;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureTest3 {
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(5);
CompletableFuture<Integer> longRunningcompletableFuture = CompletableFuture.supplyAsync(() -> {
for (int i = 0; i < 1; i--) {
System.out.println("i " + i);
sleep();
}
return 1; // we will newer reach this line so the thread will be stuck
});
CompletableFuture<Integer> completor = CompletableFuture.supplyAsync(() -> {
System.out.println("completing the longRunningcompletableFuture");
longRunningcompletableFuture.complete(1000);
System.out.println("completed the longRunningcompletableFuture");
return 10;
});
Thread.sleep(10000);
System.out.println("completor...");
int i = completor.get();
System.out.println("completor i:" + i);
System.out.println("completor...");
System.out.println("completableFutureToBeCompleted2...");
int i2 = longRunningcompletableFuture.get();
System.out.println("completableFutureToBeCompleted2: " + i2);
System.out.println("completableFutureToBeCompleted2...");
}
private static void sleep() {
try {Thread.sleep(1000);}catch (Exception e) {}
}
}
output:
i 0
completing the longRunningcompletableFuture
completed the longRunningcompletableFuture
i -1
i -2
i -3
i -4
i -5
i -6
i -7
i -8
i -9
i -10
completor...
completor i:10
completor...
completableFutureToBeCompleted2...
completableFutureToBeCompleted2: 1000
completableFutureToBeCompleted2...
Though there is an answer marked as correct, the reason is quite different - please see documentation for CompletableFuture.cancel(mayInterruptIfRunning) method and read the article CompletableFuture can't be interrupted to understand the problem better.
This issue is addressed in my Tascalate Concurrent library, the changes to your code should be:
From
CompletableFuture<Void> async = CompletableFuture.runAsync(() -> {
...
});
To
Promise<Void> async = CompletableTask.runAsync(() -> {
...
}, someExplicitExecutor);
...and you will get expected behavior (executor thread is interrupted, AutoClosable is closed, async is completed with CancellationException).
You can read more about the library in my blog
I also face this problem in Java 8 SE. For me, it's important not to use third-party libraries.
cancel(mayInterruptIfRunning) this value has no effect in this implementation because interrupts are not used to control processing.
The idea is to use Thread.interrupt() when calling cancel(), but only for Runnable.
/** Enable and disable the interrupt */
private static class Interruptor {
volatile boolean interrupted;
volatile Runnable interrupt;
/** Enable interrupt support */
synchronized boolean start() {
if (interrupted) {
return false;
}
Thread runThread = Thread.currentThread();
interrupt = () -> {
if (runThread != Thread.currentThread()) {
runThread.interrupt();
}
};
return true;
}
/** Interrupt Runnable */
synchronized void interrupt() {
if (interrupted) {
return;
}
interrupted = true;
if (interrupt != null) {
interrupt.run();
interrupt = null;
}
}
/** Disable interrupt support */
synchronized void finish() {
interrupt = null;
}
}
/** CompletableFuture with interrupt support */
public static CompletableFuture<Void> runAsyncInterrupted(Runnable run) {
final Interruptor interruptor = new Interruptor();
Runnable wrap = () -> {
if (!interruptor.start()) { // allow interruption
return; // was canceled before the thread started
}
try {
run.run(); // can be interrupted
} finally {
interruptor.finish(); // can no longer be interrupted
}
};
CompletableFuture<Void> cfRun = CompletableFuture.runAsync(wrap);
// here is caught "CompletableFuture.cancel()"
cfRun.whenComplete((r, t) -> {
if (t instanceof CancellationException) {
interruptor.interrupt();
}
});
return cfRun;
}
Example of use
Runnable mySlowIoRun = () -> {
try {
InputStream is = openSomeResource(); // open resource
try {
// there may be problem (#1) with reading,
// such as loss of network connection
int bt = is.read();
// ..
// .. some code
} finally {
is.close(); // problem (#2): releases any system resources associated with the stream
}
} catch (Throwable th) {
throw new RuntimeException(th);
}
};
CompletableFuture<Void> cf = runAsyncInterrupted(mySlowIoRun);
try {
cf.get(5, TimeUnit.SECONDS); // 5 sec timeout
} catch (Throwable th) {
cf.cancel(true); // cancel with interrupt mySlowIoRun
throw th;
}
So this is a generalization of how I typically handle the problem.. pass-in a cancellable state, and close resources IMMEDIATELY after the open state.
private static BufferedReader openFile(String fn) {
try {
return Files.newBufferedReader(Paths.get(fn));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
static class Util {
static void closeQuietly(AutoCloseable c) {
if (c == null) return;
try {
c.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static <T extends AutoCloseable, R> R runThenCloseQuietly(T c, Function<T,R> cb) {
try {
return cb.apply(c);
} finally {
closeQuietly(c);
}
}
static <T extends AutoCloseable, R> Optional<R> runThenCloseQuietlyCancellable(BooleanSupplier cancelled
, T c, Function<T,Optional<R>> cb) {
if (c == null) return Optional.empty(); // safe doesn't throw
try {
if (cancelled.getAsBoolean()) return Optional.empty(); // might throw, wrap for safety
return cb.apply(c); // might throw
} finally {
closeQuietly(c); // might throw, but at least we're closed
}
}
private static Optional<String> emptyString() {
return Optional.empty();
}
}
interface Cancellable {
boolean isCancelled();
void cancel();
}
static class CancellableAB implements Cancellable {
private final AtomicBoolean cancelled;
CancellableAB(AtomicBoolean cancelled) {
this.cancelled = cancelled;
}
#Override
public boolean isCancelled() {
return cancelled.get();
}
#Override
public void cancel() {
cancelled.set(true);
}
}
static class CancellableArray implements Cancellable {
private final boolean[] cancelled;
private final int idx;
CancellableArray(boolean[] cancelled) {
this(cancelled, 0);
}
CancellableArray(boolean[] cancelled, int idx) {
this.cancelled = cancelled;
this.idx = idx;
}
#Override
public boolean isCancelled() {
return cancelled[idx];
}
#Override
public void cancel() {
cancelled[idx]=true;
}
}
static class CancellableV implements Cancellable {
volatile boolean cancelled;
#Override
public boolean isCancelled() {
return cancelled;
}
#Override
public void cancel() {
this.cancelled = true;
}
}
/**
* The only reason this is a class is because we need SOME external object for the lambda to check for mutated
* cancelled state.
* This gives the added benefit that we can directly call cancel on the resource.
* We allow a cancellable to be passed in to CHAIN-IN cancellable state. e.g. if cancellation should affect MULTIPLE
* CompletableFuture states, we don't want other promises to tie references to this task.. So the cancellable
* object can be externalized.
*
* Normally you don't need this much genericism, you can directly implement a volatile 'cancel boolean'.
* But this allows you to create a C.F. task as a 3rd party library call - gives maximum flexibility to invoker.
*
*/
static class FooTask {
volatile Cancellable cancelled;
String fileName;
public FooTask(String fileName) {
this.fileName = fileName;
this.cancelled = new CancellableV();
}
public FooTask(String fileName, Cancellable cancelled) {
this.cancelled = cancelled;
}
public boolean isCancelled() {
return cancelled.isCancelled();
}
public void cancel() {
cancelled.cancel();
}
/**
* asynchronously opens file, scans for first valid line (closes file), then processes the line.
* Note if an exception happens, it's the same as not finding any lines. Don't need to special case.
* Use of utility functions is mostly for generic-mapping
* (avoiding annoying double-type-casting plus editor warnings)
*/
CompletableFuture<Optional<Long>> run1() {
return
CompletableFuture.supplyAsync(() -> openFile(fileName))
.thenApplyAsync(c -> { // this stage MUST close the prior stage
if(cancelled.isCancelled() || c == null) return Util.emptyString(); // shouldn't throw
try {
return c
.lines()
.filter(line -> !cancelled.isCancelled())
.filter(line -> !line.startsWith("#"))
.findFirst();
} catch (RuntimeException e) {
Util.closeQuietly(c);
throw new RuntimeException(e);
}
}
)
.thenApplyAsync(oLine -> // this stage doesn't need closing
oLine
.map(line -> line.split(":"))
.map(cols -> cols[2])
.map(Long::valueOf)
)
;
}
/**
* Same as run1 but avoids messy brackets + try-finally
*/
CompletableFuture<Optional<Long>> run2() {
return
CompletableFuture.supplyAsync(() -> openFile(fileName))
.thenApplyAsync(c -> // this stage MUST close the prior stage
Util.runThenCloseQuietly(
c
, r -> cancelled.isCancelled() ? Util.emptyString() // shouldn't throw
: r
.lines()
.filter(line -> !cancelled.isCancelled())
.filter(line -> !line.startsWith("#"))
.findFirst()
))
.thenApplyAsync(oLine -> // this stage doesn't need closing
oLine
.map(line -> line.split(":"))
.map(cols -> cols[2])
.map(Long::valueOf)
)
;
}
/**
* Same as run2 but avoids needing the teneary operator - says Cancellable in func-name so is more readable
*/
CompletableFuture<Optional<Long>> run3() {
return
CompletableFuture.supplyAsync(() -> openFile(fileName))
.thenApplyAsync(c -> // this stage MUST close the prior stage
Util.runThenCloseQuietlyCancellable(
cancelled::isCancelled // lambda here is slightly easier to read than explicit if-statement
, c
, r -> r
.lines()
.filter(line -> !cancelled.isCancelled())
.filter(line -> !line.startsWith("#"))
.findFirst()
))
.thenApplyAsync(oLine -> // this stage doesn't need closing
oLine
.map(line -> line.split(":"))
.map(cols -> cols[2])
.map(Long::valueOf)
)
;
}
}
#Test
public void testFooGood() {
var task = new FooTask("/etc/passwd");
var cf = task.run3();
var oVal = cf.join();
assertTrue(oVal.isPresent());
System.out.println(oVal.get()); // should not throw
}
#Test
public void testFooCancel() {
var task = new FooTask("/etc/passwd");
var cf = task.run3();
task.cancel();
var oVal = cf.join();
assertTrue(oVal.isEmpty());
}

Categories

Resources