SemaphoreSlim deadlocks on 9th thread - c#

I have a test automation environment with multithreaded tests that use a shared HttpClient to test methods on our Web API. After the HttpClient has been initialized, it can be used by all of our tests running on multiple threads, since it is a thread safe object. However, keeping the initialization from happening more than once is a challenge. Furthermore, it includes the await keyword in it, so it cannot use any basic lock technology to ensure the initialization operation is atomic.
To make sure the initialization happens properly, I am using a SemaphoreSlim to create a mutex for initialization. To get access to the object, all tests have to call a function that uses the SemaphoreSlim to make sure it has been properly initialized by the first thread to request it.
I found the following implementation for using SemaphoreSlim on this web page.
public class TimedLock
{
private readonly SemaphoreSlim toLock;
public TimedLock()
{
toLock = new SemaphoreSlim(1, 1);
}
public LockReleaser Lock(TimeSpan timeout)
{
if (toLock.Wait(timeout))
{
return new LockReleaser(toLock);
}
throw new TimeoutException();
}
public struct LockReleaser : IDisposable
{
private readonly SemaphoreSlim toRelease;
public LockReleaser(SemaphoreSlim toRelease)
{
this.toRelease = toRelease;
}
public void Dispose()
{
toRelease.Release();
}
}
}
I use this class like so:
private static HttpClient _client;
private static TimedLock _timedLock = new();
protected async Task<HttpClient> GetClient()
{
using (_timedLock.Lock(TimeSpan.FromSeconds(600)))
{
if (_client != null)
{
return _client;
}
MyWebApplicationFactory<Startup> factory = new();
_client = factory.CreateClient();
Request myRequest = new Request()
{
//.....Authentication code
};
HttpResponseMessage result = await _client.PostAsJsonAsync("api/accounts/Authenticate", myRequest);
result.EnsureSuccessStatusCode();
AuthenticateResponse Response = await result.Content.ReadAsAsync<AuthenticateResponse>();
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Response.Token);
return _client;
}
}
It worked flawlessly until just recently, when I added a ninth thread to my code. I've had to dial that back to 8 threads, because whenever I allow a 9th thread to call the TimedLock.Lock method, the entire program deadlocks.
Does anyone know what might be going on, or how to work around this problem?

OK. I figured out my own problem, and it really was MY own problem, nobody else's.
If you compare my code above really closely to the source that I quoted as getting it from, you'll notice there's actually a difference. The original code implements the Lock function asynchronously using the WaitAsync function built into SemaphoreSlim:
public async Task<LockReleaser> Lock(TimeSpan timeout)
{
if(await toLock.WaitAsync(timeout))
{
return new LockReleaser(toLock);
}
throw new TimeoutException();
}
And of course, in my code that uses it, add the await keyword at the proper place to take care of the added Task object properly:
...
using (await _timedLock.Lock(TimeSpan.FromSeconds(6000)))
{
...
Yesterday I "discovered" that if I changed the toLock object to use WaitAsync, the problem magically went away, and I was SO proud of myself. But then just a few minutes ago, when I was copying and pasting the "original" code into my question, I realized that the "original" code actually included "my" fix!
I now remember looking at this a few months ago and wondering why they needed to make this an Async function. So in my superior wisdom, I tried it without the Async, saw that it worked fine, and continued on until I just recently started using enough threads to demonstrate why it is necessary!
So to keep from confusing people, I changed the code in my question to be the bad code that I originally changed it to be, and put the truly original good code above here in "my" answer, which actually should be credited to Richard Blewett, the author of the referenced article.
I can't say I completely understand why this fix actually works, so any further answers that can explain it better are more than welcome!

Related

How do I prevent by Rx test from hanging?

I am reproducing my Rx issue with a simplified test case below. The test below hangs. I am sure it is a small, but fundamental, thing that I am missing, but can't put my finger on it.
public class Service
{
private ISubject<double> _subject = new Subject<double>();
public void Reset()
{
_subject.OnNext(0.0);
}
public IObservable<double> GetProgress()
{
return _subject;
}
}
public class ObTest
{
[Fact]
private async Task SimpleTest()
{
var service = new Service();
var result = service.GetProgress().Take(1);
var task = Task.Run(async () =>
{
service.Reset();
});
await result;
}
}
UPDATE
My attempt above was to simplify the problem a little and understand it. In my case GetProgress() is a merge of various Observables that publish the download progress, one of these Observables is a Subject<double> that publishes 0 everytime somebody calls a method to delete the download.
The race condition identified by Enigmativity and Theodor Zoulias may(??) happen in real life. I display a view which attempts to get the progress, however, quick fingers delete it just in time.
What I need to understand a bit more is if the download is started again (subscription has taken place by now, by virtue of displaying a view, which has already made the subscription) and somebody again deletes it.
public class Service
{
private ISubject<double> _deleteSubject = new Subject<double>();
public void Reset()
{
_deleteSubject.OnNext(0.0);
}
public IObservable<double> GetProgress()
{
return _deleteSubject.Merge(downloadProgress);
}
}
Your code isn't hanging. It's awaiting an observable that sometimes never gets a value.
You have a race condition.
The Task.Run is sometimes executing to completion before the await result creates the subscription to the observable - so it never sees the value.
Try this code instead:
private async Task SimpleTest()
{
var service = new Service();
var result = service.GetProgress().Take(1);
var awaiter = result.GetAwaiter();
var task = Task.Run(() =>
{
service.Reset();
});
await awaiter;
}
The line await result creates a subscription to the observable. The problem is that the notification _subject.OnNext(0.0) may occur before this subscription, in which case the value will pass unobserved, and the await result will continue waiting for a notification for ever. In this particular example the notification is always missed, at least in my PC, because the subscription is delayed for around 30 msec (measured with a Stopwatch), which is longer than the time needed for the task that resets the service to complete, probably because the JITer must load and compile some RX-related assembly. The situation changes when I do a warm-up by calling new Subject<int>().FirstAsync().Subscribe() before running the example. In that case the notification is observed almost always, and the hanging is avoided.
I can think of two robust solutions to this problem.
The solution suggested by Enigmativity, to create an awaitable subscription before starting the task that resets the service. This can be done with either GetAwaiter or ToTask.
To use a ReplaySubject<T> instead of a plain vanilla Subject<T>.
Represents an object that is both an observable sequence as well as an observer. Each notification is broadcasted to all subscribed and future observers, subject to buffer trimming policies.
The ReplaySubject will cache the value so that it can be observed by the future subscription, eliminating the race condition. You could initialize it with a bufferSize of 1 to minimize the memory footprint of the buffer.

CancellationToken: why not to use AsyncLocal context instead of passing parameter to every async method?

I see microsoft enforces this pattern for async methods:
async Task<object> DoMyOperationAsync(int par1, string par2,..., CancellationToken token=default(CancellationToken))
{
...
CancellationToken.ThrowIfRequested();
...
}
Every single method should have that ugly CancellationToken token=default(CancellationToken) parameter even though most of the time it's not even being used, but just passed through at best.
Why instead of this we cannot just use some sort of CancellationTokenContext, and use it in methods that actually need it?
public class CancellationTokenContext
{
static AsyncLocal<CancellationToken> asyncContext = new AsyncLocal<CancellationToken>();
public static CancellationToken Current {
get {
return asyncContext.Value;
}
set {
asyncContext.Value = value;
}
}
public static void ThrowIfRequested() {
Current.ThrowIfCancellationRequested();
}
}
public class MyClassWithAsyncMethod{
public async Task<object> DoMyOperationAsync(int par1, string par2,...)
{
...
CancellationTokenContext.ThrowIfRequested();
...
}
}
The main reason for not using AsyncLocal is that it's much slower. In fact, it's a dictionary lookup, so it should be approximate 20x slower (~ 20ns vs 1ns).
One other factor is: explicit is usually better than implicit. Though I agree it's a very annoying part of async implementation.
Maybe the main reason is that CancellationToken is a structure. So if you pass it as a parameter, it will be kept in a call stack, without need to allocate memory in heap. But if you use AsyncLocal instead, it will use boxing/unboxing to deal with a cancellation token. In case when async operations are heavily used, i.e. some ASP.NET Core app, it may even affect overall performance because of additional workload for garbage collector.
Other possible problem which I see here, is need to setup up and then restore AsyncLocal for every sub-call which require different cancellation token. For example:
private async Task DoSomeWork()
{
var currentCancallationToken = _ambientCancellationToken.Value;
_ambientCancellationToken.Value = CancellationToken.None;
// i.e. we should finish logging no matter of cancellation signal for calling method, for whatever reason
await _myCustomLogger.AsyncInfo("Do 1st step");
// restore ambient cancellation token behavior to make next step of async flow
_ambientCancellationToken.Value = currentCancallationToken;
await Do1StepOfWork();
_ambientCancellationToken.Value = CancellationToken.None;
// ...and here we go again
await _myCustomLogger.AsyncInfo("Do 2nd step");
// ...it became annoying...
_ambientCancellationToken.Value = currentCancallationToken;
await Do2StepOfWork();
}
But from other prospective, if you are sure, that your app (not a public library/framework) will not make several thousandths async flows per second, and you sure, that you will not use mixed cancellation contexts, it seems ok to use such approach.

Cancelling Parallel.For with CancellationToken using method

I need to Cancel all Tasks made by Parallel.For when using function.
For example:
class Example
{
private CancellationTokenSource cts;
public Example()
{
cts = new CancellationTokenSource();
}
public void plsCompute()
{
ParallelOptions po = new ParallelOptions{ CancellationToken = cts.Token };
Parallel.For(0, 10, po, (i) => compute());
}
public void plsStopComputing()
{
cts.Cancel();
}
public void compute()
{
for(int i=0;i<100000;i++){
if(//token was cancelled){
Console.WriteLine("Cancelled");
return;
}
//do something
}
}
I need to stop all task that are currently runing, not creating new tasks.
Is there any way to achieve this?
All examples I've seen was with function made in lambda, so there I could access cts. I need it to be in other funtion. Also if I passed token as a parameter it didnt work. cts token was changed but in other threads it didnt change.
Any help appriciated :)
EDIT
As I mentioned in comment, I got deeper, this example is not really what I want. These methods are invoked by messeges sent through msmq from an wpf app. I think that every time I call from wpf, service makes new instance of this class, so it cancels new instance instead of the one that is already computing.
Is there a way to call the same instance?
The problem was with invoking by msmq, that every time message was received it created new instance of class.
I've solved it adding
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
Above declaration of class that implements service

Reusable HttpClient instance vs static variable (Heavily used in multiple threads)?

I currently have an instance variable of a HttpClient which is used in an instance method to download an image. The method is always called in a Task.Run().
This method can be called thousands of times in a minute and doesn't crash or produce any errors. But I just wanted to know what benefits, if any, there would be if I switched to a static HttpClient, apart from perhaps being more thread safe.
Here is some code for context:
HttpClient client = new HttpClient(); // Make this static?
// Always called in a Task.Run(() => DownloadImage(Obj));
public async void DownloadImage(Object obj)
{
FormUrlEncodedContent formContent = GetFormContent(Obj);
HttpResponseMessage Result = await client.PostAsync("URL", formContent).ConfigureAwait(false);
byte[] Data = Result.Content.ReadAsByteArrayAsync().Result;
StaticClass.Images[Obj.ID] = ImageSource.FromStream(() => new MemoryStream(Data));
formContent.Dispose();
Result.Dispose();
}
No
Since you are not using async version and you are calling ReadAsByteArrayAsync().Result you will most likely end up in deadlocks.
Recommended
Following is the most recommended way,
static HttpClient client = new HttpClient ();
public async Task DownloadImage(Object obj)
{
using(FormUrlEncodedContent formContent = GetFormContent(Obj)) {
using(HttpResponseMessage Result = await
client.PostAsync("URL", formContent)
.ConfigureAwait(false)){
byte[] Data = await Result.Content.ReadAsByteArrayAsync();
StaticClass.Images[Obj.ID] = ImageSource.FromStream(
() => new MemoryStream(Data));
}
}
}
Also don't call Dispose, use using block, Dispose will not be executed if an exception is thrown, using block will properly Dispose even in case of exception.
As long as, all disposables are wrapped in using and async await are used properly, HttpClient will perform best with single static instance.
HttpClient is intented by microsoft to be used as a static object. Even if the time to instatiate it every time is trivial, because other issues could arise.
his link contains a simple implementation :
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client .
Since you will be using the client in more more cases than simple getting an image I would suggest a class with static methods (async if you may) for get/post/put/delete where you would inserting method names and objects dynamically.

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.

Categories

Resources