Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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...

like image 463
vach Avatar asked Sep 25 '14 14:09

vach


1 Answers

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.

like image 58
Holger Avatar answered Oct 26 '22 22:10

Holger