Git Product home page Git Product logo

Comments (129)

NickCraver avatar NickCraver commented on July 17, 2024 101

I'd love to see a [assembly:TaskConfigureAwait(false)] added to the framework. People are working around this (but not in netstandard) with Fody today: https://www.nuget.org/packages/ConfigureAwait.Fody

async/await is great, but there are a few very big and very consistent pain points for library authors and app owners alike. At the top of my list are:

  • .ConfigureAwait(false) almost everywhere
  • Stack traces being incredibly noisy

We've had async for over 4 years now and neither of these has improved at all. Any language feature which requires repeating something verbose in almost every place is a bad experience. On top of that, forgetting to add the verbose thing (and many new to async do) defaulting to failure (overhead in this case) adds to the priority, IMO. Please, let's give library authors a break here.

...or we can make .ConfigureAwait(false) a VS 2017 built-in keyboard shortcut.

from csharplang.

vermorel avatar vermorel commented on July 17, 2024 58

+1 It's indeed a pain to have ConfigureAwait(false) everywhere in practice for all libraries.

from csharplang.

petertiedemann avatar petertiedemann commented on July 17, 2024 22

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.

from csharplang.

asbjornu avatar asbjornu commented on July 17, 2024 18

I too find the current default maddening. What percentage does sync-requiring GUI code represent of all C# code being written today, anyway? Sigh.

from csharplang.

ufcpp avatar ufcpp commented on July 17, 2024 15

How do you guys think of an approach with Task-like (Generalized Async Return Types):
https://github.com/ufcpp/ContextFreeTask

This requires no IL-weaving or no new compiler feature.

from csharplang.

ashmind avatar ashmind commented on July 17, 2024 13

@wachulski This is very practical, but I feel we should have more ambition and aim for cleaner code.
It's like if instead of await we got an analyzer that lets us write ContinueWith() better.

from csharplang.

AgentFire avatar AgentFire commented on July 17, 2024 12

That's actually possible. We could add ConfigureAwait(false); to the completion list off the task.

That'd still produce hude code redundancy.
Personally, I never use the same any line of code more than 2 times per solution. Code repeatedness makes my eyes bleed.

Looking forward to ThreadPool.MakeDefaultConfigureAwait(false); with a desperate hope in my eyes.

from csharplang.

effyteva avatar effyteva commented on July 17, 2024 12

Almost 3 years since the proposal,
Perhaps it's time to take care of this?
Assembly/Class/Method attributes could save tons of hassle.
There's a Fody addin for that https://github.com/Fody/ConfigureAwait, which we haven't tried, as we prefer a native solution, as modifying the IL doesn't seem like a reliable solution for us.

from csharplang.

paulomorgado avatar paulomorgado commented on July 17, 2024 8

Take this sample code:

void Main()
{
    SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
    Console.WriteLine(SynchronizationContext.Current?.GetType().Name);
    M().Wait();
    Console.WriteLine(SynchronizationContext.Current?.GetType().Name);
}

async Task M()
{
    Console.WriteLine(SynchronizationContext.Current?.GetType().Name);
    await Task.CompletedTask.ConfigureAwait(false);
    Console.WriteLine(SynchronizationContext.Current?.GetType().Name);
    await Task.Delay(10).ConfigureAwait(false);
    Console.WriteLine(SynchronizationContext.Current?.GetType().Name);
}

You'll find that the output is this:

WindowsFormsSynchronizationContext
WindowsFormsSynchronizationContext
WindowsFormsSynchronizationContext
null
WindowsFormsSynchronizationContext

ConfigureAwait(false) has no effect on completed tasks.

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 ConfigureAwait(false) on subsequent calls, their continuation will be posted to the captured synchronization context.

So, to be effective, if you use ConfigureAwait(false) in one method, you'll have to use it on all awaits.

from csharplang.

davidroth avatar davidroth commented on July 17, 2024 8

Agree with @tmat: IL rewriters come with significant downsides:

  • big impact on build performance (ex. In one of our projects we are using fody.propertychanged. It takes 15 seconds on each build to IL rewrite our assemblies).

  • Metadata issues: Problems with debugging, expression evaluators, and non functional ENC.

from csharplang.

ashmind avatar ashmind commented on July 17, 2024 6

@alrz I don't see why not, but I can't say I understand all the edge cases perfectly.
Let me know if I'm missing something here.

from csharplang.

wachulski avatar wachulski commented on July 17, 2024 6

@ashmind What about:

Option C

Incorporate ConfigureAwaitChecker.Analyzer (counterpart) into native Roslyn analysers set with 2 diagnostics (and fixers):

  • Report when .ConfigureAwait(...) not explicitly set
    • opt-out
    • warning level by default
  • Report when .ConfigureAwait(false) not set
    • opt-in
    • error level by default

As for per solution/project configuration means new .editorconfig options might be introduced:

  • configureawait_must_be_explicit // false by default
  • configureawait_must_be_false // false by default
    Those would allow for opt-in/out up to project level basis.

from csharplang.

theunrepentantgeek avatar theunrepentantgeek commented on July 17, 2024 6

Check out the other issues that discuss trying to eliminate ConfigureAwait() calls - it's been extensively discussed.

Essentially, however, my understanding boils down to this:

The language has no knowledge of implementation details. The async and await keywords only require that the types involved are awaitable.

The most commonly used implementation (by far) is the Task Parallel Library (TPL), It's the TPL that introduces the Task and ValueTask types, along with the concept of a synchronization context.

Crucially, nothing in the language requires you to use the TPL implementation.

Adding a language feature to address this library issue seems (to me) like the wrong approach. It would irrevocably bind the feature to a very specific implementation, and very likely tie things up so tightly that any future innovation in the area would be impossible.

It would be far better to work out how to move the design of the TPL forward in a useful way that eliminates the ConfigureAwait() warts while still solving the problems that Synchronization Contexts were introduced to solve.

But, that's a discussion for a different repo.

from csharplang.

alexyakunin avatar alexyakunin commented on July 17, 2024 6

I like the original proposal with a few modifications for the consistency:

  • [ConfigureAwaitDefault(bool? configureAwaitDefault = null)] should be applicable on assembly, class, and method level (the nearest one wins)
  • <ConfigureAwaitDefault>configureAwaitDefault</ConfigureAwaitDefault> property should work on .csproj level (this maps to addition of the same attribute on assembly level)
  • configureAwaitDefault can be null or omitted - this matches the current behavior.

The option should impact only the code generated by C# async state machine builder. When configureAwaitDefault != null the builder should additionally invoke ConfigureAwait(configureAwaitDefault) on whatever is awaited if ConfigureAwait method is available (or an extension method with the same name). This means you are free to call ConfigureAwait manually as well, and in this case configureAwaitDefault won't make any difference.

Thoughts?

from csharplang.

sharwell avatar sharwell commented on July 17, 2024 5

Over the past year, vs-threading has reversed my view on this issue. I am very much looking forward to no longer needing to specify ConfigureAwait outside of rare edge cases where ConfigureAwait(false) is required for correctness.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024 5

On a personal level, i like @HaloFour 's proposal the most. The solution is visible in your source and easy to add. I don't like any sort of implicit changes here. Extension operators and implicit assembly changes are highly undesirable ot me. When it comes to await behavior and async/concurrency, i want to be able to see what's going on in the code front and center.

once you have a handful of awaits, i think it's very reasonable to just do:

public async Task<Something> DoSomeStuff()
{
    using var _ = Task.DisableSynchronizationContext()

    // no more sync context to worry about
    var x = await DoSomething();
    var y = await DoSomethingElse();
    var z = await DoOtherStuff();
    return await MixItAllTogether(x, y, z);
}

A very big positive is that it's simple, can be done today, and is very visible in the code. It's something i woudl accept into codebases i work on, unlike several other proposals that are highly problematic.

from csharplang.

theunrepentantgeek avatar theunrepentantgeek commented on July 17, 2024 5

the number of libraries should certainly exceed the number of applications that depend on the synchronization context...

Have you heard of Dark Mater Developers?

Scott Hanselman posits (quite reasonably) that the developers you see bloggings, tweeting, posting on social media, attending conferences (where available) and so on are the slim minority of developers. The rest of them are working developers, delivering business value as a part of their 9-5 job, getting their job done and then heading home to enjoy other pursuits.

I'd suggest that the number of libraries requiring asynchrony would be vanishingly small compared with the number of WinForms and WPF applications being written and maintained right now in 2021.

I worked full time as a .NET developer for a bank between early 2004 and late 2015 and I wrote literally dozens of them that are still in daily use.

from csharplang.

blankensteiner avatar blankensteiner commented on July 17, 2024 4

@HaloFour @CyrusNajmabadi

Thanks for the replies! I appreciate you taking the time.

I've seen (relatively few) problems going unsolved for years and it seems to happen when a problem generates multiple issues within multiple repositories. That makes it very hard to see who has the ball and what the overall status is.
I don't know if Microsoft has any cross-team meetings to look at "over X-year-old" issues, but I think some coordination could be useful here. To me, it sounds like a problem that Microsoft is able to solve, the right people just need to be put together and focus on this.
If there really is no good solution, not even one that can be defined by a cross-team effort, then that's fine, but then close the issues with proposals you can't/won't implement. Then there is at least some "progress" and a "result".
Even better, elevate the problem to a "Super Challenge" and make a community-wide contest out of it or something, just to let it be known that there is a hard problem that will go unsolved unless something extraordinary is done about it (just a suggestion from a frustrated developer).

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024 4

@Kir-Antipov

For every JSON.NET library there are millions of apps that consume it. Even for corporate helper libraries, there's no point to them if they're not used by more than one application. I don't have the numbers which led to this decision, but the .NET and C# teams did at the time based on feedback and VS telemetry. I'd easily put money on there being more WinForm internal business apps written every year than all libraries. And I'd wager still that the majority of libraries written are still context sensitive and intended to be consumed by a specific flavor of application.

And no matter how we answer it today, the problem still remains for a nonzero number of people, and it needs to be somehow solved

It is solved, by virtue of being able to call CA(false). Yes, this is intentionally more painful for the minority of developers, and it sucks to be in that minority of developers.

I'd also note that, as mentioned above, this isn't really a language issue. The C# language doesn't know anything about context, it only understands the awaiter pattern. What that awaiter does with context is entirely opaque to the language.

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024 4

@vitidev

What feedback s and telemetry can be on functionality that did not exist at that time?!

Feedback on the ratio of project types.

And why it was decided to do exactly
ConfigureAwait(false)
but not with something
await? / await! / ^await etc

Because there was no need to make the language aware of it. Adding policy concerns only complicates the language and makes it harder to evolve. ConfigureAwait is just a normal method that exists on Task and a handful of other types that provide built-in support for synchronization. Other awaitables could offer entirely different methods to configure synchronization or other policy.

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024 3

@AgentFire

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.

from csharplang.

theunrepentantgeek avatar theunrepentantgeek commented on July 17, 2024 3

@heku The Language Team’s stance on introduction of language dialects is unlikely to change, especially for a flag like this that could make two versions of what is otherwise an identical assembly incompatible.

from csharplang.

blankensteiner avatar blankensteiner commented on July 17, 2024 3

It's just mind-blowing and very sad that this is still an issue in 2020.
Microsoft, I don't want to be a jerk, but I truly believe in being honest and direct and I have to say that in my opinion you have drop the ball on this one and it's just not good enough.
You want feedback, you want to be open source, you want a community, well then don't ignore a major plain like this one for years. I get that it's hard to solve and maybe it's falling between two chairs and so on, but it has been years now!
Please, solve this. If I still have to ConfigureAwait(false) things when .NET 5 comes out, you'll have to explain to my kids why my brains are on the ceiling. No, seriously though, I'm happy with my choice to be a .NET developer and the general direction of .NET Core and .NET 5, but you need to fix this and you need to fix it now...
And no, ConfigureAwait.Fody is not an option, it doesn't calculate a new checksum and thereby making it impossible to upload the snupkg to NuGet.
Hoping for a status update and an ETA.

from csharplang.

jmarolf avatar jmarolf commented on July 17, 2024 3

@Kir_Antipov At the time (almost 10 years ago) the data I had access to had a range. Application developers represented 80%-90% of the .NET developer community with 10%-20% being made up of library developers. Based on that, and the fact that all applications you could build at the time would need ConfigureAwait(true) it was set as the default. But even without real data lets just go through the intellectual exercise:

Suppose that everyone whoever writes an application (Which we will define as something they are responsible for the deployment of) also writes a library and publishes it online. This would be an amazing universe where:

  • Almost all code is novel (If everyone is publishing a library it means that one they could have re-used didn't already exist)
  • 50% of all code is used in application and 50% is used in libraries

If we instead say that every developer is not going to write a json library since one already exists then they means that the number of applications over libraries will be >50%.

from csharplang.

Kir-Antipov avatar Kir-Antipov commented on July 17, 2024 3

@HaloFour,

For every JSON.NET library there are millions of apps that consume it

Not every library is consumed by millions of users. Moreover, not every library is consumed by more than one application. So, it's still Many-to-Many and which Many is greater is still not obvious. And I don't know why are we still arguing about it, because:

  1. I won't believe anything without scientific fact being provided. "These guys have private telemetry which says so" is not a scientific fact. Among which participants was telemetry going? In what time period did this happen? How was the final value calculated? How much has changed since then?

  2. My belief (based on the personal experience) does nothing with the problem of this topic

It is solved, by virtue of being able to call CA(false)

No-no-no, stop it right there, sir. It's just like saying: "The asynchronous programming problem was solved by the Thread class" (A warning for the guy who is about to write that async != multithreading: this was exactly the point, thanks)

This was a completely different problem, the solution of which led to another, smaller (?) problem, and that's why we're all here

I'd also note that, as mentioned above, this isn't really a language issue. The C# language doesn't know anything about context, it only understands the awaiter pattern. What that awaiter does with context is entirely opaque to the language.

I completely agree. The problem is presented by the framework, so it would be nice if framework itself could fix this, or some new general purpose language feature may be introduced that can also help in solving this problem (that's my point, I don't like tricky compiler-behavior switches too)


@jmarolf,

At the time (almost 10 years ago) the data I had access to had a range. Application developers represented 80%-90%

I got it numerous times: there's some closed telemetry data from like 10 years ago that says so, nice, there's no need to repeat it that many times, it makes nothing.

Well, ok. Let's imagine that the telemetry gave the most accurate results and they're still applicable. Is 10-20% of .NET developers a small number? If they are a minority, do they have to suffer? If they are a minority, do they have no right for a better life? Is minority negligible? I don't think this is the politics of the modern world.

I can understand logic behind CA(true) as default, but I can't understand why there's still no built-in solution to make library developers' life easier.

Shouldn't we be more nice to library developers? The bare framework is of course still good on its own, but where would we be without all these fancy libraries floating around? Why, for example, is Python so popular? The beauty of the syntax, the speed of work (ok-ok, I'm done, for real now) and, of course, a huge number of the most diverse libraries. What's happening at this time in the world of .NET library developers?

I know a lot of people who eventually gave up on .ConfigureAwait(false), they simply decided that if your application has a synchronization context, that's your problem, not theirs. Not the best solution, don't you think? Do I blame them? Can I blame them? Of course not. CA(false) is terrible: you constantly need to keep it in mind, add it to almost each method call, and periodically wipe the blood pouring from your eyes because of the boilerplate code that is smeared all over your screen.

I even know a good developer who rage quit C#-programming because of this. The poor fellow literally had a mental breakdown after yet another .ConfigureAwait(false).

So, it seems like a lot of us have some "CA(false)-durability" :)

But even without real data lets just go through the intellectual exercise

What's so intellectual about this "exercise"? Finding out the way it's broken? Kinda easy for real "intellectual exercise".

Let's take a closer look at your "amazing universe" and write down the key rules:

  1. All libraries and apps are unique (Almost all code is novel)
  2. Every app depends on 1 library, which describes its business logic (I believe, 'cause it was not stated directly, but depending on some kind of random library in this situation seems way more ridiculous) (Suppose that everyone whoever writes an application [...] also writes a library)

If we instead say that every developer is not going to write a json library since one already exists

And here you apply an expression from the real world (so your interlocutors can lose the thread of reasoning while they are busy processing the joy of recognition: "Oh, hey, I use JSON libraries in every project too, seems alright") to your invented world with made up rules. Since this partially breaks the first rule (apps are no longer unique as business logic was fully duplicated, but libraries are) while you're focusing on the second, I'm sure this was done on purpose.

But even that's not all! You used the word "every" very well. So now you're basically comparing the length of an infinite series of constants (apps which business logic's described by json library) to the length of a series of one element (json library itself). And here even an idiot would agree that the number of applications is slightly greater than the number of libraries, which, in fact, is what you wanted to prove.

And this whole thing is called sophistry. Don't get me wrong, big fan, and you did it well, but it's not the right place to do such things, I believe


To end this post, I want to describe my position, because of which I was so sure that the number of libraries outnumbered the number of applications.

To put it simply and briefly, this belief is based on personal experience: every project (personal or corporate) that I have worked on over the past 5 years has "spawned" from one to several dozen new libraries. If you'll take the average ratio of libraries per application from my experience, you'll get something like 4.

In longer terms, you forgot one thing about libraries: they're the logical unit just as functions or classes. If a function becomes too large, we split it into several ones. Small functions are easier to maintain, easier to debug, easier to test (we don't have the complexity index for no reason). The same applies to libraries. Their goal is far from just reusing some code.

Even for corporate helper libraries, there's no point to them if they're not used by more than one application

So the existence of a library that is used by only one project is more than justified.


I hope we're done. If there're still people who want to prove to me that the number of libraries exceeds the number of applications, please re-read the first statement of the first paragraph of this message and do not clog the thread (unless you have actual numbers on hand). We've already gone offtopic too much

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024 3

but I can't understand why there's still no built-in solution to make library developers' life easier.

Personally, as a library writer, I find the situation totally fine. I write code without ever writing CA (except the occasional CA(true)). Then, at the end, I use an analyzer/fixer to add an the CA(false)s to everything I didn't annotate.

It's a single step and then I'm done. So, as one of the language designers, I just don't feel any sort of massive need to do something here. This is compounded by having not seen any good proposals that I like here that would even address the issue.

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024 2

I'd say that this is a CoreFX issue. ConfigureAwait is nothing more than an instance method on Task/Task<T>, the language provides no specific support for calling it.

from csharplang.

dominikjeske avatar dominikjeske commented on July 17, 2024 2

It should be for method, class and assembly level ;)

from csharplang.

petertiedemann avatar petertiedemann commented on July 17, 2024 2

@sharwell Can you enlighten us on how vs-threading resolves this (it seems to be focused on applications, and i am doing libraries that can be used in any context)?

from csharplang.

heku avatar heku commented on July 17, 2024 2

Or maybe can you introduce a compiler flag to change the default value?

e.g. in .csproj

<TaskConfigureAwaitDefaultValue>False</TaskConfigureAwaitDefaultValue>

Then C# compiler compiles await task as await task.ConfigureAwait(false)

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024 2

@blankensteiner

Hoping for a status update and an ETA.

A proposal without a champion on the language design team has no status or ETA. This project is opensource and as a result you are free to fork it and do anything you want, but being opensource doesn't mean that the team has to implement any proposed feature requests. Thus far this is deemed to not be a language issue. C#, as a language, has no concept of synchronization contexts. All proposals here want to tie await to the TPL, which is undesirable.

from csharplang.

blankensteiner avatar blankensteiner commented on July 17, 2024 2

@HaloFour

This project is opensource and as a result you are free to fork it and do anything you want, but being opensource doesn't mean that the team has to implement any proposed feature requests

Right, so, a good solution to an entire community begging for a solution is for everyone to just fork our common platform and do what we like?

I simply don't believe there are people out there that think ConfigureAwait(false)'ing every await is the right solution. I can't say which team within Microsoft should solve this, nor can I say which solution is the best, but I do see a major pain going unanswered for years and years. Surely, we can all agree that someone has to do something and that someone is within Microsoft.

from csharplang.

jnm2 avatar jnm2 commented on July 17, 2024 2

I haven't met a developer who doesn't want this fixed on an assembly/project level but arguing this doesn't help the issue.

🙋‍♂️ Hi! Now you have 😀

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024 2

It's kind of a shame that the AOP qualities of source generators ended up being rejected. If this API existed in the BCL (or could be written by a third-party), it seems like it would be trivial to have such a generator rewrite async members to call this API before invoking the actual method, e.g.:

public replace Task<T> FooAsync() {
    using (Task.DisableSynchronization()) {
        return original();
    }
}

from csharplang.

sharwell avatar sharwell commented on July 17, 2024 1

@petertiedemann A good starting place is this recent document:
https://github.com/Microsoft/vs-threading/blob/master/doc/library_with_jtf.md

The basic idea is a library that avoids the use of ConfigureAwait will execute code on the synchronization context(s) that appear at the entry points to the library API. If those entry points occur on resource-limited contexts (e.g. the single main thread), the caller is expected to use a deadlock mitigation strategy. If the library further uses JoinableTaskContext internally (as opposed to just the implicit dependency from using ConfigureAwait(true)), the caller is expected to use vs-threading as the mitigation strategy.

from csharplang.

tmat avatar tmat commented on July 17, 2024 1

Generators would address that.

from csharplang.

alexk8 avatar alexk8 commented on July 17, 2024 1

Please assign a milestone, this feature is awaited.ConfigureAwait(false) by all c# devs :)
Now we have await on almost each line of code. Do we still need to write ConfigureAwait(false) or use IL-rewriters mandatory?

from csharplang.

yaakov-h avatar yaakov-h commented on July 17, 2024 1

@alexk8 as a whole, solving the ConfigureAwait problem is earmarked for some future release, be it C# 9.0 or 10.0 or 11.0 etc. [source]

This particular solution, not so much.

from csharplang.

pablocar80 avatar pablocar80 commented on July 17, 2024 1

@theunrepentantgeek what if instead of an assembly-level flag, we add a class-level attribute? The attribute would make all methods of the class automatically wrapped inside ConfigureAwait(false) for all callers of the class.

from csharplang.

jmarolf avatar jmarolf commented on July 17, 2024 1

Currently this is a potential solution that I would be most ok with: #2649

But there is still a lot of design work ahead of us.

from csharplang.

theunrepentantgeek avatar theunrepentantgeek commented on July 17, 2024 1

the compiler gives warnings that we should be calling ConfigureAwait(false)

There are a lot of static analysis libraries, such as Roslynator that produce that specific warning. In Roslynator, it's RCS1090.

Chances are you're seeing that warning because you've opted-in to using a library that generates it.

If the warning isn't valid for your context, disable it.

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024 1

@Kir-Antipov

I don't quite understand why CA(true), which assumes our desire to stay in the sync context, takes precedence over CA(false) that doesn't care about it, which is applicable to most libraries as well as web projects

Because it is estimated that applications outnumber libraries by at least 10 to 1 and that if CA(false) was the default that a significant percentage of more code would require CA(true) than needs CA(false) today. I would also make the argument that those application developers would also tend to be more junior developers who would be less familiar with the nuances of threading and synchronization and more likely to make mistakes if they had to be aware where they would need to apply CA(true).

from csharplang.

jnm2 avatar jnm2 commented on July 17, 2024 1

Keep in mind that the original ASP.NET app model also required app-level awaits to capture the synchronization context, and ASP.NET Core dropping that is a more recent development. Also unit tests sometimes require capturing the synchronization context, depending on the framework. Reusable helpers, libraries and (most, not all) console apps are the exception when they are framework-independent (agnostic to any app model).

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024 1

To put it simply and briefly, this belief is based on personal experience:

That's fine. But that's your personal experience. The idea of driving this off of data is to make a better default for the ecosystem as a whole, with the understanding that those that don't fall into the default may have a small amount of extra work.

You fall into the other group, so this happens. It's ok if that's the case. As I mentioned, working in the roslyn libs is a major part off my job. So I too have to deal with CA(false). But my development approach is simply too let automated tooling handle it for me, since it is fast and so simple to do :-)

I genuinely almost never even think about it. The only time I do are when I actually need to carefully use CA(true).

from csharplang.

jnm2 avatar jnm2 commented on July 17, 2024 1

ConfigureAwait(false) is an optimization, not a correctness issue. The incorrectness leading to the deadlock problem is in blocking the UI thread in the first place. This is true whether it's a call to task.Result or some other form of blocking wait for something to change.

The separate problem of running CPU-heavy code on the UI thread is a problem that already exists with or without ConfigureAwait. The library might even have ConfigureAwait(false) and still end up bottlenecking the UI thread:

var x = await SomeIOStuffAsync().ConfigureAwait(false);
// On the UI thread still, if the previous await happens to complete synchronously
CpuHeavyStuff(x);

There various ways to assign responsibility for this, but ConfigureAwait(false) only makes this kind of problem more intermittent without solving it.

from csharplang.

alrz avatar alrz commented on July 17, 2024

@ashmind A quick question, does the following (from #114 comment) needs ConfigureAwait(false) on everyawait?

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.

from csharplang.

ashmind avatar ashmind commented on July 17, 2024

@HaloFour But can it be implemented in CoreFX without option B support by compiler?
How would you know which assembly you are currently awaiting in?

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024

Considering how loose the awaiter spec allows for resolving the GetAwaiter method I'm kind of surprised that it doesn't allow for the method to contain optional parameters today. I'd be game for allowing it, and of course I'm all for the expansion of the caller info attributes.

from csharplang.

lanwin avatar lanwin commented on July 17, 2024

@ufcpp pretty nice solution.

Why they didnt added something like that to the TPL?

from csharplang.

alrz avatar alrz commented on July 17, 2024

..or we can make .ConfigureAwait(false) a VS 2017 built-in keyboard shortcut.

That's actually possible. We could add ConfigureAwait(false); to the completion list off the task.

So that would be C Enter.

from csharplang.

AgentFire avatar AgentFire commented on July 17, 2024

@HaloFour

here's no reliable way I see that this could be implemented purely in library; it would need to be based in language support. As such, I'm going to close this out. Thanks for the interest.

Meh

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024

@AgentFire

That's a proposal for an attribute, not a helper method. The failing of trying to manage that via an attribute is that it's far from obvious how the behavior is applied and when. A helper method that is invoked imperatively would not have that same issue.

from csharplang.

dominikjeske avatar dominikjeske commented on July 17, 2024

It would be great to have this - my code is polluted with lot of ConfigureAwait. Pleas do something with this – it should not be a big thing.

from csharplang.

enihcam avatar enihcam commented on July 17, 2024

Or maybe we can add a function-level attribute? For example:

[ConfigureAwait(false)]
public async Task FooBar() {...}

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

we prefer a native solution, as modifying the IL doesn't seem like a reliable solution for us.

If there's a working solution... why not use it? :) Is there actually something unreliable here?

from csharplang.

tmat avatar tmat commented on July 17, 2024

@CyrusNajmabadi I have yet to see an IL rewriter that correctly preserves debugging information. Also EnC won't work if you IL rewrite compiler outputs. I generally do not recommend using IL rewriters.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

It's seems like a particularly onerous restriction to eliminate this entire class of tools. It basically means that all of that functionality must always be built into the compiler. That seems unfortunate.

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024

@blankensteiner

I simply don't believe there are people out there that think ConfigureAwait(false)'ing every await is the right solution.

The TPL optimized for library consumers, where synchronization is the more commonly necessary. If you're putting ConfigureAwait(false) you're either doing it wrong, or you are only writing libraries and represent a minority of the development community. The majority of developers don't need to do anything.

Either way, C# doesn't know that ConfigureAwait even exists. The "awaiter" pattern is completely synchronization agnostic. IMO, this is better solved via APIs in or on top of TPL.

from csharplang.

blankensteiner avatar blankensteiner commented on July 17, 2024

@HaloFour

The majority of developers don't need to do anything.

Having worked with software development 12 years now, in multiple companies, having had hundreds of developer colleagues, doing all kinds of application types, I can't think of anyone who hasn't written to write a library at some point.
The number of times I had to help people debug something and found the problem to be missing ConfigureAwait(false) can't be counted using my fingers alone.

Either way, C# doesn't know that ConfigureAwait even exists. The "awaiter" pattern is completely synchronization agnostic. IMO, this is better solved via APIs in or on top of TPL.

I'm sure you are right. Which repo/Microsoft-team do I have to contact? Even better, do you know any specific people?

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

It's just mind-blowing and very sad that this is still an issue in 2020.

No one has an adequate solution for this issue.

You want feedback, you want to be open source, you want a community, well then don't ignore a major plain like this one for years. I get that it's hard to solve and maybe it's falling between two chairs and so on, but it has been years now!

We do not have a solution that we find adequate for this problem. Wanting this badly doesn't change that.

but you need to fix this and you need to fix it now...

We don't have a solution that people think is adequate to fix the issue :-/

Hoping for a status update and an ETA.

There is no status update. We're in the same boat as before. Hopefully someone can come up with a solution that all stakeholders feel is suitable.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

Having worked with software development 12 years now, in multiple companies, having had hundreds of developer colleagues, doing all kinds of application types, I can't think of anyone who hasn't written to write a library at some point.

That's not what was said. What was said was:

The TPL optimized for library consumers, where synchronization is the more commonly necessary. If you're putting ConfigureAwait(false) you're either doing it wrong

The design is to optimize for the majority case at the expense of the minority. You're in the minority, and thus this is unpleasant for you.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

I simply don't believe there are people out there that think ConfigureAwait(false)'ing every await is the right solution.

Thinking that we don't hav ethe right solution doesn't magically make a right solution appear. There are tons of things i do not like, but i don't know how to solve them. So the status quo is the best state we know of.

I can't say which team within Microsoft should solve this, nor can I say which solution is the best, but I do see a major pain going unanswered for years and years. Surely, we can all agree that someone has to do something and that someone is within Microsoft.

Teams at MS have looked at this, and no one has been able to come up with a suitable improvement. If you have a proposal, please let us know! :)

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024

@blankensteiner

I'm sure you are right. Which repo/Microsoft-team do I have to contact? Even better, do you know any specific people?

I've proposed the following API that would temporarily reset the SynchronizationContext and TaskScheduler within a using block:

dotnet/runtime#1448

That would apply to all methods called within that block so that it would only need to be used at the boundary of any libraries or other such code.

I'm not saying that this is the best answer, but it's a relatively easy API both to implement and use. I could see it being combined with analyzers that could help identify when you'd need to use it.

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024

@jmarolf

But there is still a lot of design work ahead of us.

I already noted my many objections within the comments of that proposal. It's a shotgun and it will only result in very subtle bugs, assuming that it can survive design around the 5+ orthogonal features that would have to be individually shipped to enable it at all.

~20 or so LOC in the BCL would obviate the need for a language solution. Keep the concerns of the TPL to the TPL. That seems to me a better option than expecting everyone to have to copy&paste their own flavor of an await operator within their own project and hope that they scoped it carefully enough to only affect the code they intend it to.

from csharplang.

jmarolf avatar jmarolf commented on July 17, 2024

Which is why I think the most correct answer is @CyrusNajmabadi's: We have no workable solution at this time. Creeping knowledge of how the BCL implements Task into the language is not something anyone in the LDM is excited about. Having a solution in the runtime or framework that could be idiomatically exposed in the language would be best and imho and Stephen's suggestion is a step in that direction but it has a lot of design issues (a lot) that would need to be worked out. I can see a path forward in #2649 but I do think there are lots and lots of usability and design questions that may kill it.

from csharplang.

pablocar80 avatar pablocar80 commented on July 17, 2024

Right now there is a default behavior, by not calling ConfigureAwait at all. But if we use that, the compiler gives warnings that we should be calling ConfigureAwait(false) to override that default behavior. There seems to be a disconnect there, with a default behavior that is also a non desirable one.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

But if we use that, the compiler gives warnings that we should be calling ConfigureAwait(false)

There is no such compiler warning.

image

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

with a default behavior that is also a non desirable one.

It's desirable to me much of the time. When i'm doing libraries it isn't. But for non-lib code, it works very nicely.

from csharplang.

pablocar80 avatar pablocar80 commented on July 17, 2024

Ok not a compiler warning exactly, a warning from the code analyzer. The default works for me too and I dismiss the warning. If you don’t include that analyzer, visual studio gives you a huge warning banner that you should be including it. Not a big deal, I can turn off this specific warning in the analyzer, though still feels like there’s a disconnect when you have the analyzer telling you that the default behavior is wrong and you should override it every single time.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

Not a big deal, I can turn off this specific warning in the analyzer, though still feels like there’s a disconnect when you have the analyzer telling you that the default behavior is wrong and you should override it every single time.

You should file an issue with that analyzer. This is not the repo for that. We do not control the behaviors and decisions that individual analyzer packages decide on.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

I've seen (relatively few) problems going unsolved for years

I mean... there are literally several thousand issues in this repo. Most of them are multi-year long :) So it's the norm that nearly all issues and concerns raised are just not going to be solved (possibly ever).

I don't know if Microsoft has any cross-team meetings to look at "over X-year-old" issues,

Yes. The teams meet and discuss what they think warrants attention. This has not risen to the level of outweighing the other stuff that is our current priority

If there really is no good solution, not even one that can be defined by a cross-team effort, then that's fine, but then close the issues with proposals you can't/won't implement. Then there is at least some "progress" and a "result".

I don't see a need to close this. Perhaps something will come of it in the future. I don't want to stifle people's ability to communicate and discuss this issue, or to discuss the pros and cons of this particular proposal :)

and make a community-wide contest out of it or something, just to let it be known that there is a hard problem that will go unsolved unless something extraordinary is done about it (just a suggestion from a frustrated developer).

I don't really see what about this issue warrants that. This is just another issue. To some it may be super impactful, to otehrs it may not show up on the radar at all. I don't think we want to just take the backlog of hundreds/thousands of bugs and just say "hey... we're running a contents to find solutions" just because some people really find the current state of affairs highly unpleasant. :-/

Like i said before, i think there's a perfectly reasonable solution. @HaloFour has already showed what it is. Given a reasonable solution, and no actual language-proposals that i think are really great, i know that this isn't an area i would champion to move forward.


TBH, @blankensteiner, it's not clear to me why you just taking the approach i mention here would not be suitable.

from csharplang.

blankensteiner avatar blankensteiner commented on July 17, 2024

@CyrusNajmabadi

I don't really see what about this issue warrants that. This is just another issue. To some it may be super impactful, to otehrs it may not show up on the radar at all

I doubt that. What you are seeing here is feedback from those who don't mind voicing their frustration. Do you really know the number of developers who want this fixed? The number of those swallowing the fact that they have to use ConfigureAwait.Fody or manually setting CAF?
I haven't met a developer who doesn't want this fixed on an assembly/project level but arguing this doesn't help the issue.

TBH, @blankensteiner, it's not clear to me why you just taking the approach i mention here would not be suitable.

It's a fine quick fix until a real solution is found. One where it's a switch/compile-setting or something that doesn't mean I have to insert some code everywhere.

This is my two cents to this discussions. If you feel that this issue is being given the attention it deserves, then let's stop the conversation here and just see when/how/if this is solved.
Have a nice weekend.

from csharplang.

blankensteiner avatar blankensteiner commented on July 17, 2024

🙋‍♂️ Hi! Now you have 😀

April fools day arrived early this year :-)

from csharplang.

jnm2 avatar jnm2 commented on July 17, 2024

I'm serious. One more subtle invisible thing to mentally track is not thrilling. It is what it is, and it's not even that bad. It takes more typing to propagate cancellation tokens.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

I haven't met a developer who doesn't want this fixed on an assembly/project level but arguing this doesn't help the issue.

Shutting down part of the argument because you don't like that perspective is not helpful. Yes, the problem becomes easy if we no longer have to be concerned about the things you don't care about. However, that's not how we can actually do the design.

I, for one, am absolutely not ok with a ambient switch changing the runtime semantics of my code. We have that in an another place in the language (present in 1.0), and it's viewed as a painful mistake we don't want to repeat.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

It's a fine quick fix until a real solution is found.

What makes it not "a real solution".

It's simple, effective, and totally clear in the code.

from csharplang.

qrli avatar qrli commented on July 17, 2024

Inspired by @HaloFour : ASP.NET/Core allows Action Filter attribute to add logic before and after a function implementation. Python attribute also enables that. And C# does have attributes affecting function behavior, e.g. force-inline, but not adding logic.
Of course, an AOP rewriter can detect an attribute and rewrite the code from:

[DisableSynchronizationContext]
public async Task<T> FooAsync() {
    await BarAsync();
}

To

public async Task<T> FooAsync() {
    using var _ = Task.DisableSynchronizationContext();
    await BarAsync();
}

from csharplang.

daniel-liuzzi avatar daniel-liuzzi commented on July 17, 2024

I stumbled upon this right after seeing the announcement for source generators earlier today.

Introducing C# Source Generators | .NET Blog

Wonder if they could be the key to make ConfigureAwait(false) the default.

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024

@daniel-liuzzi

I don't think source generators can be used to accomplish that. The generator can only add new source, it can't modify existing source.

from csharplang.

daniel-liuzzi avatar daniel-liuzzi commented on July 17, 2024

Ah, I didn't know about that. Bummer! Oh well… 😐

And you're totally right. I missed that bit from their FAQ: (emphasis mine)

How do Source Generators compare to other metaprogramming features like macros or compiler plugins?

Source Generators are a form of metaprogramming, so it’s natural to compare them to similar features in other languages like macros. The key difference is that Source Generators don’t allow you rewrite user code. We view this limitation as a significant benefit, since it keeps user code predictable with respect to what it actually does at runtime. We recognize that rewriting user code is a very powerful feature, but we’re unlikely to enable Source Generators to do that.

from csharplang.

pablocar80 avatar pablocar80 commented on July 17, 2024

If the generator could create methods that overwrite existing ones, then we could create something that wraps every Task function with ConfigureAwait(false).

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024

@pablocar80

Generators can't overwrite existing code. If the generator did emit a duplicate method that would only result in a compiler error because you'd have two methods with the same signature.

from csharplang.

alexyakunin avatar alexyakunin commented on July 17, 2024

As for generators, I'd rather opt out for some standard solution (i.e. something that's supported by C# compiler), otherwise we'll end up with a plethora of non-standard ones here, which will make the problem even worse.

from csharplang.

Kir-Antipov avatar Kir-Antipov commented on July 17, 2024

Top most evil villains in history:

  1. That guy who thought .ConfigureAwait(true) by default would be a great idea

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

That guy who thought .ConfigureAwait(true) by default would be a great idea

I prefer CA(true) as the default almost universally for all my personal work. It's ideal. For narrower cases, CA(false) is more appropriate. But it's for a more complex domain where i need to think about those sorts of subtleties.

CA(false) being the default woudl be utterly broken for some many normal and simple cases.

from csharplang.

Kir-Antipov avatar Kir-Antipov commented on July 17, 2024

I prefer CA(true) as the default almost universally for all my personal work

@CyrusNajmabadi, can you add some examples, please (if it's not very time-consuming for you)? Once you've already convinced me on one issue, maybe here you'll make some good points too :)

As for now, I only see a bunch of frustrated developers (myself included) running around their projects putting out CA calls. I don't quite understand why CA(true), which assumes our desire to stay in the sync context, takes precedence over CA(false) that doesn't care about it, which is applicable to most libraries as well as web projects

from csharplang.

Kir-Antipov avatar Kir-Antipov commented on July 17, 2024

Because it is estimated that applications outnumber libraries by at least 10 to 1

@HaloFour, is it really? 0_o Wow. Even at the glory days of the .NET Framework, I would hardly believe in such a ratio, but at the present time, it seemed to me that the number of libraries should certainly exceed the number of applications that depend on the synchronization context... Could you share a link to the research papers, please? I can't find any reliable source, but it seems really interesting

I would also make the argument that those application developers would also tend to be more junior developers

Not even a question, true for most of the cases that I personally encountered

Well, if 10 to 1 ratio is real, we, library-writers, are the ones to suffer (if not suffer at all is not even on option :))

from csharplang.

theunrepentantgeek avatar theunrepentantgeek commented on July 17, 2024

I don't quite understand why CA(true), which assumes our desire to stay in the sync context, takes precedence over CA(false) that doesn't care about it, which is applicable to most libraries as well as web projects

The answer lies in the history of the async/await keywords in C# - they were released in C#5 in 2012. The developers working on this stuff were doing so in 2011 (and earlier), a decade ago.

One of the motivations for the asynchrony feature (well, one of the primary ways it was marketed and demonstrated, anyway) was that it made it dramatically simpler to avoid user interfaces that locked up when developing applications using WinForms and WPF.

Both of those user interface frameworks have a baked in assumption that you only interact with UI elements from the exact same thread that initially created them. This rule is inviolable - break it and your application will crash, hard.

The libraries built to support asynchrony therefore ensured that execution returned to the original thread when tasks completed - to do anything else would have made the feature difficult to use to the point of being useless.

from csharplang.

qrli avatar qrli commented on July 17, 2024

Well, if 10 to 1 ratio is real, we, library-writers, are the ones to suffer

Besides what others have said:

  • The only library authors who suffer from this is those who create cross-framework libraries which can be used in apps of different sync contexts. If your library is for a single framework, e.g. WinFrom, you know the target sync context so you simply stick with default behavior, and only ConfigureAwait(false) when necessary. For newer frameworks like ASP.NET Core, you no longer need to bother with it.
  • As long as your library is intended for different sync contexts, you must know the sync context for each use case. ConfigureAwait(false) may crash the consumer app, while ConfigureAwait(true) may deadlock it. Neither is safe default.
  • Having class library projects in an app solution still counts as app here, because they are specific for the app's choice of sync context.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

@CyrusNajmabadi, can you add some examples

Every application i could write would prefer this. if i asynchronously handle something, i want to return back to the UI thread to go continue interacting with it.

from csharplang.

Kir-Antipov avatar Kir-Antipov commented on July 17, 2024

Keep in mind that the original ASP.NET app model also required app-level awaits to capture the synchronization context, and ASP.NET Core dropping that is a more recent development. Also unit tests sometimes require capturing the synchronization context, depending on the framework. Reusable helpers, libraries and (most, not all) console apps are the exception when they are framework-independent (agnostic to any app model)

@jnm2, I do keep this in mind, but that doesn't change in any way that the ratio of 10 to 1 is surprising to me, as there's no simple logical outcome that makes "reusable helpers, libraries and (most, not all) console apps" an exception and makes context-dependent apps the rule, and not vice versa. No matter how you say this, Libraries-to-Apps is still Many-to-Many


One of the motivations for the asynchrony feature (well, one of the primary ways it was marketed and demonstrated, anyway) was that it made it dramatically simpler to avoid user interfaces that locked up when developing applications using WinForms and WPF

@theunrepentantgeek, well, yeah, I understand the part of the argument about WPF, WinForms, maybe even Silverlight (I stopped using it way before async/await, but it was still alive back in the days, as I remember it clearly), but this approach kinda makes assumption that C# has no future outside of Windows*

Like, ok, nowadays a lot of code is sync context-sensitive (at the end of the day it will be consumed by either ASP.NET, WinForms, WPF or Silverlight anyway. Console apps are an exception, as their number is definitely smaller compared to other applications, I agree), but what about the future? OS-free environment encourages the development of general purpose libraries, so even if apps outnumber libraries now*, it will change in the future*


Have you heard of Dark Mater Developers?

Interesting article to read and to think about! :)

So, you assume that "dark matter developers" only (or mostly) develop applications. What about corporate libraries? Reusable code? And so on


The only library authors who suffer from this is those who create cross-framework libraries

@qrli, you said that like they're a minority :)
I've indexed all 325626 (I don't know for sure why this number doesn't match one on the main page) libraries on the https://nuget.org (I do hope they won't blacklist my IP for this small DDoS xD), excluded from them all that depend or may depend on some context-sensitive framework (like WPF, ASP.NET and so on), and received 282141 libraries in the output. So roughly 85% of the libraries are context independent

As long as your library is intended for different sync contexts, you must know the sync context for each use case. ConfigureAwait(false) may crash the consumer app, while ConfigureAwait(true) may deadlock it. Neither is safe default

I’m having trouble parsing "is intended for different sync contexts". Do you mean some GUI library, that targets several frameworks like WPF/WinForms at the same time, or some general purpose library?

If it's first, then yeh, maybe.
If it's second, then I can't imagine such a scenario.

Having class library projects in an app solution still counts as app here, because they are specific for the app's choice of sync context.

Not true at all. My latest Avalonia app consists of 14 projects. And only 1 is context dependent - the app itself. Even VM project was built in such a way that it doesn't rely on the sync context


Every application i could write would prefer this. if i asynchronously handle something, i want to return back to the UI thread to go continue interacting with it.

@CyrusNajmabadi, what about libraries, if you write any?*


*P.S. - These statements are written on behalf of someone who believes there're more libraries than applications. You, of course, have shaken my confidence about this a little bit, but I've not found any verified calculations, so this issue remains in the category of "religion", not facts. At the end of the day, this ratio plays a role only in the question of whether .ConfigureAwait(true) as default was chosen correctly. And no matter how we answer it today, the problem still remains for a nonzero number of people, and it needs to be somehow solved

from csharplang.

vitidev avatar vitidev commented on July 17, 2024

.NET and C# teams did at the time based on feedback and VS telemetry.

What feedback s and telemetry can be on functionality that did not exist at that time?!

And why it was decided to do exactly
ConfigureAwait(false)
but not with something
await? / await! / ^await etc

from csharplang.

bernd5 avatar bernd5 commented on July 17, 2024

Nowadays it is not required to use Task - it would be possible to create another awaitable object with other default behaviour. This could be implemented in the runtime or a regular user library - similar to ValueTask. And it could be implicit convertible to and from Task....

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024

@Kir-Antipov

Solutions that make the lives better for library developers would certainly be entertained, although it's a steep hill to climb to get the language designers on board that TPL-specific policy should be something baked into the language. API solutions have been better received, such as the ability to clear the synchronization context for a specific scope, which can be applied at the library entry points and eliminate the need to call CA(false) at every other await. Something like this.

As for libraries v. applications, those who make the decisions had the data to decide which audience should have the optimized experience. You have your own anecdotal evidence that contradicts those decisions, and that's fine, but even if the team wanted to revisit it they couldn't without breaking massive amounts of code. And I would bet money that the telemetry still bears out that the majority of applications are internal WinForm business apps, by a very large margin.

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024

@Kir-Antipov

In longer terms, you forgot one thing about libraries: they're the logical unit just as functions or classes. If a function becomes too large, we split it into several ones. Small functions are easier to maintain, easier to debug, easier to test (we don't have the complexity index for no reason). The same applies to libraries. Their goal is far from just reusing some code.

IMO, If you're using libraries specifically for the purpose of organizing code that would have otherwise lived in the application itself then the guidelines of adhering to CA(false) need not apply. This is also true for libraries that are reused across very similar applications. It's not the project type that matters, it's where that assembly is used and the threading restrictions with which you need to be concerned. If all you write is ASP.NET Core apps and your libraries exist to service those apps then forget that CA(true/false) exists.

Some will likely disagree with me on these points out of sheer abundance of caution, and they're not wrong. But IMO you shouldn't need to change the code you write only by virtue of where that code lives.

from csharplang.

Kir-Antipov avatar Kir-Antipov commented on July 17, 2024

@HaloFour

Solutions that make the lives better for library developers would certainly be entertained

And that's the good news

Something like this

I've seen this proposal before, and it's certainly better than the current order of things, but it's still not the real solution: we're replacing thousands of boilerplate-affected lines of code with hundreds of boilerplate-only lines of code

Well, about API changes: some people have previously proposed a new class that behaves the same as task.ConfigureDefault(false). Maybe this is the solution? It doesn't break existing code, it doesn't require new language features and there's no boilerplate involved.

async ContextlessTask Foo()
{
    await Task.Delay(30).ConfigureAwait(true);
    LetsAccessSomeUI(); // Ok
    await Task.Delay(30);
    // LetsAccessSomeUI(); // Exception
}

async Task Bar()
{
    await Foo();
    LetsAccessSomeUI(); // Ok
    await Foo().ConfigureAwait(false);
    // LetsAccessSomeUI(); // Exception
}

Which downsides it may have?

but even if the team wanted to revisit it they couldn't without breaking massive amounts of code

Nobody asks to reevaluate that decision (I hope), of course not. Breaking changes that don't make the impossible possible aren't worth it ;)

Some will likely disagree with me on these points out of sheer abundance of caution, and they're not wrong. But IMO you shouldn't need to change the code you write only by virtue of where that code lives.

I am the type of person who approaches the task in a more abstract way, so I assume that the library will be reused even if it's very likely it won't (but it often pays off). But it's fair point indeed :)

from csharplang.

vitidev avatar vitidev commented on July 17, 2024

If all you write is ASP.NET Core apps and your libraries exist to service those apps then forget that CA(true/false) exists.

I’ve heard ConfigureAwait(false) is no longer necessary in .NET Core. True?

So to say that ConfigureAwait (false) is only needed in libraries is a bit presumptuous

from csharplang.

HaloFour avatar HaloFour commented on July 17, 2024

@vitidev

I mentioned ASP.NET Core specifically, which doesn't use a SynchronizationContext and where ConfigureAwait(false) is basically a no-op as a result. If your libraries only ever run in such an environment then you don't need CA(false). If you're using .NET Core with WinForms or other UI frameworks then you might want CA(false).

I also think that @jnm2 makes an excellent point that CA(false) is really more an optimization. There are scenarios where you want to try to avoid resuming the coroutine on the UI thread, but it's almost never a requirement. Deadlocks would only occur if the code blocks, and that's already possible in UI apps that block or loop expecting a signal to happen that must be marshaled to the UI thread. Otherwise it's largely about trying to keep processing off of the UI thread so that it doesn't impact the responsiveness of the UI, and if that isn't a concern for your code then you don't really need to worry about it so much.

from csharplang.

vitidev avatar vitidev commented on July 17, 2024

If your libraries only ever run in such an environment then you don't need CA(false)

It doesn’t mean, however, that there will never be a custom SynchronizationContext or TaskScheduler present. If some user code (or other library code your app is using) sets a custom context and calls your code, or invokes your code in a Task scheduled to a custom TaskScheduler, then even in ASP.NET Core your awaits may see a non-default context or scheduler that would lead you to want to use ConfigureAwait(false).

I am not a fortuneteller.
Even asp.net core project can consist of several modules, the future of which is unknown
I want to make sure there won't be a situation where in the future someone will use my class and get a deadlock because I assumed it would never happen. Therefore, I always explicitly use ConfigureAwait(false) everywhere to be sure that my code works in any environment as expected.

from csharplang.

CyrusNajmabadi avatar CyrusNajmabadi commented on July 17, 2024

Therefore, I always explicitly use ConfigureAwait(false) everywhere to be sure that my code works in any environment as expected.

I do the same for the libraries i write. That said, i don't explicitly write it myself. Or, at least, i don't try to write out every single one of these. I just use the quickfix to apply it to the entire doc at once:

image

image

from csharplang.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.