views:

337

answers:

1

I've got a method implemented using the Event-based Asynchronous method pattern. I also want to offer a synchronous version of that method, but don't want to re-write it (because the method involves a call to WCF from Silverlight, the Async version has to be the primary method).

I've come up with the following generic method to convert an Event-based Asynchronous call to a Synchronous one:

 Func<TArg1, TArg2, TArg3, TEventArgs> 
    CreateSynchronousMethodFromAsync<TArg1, TArg2, TArg3, TEventArgs>(
       Action<TArg1, TArg2, TArg3, EventHandler<TEventArgs>> asyncMethod) 
         where TEventArgs : AsyncCompletedEventArgs
    {
        Func<TArg1, TArg2, TArg3, TEventArgs> syncMethod = (arg1, arg2, arg3) =>
         {
             TEventArgs eventArgs = null;

             using (var waitHandle = new ManualResetEvent(false))
             {
                 asyncMethod(arg1, arg2, arg3, (sender, e) =>
                                          {
                                              eventArgs = e;
                                              waitHandle.Set();
                                          });

                 waitHandle.WaitOne();

                 return eventArgs;
             }
         };

        return syncMethod;
    }

So if I have this asynchronous method:

void ConnectAsync(string address, 
     string userName, 
     string password, 
     EventHandler<ConnectCompletedEventArgs> completionCallback)

I can convert it into a synchronous call like this:

public void Connect(string address, string userName, string password)
    {
        Func<string, string, string, ConnectCompletedEventArgs> connect = 
            CreateSynchronousMethodFromAsync<string, string, string, ConnectCompletedEventArgs>(ConnectAsync);

        var connectResult = connect(address, userName, password);

        if (connectResult.Error != null)
        {
            throw connectResult.Error;
        }
    }

My concern is about the use of the eventArgs variable which gets captured in the closure. It's being set in one thread, and accessed from another. Is my use of the ManualResetEvent sufficient to guarantee a correct read of the value once the event has been signalled, or do I need to do something else?

Well you're at it, you might want to comment about the handling of Exceptions here. My plan is that the Async method will wrap an exceptions that occur lower down the stack in a ConnectionException, or something like that, so my thinking is that rethrowing the Exception is correct in this case.

+1  A: 

Based on the ASync pattern discussed on the page you referred to, that looks like a pretty good attempt at wrapping the async call.

What makes me hesitate however is the name of the method 'BeginConnect'; some .NET classes have 'BeginXxx' / 'EndXxx' pairs to handle async calls, and these typically specify that correct operation requires that the 'EndXxx' call is invoked from the callback handler, which is something that your scheme does not cater for.

If the call you are wrapping does in fact conform to the pattern discussed on the linked page, this should work, if the call you are wrapping is one of the second kind you're not quite there...

jerryjvl
My code is designed only for use with methods conforming to the Event-base Asynchronous method pattern - my inner method, BeginConnect, whose implementation I've not shown, takes care of wrapping up the Begin/End calls.
Samuel Jack
Note that rather than 'throw connectionResult.Error' you may want to wrap that as an inner exception into a new exception so that the stack trace of the original exception is preserved.
jerryjvl
@Sam: I'd recommend naming the method based on the convention in the linked article then to avoid confusion... make it 'ConnectAsync' instead.
jerryjvl
Good point - I'll do that.
Samuel Jack