There are lots of guidelines for when to use ConfigureAwait(false)
, when using await/async in C#.
It seems the general recommendation is to use ConfigureAwait(false)
in library code, as it rarely depends on the synchronization context.
However, assume we are writing some very generic utility code, which takes a function as input. A simple example could be the following (incomplete) functional combinators, to make simple task-based operations easier:
Map:
public static async Task<TResult> Map<T, TResult>(this Task<T> task, Func<T, TResult> mapping)
{
return mapping(await task);
}
FlatMap:
public static async Task<TResult> FlatMap<T, TResult>(this Task<T> task, Func<T, Task<TResult>> mapping)
{
return await mapping(await task);
}
The question is, should we use ConfigureAwait(false)
in this case? I am unsure how the context capture works wrt. closures.
On one hand, if the combinators are used in a functional way, the synchronization context should not be necessary. On the other hand, people might misuse the API, and do context dependent stuff in the provided functions.
One option would be to have separate methods for each scenario (Map
and MapWithContextCapture
or something), but it feels ugly.
Another option might be to add the option to map/flatmap from and into a ConfiguredTaskAwaitable<T>
, but as awaitables don't have to implement an interface this would result in a lot of redundant code, and in my opinion be even worse.
Is there a good way to switch the responsibility to the caller, such that the implemented library doesn't need to make any assumptions on whether or not the context is needed in the provided mapping-functions?
Or is it simply a fact, that async methods don't compose too well, without various assumptions?
EDIT
Just to clarify a few things:
- The problem does exist. When you execute the "callback" inside the utility function, the addition of
ConfigureAwait(false)
will result in a null sync. context. - The main question is how we should tackle the situation. Should we ignore the fact that someone might want to use the sync. context, or is there a good way to shift the responsibility out to the caller, apart from adding some overload, flag or the like?
As a few answers mention, it would be possible to add a bool-flag to the method, but as I see it, this is not too pretty either, as it will have to be propagated all the way through the API's (as there are more "utility" functions, depending on the ones shown above).
ConfigureAwait(false)
all you had to do was run the code once with a delegate that prints out the current context, or even just code that would crash if the original context wasn't captured. It'd have taken you far less time than writing this question, given that you already have all of the code written out. – CruzeiroConfigureAwait(false)
to your method will result in the current context being null or non-null in the callback. If you know that it's non-null, you should make that clear in the question that you're simply asking about how to best deal with that fact. Specifically, your question states:I am unsure how the context capture works wrt. closures.
This indicates you don't know what will happen. If you do know, but don't know how to expose a particular set of functionality given that behavior, then that is where you're being unclear. – Cruzeiro