I have a strange problem in my MVC 4.0 application. I use REST web services (Amazon Associate) . I created a method, which I use from everywhere. The shortened version is this:
private async Task<XElement> GetRequest(string url)
{
string myresponse;
HttpResponseMessage response = null;
HttpClient client = new HttpClient();
try
{
response = await client.GetAsync(url);
myresponse = response.Content.ToString();
if (myresponse.Contains("503"))
{
myTrace.WriteLine("503 Sleep.....");
Thread.Sleep(3000); // looks like amazon us does not like fast requests....
return await GetRequest(url); //restart after pausing....
}
}
catch (TaskCanceledException ex)
{
myTrace.WriteLine("TaskCancelled From GetRequest: " + ex);
return null;
}
catch (HttpRequestException ex)
{
myTrace.WriteLine("RequestException Sleep.....");
Thread.Sleep(300000); // 5 minutes de pause
}
catch (Exception ex)
{
myTrace.WriteLine("From GetRequest: " + ex);
return null;
}
try
{
XElement content = await response.Content.ReadAsAsync<XElement>();
response.Dispose();
client.Dispose();
return content;
}
catch (Exception)
{
return null;
}
}
Nothing fancy, it does work perfectly well....But, now, on a specific call, it bombs on client.GetAsync(url)
. At first I suspected something in the url to be wrong, so I grabbed it from a debugger session and pasted it directly in my browser, got the expected answer...
So, nothing wrong with the URL. Made a little Unit Test, works just fine with that same specific URL...
As it bombs in the debugger, difficult to see what's wrong. (There are no exceptions thrown!). Finally, I saw with IntelliTrace that there ARE exceptions, seemingly inside System.Threading.Tasks
. Difficult to pin point, as the call Stack is a bit confusing for my NON expert eyes....
Here is the call stack I get from a previous pass in the code:
> System.Web.dll!System.Web.ThreadContext.AssociateWithCurrentThread(bool setImpersonationContext = {unknown}) C#
System.Web.dll!System.Web.HttpApplication.OnThreadEnterPrivate(bool setImpersonationContext = {unknown}) C#
System.Web.dll!System.Web.HttpApplication.OnThreadEnter() C#
System.Web.dll!System.Web.HttpApplication.System.Web.Util.ISyncContext.Enter() C#
System.Web.dll!System.Web.Util.SynchronizationHelper.SafeWrapCallback(System.Action action = {unknown}) C#
System.Web.dll!<>c__DisplayClass9.AnonymousMethod(System.Threading.Tasks.Task _ = {unknown}) C#
mscorlib.dll!System.Threading.Tasks.ContinuationTaskFromTask.InnerInvoke() C#
mscorlib.dll!System.Threading.Tasks.Task.Execute() C#
mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj = {unknown}) C#
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext = {unknown}, System.Threading.ContextCallback callback = {unknown}, object state = {unknown}, bool preserveSyncCtx = {unknown}) C#
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext = {unknown}, System.Threading.ContextCallback callback = {unknown}, object state = {unknown}, bool preserveSyncCtx = {unknown}) C#
mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot = {unknown}) C#
mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution = {unknown}) C#
mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() C#
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() C#
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() C#
Anyway, this looks definitely linked to Tasks, Async, Background workers, etc... Is there a good way to "clear" all other running tasks, to avoid this problem?
Thanks for your help, Bernard.
Adding on to @kcar's answer, I had a very similar issue where there were multiple awaits on a code path that had a single method that was not awaited, like:
public async Task JsonResult BookThing(InputModel model)
{
// Do some stuff
thisIsAnAsyncMethod(Model model); // Fire and forget
return Json(null);
}
protected async Task thisIsAnAsyncMethod(Model model)
{
await oneThing();
await anotherThing();
await somethingElse();
}
That caused awaits to randomly fail without letting me catch the Exception - because TPL was trying to rejoin a Context that had been nulled out, so it was throwing a NullReferenceException outside of try/catch.
This is very hard to diagnose. In Production you won't see anything in try/catch, and in Visual Studio which await gets scheduled to rejoin the original context is somewhat random - it depends on what the TaskScheduler happens to decide to do.
If you didn't want to fire and forget the obvious answer is to await the async method - you'll have a compiler warning reminding you to do so.
If you did want to fire and forget the solution is to explicitly start a new Task. The best way to do that is covered in this answer about fire and forget Tasks.
From the looks of it, your code can complete before one of the threads put to sleep in an exception event from the first try block are resolved, so after 5 minutes when they wake up, there isn't an original thread to rejoin resulting in a NullReferenceException from AssociateWithCurrentThread
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With