Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapping an asynchronous computation into a synchronous (blocking) computation

similar questions:

  • Pattern for wrapping an Asynchronous JavaScript function to make it synchronous
  • Wrapping an asynchronous method synchronously in C#

I have an object with a method I would like to expose to library clients (especially scripting clients) as something like:

interface MyNiceInterface {     public Baz doSomethingAndBlock(Foo fooArg, Bar barArg);     public Future<Baz> doSomething(Foo fooArg, Bar barArg);     // doSomethingAndBlock is the straightforward way;     // doSomething has more control but deals with     // a Future and that might be too much hassle for     // scripting clients } 

but the primitive "stuff" I have available is a set of event-driven classes:

interface BazComputationSink {     public void onBazResult(Baz result); }  class ImplementingThing {     public void doSomethingAsync(Foo fooArg, Bar barArg, BazComputationSink sink); } 

where ImplementingThing takes inputs, does some arcane stuff like enqueueing things on a task queue, and then later when a result occurs, sink.onBazResult() gets called on a thread that may or may not be the same thread as ImplementingThing.doSomethingAsync() was called.

Is there a way I can use the event-driven functions I have, along with concurrency primitives, to implement MyNiceInterface so scripting clients can happily wait on a blocking thread?

edit: can I use FutureTask for this?

like image 562
Jason S Avatar asked Feb 01 '10 22:02

Jason S


People also ask

Should I expose synchronous wrappers for asynchronous methods?

In my discussion of “async over sync,” I strongly suggested that if you have an API which internally is implemented synchronously, you should not expose an asynchronous counterpart that simply wraps the synchronous method in Task. Run.

Can we call synchronous method from another asynchronous method?

Yes, We can call a synchronous method inside a future method. Both will run on Asynchronous Only.

How do you convert asynchronous to synchronous in Java?

Using Future to convert Async to Sync method call Future is an Interface which represents the result of an asynchronous computation. We can use Future. get() or Future. get(long timeout, TimeUnit unit) to get result as a sync method call.


2 Answers

Using your own Future implemenation:

public class BazComputationFuture implements Future<Baz>, BazComputationSink {      private volatile Baz result = null;     private volatile boolean cancelled = false;     private final CountDownLatch countDownLatch;      public BazComputationFuture() {         countDownLatch = new CountDownLatch(1);     }      @Override     public boolean cancel(final boolean mayInterruptIfRunning) {         if (isDone()) {             return false;         } else {             countDownLatch.countDown();             cancelled = true;             return !isDone();         }     }      @Override     public Baz get() throws InterruptedException, ExecutionException {         countDownLatch.await();         return result;     }      @Override     public Baz get(final long timeout, final TimeUnit unit)             throws InterruptedException, ExecutionException, TimeoutException {         countDownLatch.await(timeout, unit);         return result;     }      @Override     public boolean isCancelled() {         return cancelled;     }      @Override     public boolean isDone() {         return countDownLatch.getCount() == 0;     }      public void onBazResult(final Baz result) {         this.result = result;         countDownLatch.countDown();     }  }  public Future<Baz> doSomething(Foo fooArg, Bar barArg) {     BazComputationFuture future = new BazComputationFuture();     doSomethingAsync(fooArg, barArg, future);     return future; }  public Baz doSomethingAndBlock(Foo fooArg, Bar barArg) {     return doSomething(fooArg, barArg).get(); } 

The solution creates a CountDownLatch internally which is cleared once the callback is received. If the user calls get, the CountDownLatch is used to block the calling thread until the computation completes and call the onBazResult callback. The CountDownLatch will assure that if the callback occurs before get() is called the get() method will return immediately with a result.

like image 75
Michael Barker Avatar answered Sep 25 '22 00:09

Michael Barker


Well, there is the simple solution of doing something like:

public Baz doSomethingAndBlock(Foo fooArg, Bar barArg) {   final AtomicReference<Baz> notifier = new AtomicReference();   doSomethingAsync(fooArg, barArg, new BazComputationSink() {     public void onBazResult(Baz result) {       synchronized (notifier) {         notifier.set(result);         notifier.notify();       }     }   });   synchronized (notifier) {     while (notifier.get() == null)       notifier.wait();   }   return notifier.get(); } 

Of course, this assumes that your Baz result will never be null…

like image 40
Paul Wagland Avatar answered Sep 25 '22 00:09

Paul Wagland