There are a number of questions on SO about how to avoid deadlocks in async code (for example, HttpClient
methods) being called from sync code, like this. I'm aware of the various ways to avoid these deadlocks.
In contrast, I'd like to learn about strategies to aggravate or trigger these deadlocks in faulty code during testing.
Here's an example bit of bad code that recently caused problems for us:
public static string DeadlockingGet(Uri uri)
{
using (var http = new HttpClient())
{
var response = http.GetAsync(uri).Result;
response.EnsureSuccessStatusCode();
return response.Content.ReadAsStringAsync().Result;
}
}
It was being called from an ASP.NET app, and thus had a non-null
value of SynchronizationContext.Current
, which provided the fuel for a potential deadlock fire.
Aside from blatantly misusing HttpClient, this code deadlocked in one of our company's servers... but only sporadically.
My attempt to repro deadlock
I work in QA, so I tried to repro the deadlock via a unit test that hits a local instance of Fiddler's listener port:
public class DeadlockTest
{
[Test]
[TestCase("http://localhost:8888")]
public void GetTests(string uri)
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
var context = SynchronizationContext.Current;
var thread = Thread.CurrentThread.ManagedThreadId;
var result = DeadlockingGet(new Uri(uri));
var thread2 = Thread.CurrentThread.ManagedThreadId;
}
}
A couple things to note:
By default, a unit test has a null
SynchronizationContext.Current
, and so.Result
captures the context ofTaskScheduler
, which is the thread pool context. Therefore I useSetSynchronizationContext
to set it to a specific context, to more closely emulate what happens in an ASP.NET or UI context.I've configured Fiddler to wait a while (~1 minute) before responding back. I've heard from coworkers that this may help repro the deadlock (but I have no hard evidence this is the case).
I've ran it with debugger to make sure that
context
is non-null
andthread == thread2
.
Unfortunately, I've had no luck triggering deadlocks with this unit test. It always finishes, no matter how long the delay in Fiddler is, unless the delay exceeds the 100-second default Timeout
of HttpClient
(in which case it just blows up with an exception).
Am I missing an ingredient to ignite a deadlock fire? I'd like to repro the deadlocks, just to be positive that our eventual fix actually works.