[dotnet/csharplang] await should not capture SynchronizationContext by default - Csharp

I believe that the use of ConfigureAwait(true) is much less than ConfigureAwait(false), so instead of thinking about how to improve the ease of use of ConfigureAwait(false), let ConfigureAwait(false) be the default behavior, and let users who need ConfigureAwait(true) write it.

Asked Nov 13 '21 07:11
avatar ahdung
ahdung

9 Answer:

@CyrusNajmabadi Yes, I can't prove it, maybe we need a broad vote something.

This is not a democracy.

The evidence and arguments have already been provided by the teams most knowledgeable about this domain. That was not sufficient to go against the general C#/Roslyn tenets around compat.

Your best bet woudl be to throw in your support for the proposals that improve the ergonomics for library authors, without sacrificing compat. Those proposals could actually happen. Proposals that break billions of lines of code across tens of thousands of companies is just not going to happen.

1
Answered Aug 26 '19 at 08:37
avatar  of CyrusNajmabadi
CyrusNajmabadi

I don't know what you mean here, NRTs have been specifically designed to be backwards compatible.

1
Answered Aug 26 '19 at 07:58
avatar  of Tom01098
Tom01098

@CyrusNajmabadi

But in no way is it the broader use case. The vast majority of use is through normal await x without CA (which is the same as CA(true)), and which benefits from that being the default.

I find the exact opposite.

Capturing the current synchronization context is typically only needed in UI event handlers.

In console apps, ASP.NET Core, and in my own experience, unit tests, it has nil effect, as the synchronization context is the default threadpool context.

Most code that uses await is generally library code, since await is contagious. A single UI event handler using an implicit ConfigureAwait(true) is typically sitting on top of many lower calls that all have to call ConfigureAwait(false).

Since library code could be used by UI code, or in other scenarios where capturing the synchronization context can be anywhere from non-performant to full-blown deadlocking, it has to take the cautious approach and litter ConfigureAwait(false) everywhere.

This is a somewhat pervasive issue, which is why we have #645, #2542 and #2649, among others, and even the LDM has acknowledged it for addressing in a future release.

This particular Issue would introduce an unacceptable breaking chance, but you can't just dismiss the underlying need.

1
Answered Aug 26 '19 at 08:22
avatar  of yaakov-h
yaakov-h

Of course the default cannot and should not be altered.

That said, I totally disagree with @CyrusNajmabadi. Are you really doing that much stuff on the UI thread that ConfigureAwait(true) is a sensible default?

I only do the latter for library code...

The vast majority of C# code out there is library code- or should be library code if it were properly factored (this includes web apps and desktop apps)- and the design that was chosen puts you squarely in the pit of failure. The design that was chosen optimizes for the "my first Windows Form Application" case over the work most developers are doing day in and day out.

Whenever I read code using await and there is no ConfigureAwait, it's almost an error. Just look at the popularity of ConfigureAwait analyzers for both VS proper and ReSharper to see the evidence of this.

IMHO, the async/await feature as it exists in the language today is broken in a fundamental way because of the poor default choice and the need for such a verbose helper method to be used everywhere.

1
Answered Aug 26 '19 at 14:29
avatar  of MgSam
MgSam

I believe that the use of ConfigureAwait(true) is much less than ConfigureAwait(false)

Why do you believe that? In every application i've ever written, CA(true) far outweighs CA(false). I only do the latter for library code, and there is tons more non-library code than library-code out there.

Since Nullable reference types, there's nothing that can't be broken.

I don't know what you mean. NRT breaks no code at all. You can opt into it entirely without any of your code semantics changing at all. That was a major point of the design of it.

1
Answered Aug 26 '19 at 07:58
avatar  of CyrusNajmabadi
CyrusNajmabadi

@ahdung I don't know the point you're trying to make. You made a broad claim of: I believe that the use of ConfigureAwait(true) is much less than ConfigureAwait(false)

However, nothing we've seen has indicated that's the case. There are certainly users and codebases for which CA(false) is by far the dominant pattern. But in no way is it the broader use case. The vast majority of use is through normal await x without CA (which is the same as CA(true)), and which benefits from that being the default.

1
Answered Aug 26 '19 at 08:12
avatar  of CyrusNajmabadi
CyrusNajmabadi

The hang-up is that breaking most UI applications written in the last 7 years is unacceptable. This is going nowhere unless you come up with a way of avoiding that level of breakage.

There are already proposals for this.

1
Answered Aug 26 '19 at 09:22
avatar  of canton7
canton7

This is really an education issue. Years ago someone was bite by careless use of await, and then come to the conclusion that ConfigureAwait(false) should be used everywhere. Then this crap spreads out everywhere, and now it is causing more trouble than deadlocking.

Concurrent programming is hard. It is impossible to guarantee carelessly written async code to be correct, no matter what default behavior is. Devs should know what sync context their code is supposed to run in, rather than saying removing all locks will make their program perfect.

1
Answered Aug 26 '19 at 12:36
avatar  of qrli
qrli

@Tom01098 I don't think it's a problem

You are proposing that people have to unlearn everything they may know so far about tasks and CA. You are doing this, despite not having given sufficient explanation about why this would be beneficial. And you're proposal seems to involve potentially breaking nearly every app out there that uses CA.

This is a major problem. It's a complete non starter. It goes against a major pillar of C#/Roslyn in that breaking changes will only happen if both super valuable and of extremely low chance of being impactful on the existing ecosystem. This proposal violates both of those.

1
Answered Aug 26 '19 at 08:20
avatar  of CyrusNajmabadi
CyrusNajmabadi