I am getting intermittent deadlocks when using HttpClient
to send http requests and sometimes they are never returning back to await SendAsync
in my code. I was able to figure out the thread handling the request internally in HttpClient
/HttpClientHandler
for some reason has a SynchronizationContext
during the times it is deadlocking. I would like to figure out how the thread getting used ends up with a SynchronizationContext
, when normally they don't have one. I would assume that whatever object is causing this SynchronizationContext
to be set is also blocking on the Thread
, which is causing the deadlock.
Would I be able to see anything relevant in the TPL ETW events?
How can I troubleshoot this?
Edit 2:
The place that I have been noticing these deadlocks is in a wcf ServiceContract
(see code below) inside of a windows service. The SynchronizationContext
that is causing an issue is actually a WindowsFormsSynchronizationContext
, which I assume is caused by some control getting created and not cleaned up properly (or something similar). I realize there almost certainly shouldn't be any windows forms stuff going on inside of a windows service, and I'm not saying I agree with how it's being used. However, I didn't write any of the code using it, and I can't just trivially go change all of the references.
Edit: here is an example of the general idea of the wcf service I was having a problem with. It's a simplified version, not the exact code:
[ServiceContract]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
internal class SampleWcfService
{
private readonly HttpMessageInvoker _invoker;
public SampleWcfService(HttpMessageInvoker invoker)
{
_invoker = invoker;
}
[WebGet(UriTemplate = "*")]
[OperationContract(AsyncPattern = true)]
public async Task<Message> GetAsync()
{
var context = WebOperationContext.Current;
using (var request = CreateNewRequestFromContext(context))
{
var response = await _invoker.SendAsync(request, CancellationToken.None).ConfigureAwait(false);
var stream = response.Content != null ? await response.Content.ReadAsStreamAsync().ConfigureAwait(false) : null;
return StreamMessageHelper.CreateMessage(MessageVersion.None, "GETRESPONSE", stream ?? new MemoryStream());
}
}
}
Adding ConfigureAwait(false)
to the 2 places above didn't completely fix my problem because a threadpool thread used to service a wcf request coming into here may already have a SynchronizationContext
. In that case the request makes it all the way through this whole GetAsync
method and returns. However, it still ends up deadlocked in System.ServiceModel.Dispatcher.TaskMethodInvoker
, because in that microsoft code, it doesn't use ConfigureAwait(false)
and I want to assume there is a good reason for that (for reference):
var returnValueTask = returnValue as Task;
if (returnValueTask != null)
{
// Only return once the task has completed
await returnValueTask;
}
It feels really wrong, but would converting this to using APM (Begin/End) instead of using Tasks fix this? Or, is the only fix to just correct the code that is not cleaning up its SynchronizationContext
properly?