Setting Thread.CurrentPrincipal with async/await - c#

Below is a simplified version of where I am trying to set Thread.CurrentPrincipal within an async method to a custom UserPrincipal object but the custom object is getting lost after leaving the await even though it's still on the new threadID 10.
Is there a way to change Thread.CurrentPrincipal within an await and use it later without passing it in or returning it? Or is this not safe and should never be async? I know there are thread changes but thought async/await would handle synching this for me.
[TestMethod]
public async Task AsyncTest()
{
var principalType = Thread.CurrentPrincipal.GetType().Name;
// principalType = WindowsPrincipal
// Thread.CurrentThread.ManagedThreadId = 11
await Task.Run(() =>
{
// Tried putting await Task.Yield() here but didn't help
Thread.CurrentPrincipal = new UserPrincipal(Thread.CurrentPrincipal.Identity);
principalType = Thread.CurrentPrincipal.GetType().Name;
// principalType = UserPrincipal
// Thread.CurrentThread.ManagedThreadId = 10
});
principalType = Thread.CurrentPrincipal.GetType().Name;
// principalType = WindowsPrincipal (WHY??)
// Thread.CurrentThread.ManagedThreadId = 10
}

I know there are thread changes but thought async/await would handle synching this for me.
async/await doesn't do any syncing of thread-local data by itself. It does have a "hook" of sorts, though, if you want to do your own syncing.
By default, when you await a task, it will capture the curent "context" (which is SynchronizationContext.Current, unless it is null, in which case it is TaskScheduler.Current). When the async method resumes, it will resume in that context.
So, if you want to define a "context", you can do so by defining your own SynchronizationContext. This is a not exactly easy, though. Especially if your app needs to run on ASP.NET, which requires its own AspNetSynchronizationContext (and they can't be nested or anything - you only get one). ASP.NET uses its SynchronizationContext to set Thread.CurrentPrincipal.
However, note that there's a definite movement away from SynchronizationContext. ASP.NET vNext does not have one. OWIN never did (AFAIK). Self-hosted SignalR doesn't either. It's generally considered more appropriate to pass the value some way - whether this is explicit to the method, or injected into a member variable of the type containing this method.
If you really don't want to pass the value, then there's another approach you can take as well: an async-equivalent of ThreadLocal. The core idea is to store immutable values in a LogicalCallContext, which is appropriately inherited by asynchronous methods. I cover this "AsyncLocal" on my blog (there are rumors of AsyncLocal coming possibly in .NET 4.6, but until then you have to roll your own). Note that you can't read Thread.CurrentPrincipal using the AsyncLocal technique; you'd have to change all your code to use something like MyAsyncValues.CurrentPrincipal.

The Thread.CurrentPrincipal is stored in the ExecutionContext which is stored in the Thread Local Storage.
When executing a delegate on another thread (with Task.Run or ThreadPool.QueueWorkItem) the ExecutionContext is captured from the current thread and the delegate is wrapped in ExecutionContext.Run. So if you set the CurrentPrincipal before calling Task.Run, it would still be set inside the Delegate.
Now your problem is that you change the CurrentPrincipal inside Task.Run and the ExecutionContext is only flowed one way. I think this is the expected behavior in most case, a solution would be to set the CurrentPrincipal at the start.
What you originally want is not possible when changing the ExecutionContext inside a Task, because Task.ContinueWith capture the ExecutionContext too. To do it you would have to capture somehow the ExecutionContext right after the Delegate is ran and then flowing it back in a custom awaiter's continuation, but that would be very evil.

You could use a custom awaiter to flow CurrentPrincipal (or any thread properties, for that matter). The below example shows how it might be done, inspired by Stephen Toub's CultureAwaiter. It uses TaskAwaiter internally, so synchronization context (if any) will be captured, too.
Usage:
Console.WriteLine(Thread.CurrentPrincipal.GetType().Name);
await TaskExt.RunAndFlowPrincipal(() =>
{
Thread.CurrentPrincipal = new UserPrincipal(Thread.CurrentPrincipal.Identity);
Console.WriteLine(Thread.CurrentPrincipal.GetType().Name);
return 42;
});
Console.WriteLine(Thread.CurrentPrincipal.GetType().Name);
Code (only very slightly tested):
public static class TaskExt
{
// flowing Thread.CurrentPrincipal
public static FlowingAwaitable<TResult, IPrincipal> RunAndFlowPrincipal<TResult>(
Func<TResult> func,
CancellationToken token = default(CancellationToken))
{
return RunAndFlow(
func,
() => Thread.CurrentPrincipal,
s => Thread.CurrentPrincipal = s,
token);
}
// flowing anything
public static FlowingAwaitable<TResult, TState> RunAndFlow<TResult, TState>(
Func<TResult> func,
Func<TState> saveState,
Action<TState> restoreState,
CancellationToken token = default(CancellationToken))
{
// wrap func with func2 to capture and propagate exceptions
Func<Tuple<Func<TResult>, TState>> func2 = () =>
{
Func<TResult> getResult;
try
{
var result = func();
getResult = () => result;
}
catch (Exception ex)
{
// capture the exception
var edi = ExceptionDispatchInfo.Capture(ex);
getResult = () =>
{
// re-throw the captured exception
edi.Throw();
// should never be reaching this point,
// but without it the compiler whats us to
// return a dummy TResult value here
throw new AggregateException(edi.SourceException);
};
}
return new Tuple<Func<TResult>, TState>(getResult, saveState());
};
return new FlowingAwaitable<TResult, TState>(
Task.Run(func2, token),
restoreState);
}
public class FlowingAwaitable<TResult, TState> :
ICriticalNotifyCompletion
{
readonly TaskAwaiter<Tuple<Func<TResult>, TState>> _awaiter;
readonly Action<TState> _restoreState;
public FlowingAwaitable(
Task<Tuple<Func<TResult>, TState>> task,
Action<TState> restoreState)
{
_awaiter = task.GetAwaiter();
_restoreState = restoreState;
}
public FlowingAwaitable<TResult, TState> GetAwaiter()
{
return this;
}
public bool IsCompleted
{
get { return _awaiter.IsCompleted; }
}
public TResult GetResult()
{
var result = _awaiter.GetResult();
_restoreState(result.Item2);
return result.Item1();
}
public void OnCompleted(Action continuation)
{
_awaiter.OnCompleted(continuation);
}
public void UnsafeOnCompleted(Action continuation)
{
_awaiter.UnsafeOnCompleted(continuation);
}
}
}

ExecutionContext, which contains SecurityContext, which contains CurrentPrincipal, is pretty-much always flowed across all asynchronous forks. So in your Task.Run() delegate, you - on a separate thread as you note, get the same CurrentPrincipal. However, under the hood, you get the context flowed via ExecutionContext.Run(...), which states:
The execution context is returned to its previous state when the
method completes.
I find myself in strange territory differing with Stephen Cleary :), but I don't see how SynchronizationContext has anything to do with this.
Stephen Toub covers most of this in an excellent article here.

Related

Unexpected values for AsyncLocal.Value when mixing ExecutionContext.SuppressFlow and tasks

In an application I am experiencing odd behavior due to wrong/unexpected values of AsyncLocal: Despite I suppressed the flow of the execution context, I the AsyncLocal.Value-property is sometimes not reset within the execution scope of a newly spawned Task.
Below I created a minimal reproducible sample which demonstrates the problem:
private static readonly AsyncLocal<object> AsyncLocal = new AsyncLocal<object>();
[TestMethod]
public void Test()
{
Trace.WriteLine(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription);
var mainTask = Task.Factory.StartNew(() =>
{
AsyncLocal.Value = "1";
Task anotherTask;
using (ExecutionContext.SuppressFlow())
{
anotherTask = Task.Run(() =>
{
Trace.WriteLine(AsyncLocal.Value); // "1" <- ???
Assert.IsNull(AsyncLocal.Value); // BOOM - FAILS
AsyncLocal.Value = "2";
});
}
Task.WaitAll(anotherTask);
});
mainTask.Wait(500000, CancellationToken.None);
}
In nine out of ten runs (on my pc) the outcome of the Test-method is:
.NET 6.0.2
"1"
-> The test fails
As you can see the test fails because within the action which is executed within Task.Run the the previous value is still present within AsyncLocal.Value (Message: 1).
My concrete questions are:
Why does this happen?
I suspect this happens because Task.Run may use the current thread to execute the work load. In that case, I assume lack of async/await-operators does not force the creation of a new/separate ExecutionContext for the action. Like Stephen Cleary said "from the logical call context’s perspective, all synchronous invocations are “collapsed” - they’re actually part of the context of the closest async method further up the call stack". If that’s the case I do understand why the same context is used within the action.
Is this the correct explanation for this behavior? In addition, why does it work flawlessly sometimes (about 1 run out of 10 on my machine)?
How can I fix this?
Assuming that my theory above is true it should be enough to forcefully introduce a new async "layer", like below:
private static readonly AsyncLocal<object> AsyncLocal = new AsyncLocal<object>();
[TestMethod]
public void Test()
{
Trace.WriteLine(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription);
var mainTask = Task.Factory.StartNew(() =>
{
AsyncLocal.Value = "1";
Task anotherTask;
using (ExecutionContext.SuppressFlow())
{
var wrapper = () =>
{
Trace.WriteLine(AsyncLocal.Value);
Assert.IsNull(AsyncLocal.Value);
AsyncLocal.Value = "2";
return Task.CompletedTask;
};
anotherTask = Task.Run(async () => await wrapper());
}
Task.WaitAll(anotherTask);
});
mainTask.Wait(500000, CancellationToken.None);
}
This seems to fix the problem (it consistently works on my machine), but I want to be sure that this is a correct fix for this problem.
Many thanks in advance
Why does this happen? I suspect this happens because Task.Run may use the current thread to execute the work load.
I suspect that it happens because Task.WaitAll will use the current thread to execute the task inline.
Specifically, Task.WaitAll calls Task.WaitAllCore, which will attempt to run it inline by calling Task.WrappedTryRunInline. I'm going to assume the default task scheduler is used throughout. In that case, this will invoke TaskScheduler.TryRunInline, which will return false if the delegate is already invoked. So, if the task has already started running on a thread pool thread, this will return back to WaitAllCore, which will just do a normal wait, and your code will work as expected (1 out of 10).
If a thread pool thread hasn't picked it up yet (9 out of 10), then TaskScheduler.TryRunInline will call TaskScheduler.TryExecuteTaskInline, the default implementation of which will call Task.ExecuteEntryUnsafe, which calls Task.ExecuteWithThreadLocal. Task.ExecuteWithThreadLocal has logic for applying an ExecutionContext if one was captured. Assuming none was captured, the task's delegate is just invoked directly.
So, it seems like each step is behaving logically. Technically, what ExecutionContext.SuppressFlow means is "don't capture the ExecutionContext", and that is what is happening. It doesn't mean "clear the ExecutionContext". Sometimes the task is run on a thread pool thread (without the captured ExecutionContext), and WaitAll will just wait for it to complete. Other times the task will be executed inline by WaitAll instead of a thread pool thread, and in that case the ExecutionContext is not cleared (and technically isn't captured, either).
You can test this theory by capturing the current thread id within your wrapper and comparing it to the thread id doing the Task.WaitAll. I expect that they will be the same thread for the runs where the async local value is (unexpectedly) inherited, and they will be different threads for the runs where the async local value works as expected.
If you can, I'd first consider whether it's possible to replace the thread-specific caches with a single shared cache. The app likely predates useful types such as ConcurrentDictionary.
If it isn't possible to use a singleton cache, then you can use a stack of async local values. Stacking async local values is a common pattern. I prefer wrapping the stack logic into a separate type (AsyncLocalValue in the code below):
public sealed class AsyncLocalValue
{
private static readonly AsyncLocal<ImmutableStack<object>> _asyncLocal = new();
public object Value => _asyncLocal.Value?.Peek();
public IDisposable PushValue(object value)
{
var originalValue = _asyncLocal.Value;
var newValue = (originalValue ?? ImmutableStack<object>.Empty).Push(value);
_asyncLocal.Value = newValue;
return Disposable.Create(() => _asyncLocal.Value = originalValue);
}
}
private static AsyncLocalValue AsyncLocal = new();
[TestMethod]
public void Test()
{
Console.WriteLine(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription);
var mainTask = Task.Factory.StartNew(() =>
{
Task anotherTask;
using (AsyncLocal.PushValue("1"))
{
using (AsyncLocal.PushValue(null))
{
anotherTask = Task.Run(() =>
{
Console.WriteLine("Observed: " + AsyncLocal.Value);
using (AsyncLocal.PushValue("2"))
{
}
});
}
}
Task.WaitAll(anotherTask);
});
mainTask.Wait(500000, CancellationToken.None);
}
This code sample uses Disposable.Create from my Nito.Disposables library.

Async method that returns result and Task

I'm trying to design a public method that returns a quick result and, if needed, kicks off a long-running background task.
The result is retrieved within the method pretty quickly, and once it is retrieved it needs to be immediately available to the caller, without waiting for the potential background task to complete.
The background task is either not needed at all, or it must run for a significant amount of time - longer than the caller and all other logic in the program would take to complete without it.
Looking through the MS docs, the best design option I can come up with is to return two things from this method: the result, as well as a Task for the background task.
I don't see a better way to do this, but there are some downsides: first, the responsibility for making sure the background task completes falls on the caller of the method, even though the caller really just wants to consume the immediate result and not be concerned with the behind-the-scene stuff. Second, it is awkward to return null if the background task isn't needed to begin with: now the caller must ensure the task isn't null, in addition to making sure it completes if it is null.
What other options are available for this sort of situation?
Here's an example of what my current design looks like:
public async Task<Tuple<string, Task>> GetAndSave() {
var networkResult = await GetFromNetworkAsync();
if (NeedsToSave(networkResult)) {
var saveTask = SaveToDiskAsync(networkResult);
return new Tuple<string, Task>(networkResult, saveTask);
}
else {
return new Tuple<string, Task>(networkResult, null);
}
}
first, the responsibility for making sure the background task completes falls on the caller of the method, even though the caller really just wants to consume the immediate result and not be concerned with the behind-the-scene stuff.
If it's important to make sure the background task completes then instead of returning the Task you could hand it off to another object (that has been injected into the class that has your GetAndSave method). For example:
public class Foo
{
readonly Action<Task> _ensureCompletion;
public Foo(Action<Task> ensureCompletion)
{
_ensureCompletion = ensureCompletion;
}
public async Task<string> GetAndSaveAsync() // Your method
{
string networkResult = await GetFromNetworkAsync();
if (NeedsToSave(networkResult))
{
Task saveTask = SaveToDiskAsync(networkResult);
_ensureCompletion(saveTask); // This is a synchronous call... no await keyword here. And it returns `void`
}
return networkResult;
}
Task<string> GetFromNetworkAsync() {...}
bool NeedsToSave(string x) {...}
Task SaveToDiskAsync(string x) {...}
}
Now you can inject whatever follow-up behavior you desire for the saveTask. For example, you could write stuff out to the console depending on how it goes:
async Task DoStuff()
{
var foo = new Foo(async task =>
// ^^^^^ More on this in a moment
{
try
{
await task;
Console.Writeline("It worked!");
}
catch (Exception e)
{
Console.Writeline(e.ToString());
}
});
var immediateResult = await foo.GetAndSaveAsync();
// do stuff with `immediateResult`
}
Now, the thing that might be confusing about this (and the power behind this type of solution) is how you can have a synchronous call on the one hand:
_ensureCompletion(saveTask); // This is a synchronous call... no await keyword here
...that does asynchronous things:
var foo = new Foo(async task => ...);
// ^^^^^ This statement lambda is asynchronous
(The injected delegate might even write out to the console on a different thread than whatever thread called GetAndSaveAsync()!)
There's no magic here. It all comes down to SynchronizationContext and the inner workings of async/await.
When the compiler encounters the await keyword (in an async context) then it will do a few things:
Turn everything after the await into a continuation (basically a delegate with some state)
Replace the await keyword with something like SynchronizationContext.Current.Post(continuation)
In other words, this:
async void EnsureCompletion(Task task)
{
try
{
await task;
Console.Writeline("It worked!");
}
catch (Exception e)
{
Console.Writeline(e.ToString());
}
}
...gets turned into something like this:
void EnsureCompletion(Task task)
{
try
{
task.ContinueWith(t => SynchronizationContext.Current.Post(_ =>
{
if (t.IsCompletedSuccessfully)
{
Console.Writeline("It worked!");
}
else
{
Console.Writeline(task.Exception.ToString());
}
});
}
catch (Exception e)
{
Console.Writeline(e.ToString());
}
}
As you can see, EnsureCompletion is an entirely synchronous function that does (almost) the exact same thing as its asynchronous form. Notice how it returns void and everything. This is how you can jam an asynchronous statement lambda into a delegate parameter that has a synchronous signature.
I hinted that the console writing might happen on a totally different thread. That's because async/await is orthogonal to threading. It depends on whatever the currently assigned implementation of SynchronizationContext is programmed to do. Unless you're using WPF or WinForms then by default there will be no SynchronizationContext and continuations (the things that get passed to SynchronizationContext.Post) will just be tossed over to whatever thread happens to be free in the default thread pool.
Second, it is awkward to return null if the background task isn't needed to begin with: now the caller must ensure the task isn't null, in addition to making sure it completes if it is null.
I'm of the opinion that null was a design mistake in C#. (Ask me what some other ones are :) ). If you return null from this method:
public Task DoSomethingAsync()
{
return null; // Compiles just fine
}
...then anyone who uses it will encounter a NullReferenceException when they await it.
async Task Blah()
{
await DoSomethingAsync(); // Wham! NullReferenceException :(
}
(the reason why comes back to how the compiler desugurs the await keyword)
...and it's awkward to have to check for nulls everywhere.
Much better would be to just return Task.CompletedTask as #juharr said.
But if you just hand off the background task like I showed above then you don't have to worry about this.

Task.Yield() in library needs ConfigureWait(false)

It's recommended that one use ConfigureAwait(false) whenever when you can, especially in libraries because it can help avoid deadlocks and improve performance.
I have written a library that makes heavy use of async (accesses web services for a DB). The users of the library were getting a deadlock and after much painful debugging and tinkering I tracked it down to the single use of await Task.Yield(). Everywhere else that I have an await, I use .ConfigureAwait(false), however that is not supported on Task.Yield().
What is the recommended solution for situations where one needs the equivalent of Task.Yield().ConfigureAwait(false)?
I've read about how there was a SwitchTo method that was removed. I can see why that could be dangerous, but why is there no equivalent of Task.Yield().ConfigureAwait(false)?
Edit:
To provide further context for my question, here is some code. I am implementing an open source library for accessing DynamoDB (a distributed database as a service from AWS) that supports async. A number of operations return IAsyncEnumerable<T> as provided by the IX-Async library. That library doesn't provide a good way of generating async enumerables from data sources that provide rows in "chunks" i.e. each async request returns many items. So I have my own generic type for this. The library supports a read ahead option allowing the user to specify how much data should be requested ahead of when it is actually needed by a call to MoveNext().
Basically, how this works is that I make requests for chunks by calling GetMore() and passing along state between these. I put those tasks in a chunks queue and dequeue them and turn them into actual results that I put in a separate queue. The NextChunk() method is the issue here. Depending on the value of ReadAhead I will keeping getting the next chunk as soon as the last one is done (All) or not until a value is needed but not available (None) or only get the next chunk beyond the values that are currently being used (Some). Because of that, getting the next chunk should run in parallel/not block getting the next value. The enumerator code for this is:
private class ChunkedAsyncEnumerator<TState, TResult> : IAsyncEnumerator<TResult>
{
private readonly ChunkedAsyncEnumerable<TState, TResult> enumerable;
private readonly ConcurrentQueue<Task<TState>> chunks = new ConcurrentQueue<Task<TState>>();
private readonly Queue<TResult> results = new Queue<TResult>();
private CancellationTokenSource cts = new CancellationTokenSource();
private TState lastState;
private TResult current;
private bool complete; // whether we have reached the end
public ChunkedAsyncEnumerator(ChunkedAsyncEnumerable<TState, TResult> enumerable, TState initialState)
{
this.enumerable = enumerable;
lastState = initialState;
if(enumerable.ReadAhead != ReadAhead.None)
chunks.Enqueue(NextChunk(initialState));
}
private async Task<TState> NextChunk(TState state, CancellationToken? cancellationToken = null)
{
await Task.Yield(); // ** causes deadlock
var nextState = await enumerable.GetMore(state, cancellationToken ?? cts.Token).ConfigureAwait(false);
if(enumerable.ReadAhead == ReadAhead.All && !enumerable.IsComplete(nextState))
chunks.Enqueue(NextChunk(nextState)); // This is a read ahead, so it shouldn't be tied to our token
return nextState;
}
public Task<bool> MoveNext(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if(results.Count > 0)
{
current = results.Dequeue();
return TaskConstants.True;
}
return complete ? TaskConstants.False : MoveNextAsync(cancellationToken);
}
private async Task<bool> MoveNextAsync(CancellationToken cancellationToken)
{
Task<TState> nextStateTask;
if(chunks.TryDequeue(out nextStateTask))
lastState = await nextStateTask.WithCancellation(cancellationToken).ConfigureAwait(false);
else
lastState = await NextChunk(lastState, cancellationToken).ConfigureAwait(false);
complete = enumerable.IsComplete(lastState);
foreach(var result in enumerable.GetResults(lastState))
results.Enqueue(result);
if(!complete && enumerable.ReadAhead == ReadAhead.Some)
chunks.Enqueue(NextChunk(lastState)); // This is a read ahead, so it shouldn't be tied to our token
return await MoveNext(cancellationToken).ConfigureAwait(false);
}
public TResult Current { get { return current; } }
// Dispose() implementation omitted
}
I make no claim this code is perfect. Sorry it is so long, wasn't sure how to simplify. The important part is the NextChunk method and the call to Task.Yield(). This functionality is used through a static construction method:
internal static class AsyncEnumerableEx
{
public static IAsyncEnumerable<TResult> GenerateChunked<TState, TResult>(
TState initialState,
Func<TState, CancellationToken, Task<TState>> getMore,
Func<TState, IEnumerable<TResult>> getResults,
Func<TState, bool> isComplete,
ReadAhead readAhead = ReadAhead.None)
{ ... }
}
The exact equivalent of Task.Yield().ConfigureAwait(false) (which doesn't exist since ConfigureAwait is a method on Task and Task.Yield returns a custom awaitable) is simply using Task.Factory.StartNew with CancellationToken.None, TaskCreationOptions.PreferFairness and TaskScheduler.Current. In most cases however, Task.Run (which uses the default TaskScheduler) is close enough.
You can verify that by looking at the source for YieldAwaiter and see that it uses ThreadPool.QueueUserWorkItem/ThreadPool.UnsafeQueueUserWorkItem when TaskScheduler.Current is the default one (i.e. thread pool) and Task.Factory.StartNew when it isn't.
You can however create your own awaitable (as I did) that mimics YieldAwaitable but disregards the SynchronizationContext:
async Task Run(int input)
{
await new NoContextYieldAwaitable();
// executed on a ThreadPool thread
}
public struct NoContextYieldAwaitable
{
public NoContextYieldAwaiter GetAwaiter() { return new NoContextYieldAwaiter(); }
public struct NoContextYieldAwaiter : INotifyCompletion
{
public bool IsCompleted { get { return false; } }
public void OnCompleted(Action continuation)
{
var scheduler = TaskScheduler.Current;
if (scheduler == TaskScheduler.Default)
{
ThreadPool.QueueUserWorkItem(RunAction, continuation);
}
else
{
Task.Factory.StartNew(continuation, CancellationToken.None, TaskCreationOptions.PreferFairness, scheduler);
}
}
public void GetResult() { }
private static void RunAction(object state) { ((Action)state)(); }
}
}
Note: I don't recommend actually using NoContextYieldAwaitable, it's just an answer to your question. You should be using Task.Run (or Task.Factory.StartNew with a specific TaskScheduler)
I noticed you edited your question after you accepted the existing answer, so perhaps you're interested in more rants on the subject. Here you go :)
It's recommended that one use ConfigureAwait(false) whenever when you
can, especially in libraries because it can help avoid deadlocks and
improve performance.
It's recommended so, only if you're absolutely sure that any API you're calling in your implementation (including Framework APIs) doesn't depend on any properties of synchronization context. That's especially important for a library code, and even more so if the library is suitable for both client-side and server-side use. E.g, CurrentCulture is a common overlook: it would never be an issue for a desktop app, but it well may be for an ASP.NET app.
Back to your code:
private async Task<TState> NextChunk(...)
{
await Task.Yield(); // ** causes deadlock
var nextState = await enumerable.GetMore(...);
// ...
return nextState;
}
Most likely, the deadlock is caused by the client of your library, because they use Task.Result (or Task.Wait, Task.WaitAll, Task.IAsyncResult.AsyncWaitHandle etc, let them search) somewhere in the outer frame of the call chain. Albeit Task.Yield() is redundant here, this is not your problem in the first place, but rather theirs: they shouldn't be blocking on the asynchronous APIs and should be using "Async All the Way", as also explained in the Stephen Cleary's article you linked.
Removing Task.Yield() may or may not solve this problem, because enumerable.GetMore() can also use some await SomeApiAsync() without ConfigureAwait(false), thus posting the continuation back to the caller's synchronization context. Moreover, "SomeApiAsync" can happen to be a well established Framework API which is still vulnerable to a deadlock, like SendMailAsync, we'll get back to it later.
Overall, you should only be using Task.Yield() if for some reason you want to return to the caller immediately ("yield" the execution control back to the caller), and then continue asynchronously, at the mercy of the SynchronizationContext installed on the calling thread (or ThreadPool, if SynchronizationContext.Current == null). The continuation well may be executed on the same thread upon the next iteration of the app's core message loop. Some more details can be found here:
Task.Yield - real usages?
So, the right thing would be to avoid blocking code all the way. However, say, you still want to make your code deadlock-proof, you don't care about synchronization context and you're sure the same is true about any system or 3rd party API you use in your implementation.
Then, instead of reinventing ThreadPoolEx.SwitchTo (which was removed for a good reason), you could just use Task.Run, as suggested in the comments:
private Task<TState> NextChunk(...)
{
// jump to a pool thread without SC to avoid deadlocks
return Task.Run(async() =>
{
var nextState = await enumerable.GetMore(...);
// ...
return nextState;
});
}
IMO, this is still a hack, with the same net effect, although a much more readable one than using a variation of ThreadPoolEx.SwitchTo(). Same as SwitchTo, it still has an associated cost: a redundant thread switch which may hurt ASP.NET performance.
There is another (IMO better) hack, which I proposed here to address the deadlock with aforementioned SendMailAsync. It doesn't incur an extra thread switch:
private Task<TState> NextChunk(...)
{
return TaskExt.WithNoContext(async() =>
{
var nextState = await enumerable.GetMore(...);
// ...
return nextState;
});
}
public static class TaskExt
{
public static Task<TResult> WithNoContext<TResult>(Func<Task<TResult>> func)
{
Task<TResult> task;
var sc = SynchronizationContext.Current;
try
{
SynchronizationContext.SetSynchronizationContext(null);
task = func(); // do not await here
}
finally
{
SynchronizationContext.SetSynchronizationContext(sc);
}
return task;
}
}
This hack works in the way it temporarily removes the synchronization context for the synchronous scope of the original NextChunk method, so it won't be captured for the 1st await continuation inside the async lambda, effectively solving the deadlock problem.
Stephen has provided a slightly different implementation while answering the same question. His IgnoreSynchronizationContext restores the original synchronization context on whatever happens to be the continuation's thread after await (which could be a completely different, random pool thread). I'd rather not restore it after await at all, as long as I don't care about it.
Inasmuch as the useful and legit API you're looking for is missing, I filed this request proposing its addition to .NET.
I also added it to vs-threading so that the next release of the Microsoft.VisualStudio.Threading NuGet package will include this API. Note that this library is not VS-specific, so you can use it in your app.

When is using Task.Run wasteful or delusional?

I have an interface that reads/writes an object to storage. In one case the storage is a database with async methods. In the other case it's just a cookie.
I gather that its recommended to use async back along the path ending at an async call, so it seems to make sense for the interface to be async as well. But in the cookie case, I'm just setting a couple fields and sticking it in the response so there isn't any async there yet. I can wrap that bit in await Task.Run() to match the new interface but I don't know if this is advisable or if it has some negative impact on performance.
What to do?
public interface IProfileStore
{
async Task SetProfile(UserProfile profile);
}
public async Task SetProfile(UserProfile profile)
{
// Look mom, I'm needlessly async
await Task.Run(() =>
{
var cookie = new HttpCookie(AnonymousCookieName);
cookie["name"] = profile.FullName;
HttpContext.Current.Response.Cookies.Add(cookie);
});
}
You should not do that; you're just creating needless threadpool churn.
Instead, remove the async keyword from the method and simply return Task.FromResult(0) to return a synchronously-completed task
If you're performing a very short quickly completed operation then you're quite right that there is likely no need to use Task.Run to push the work to another thread. The act of scheduling the code in the thread pool is likely going to take longer than just doing it.
As for how to do that, just remove the await Task.Run that you have no need for and voila, you're all set. You have a synchronous operation that is still wrapped in a Task and so still matches the required interface.
Almost as SLaks suggests if you were doing something async but return the Task, so:
public Task SetProfile(UserProfile profile)
{
return Task.Run(() =>
{
var cookie = new HttpCookie(AnonymousCookieName);
cookie["name"] = profile.FullName;
HttpContext.Current.Response.Cookies.Add(cookie);
});
}
However as he suggests in this case:
public Task SetProfile(UserProfile profile)
{
var cookie = new HttpCookie(AnonymousCookieName);
cookie["name"] = profile.FullName;
HttpContext.Current.Response.Cookies.Add(cookie);
return Task.FromResult(null);
}
Return null as its a system cached completed Task.

How to cancel a TaskCompletionSource using a timeout

I have the function that I call asynchronously using the await keyword:
public Task<StatePropertyEx> RequestStateForEntity(EntityKey entity, string propName)
{
var tcs = new TaskCompletionSource<StateInfo>();
try
{
var propInstance = BuildCacheKey(entity, propName);
StateCacheItem cacheItem;
if (_stateCache.TryGetValue(propInstance, out cacheItem))
{
tcs.SetResult( new StateInfo (cacheItem.State.Name, cacheItem.State.Value) );
return tcs.Task;
}
//state not found in local cache so save the tcs for later and request the state
var cacheKey = BuildCacheKey(entity, propName);
_stateRequestItemList.TryAdd(cacheKey, new StateRequestItem(entity, propName, tcs));
_evtClient.SubmitStateRequest(entity, propName);
return tcs.Task;
}
catch (Exception ex)
{
tcs.SetException(ex);
return tcs.Task;
}
}
The function has a look to see if it has the information it needs and if it does it returns it. If it doesn’t have the details it sends a request out which should eventually come in as an event. At that point my code (not shown) finds the stored TaskCompletionSource item, sets the result and returns it. This all works fine but I have now been asked to consider a situation where a reply may never be returned when I request state via the “_evtClient.SubmitStateRequest(entity, propName);” line. I need to implement some sort of timeout mechanism so I can cancel the TCS task so the function caller can fail gracefully. I’ve been looking on SO and the internet and can’t find anything that looks right. I’m now not sure if I need to restructure the above code in a different way. Can anyone advise or point me to a similar scenario?
The code that calls the above function can call it in a single hit like this:
var stateProperty = await RequestStateForEntity(key, stateName);
or in a batch, like this:
await
Task.WhenAll(
stateDefinitions.Select(stateDefinition => stateDefinition.Name)
.Select(
stateName =>
Task.Factory.StartNew(
async () => results.Add(await RequestStateForEntity(key, stateName)))
.Unwrap())
.ToArray());
First off, what you really want to enable is cancellation. The fact that the cancellation comes from a timeout is just a footnote.
.NET has some great built-in support for cancellation, and the Task-based Asynchronous Pattern prescribes how to use it.
Essentially, what you want to do is take a CancellationToken:
Task<StatePropertyEx> RequestStateForEntity(EntityKey entity, string propName,
CancellationToken cancellationToken);
Next, you want to respond when that token is signaled. Ideally, you would want to just pass the CancellationToken down to the _evtClient so that the request is truly cancelled:
_evtClient.SubmitStateRequest(entity, propName, cancellationToken);
This is the normal way of implementing cancellation, and it works great if SubmitStateRequest already understands cancellation. Often the event arguments have a flag indicating cancellation (e.g., AsyncCompletedEventArgs.Cancelled). If at all possible, use this approach (i.e., change _evtClient as necessary to support cancellation).
But sometimes this just isn't possible. In this case, you can choose to pretend to support cancellation. What you're actually doing is just ignoring the request if it completes after it was cancelled. This is not the most ideal situation but sometimes you have no choice.
Personally, I don't really like this kind of approach since it makes the API "lie": the method signature claims to support cancellation but it really is just faking it. So first, I recommend documenting this. Put in a code comment apology explaining that _evtClient doesn't support cancellation, and the "cancellation" is actually just pretend cancellation.
Then, you'll need to hook into the CancellationToken yourself, after the state request item is in the list but before the actual request is sent:
var item = new StateRequestItem(entity, propName, tcs);
_stateRequestItemList.TryAdd(cacheKey, item);
item.CancellationRegistration = cancellationToken.Register(() =>
{
StateRequestItem cancelledItem;
if (!_stateRequestItemList.TryRemove(cacheKey, out cancelledItem))
return;
cancelledItem.TaskCompletionSource.TrySetCanceled();
});
_evtClient.SubmitStateRequest(entity, propName);
Finally, you'll need to update your event handler completion code (not shown) to ignore the situation where the state request item has already been removed, and to dispose the CancellationRegistration if the state request item is found.
Once your method supports cancellation, then it's easy to cancel via a timer:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
CancellationToken token = cts.Token;
or from any other kind of situation. Say, if the user cancels whatever (s)he's doing. Or if another part of the system decides it doesn't need that data anymore. Once your code supports cancellation, it can handle cancellation for any reason.
You may pass a CancellationToken to your method, which can internally implement the cancellation logic:
public Task<StatePropertyEx> RequestStateForEntity(
EntityKey entity, string propName, CancellationToken token)
{
var tcs = new TaskCompletionSource<StateInfo>();
try
{
// Cache checking
_evtClient.SubmitStateRequest(entity, propName, token);
return tcs.Task;
}
catch (Exception ex)
{
tcs.SetException(ex);
return tcs.Task;
}
}
And inside SubmitStateRequest:
token.ThrowIfCancellationRequest();
Note ThrowIfCancellationRequest will throw a OperationCanceledException which you will need to handle. If you are making a blocking call, you can internally set the CancellationTokenSource with a TimeSpan:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));

Categories

Resources