[Proposal] Forcing ConfigureAwait(false) on assembly level #8717
Replies: 129 comments
-
@ashmind A quick question, does the following (from #114 comment) needs static async Task<TResult> UsingAsync<T, TResult>(this T disposable, Func<T, Task<TResult>> func)
where T : IAsyncDisposable
{
try { return await func(disposable); }
finally { await disposable.DisposeAsync(); }
} assuming that it is in a library. |
Beta Was this translation helpful? Give feedback.
-
@alrz I don't see why not, but I can't say I understand all the edge cases perfectly. |
Beta Was this translation helpful? Give feedback.
-
Take this sample code:
You'll find that the output is this:
If a task is already completed, then the execution continues on the same thread (that might be a thread with a synchronization context). If you don't use So, to be effective, if you use |
Beta Was this translation helpful? Give feedback.
-
I'd say that this is a CoreFX issue. |
Beta Was this translation helpful? Give feedback.
-
@HaloFour But can it be implemented in CoreFX without option B support by compiler? |
Beta Was this translation helpful? Give feedback.
-
Considering how loose the awaiter spec allows for resolving the |
Beta Was this translation helpful? Give feedback.
-
+1 It's indeed a pain to have |
Beta Was this translation helpful? Give feedback.
-
I'd love to see a
We've had ...or we can make |
Beta Was this translation helpful? Give feedback.
-
Just want to confirm that this is a major pain for library authors. Installing analyzers into hundreds of projects and then having to set them all to Error severity just to ensure we add this ugly ConfigureAwait call is pretty de-motivating. But another alternative could be to introduce another await-style keyword as syntactic sugar for await with ConfigureAwait ( false ). Not sure what a good name would be though. |
Beta Was this translation helpful? Give feedback.
-
How do you guys think of an approach with Task-like (Generalized Async Return Types): This requires no IL-weaving or no new compiler feature. |
Beta Was this translation helpful? Give feedback.
-
@ufcpp pretty nice solution. Why they didnt added something like that to the TPL? |
Beta Was this translation helpful? Give feedback.
-
I too find the current default maddening. What percentage does sync-requiring GUI code represent of all C# code being written today, anyway? Sigh. |
Beta Was this translation helpful? Give feedback.
-
That's actually possible. We could add So that would be C Enter. |
Beta Was this translation helpful? Give feedback.
-
That'd still produce hude code redundancy. Looking forward to |
Beta Was this translation helpful? Give feedback.
-
Proposals for BCL additions can be made on the CoreFX repository as any such helper method won't require language changes. You'd have to describe exactly what you expect it to do. |
Beta Was this translation helpful? Give feedback.
-
I would also certainly love to see something akin to Library authors could apply that and those of us who would actually prefer to be explicit about needed the continuation to run on the same context/tread where it is needed, could just apply that to all that we do. I am very curious about some of the all the defense made for the default chosen behavior here, to me it's one of the biggest design flaws of the TPL, and I certainly don't buy the "most people would be best off with the current default", not then, not now...
Hopefully though, most of those 80%-90% was working on something bigger than just a small little "All code in the GUI" application, and if so, then suddenly they where actually developing internal libraries. Even though a library is not shared in the world, and even if it's never shared in your organisation, doesn't mean you shouldn't treat it well. And in terms of old patterns, why would I not want my BusinessLogic or DataAccess code to execute on the first ready and available thread? Why would I at all want to lock my application to only ever execute on the GUI thread in any other places than where it was absolutely needed? - That just doesn't make any sense to me. We jumped hoops before to offload work to other threads than the GUI, utilizing the CPU's cores as best we could. So it's not about what type of developer there are most of, it's about what type of code there is most of to me, in the context of a windows forms application, if your code does not modify any form controls, draw on any surfaces etc. It's NOT UI code, and therefore should not require to execute on your UI Context, instead it should align to the recommendation for libraries, hence applying Take this small, simple, dumb - but illustrative example.
In that example, why would I not want to add that ".ConfigureAwait(false)"?, what would the need be? I would much rather have had to do:
Because then it's explicit to me. This is the piece of code that has a special requirement, not the DoHardWork method, The DoHardWork method doesn't care if it's executed on the GUI thread or not:
Therefore, DoHardWork should not need to add anything special like
Hence... it should be here I mark that I have a special requirement, namely that i require this piece of code to execute on the GUI Thread. That also means that it's no longer "MAGIC", And in clearly signals that there is a special case/requirement here.
The standing recommendation from microsoft on this matter is that library authors should probably always use Besides, any library you use provides you with a "contract" (the contract is often implicit though, so you have to discover it), if you don't uphold that contract and it means your application crashes, well then that is on you. So if a library applies Now... If we talk about flipping the "default" behavior for all Tasks tomorrow, then obviously it would be a massive breaking change that will break applications all over the world. There is no doubt about that, that is why I think that introducing a Where as solutions like The important thing is that whatever is done to ease this, is something that only affects code "in your control". Sorry if this became a bit "ranty" . |
Beta Was this translation helpful? Give feedback.
-
@jeme You could take this argument even further: Should there be a These things are super dangerous. They make code non-local, non-composable and the bugs are nasty. But they are so convenient... I'm really undecided on this. I sometimes think that these contexts optimize for novice programmers and demo-ware in which the goal is to make things "just work". The more scale the codebase has the more it requires preciseness and modularity. Creating a |
Beta Was this translation helpful? Give feedback.
-
An interesting approach would be to use a different version of the TPL library that always do that. There could be two versions of the same library sharing the same contract and you could choose between UI/Library by simply adding the correct reference to your project.
Certainly, that totally was a design flaw. But asking for breaking changes in working code is a obvious no go. So that's why I ask for another new incompatible and potentially different version of the TPL that does the correct thing.
Seconded, explicit is always better.
Perhaps one could create a Roslyn analyzer/rewriter. On the library side, It would to detect all uses of
I bet if some library is fooling with this, it would be better having its own Task library and Task Pool than using the shared one, in this case my solution would not work that well .
I would find it amusing if that happened, it means the library contract is badly designed, it has literal undefined behavior and just happens to work. If you get to this point, all bets are lost, you can either live with it and don't change anything, or rewrite all uses of Task library. Changing a task library is a big task, but arguably doable, if your software has proper tests, shouldn't be too bad.
As I said, asking for breaking changes in working code is a obvious no go.
This is TERRIBLE, this will do more harm than good, don't ever think about it. Better literally to not do anything than creating Global State that configures behavior of asynchronous code.
My solution of replacing |
Beta Was this translation helpful? Give feedback.
-
The perils of implicit hidden mutable state. Async code should be Pure, but that's impossible to control with C#... If only there was better research on Dependent Types, you could tag the contexts in the type and it would be a compile time error to use some functions in some contexts, you could explicit code in the type the invariants you require. For example, this code needs to never run continuations in the same Thread because its a danger. You could do that with generics, but it becomes a pain very fast without
then on calling, inside another async.
Actually you could even get rid of the async/await keyword, you know you are inside Task context. But this is for future research... (if only I had a position in the Research Division, I would love to play with those things). |
Beta Was this translation helpful? Give feedback.
-
If your projects never need synchronization then you'd be fine simply never setting a You call |
Beta Was this translation helpful? Give feedback.
-
IMHO, this may be implemented like nullable context, with ability to enable/disable implicit *.csproj: <ConfigureAwait>false</ConfigureAwait> *.cs: #configureawait true
public static async Task ContextDependentMethod() { ... }
#configureawait restore UPD: Ok, introducing additional context is bad idea, class/method attribute for changing assembly-level default will be better. |
Beta Was this translation helpful? Give feedback.
-
Two years later, and I don't see any default tooling in VS. I have to add the VS Threading library. And if tooling knows to just blindly add it everywhere, then the compiler should be able to do the same and reduce the noise. |
Beta Was this translation helpful? Give feedback.
-
It isn't designed to be blindly added everywhere, though. |
Beta Was this translation helpful? Give feedback.
-
dotnet_diagnostic.CA2007.severity = warning |
Beta Was this translation helpful? Give feedback.
-
With the death of WinForms and WPF it should be safe to deprecate ConfigureAwait |
Beta Was this translation helpful? Give feedback.
-
Someone should tell the millions of developers using WinForms and WPF that this is happening. 😁 More seriously, those technologies are far from dead - there are a huge number of actively maintained projects and products that still deliver huge value. |
Beta Was this translation helpful? Give feedback.
-
If it's a non-UI library, then we are adding it blindly everywhere. We haven't had any issues yet and if we did, it would be the exception and we would be happy to explicitly set it. Even @CyrusNajmabadi says he uses tooling to add it everywhere. |
Beta Was this translation helpful? Give feedback.
-
this might interest some folks: ConfigureAwait in .NET 8 by Steve Cleary reference: dotnet/runtime#87067 |
Beta Was this translation helpful? Give feedback.
-
@spottedmahn nobody wants to write more It would be nice to have an option (in csproj, or via assembly attribute) to generate assembly-level default |
Beta Was this translation helpful? Give feedback.
-
@spottedmahn as @epeshk wrote, we I think most people posting here don't want more of that stuff but rather less! While And that probably also goes for the In fact, it just brings some new awfulness to the API, e.g. the following is invalid and will give you a runtime exception: (it compiles just fine! - although you may raise a warning to error to avoid that)
So I am not sure, It feels more like a step in the wrong direction to me. But I don't know, maybe there is some benefits to having the API like this over a more explicit one as the first I linked. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Problem
It's recommended to always use
ConfigureAwait(false)
in certain kinds of libraries. While it's definitely possible to use an analyzer (e.g. ConfigureAwaitChecker.Analyzer) to catch those cases, the analyzer has to be installed separately and the resulting code is awkward and verbose.Potential solutions
Option A
Provide an assembly-level attribute that would force compiler to generate (pattern-based)
ConfigureAwait(false)
calls for eachawait
.Option B
GetAwaiter([CallerMethod] MethodBase method = null)
Task
could use this overload to look for some attribute onmethod.Assembly
and return a correspondingly preconfigured awaiter.Beta Was this translation helpful? Give feedback.
All reactions