Create CompletableFuture from blocking method call

Viewed 1326

How can I "convert" a blocking method call to a CompletableFuture? Example:

T waitForResult() throws InterruptedException {
    obj.await(); // blocking call
    // ...
    return something;
}

I need to turn that into this:

CompletableFuture.of(this::waitForResult); // .of(Callable<T>) doesn't exist

Some things to consider:

  1. waitForResult() may throw exceptions. These have to be handled correctly, so that completableFuture.get() would throw an InterruptedException or an ExecutionException.
  2. There must not be another thread involved (supplyAsync() would do so).
  3. It must be a CompletableFuture (possibly wrapped).

I tried this, but this won't handle exceptions correctly:

CompletableFuture.completedFuture(Void.TYPE).thenApply(v -> {
    try {
        listener.await();
        // ...
        return listener.getResult();
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    } catch (SnmpException e) {
        throw new RuntimeException(e);
    }
});

I know Create CompletableFuture from a sync method call, but it doesn't help me:

  • The original code in the question blocks the main thread
  • The code in the answers either incorporates a third thread or doesn't handle exceptions correctly (correct me if I'm wrong)
3 Answers

I'm not sure I understand your requirements. Does this meet them?

private <T> CompletableFuture<T> supplySynchronously(Callable<T> callable) {
    CompletableFuture<T> f = new CompletableFuture<>();
    try {
        f.complete(callable.call());
    } catch (Exception e) {
        f.completeExceptionally(e);
    }
    return f;
}

You can try this, its an big abuse of CompletableFuture but you have to decide if its acceptable for your use case:

private static <T> CompletableFuture<T> supplySynchronously(Callable<T> callable) {
    CompletableFuture<T> f = new CompletableFuture() {

        public T get() throws InterruptedException, ExecutionException {
            synchronized (callable) {
                if (!isDone()) {
                    try {
                        T result = callable.call();
                        complete(result);
                    } catch (Exception e) {
                        completeExceptionally(e);
                    }

                }
            }
            return (T) super.get();
        }
    };
    return f;
}

Let the method Listener.await() invokes method CountDownLatch.await():

class Listener {
   CountDownLatch latch = new CountDownLatch(counter);

   void someMethod(){
     latch.countdown();
   }

   public void await() {
      latch.await();
   }
}

then you can convert it to an asynchronous in following way:

class Listener {
   AsynCountDownLatch latch = new AsynCountDownLatch(counter);

   void someMethod(){ // here nothing changed
     latch.countdown();
   }

   public CompletableFuture<Void> async() {
      return latch.fin;
   }
}

class AsynCountDownLatch extends AsynCountDownLatch {
   CompletableFuture<Void> fin = new CompletableFuture<>();

   public AsynCountDownLatch(long counter) {
     super(counter);
   }

   public void countdown() {
       super.countdown();
       if (super.getCount()==0L) {
           fin.complete(null);
       }
   }
 }

UPDT: if the listener uses another class, then that class also has to be modified/extended/replaced to convert blocking operations to non-blocking. There is no universal way to do such a conversion.

Related