Search code examples
c#async-awaitevent-driven

How can I await until I receive a callback/notification without busy-waiting?


I understand how I can await on library code to wait for a network request or other long-running action to complete, but how can I await on my own long-running action without busy waiting?


This is the busy-waiting solution. How can I make it event-driven?

public async Task<DataBlock> ClickSend() {
    // await until someone calls DataReceived()
    while (_Block == null) {
        await Task.Yield();
    }
    return _Block;
}

// Something external calls this function.
// Once called, ClickSend should complete with this data.
public void DataReceived(DataBlock data_block) {
    _Block = data_block;
}
DataBlock _Block = null;

(This question is completely different from How do I await events in C#? which is asking how to await on event handlers, but very similar to Promise equivalent in C#.)


Solution

  • Generally in concurrency a "future" is placeholder for a return value and it has an associated "promise" that is fulfilled to pass the final return value.

    In C#, they have different names: the future is a Task and the promise is a TaskCompletionSource.

    You can create a promise, await on it, and then fulfill it when you get your callback:

    TaskCompletionSource<DataBlock> _Promise = new TaskCompletionSource<DataBlock>();
    
    public async Task<DataBlock> ClickSend() {
        DataBlock block = await _Promise.Task;
        return block;
    }
    
    public void DataReceived(DataBlock data_block) {
        _Promise.TrySetResult(data_block);
    }
    

    Here's an executable example.