3

I am writing a library that offers several asynchronous methods that return CompletableFutures. The library has an internal thread pool for executing the computational work of the asynchronous methods.

I want to make sure the following two requirements are met:

  1. the returned CompletableFuture is NOT completed by an internal thread so that CompletableFuture chains external to my library are never executed by the library's internal thread pool
  2. all computations of my asynchronous method are executed by the internal thread pool and NOT by the user thread (i.e. the thread of the method caller)

So say the library has the following blocking method

Data request(Address address) {
  Message request = encode(address);
  Message response = sendAndReceive(request);
  Data responseData = decode(response);
  return responseData;
}

and a corresponding asynchronous method

  CompletableFuture<Data> requestAsync(Address address) {
    return CompletableFuture.supplyAsync(() -> encode(address), internalThreadPool)
        .thenCompose(request -> sendAndReceiveAsync(request))
        .thenApply(response -> decode(response));
  }

The first requirement is met by adding chaining .whenCompleteAsync((v,t) -> {}) as explained in this answer.

But what needs to be done to meet the second requirement?

Stefan Feuerhahn
  • 1,564
  • 1
  • 14
  • 22
  • It's true that the second part of my question "how to make sure the returned CF is not completed by the internal thread" is answered there already. Shall I change the question so it only includes the first part? – Stefan Feuerhahn Sep 24 '19 at 21:19
  • Ok I edited my question. Now it points to answer for that part of the question. – Stefan Feuerhahn Sep 25 '19 at 07:17
  • Hi @Stefan! The problem with your question is that in fact it contains two questions/problems. The duplicate I found covers indeed only one of them. Here on SO, we try to have only one problem per question so that we can keep both questions and answers simple, except when they are tightly coupled. Here it seems the problems are rather independent so I think it's better to separate them with a simple mention of the other, as you did in your edit. – Didier L Sep 25 '19 at 09:29

1 Answers1

3

A solution for the second requirement has been discussed by Sergey Kuksenko here and has been realized in Java 11's HttpClient implementation.

The requirement is not met because it is not guaranteed that decode(response) is executed by an internal thread. If encode and sendAndReceiveAsync are completed quickly, decode could actually be executed by the caller's thread.

The problem can be fixed by introducing a CompletableFuture startCf to kick-off the CF-chain.

Thus the complete solution could look like the following

CompletableFuture<Data> requestAsyncFixedAll(Address address) {
  CompletableFuture<Void> startCf = new CompletableFuture<>();
  CompletableFuture<Data> dataCf =  startCf.thenApplyAsync(v -> encode(address), internalThreadPool)
    .thenCompose(request -> sendAndReceiveAsync(request))
    .thenApply(response -> decode(response)).whenCompleteAsync((v, t) -> {});
  startCf.complete(null);
  return dataCf;
}
Stefan Feuerhahn
  • 1,564
  • 1
  • 14
  • 22