2

Say I have a method getCloseableResource() which might take an unbounded amount of time to complete and returns something that needs to be closed or cleaned up.

I want to submit a task to an executorService to get the closeable resource but only wait a limited amount of time.

Future<CloseableResource> crFuture = executorService.submit(() -> getCloseableResource());
CloseableResource cr = crFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
catch (TimeoutException e) {
  // abandon the attempt
}

The problem is that if I abandon waiting, I'll never be able to close the resource since I won't have a handle to it and the resource will never get cleaned up properly.

Is there some mechanism to specify cleanup code to execute when getCloseableResource returns so that I can abandon waiting for it AND make sure it is disposed of cleanly? Or maybe there is a more advance construct rather than an executor service.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
jbu
  • 15,831
  • 29
  • 82
  • 105
  • Why would you get a resource only to immediately close it? Your Question could use a bit more context. A **simple but semi-realistic example** would help. If you have in mind that a background task is handed a closable resource required for its work, that task should close the resource upon completing that work. The value returned by `Future#get` should be a *result*, not a resource used in producing a result. – Basil Bourque Feb 04 '23 at 00:01
  • @BasilBourque as alluded to earlier, this is for the case where I've given up waiting after some timeout threshold has passed. I want to give up and have the thread continue but I also want to make sure that the resource isn't left behind without proper cleanup – jbu Feb 04 '23 at 00:52
  • But when timing out you didn't get the resource in the first place. Why and how do you want to close it then? – michid Feb 04 '23 at 10:20
  • 1
    @michid I understand I didn't get the resource in the first place - I abandon it. But there is still an executor thread that is in the process of getting it and when it does, it won't have anyone to give it to, thus the resource won't get closed properly. Even though I won't get the resource (I abandon it) I still want some mechanism to properly dispose of it. I can't wait forever for the resource to come back properly. – jbu Feb 04 '23 at 12:45
  • A non-exhaustive list of simple approaches: (1) Use `CompletableFuture` and on a timeout attach a handler to close the resource. (2) Use finalization (deprecated) alternatives like phantom references (e.g. see `Cleaner`). (3) Invert the problem by submitting the work to the resource, so that when ready it invokes a callback and closes. Cancel the callback to no-op on timeout. – Ben Manes Feb 04 '23 at 18:09

2 Answers2

1

I think one solution is to submit a task that has some state that keeps track of whether the request was abandoned. When the resource is retrieved the executor thread can check if the main thread had abandoned it and if so, close the resource. In the main thread, if the the timeout occurs and you want to abandon the resource, just set the abandoned flag. All of this would need to be written in a thread safe way of course.

jbu
  • 15,831
  • 29
  • 82
  • 105
0

You can submit another task in the timeout case that closes the resource once it has been allocated:

Future<Closeable> crFuture = executorService.submit(this::getCloseableResource);
try {
    Closeable cr = crFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
    // ...
} catch (TimeoutException e) {
    executorService.submit(() -> {
        try {
            crFuture.get().close();
        } catch (IOException | InterruptedException | ExecutionException ex) {
            // ...
       }
    });
}
michid
  • 10,536
  • 3
  • 32
  • 59
  • This is a good quick and dirty way to do it but I don't like having to use another thread. I think the way I proposed would be a tiny bit more programming but no extra threads used. – jbu Feb 04 '23 at 19:41
  • Sure, this is the most basic approach. You could also add futures that had a timeout to a queue and regularly scan it for completed ones and close their resources. You could do this from a background thread or even from the main thread. Whether the additional complexity warrants the approach depends on the context. – michid Feb 05 '23 at 12:42