views:

269

answers:

3

I am trying to adapt an existing program (internally written) to use a different library than it originally did. I've abstracted most of library-specific code away (seems to be the easier part).

The issue is, the old library would execute a call using a blocking method, and our internal program is expecting a blocking method to be called, but the new library uses an async method.

I was wondering what the best practice would be for waiting for the async operation to complete. My first thought was to have a boolean flag called completed, and spin in a while loop until completed == true.

I should note that this is for a quick proof of concept I'm trying to put together for my stakeholder, and if the stakeholders sign off on the project, I'm going to rewrite the offending part of our program. But for the time being, I need to just wait for the block the function calling the async method until the async call is complete

+2  A: 

Pass a countdown latch to the async method, and wait on the latch. When the async method is complete, it will notify you on the countdown latch, and execution can continue. A monitor could in principal also be used, but is unsafe in the unfortunate case that the async method completes before you can call wait, and you therefore miss the notify.

Oh, and in the .NET world, a countdown latch is called a CountdownEvent: CountdownEvent Class

disown
`AutoResetEvent` will do just as well, since nothing needs to be actually counted. And since `CountdownEvent` is .NET 4.0 only as well... I'm glad they added it though.
Thorarin
+2  A: 

Instead of a flag, I'd think you'd want to use something like a mutex.

In order to do this, that library needs to have an event to notify you once your async call is done. It would look like this:

  1. Create your mutex
  2. In your async complete method, add in 'myMutex.Release();"
  3. In your main method, after you call the async method, add in "myMutex.WaitOne();". This will block the thread until your async call is complete.

It's been a while since I've used this stuff, but I'm pretty sure that's how it should work.

edit: Oops, I think I was thinking of a semaphore, not mutex:

    private static Semaphore mySemaphore = new Semaphore(0, 1);

    static void Main(string[] args)
    {
        Console.WriteLine("Waiting on async call");
        (new Thread(ASyncCallCompleted)).Start();
        mySemaphore.WaitOne();
        Console.WriteLine("Waiting Completed");
    }

    private static void ASyncCallCompleted()
    {
        Thread.Sleep(5000);
        mySemaphore.Release();
    }

edit #2: Per Thorarin's suggestion; it sounds like this class was designed to handle situations like this in .Net:

    private static AutoResetEvent mySync = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        Console.WriteLine("Waiting on async call");
        (new Thread(ASyncCallCompleted)).Start();
        mySync.WaitOne();
        Console.WriteLine("Waiting Completed");
        Console.Read();
    }

    private static void ASyncCallCompleted()
    {
        Thread.Sleep(5000);
        mySync.Set();
    }
John
this would work unless the async method is fast and release gets called before waitone...
disown
I changed the answer a bit; I think if the semaphore is released before the "WaitOne" method is called, it won't block
John
`AutoResetEvent` would make more sense than `Semaphore`. Also, this example will need to be modified if you want to take into account multiple simultaneous calls in a multithreaded environment.
Thorarin
Semaphore would work. A countdown latch and semaphore are very similar, but usually used for different purposes. See http://stackoverflow.com/questions/184147/countdownlatch-vs-semaphore
disown
@disown: There is a race condition as John mentioned. If the async call completes too fast, it might result in a `SemaphoreFullException`.
Thorarin
+1  A: 

This should work for at most one simultaneous call to BlockingCall. Otherwise you will need separate AutoResetEvent instances to determine which call has finished. Anyway:

delegate void Callback();

class BlockAsync
{
    AutoResetEvent _waitHandle = new AutoResetEvent(false);

    public void BlockingCall()
    {
        AsyncCall(AsyncCallCompleted);
        _waitHandle.WaitOne();
    }

    void AsyncCall(Callback callback)
    {
        // Example is not actually asynchronous, to demonstrate the solution
        // is free of race conditions.
        callback();
    }

    void AsyncCallCompleted()
    {
        _waitHandle.Set();
    }
}
Thorarin