steffen
steffen

Reputation: 16938

Create CompletableFuture from blocking method call

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:

Upvotes: 2

Views: 1996

Answers (3)

Alexei Kaigorodov
Alexei Kaigorodov

Reputation: 13515

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.

Upvotes: 1

HPCS
HPCS

Reputation: 1454

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;
}

Upvotes: 1

MikeFHay
MikeFHay

Reputation: 8983

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;
}

Upvotes: 1

Related Questions