As there are legitimate uses of Task.Yield() and legit uses of Task.ConfigureAwait(false), it's odd that there is no API to allow await Task.Yield().ConfigureAwait(false);. Anyone who must yield back to the caller but does not want to resume on the caller's SynchronizationContext has no convenient API to enable that.
In vs-threading we just filled this gap ourselves, but it seems like a worthwhile addition to .NET itself.
See also: https://stackoverflow.com/questions/28309185/task-yield-in-library-needs-configurewaitfalse
C#
public struct YieldAwaitable
{
public ConfiguredYieldAwaitable ConfigureAwait(bool continueOnCapturedContext);
}
Or an equivalent extension method as we've defined in vs-threading.
@AArnott do you have a specific API in mind?
Semantically it feels a bit wrong to me. In my mind, Yield has the deeper meaning "resume where I left", which isn't entirely true anymore if you change the context. That said, I may be biased because of the existing method.
Your use case reminds me a lot of the old SwitchTo method that was removed from the TPL. Actually, vs-threading does implement SwitchTo (I used that code in some of my projects), I'm curious to know why you're trying to use await Task.Yield().ConfigureAwait(false) when you could use await TaskScheduler.Default.SwitchTo(true)?
Your use case reminds me a lot of the old SwitchTo method that was removed from the TPL. Actually, vs-threading does implement SwitchTo (I used that code in some of my projects),
Yes, and that's exactly what we switched to using this week when we needed this.
I'm curious to know why you're trying to use await Task.Yield().ConfigureAwait(false) when you could use await TaskScheduler.Default.SwitchTo(true)?
Because it's where users expect to find it. Most users of await have never used or seen the SwitchTo method since it was only in the await previews. But they tend to be quite familiar with the concept of the .ConfigureAwait(false) appended syntax which does what they need when they don't want their caller to deadlock if blocking the UI thread on the result of this method.
In fact, although I wrote the SwitchTo method in the vs-threading library, when I realized during the recent PR linked to above that using await Task.Yield() would be problematic for the vs-streamjsonrpc library, I couldn't for the life of me think of an alternative. It was the PR author that eventually suggested this alternative API already exists.
In my mind, Yield has the deeper meaning "resume where I left", which isn't entirely true anymore if you change the context.
To me, I always think of the Yield() method as just forcing a yield. The "resume where I left" part of it that you mention is also true for awaiting any Task. So ya, the way await was designed was to pick up where you left off. But then in most cases you can configure it to resume on the threadpool, but there _appears_ to be no way to yield and resume on the threadpool. await Task.Run is horribly inconvenient as @davidfowl mentions in dotnet/runtime#20025 because of forcing the rest of the method into a callback syntax, which isn't always possible or desirable.
I don't think most await users appreciate the difference between awaitable types like Task and YieldAwaitable. They just want to .ConfigureAwait(false) and they can't find it. And certainly for the users who don't use vs-threading, not even TaskScheduler.Default.SwitchTo(true) is a discoverable option for them.
So my argument is that since CoreFx doesn't offer a way to force a yield and resume off the caller's context, and we believe that requirement is a valid one, we should add the API to fill the gap in the place our existing users would expect to find it. And IMO that would be by configuring the awaitable they are already using.
do you have a specific API in mind?
Yes:
public struct YieldAwaitable
{
public ConfiguredYieldAwaitable ConfigureAwait(bool continueOnCapturedContext);
}
Or an equivalent extension method as we've defined in vs-threading.
All good thoughts, but functionally it's a dup of https://github.com/dotnet/corefx/issues/15490. If we ever moved forward on enabling that functionality, either API could be considered. Thanks.
Most helpful comment
Yes, and that's exactly what we switched to using this week when we needed this.
Because it's where users expect to find it. Most users of await have never used or seen the
SwitchTomethod since it was only in the await previews. But they tend to be quite familiar with the concept of the.ConfigureAwait(false)appended syntax which does what they need when they don't want their caller to deadlock if blocking the UI thread on the result of this method.In fact, although I wrote the
SwitchTomethod in the vs-threading library, when I realized during the recent PR linked to above that usingawait Task.Yield()would be problematic for the vs-streamjsonrpc library, I couldn't for the life of me think of an alternative. It was the PR author that eventually suggested this alternative API already exists.To me, I always think of the
Yield()method as just forcing a yield. The "resume where I left" part of it that you mention is also true for awaiting any Task. So ya, the way await was designed was to pick up where you left off. But then in most cases you can configure it to resume on the threadpool, but there _appears_ to be no way to yield and resume on the threadpool.await Task.Runis horribly inconvenient as @davidfowl mentions in dotnet/runtime#20025 because of forcing the rest of the method into a callback syntax, which isn't always possible or desirable.I don't think most await users appreciate the difference between awaitable types like
TaskandYieldAwaitable. They just want to.ConfigureAwait(false)and they can't find it. And certainly for the users who don't use vs-threading, not evenTaskScheduler.Default.SwitchTo(true)is a discoverable option for them.So my argument is that since CoreFx doesn't offer a way to force a yield and resume off the caller's context, and we believe that requirement is a valid one, we should add the API to fill the gap in the place our existing users would expect to find it. And IMO that would be by configuring the awaitable they are already using.
Yes:
Or an equivalent extension method as we've defined in vs-threading.