1

I've been reading about task-like and awaitable types. I think I've got a good grasp on the basic mechanics of it (awaiter, asyncmethodbuilder...) but there's something amiss when I try to understand the usage of ConfigureAwait(false) in such scenario (if that is even possible).

My questions are:

Is ConfigureAwait reserved for "true" Tasks, meaning the usage or not of the SynchronizationContext?

If so, and say you're coding a general purpose library that exposes it's own implementations of task-like types, should you just use ConfigureAwait(false) when your code encounters Tasks?

Thanks.

João Sequeira
  • 157
  • 3
  • 12
  • 1
    my question: _why_? what specific use case do you have where you need to use task-based async programming, but can't use the built-in Task-class? – Franz Gleichmann Jul 18 '20 at 16:30
  • 2
    Is your knowledgeable answer dependent on a why? The question is if it's possible. I didn't find an answer for that on my investigation into the matter. – João Sequeira Jul 18 '20 at 16:54
  • 2
    `Is ConfigureAwait reserved for "true" Tasks` - no, it isn't. See https://devblogs.microsoft.com/dotnet/configureawait-faq/, section "What does ConfigureAwait(false) do?". – GSerg Jul 18 '20 at 16:54
  • @GSerg I actually spent the morning absorbing that post and was dismayed to see the comment section was closed or I would've directed my inquiry directly at the author, Stephen Thob. But I couldn't find a reference to ConfigureAwait for other than tasks, IAsyncEnumerable and IAsyncDisposable. – João Sequeira Jul 18 '20 at 17:10
  • 2
    Yes it is possible for any task like type... But you have to write it. See this Marc gravel library. Specifically look for `ConfiguredYieldAwaitable` which is a way to configure `Task.Yield` which has a custom awaiter. https://mgravell.github.io/PooledAwait/ – pinkfloydx33 Jul 18 '20 at 17:43
  • 1
    Also some examples of custom configured awaitables on the answers to this question https://stackoverflow.com/q/51375326/491907 – pinkfloydx33 Jul 18 '20 at 17:52
  • @pinkfloydx33 thanks. I'll look into the resources you pointed out. – João Sequeira Jul 18 '20 at 19:10

1 Answers1

2

If the task-like types of your library does not capture the context when awaited, then the users of your library will not be able to do this:

private async void Button_Click(object sender, EventArgs e)
{
    string input = Textbox1.Text;
    TaskLike<string> taskLike = YourLibrary.ProcessAsync(input);
    string result = await taskLike;
    Label1.Text = result // InvalidOperationException: Cross-thread operation not valid
}

On the other hand if the task-like types of your library always capture the synchronization context when awaited, then your library will be inefficient as a building block of other libraries, and will require from the other libraries authors to jump through hoops in order to invoke your library in a synchronization-context-free manner. So making your task-like types configurable regarding capturing the synchronization context should be a desirable feature.

Regarding whether you should use ConfigureAwait(false) when the internal code of your library encounters standard Tasks, it depends on whether your library accepts caller-supplied lambdas. If for example your library includes a method like this:

public TaskLike<string> ProcessAsync(Action action);

...then invoking the action after awaiting internally something with ConfigureAwait(false) may result to cross-thread violation exceptions, if the caller's lambda includes thread-affine code like reading properties of UI controls. To solve this problem may require to introduce a configuration parameter continueOnCapturedContext in the method's signature. At least this is the solution chosen by the Polly library.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104