This is a continuation from this question:
Multiple Task Continuation
I have changed my code as in the answer, however now I am receiving TaskCancelledExceptions when I try to run tasks.
public virtual async Task RunAsync(TaskWithProgress task)
{
Show();
TaskIsRunning();
await SetCompletedHandler(TaskComplete());
await SetCancelledHandler(TaskCancelled())
await SetFaultedHandler(TaskFaulted());
await task;
Close();
}
however the following code does not. I am a bit stuck as to why.
public virtual Task RunAsync(TaskWithProgress task)
{
Show();
TaskIsRunning();
SetCompletedHandler(TaskComplete());
SetCancelledHandler(TaskCancelled())
SetFaultedHandler(TaskFaulted());
return task;
}
The calling code basically involves the following:
await progressDialog.RunAsync(task);
Edit:
I do not cancel a cancellationtoken anywhere so I can't see why this is throwing that exception.
The three SetXXXHandler() methods basically perform the following code with different continuation status:
task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled, this.Scheduler);
The Stack trace is here:
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at FugroDXExt.frmBaseProgressAsync.<RunAsync>d__7.MoveNext() in d:\C#\FugroDXExt\trunk\frmBaseProgressAsync.cs:line 92
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at FCP.Forms.frmProcessing.<mnuApplyCenteredSmoothing_ItemClick>d__34.MoveNext() in d:\C#\FCP\FCP\Forms\frmProcessing.cs:line 578
Close() simply closes the form. If I remove that line the same thing occurs.
You say that SetCancelledHandler just adds a continuation to the task. I assume that's the same task RunAsync gets as a parameter although i can't tell by your code how SetCancelledHandler gets a task to continue on (I assume we're missing some code). Anyways...
You register 3 continuations on a task that will run when the task completes, is canceled and is faulted. Now let's assume the original task ran to completion successfully without being canceled. That means that 2 of your continuations (OnCanceled and OnFaulted) will not run because they don't need to be. The way to tell a task to not run in the TPL is to cancel it, and that happens automatically.
The difference between your 2 code snippets is that in the first one you await the task continuations, and they get canceled which explains your exception. On the second snippet you don't await the continuations, just the original task that successfully ran to completion.
P.S: I think the second option is more appropriate. You don't need to await all those continuations. You want to let them run if they need to.
TL;DR: You await a canceled continuation task. The continuation task, not the original, is the one that throws an exception.
Related
During high load, our app randomly throw this error:
System.Data.SqlClient.SqlConnection.GetOpenTdsConnection
outerType
System.AggregateException
outerMessage
A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread.
innermostType
System.InvalidOperationException
innermostMessage
Invalid operation. The connection is closed
I looked into it and looks like this issue is fixed on Microsoft.Data.SQLClient
Our code comes from old world and still uses System.Data.SqlClient.Is there a way to know if same issue exists in System.Data.SqlClient and been fixed in a new version? Or we will have to use Microsoft.Data.SQLClient? (we tried Microsoft.Data.SqlClient before and there are behaviour differences)
If you are using tasks you can do the following try that I'm not sure that you will get all the information you want but you will get more information about unhandled exception
public static void LogExceptions(this Task task)
{
task.ContinueWith( t =>
{
var aggException = t.Exception.Flatten();
foreach(var exception in aggException.InnerExceptions)
LogException(exception);
},
TaskContinuationOptions.OnlyOnFaulted);
}
And you can use like below
Task.Factory.StartNew( () =>
{
// Do your work...
}).LogExceptions();
Or you can use the TaskScheduler.UnobservedTaskException to get task exceptions where task exceptions are not observed.
For more information see the below links
A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was
Looking for what caused the "A Task's exception(s) were not observed..."
I've argued with my colleague about handling exceptions in Task.Run blocks.
For example, I have this code:
private static async Task MainAsync()
{
try
{
await Task.Run(() => throw new Exception());
}
catch (Exception)
{
Console.WriteLine("oops");
}
}
If I run this code (in debug mode), I'll get the message from vs2019 about the unhandled exception. But if I press the continue button, the app will work correctly and the exception will be handled.
Is it the correct way to catch exceptions from Task.Run?
Generally, exception in tasks (objects of class Task) are placed on tasks, i.e. if some code in a task throws exception inside that task - it is stored in Exception property in Task class.
await operator automatically "unpacks" exception and throws. So your way is totally correct, but I think stack trace might change - so you need to keep that in mind.
This question already has an answer here:
Why does the Task.WhenAny not throw an expected TimeoutException?
(1 answer)
Closed 2 years ago.
I have been reading around a bit, such as here (Catch an exception thrown by an async void method) and I thought the behaviour of async Task methods was:
that you could try/catch them as normal when using await or Task.Wait e.g. try{ await X();}catch(Exception SqlException e){...}
if you let it 'bubble up' you will instead get AggregateException
But I am finding my application is just terminating without breaking on exceptions at all. My code looks like:
internal async Task RunAsync()
{
var tasks = monitors.Select((p) => p.Value.MonitorAsync()).ToList();
await Task.WhenAny(tasks);
Console.WriteLine("ONE");
}
public static async Task Main(string[] args)
{
var app = new App();
try
{
await app.RunAsync();
Console.WriteLine("TWO");
}
catch(Exception e)
{
Console.WriteLine(e);
}
}
Setting breakpoints on "ONE" and "TWO" I can see that tasks has at least one Task with status Faulted, and t has status RanToCompletion. So the fault state is lost and no exceptions.
Clearly I'm missing something obvious, what should I be doing differently?
BTW WhenAny is used to detect unusual termination, these tasks should only ever exit due to failure. This is more of a test-bed to understand catching exceptions.
This is because of WhenAny. Please check the documentation:
The returned task will complete when any of the supplied tasks has
completed. The returned task will always end in the RanToCompletion
state with its Result set to the first task to complete. This is true
even if the first task to complete ended in the Canceled or Faulted
state.
(emphasis mine)
This means that WhenAny will not raise an exception if any of the tasks faults.
This is contrary to WhenAll, which will raise an exception if any of the tasks it waits for faults with an unhandled exception.
My WPF application consists of a main TabControl which renders multiple plugins, loaded via MEF. Technically, the ICollectionView of the plugins-collection is bound to the ItemsSource-property of the main TabControl. The IsSynchronizedWithCurrentItem-property is also set to True to keep track of the currently selected tab item.
To initialize the plugins, I subscribe the CurrentChanged-event of the ICollectionView to initialize the selected plugin via lazy loading. Since lots of the initialization tasks are async, I also declared the event handler as async.
private ICollectionView pluginsCv;
public ICollectionView PluginsCv
{
get
{
if (pluginsCv == null)
{
pluginsCv = CollectionViewSource.GetDefaultView(Plugins);
pluginsCv.CurrentChanged += async (sender, args) =>
{
await LoadCurrentSection();
};
}
return pluginsCv;
}
}
Tasks of the LoadCurrentSection-method:
Check if data was already loaded before.
If not, initialize the currently selected plugin, inter alia, reading data via EntityFramework.
LoadCurrentSection-method:
private async Task LoadCurrentSection()
{
// If no tab is selected, select the first one
if (PluginsCv.CurrentItem == null)
{
// Comment for stackoverflow: Another raise of the CurrentChanged-event does not seem to be the cause. Tried to prevent another execution of LoadCurrentSection via a bool flag, an stayed in this method (no return statement). Same result.
PluginsCv.MoveCurrentToFirst();
return;
}
// Get the ViewModel of the currently selected section/plugin
var pluginViewModel = ((IPlugin)PluginsCv.CurrentItem).PluginElement.DataContext as ISettlementScheduleSection;
// Initialize currently selected section/plugin
if (pluginViewModel != null && !pluginViewModel.DataLoaded)
await pluginViewModel.LoadData();
}
LoadData-method of the plugin:
public override async Task LoadData()
{
// ...
Costs = await costsService.GetCosts(SubProjectId, UnitTypeId);
// ...
}
Every plugin holds it's own instances of service-classes for crud operations. Those disposable service classes hold and instance of the db-Context.
Little example:
public class CostsService : ServiceBase
{
public CostsService()
: base()
{ }
public CostsService(MyDbContext db)
{
this.Context = db ?? throw new ArgumentNullException(nameof(db));
}
public async Task<IEnumerable<CostsDefinition>> GetCosts(Guid subProjectId, Guid unitTypeId)
{
return await Context.CostsDefinitions.Where(cd => cd.SubProjectId == subProjectId
&& cd.UnitTypeId == unitTypeId)
.OrderBy(cd => cd.Year).ThenBy(cd => cd.Month)
.ToListAsync();
}
}
The db-context (Context-property) is not static and is also not shared between the plugins.
The Problem: Sometimes, but not always, this way to initialize the plugins causes a System.NotSupportedException: A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe. WPF seems to raise that event multiple times. To me it is unclear in what scenarios it gets raised. When the ICollectionView is first bound to the TabControl and the first item gets automatically selected? Also when I load and insert more plugins into the source collection? ...
Frequency of the bug: In this case, timing seems to play are role. The frequency of that bug changes when running without debugging or under different network conditions.
Workaround attempt: I tried to filter additional/unnecessary calls of the CurrentChanged-handler via a bool flag, but it didn't help in all cases. I just reduced the frequency a bit. I also tried to delegate every access to PluginsCv to the Dispatcher, but it didn't help. Also, I wrote debug information when pluginsCv is null. And I got that information only once, so the CurrentChanged-event must also be subscribed only once.
Exception details:
Source of exception: "EntityFramework"
Stacktrace:
at System.Data.Entity.Internal.ThrowingMonitor.EnsureNotEntered()
at System.Data.Entity.Core.Objects.ObjectQuery`1.System.Data.Entity.Infrastructure.IDbAsyncEnumerable<T>.GetAsyncEnumerator()
at System.Data.Entity.Internal.Linq.InternalQuery`1.GetAsyncEnumerator()
at System.Data.Entity.Infrastructure.DbQuery`1.System.Data.Entity.Infrastructure.IDbAsyncEnumerable<TResult>.GetAsyncEnumerator()
at System.Data.Entity.Infrastructure.IDbAsyncEnumerableExtensions.ForEachAsync[T](IDbAsyncEnumerable`1 source, Action`1 action, CancellationToken cancellationToken)
at System.Data.Entity.Infrastructure.IDbAsyncEnumerableExtensions.ToListAsync[T](IDbAsyncEnumerable`1 source, CancellationToken cancellationToken)
at System.Data.Entity.Infrastructure.IDbAsyncEnumerableExtensions.ToListAsync[T](IDbAsyncEnumerable`1 source)
at System.Data.Entity.QueryableExtensions.ToListAsync[TSource](IQueryable`1 source)
at xyz.desk.app.ccm.business.Service.CostsService.<GetCosts>d__7.MoveNext() in D:\Dev\ThatCrazyPluginProject\desk\app\xyz.desk.app.ccm.business\Service\CostsService.cs:line 89
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at xyz.desk.app.ccm.plugin.costplan.constructionprogress.ViewModel.ContructionProgressCostPlanViewModel.<LoadData>d__58.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at xyz.desk.app.ccm.plugin.settlementscheduleshost.ViewModel.SettlementSchedulesHostViewModel.<LoadCurrentSection>d__67.MoveNext() in D:\Dev\ThatCrazyPluginProject\desk\app\xyz.desk.app.ccm.ui\ViewModel\SettlementSchedulesHostViewModel.cs:line 307
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at xyz.desk.app.ccm.plugin.settlementscheduleshost.ViewModel.SettlementSchedulesHostViewModel.<LoadData>d__65.MoveNext() in D:\Dev\ThatCrazyPluginProject\desk\app\xyz.desk.app.ccm.ui\ViewModel\SettlementSchedulesHostViewModel.cs:line 282
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at xyz.desk.app.ccm.ui.ViewModel.ProjectViewModel.<LoadCurrentSection>d__115.MoveNext() in D:\Dev\ThatCrazyPluginProject\desk\app\xyz.desk.app.ccm.ui\ViewModel\ProjectViewModel.cs:line 763
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at xyz.desk.app.ccm.ui.ViewModel.ProjectViewModel.<<get_PluginsCv>b__21_0>d.MoveNext() in D:\Dev\ThatCrazyPluginProject\desk\app\xyz.desk.app.ccm.ui\ViewModel\ProjectViewModel.cs:line 120
Any ideas to solve this issue?
Typical race condition.
Lets consider this scenario
Thread1 - Access PluginsCv
PluginsCv is null so register CurrentChanged
Thread2 - Access PluginsCv
PluginsCv is null so register CurrentChanged
Later...
CurrentChanged fires
Thread1 - await LoadCurrentSection
Thread2 - await LoadCurrentSection
Thread2 "Wait! Thread1 is doing LoadCurrentSection too and its not done yet! Error! Error!"
I'm running an Owin Selfhost based WebApi where i've put in an API unhandled exception logger via
config.Services.Add(typeof(IExceptionLogger), _apiExceptionLogger);
Relevant part of ApiExceptionLogger:
public override void Log(ExceptionLoggerContext context)
{
if (context == null || context.ExceptionContext == null) return;
Logger.Error("Unhandled exception from Web API", context.ExceptionContext.Exception);
}
The cases it's catching and logging regularly are ones where the client requests a dataset and then closes the connection while the results (JSON) are being sent back - people making a request in chrome, and then hitting the X button before all results come back :P
I've pasted a stacktrace below for completeness, just want to know two things:
Is this regular/expected behavior? AFAIK it is...I'm running a pretty default API and pipeline
Is there any way to handle this? Essentially stop request processing more gracefully in case of a cancellation (the cancellation tokens peppered throughout the request pipeline do come to mind, but doesn't look like they do much in this case, after all the tokens only support co-operative cancellation)
I haven't done any deep dive on the sequence of events happening at the socket level, so far this is only a logging nuisance.
System.Net.Http.HttpRequestException: Error while copying content to a stream. ---> System.IO.IOException ---> System.Net.HttpListenerException: The I/O operation has been aborted because of either a thread exit or an application request
at System.Net.HttpResponseStream.EndWrite(IAsyncResult asyncResult)
at Microsoft.Owin.Host.HttpListener.RequestProcessing.ExceptionFilterStream.EndWrite(IAsyncResult asyncResult)
--- End of inner exception stack trace ---
at Microsoft.Owin.Host.HttpListener.RequestProcessing.ExceptionFilterStream.EndWrite(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of inner exception stack trace ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Owin.HttpMessageHandlerAdapter.<SendResponseContentAsync>d__20.MoveNext() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Owin.HttpMessageHandlerAdapter.<SendResponseContentAsync>d__20.MoveNext()
I had a similar issue with Owin host on Raspberry Pi. This may help https://stackoverflow.com/a/30583109/1786034
Its code issue
Read file async and check.At any time if you are doing an api call and it times out it will definitely throw exception.
using (StreamReader reader = new StreamReader(await selectedFile.OpenStreamForReadAsync()))
{
while ((nextLine = await reader.ReadLineAsync()) != null)
{
contents.AppendFormat("{0}. ", lineCounter);
contents.Append(nextLine);
contents.AppendLine();
lineCounter++;
if (lineCounter > 3)
{
contents.AppendLine("Only first 3 lines shown.");
break;
}
}
}