Assaf Lavie
Assaf Lavie

Reputation: 76153

How to await on threaded callback?

I'm using a multi-threaded library that does not use async, but rather uses traditional threads and callbacks.

var connection = new SomeLib();
connection.OnConnected += (x) => { /* This is called from separate thread */ }
connection.Connect();

I am calling this code from an async function, like so:

public async Task<Boolean> MyFunc()
{
    var connection = new SomeLib();
    connection.OnConnected += (x) => { /* This is called from separate thread */ }
    connection.Connect();

    // ...

    // Need to return after OnConnected has been fired. 
    return true;
}

How can I use await to have my function "wait" for the OnConnected callback to be called?

Upvotes: 0

Views: 266

Answers (2)

Shahar Prish
Shahar Prish

Reputation: 4847

The following will work for EventHandler<> type events - but you can easily adjust them to work for different signature delegates.. First, the helper function (EventToATaskAsync) which takes an adder and remover delegates to set/unset the event:

Note that the downside of using this mechanism is that you need to write an extension method per wrapped async operation.

public static Task<A> EventToTaskAsync<A>(Action<EventHandler<A>> adder, Action<EventHandler<A>> remover)
{
    System.Threading.Tasks.TaskCompletionSource<A> tcs = new TaskCompletionSource<A>();
    EventHandler<A> onComplete = null;
    onComplete = (s, e) =>
    {
        remover(onComplete);
        tcs.SetResult(e);
    };
    adder(onComplete);
    return tcs.Task;
}

Then, I usually wrap it with an extension call to add the Task functionality to the existing class. In the following example, I am adding BeginAsync to a storyboard (but you can do that to pretty much anything that uses the event mechanism):

    public static Task BeginAsync(this Storyboard storyboard)
    {
        return EventToTaskAsync<object>(
                e => { storyboard.Completed += e; storyboard.Begin(); },
                e => storyboard.Completed -= e);
    }

Hope that helps.

Upvotes: 3

Bigjim
Bigjim

Reputation: 2255

You can do this with a wait handle. First define an AutoResetEvent:

private waitForConnect = new AutoResetEvent(false);

In the OnConnected handler, trigger the handle:

waitForConnect.Set();

In the function that calls connect, wait for the handle to trigger:

connection.Connect();
waitForConnect.WaitOne(); // This will sleep the thread until connected

Upvotes: -1

Related Questions