AsyncContext -> AsyncContextThread handling exceptions - c#

Regarding: AsyncContextThread
https://github.com/StephenCleary/AsyncEx/wiki/AsyncContext
https://github.com/StephenCleary/AsyncEx/blob/master/src/Nito.AsyncEx.Context/AsyncContextThread.cs
It's not really covered how to handle catching exceptions that occur when the thread is started.
public partial class MyService : ServiceBase
{
private readonly AsyncContextThread _thread = new AsyncContextThread();
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
public MyService()
{
InitializeComponent();
}
public void Start()
{
OnStart(null);
}
protected override void OnStart(string[] args)
{
try
{
_thread.Factory.Run(StartAsync);
}
catch (Exception ex)
{
// Exception?
}
}
private async Task StartAsync()
{
throw new Exception("things went wrong");
}
protected override void OnStop()
{
if (!_cts.IsCancellationRequested)
_cts.Cancel();
_thread.JoinAsync().GetAwaiter().GetResult();
}
}
I don't seem to catch anything in the catch{} block, in addition I tried adding these to my Program.cs (main entry point).
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
However neither of the event handler methods are triggered in the above code.
Q) How do you handle exceptions correctly in this situation?
As a side note, I'm fairly sure that using AsyncContext previously and debugging in Visual Studio did catch the exception, so I'm not sure if there's some sync. context I'm missing, when trying to add a handler for exceptions.
EDIT
I know I can try/catch within the StartAsync method, but the point I'm getting at is being able to catch any unhandled Exceptions and make sure I can log them.

I've been meaning to do a blog post on asynchronous Win32 services... for a few years now... :)
It's not really covered how to handle catching exceptions that occur when the thread is started.
The AsyncContextThread is actually started immediately upon creation. What you're doing on OnStart is queueing work to its context.
The exception for that work is observed on the Task returned from Run, and the appropriate way to observe it is using await:
protected override async void OnStart(string[] args)
{
try
{
await _thread.Factory.Run(StartAsync);
}
catch (Exception ex)
{
// Exception!
}
}
A few notes:
Normally, you should avoid async void. The exception to that rule is event handlers or event-like methods (such as OnStart). When you do use async void, you should consider having a try/catch around the entire async void method body (which we do).
await is superior to ContinueWith. In this particular case, ContinueWith will work OK, but in the general case it's not advisable: it does not understand async delegates, it does not use an appropriate default TaskScheduler, and it does not use appropriate default continuation flags. These are all things that await just handles for you.

You may use a Continuation to handle exceptions.
protected override void OnStart(string[] args)
{
_thread.Factory.Run(StartAsync).ContinueWith(t =>
{
var aggregate = t.Exception.Flatten();
foreach(var exception in aggregate.InnerExceptions)
{
// Handle exception
}
}, TaskContinuationOptions.OnlyOnFaulted);
}

Related

Async, await in C#

On closing of my application I have to do some clean up activity and I have written something like this in ClassA.cs
protected override void OnExit(ExitEventArgs e)
{
Cleanup();
base.OnExit(e);
}
private async Task Cleanup()
{
GC.Collect();
GC.WaitForPendingFinalizers();
if (databaseInitialisation != null)
{
await databaseInitialisation.InitialiseDatabase();
}
databaseFile?.Dispose();
}
I have one more class ClassB.cs
public class DatabaseInitialisation : IDatabaseInitialisation
{
private readonly string filterOptimisationPath;
private readonly Task databaseInitialisation;
private IDbConnectionFactory ConnectionFactory { get; }
public async Task InitialiseDatabase() => await databaseInitialisation;
public DatabaseInitialisation(string databaseFilePath, string filterOptimisationPath)
{
this.filterOptimisationPath = filterOptimisationPath;
ConnectionFactory = new SqLiteConnectionFactory(databaseFilePath);
databaseInitialisation = Task.Run(() => CreateDatabase(databaseFilePath));
}
}
so now when I close my application, sometimes when cleanup is called execution of the program going from await is going to onexit method without executing the dispose method
The root of your trouble is that you need to call an async method from a sync method, and you cannot make that method async because it's part of the GUI framework. The general advice here would be to make your calling method async, but since you can't, there are a few ways you can accomplish this:
Option 1 -- Task.Run
Change your OnExit method to launch Cleanup on a thread pool thread and then synchronously wait for that using GetAwaiter().GetResult() to prevent deadlocks. Since this is only running once at shutdown, it should be fine, but launching background threads like this would not be recommended if this were a method that will run often as it could potentially lead to thread pool starvation.
protected override void OnExit(ExitEventArgs e)
{
Task.Run(async () => await Cleanup().ConfigureAwait(false))
.GetAwaiter().GetResult();
base.OnExit(e);
}
Option 2 -- async void and a ManualResetEvent
This approach avoids using .GetAwaiter().GetResult() and launching on a thread pool thread, but any exception thrown in cleanup will cause the application to crash.
protected override void OnExit(ExitEventArgs e)
{
using var signal = new ManualResetEventSlim(false);
Cleanup(signal);
signal.Wait();
base.OnExit(e);
}
private async void Cleanup(ManualResetEventSlim signal)
{
if (databaseInitialisation != null)
{
await databaseInitialisation.InitialiseDatabase();
}
databaseFile?.Dispose();
signal.Set();
}
You could also use one of the Wait overloads to include a timeout.
As a side note, in a GUI application like this, you should really be calling .ConfigureAWait(false) on every task you await that isn't being called directly from a GUI event. Not doing so risks deadlocks.
One caveat to both these solutions is that if your Cleanup method or anything in its call stack, both of these methods will deadlock, and in that case, another solution is needed.

How to use EndInvoke when events/delegates called are not your responsibility

I currently have a class that receives information constantly from an API.
When its received that information it fires an event/delegate which other classes can subscribe to.
Because I don't want to block the thread the class is running on I use delegate.BeginInvoke to fire the event.
eg.
object UpdateLock=new object();
List<Action<object, UpdateArgs>> UpdateSubscribersList = new List<Action<object, UpdateArgs>>();
public void UpdateSubscribe(Action<object, UpdateArgs> action)
{
lock (UpdateLock)
{
UpdateSubscribersList.Add(action);
}
}
public bool UpdateUnSubscribe(Action<object, UpdatePortfolioArgs> action)
{
lock (UpdateLock)
{
return UpdateSubscribersList.Remove(action);
}
}
public virtual void onUpdate(UpdateArgs args)
{
lock (UpdateLock)
{
foreach (Action<object, UpdateArgs> action in UpdateSubscribersList)
{
action.BeginInvoke(args, null, null);
}
}
}
This is just an example. Don't worry about the use of the list rather than a multicast delegate - there's some other code which caused me to use that.
Anyway I have two questions:
1) where should I put EndInvoke so it doesn't block the thread? I believe this question has already been asked, but I'm not so clear on it, and anyway it's not my main question.
2) The main purpose of EndInvoke is to clean up the thread and handle exceptions. Now cleaning up the thread is fine, but how can I handle exceptions? I have no idea which code will be called by these delegates, and that code is not my responsibility. So is there anyway I can get the subscriber to these events to have to deal with the clean up code and the exceptions, rather than my class? Or is their anyway that he can access anything returned by EndInvoke at all, so that if he wants to he can deal with any exceptions?
I think this pattern should work for you.
public void StartAsync(Action a)
{
a.BeginInvoke(CallBack, a); // Pass a Callback and the action itself as state
// or a.BeginInvoke(Callback, null); then see alternative in Callback
}
private void CallBack(IAsyncResult ar)
{
// In the callback you can get the delegate
Action a = ar.AsyncState as Action;
// or
// Action a = ((AsyncCallback)ar).AsyncDelegate as Action
try
{
// and call EndInvoke on it.
a?.EndInvoke(ar); // Exceptions will be re-thrown when calling EndInvoke
}
catch( Exception ex )
{
Log.Error( ex, "Exception in onUpdate-Action!" );
}
}
You should be able to adapt this to your Action<object, UpdateArgs> delegate.
I used StartAsync just for brevity. This would be inside the for loop of your onUpdate Method, of course.
Mind that Callback will be called on the Async-Thread! If you want to update GUI elements here, you'll need to marshal that back to the GUI-Thread.
If you add an event to your class
public event EventHandler<Exception> OnError;
you can publish those exceptions :
catch( Exception ex )
{
Log.Error( ex, "Exception in onUpdate-Action!" );
OnError?.Invoke( a, ex );
}

How to catch/observe an unhandled exception thrown from a Task

I'm trying to log / report all unhandled exceptions in my app (error reporting solution). I've come across a scenario that is always unhandled. I'm wondering how would I catch this error in an unhandled manner. Please note that I've done a ton of research this morning and tried a lot of things.. Yes, I've seen this, this and many more. I'm just looking for a generic solution to log unhandled exceptions.
I have the following code inside of a console test apps main method:
Task.Factory.StartNew(TryExecute);
or
Task.Run((Action)TryExecute);
as well as the following method:
private static void TryExecute() {
throw new Exception("I'm never caught");
}
I'm already tried wiring up to the following in my app, but they are never called.
AppDomain.CurrentDomain.UnhandledException
TaskScheduler.UnobservedTaskException
In my Wpf app where I initially found this error I also wired up to these events but it was never called.
Dispatcher.UnhandledException
Application.Current.DispatcherUnhandledException
System.Windows.Forms.Application.ThreadException
The only handler that is called ever is:
AppDomain.CurrentDomain.FirstChanceException
but this is not a valid solution as I only want to report uncaught exceptions (not every exception as FirstChanceException is called before any catch blocks are ever executed / resolved.
The TaskScheduler.UnobservedTaskException event should give you what you want, as you stated above. What makes you think that it is not getting fired?
Exceptions are caught by the task and then re-thrown, but not immediately, in specific situations. Exceptions from tasks are re-thrown in several ways (off the top of my head, there are probably more).
When you try and access the result (Task.Result)
Calling Wait(), Task.WaitOne(), Task.WaitAll() or another related Wait method on the task.
When you try to dispose the Task without explicitly looking at or handling the exception
If you do any of the above, the exception will be rethrown on whatever thread that code is running on, and the event will not be called since you will be observing the exception. If you don't have the code inside of a try {} catch {}, you will fire the AppDomain.CurrentDomain.UnhandledException, which sounds like what might be happening.
The other way the exception is re-thrown would be:
When you do none of the above so that the task still views the exception as unobserved and the Task is getting finalized. It is thrown as a last ditch effort to let you know there was an exception that you didn't see.
If this is the case and since the finalizer is non-deterministic, are you waiting for a GC to happen so that those tasks with unobserved exceptions are put in the finalizer queue, and then waiting again for them to be finalized?
EDIT: This article talks a little bit about this. And this article talks about why the event exists, which might give you insight into how it can be used properly.
I used the LimitedTaskScheduler from MSDN to catch all exceptions, included from other threads using the TPL:
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
{
/// Whether the current thread is processing work items.
[ThreadStatic]
private static bool currentThreadIsProcessingItems;
/// The list of tasks to be executed.
private readonly LinkedList tasks = new LinkedList(); // protected by lock(tasks)
private readonly ILogger logger;
/// The maximum concurrency level allowed by this scheduler.
private readonly int maxDegreeOfParallelism;
/// Whether the scheduler is currently processing work items.
private int delegatesQueuedOrRunning; // protected by lock(tasks)
public LimitedConcurrencyLevelTaskScheduler(ILogger logger) : this(logger, Environment.ProcessorCount)
{
}
public LimitedConcurrencyLevelTaskScheduler(ILogger logger, int maxDegreeOfParallelism)
{
this.logger = logger;
if (maxDegreeOfParallelism Gets the maximum concurrency level supported by this scheduler.
public override sealed int MaximumConcurrencyLevel
{
get { return maxDegreeOfParallelism; }
}
/// Queues a task to the scheduler.
/// The task to be queued.
protected sealed override void QueueTask(Task task)
{
// Add the task to the list of tasks to be processed. If there aren't enough
// delegates currently queued or running to process tasks, schedule another.
lock (tasks)
{
tasks.AddLast(task);
if (delegatesQueuedOrRunning >= maxDegreeOfParallelism)
{
return;
}
++delegatesQueuedOrRunning;
NotifyThreadPoolOfPendingWork();
}
}
/// Attempts to execute the specified task on the current thread.
/// The task to be executed.
///
/// Whether the task could be executed on the current thread.
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// If this thread isn't already processing a task, we don't support inlining
if (!currentThreadIsProcessingItems)
{
return false;
}
// If the task was previously queued, remove it from the queue
if (taskWasPreviouslyQueued)
{
TryDequeue(task);
}
// Try to run the task.
return TryExecuteTask(task);
}
/// Attempts to remove a previously scheduled task from the scheduler.
/// The task to be removed.
/// Whether the task could be found and removed.
protected sealed override bool TryDequeue(Task task)
{
lock (tasks)
{
return tasks.Remove(task);
}
}
/// Gets an enumerable of the tasks currently scheduled on this scheduler.
/// An enumerable of the tasks currently scheduled.
protected sealed override IEnumerable GetScheduledTasks()
{
var lockTaken = false;
try
{
Monitor.TryEnter(tasks, ref lockTaken);
if (lockTaken)
{
return tasks.ToArray();
}
else
{
throw new NotSupportedException();
}
}
finally
{
if (lockTaken)
{
Monitor.Exit(tasks);
}
}
}
protected virtual void OnTaskFault(AggregateException exception)
{
logger.Error(exception);
}
///
/// Informs the ThreadPool that there's work to be executed for this scheduler.
///
private void NotifyThreadPoolOfPendingWork()
{
ThreadPool.UnsafeQueueUserWorkItem(ExcuteTask, null);
}
private void ExcuteTask(object state)
{
// Note that the current thread is now processing work items.
// This is necessary to enable inlining of tasks into this thread.
currentThreadIsProcessingItems = true;
try
{
// Process all available items in the queue.
while (true)
{
Task item;
lock (tasks)
{
// When there are no more items to be processed,
// note that we're done processing, and get out.
if (tasks.Count == 0)
{
--delegatesQueuedOrRunning;
break;
}
// Get the next item from the queue
item = tasks.First.Value;
tasks.RemoveFirst();
}
// Execute the task we pulled out of the queue
TryExecuteTask(item);
if (!item.IsFaulted)
{
continue;
}
OnTaskFault(item.Exception);
}
}
finally
{
// We're done processing items on the current thread
currentThreadIsProcessingItems = false;
}
}
}
And than the "registration" of the TaskScheduler as the default using Reflection:
public static class TaskLogging
{
private const BindingFlags StaticBinding = BindingFlags.Static | BindingFlags.NonPublic;
public static void SetScheduler(TaskScheduler taskScheduler)
{
var field = typeof(TaskScheduler).GetField("s_defaultTaskScheduler", StaticBinding);
field.SetValue(null, taskScheduler);
SetOnTaskFactory(new TaskFactory(taskScheduler));
}
private static void SetOnTaskFactory(TaskFactory taskFactory)
{
var field = typeof(Task).GetField("s_factory", StaticBinding);
field.SetValue(null, taskFactory);
}
}

Catch unhandled exceptions from async

When an async method that is awaited upon throws an exception, the exception is stored somewhere and throwing it is delayed. In a WinForms or WPF application, it uses SynchronizationContext.Current to post throwing of the exception. However, in e.g. a console application, it throws the exception on a thread pool and it brings down the application.
How can I prevent exceptions thrown from an async method from bringing down the application?
EDIT:
Appearantly the issue I'm describing is because I have void async methods. See comments.
How can I prevent exceptions thrown from an async method from bringing down the application?
Follow these best practices:
All async methods should return Task or Task<T> unless they have to return void (e.g., event handlers).
At some point, you should await all Tasks returned from async methods. The only reason you wouldn't want to do this is if you no longer care about the result of the operation (e.g., after you cancel it).
If you need to catch an exception from an async void event handler, then catch it in the event handler - exactly like you would do if this was synchronous code.
You may find my async / await intro post helpful; I cover several other best practices there as well.
When the async method is started, it captures the current synchronization context. A way to solve this issue is to create your own synchronization context which captures the exception.
The point here is that the synchronization context posts the callback to the thread pool, but with a try/catch around it:
public class AsyncSynchronizationContext : SynchronizationContext
{
public override void Send(SendOrPostCallback d, object state)
{
try
{
d(state);
}
catch (Exception ex)
{
// Put your exception handling logic here.
Console.WriteLine(ex.Message);
}
}
public override void Post(SendOrPostCallback d, object state)
{
try
{
d(state);
}
catch (Exception ex)
{
// Put your exception handling logic here.
Console.WriteLine(ex.Message);
}
}
}
In the catch above you can put your exception handling logic.
Next, on every thread (SynchronizationContext.Current is [ThreadStatic]) where you want to execute async methods with this mechanism, you must set the current synchronization context:
SynchronizationContext.SetSynchronizationContext(new AsyncSynchronizationContext());
The complete Main example:
class Program
{
static void Main(string[] args)
{
SynchronizationContext.SetSynchronizationContext(new AsyncSynchronizationContext());
ExecuteAsyncMethod();
Console.ReadKey();
}
private static async void ExecuteAsyncMethod()
{
await AsyncMethod();
}
private static async Task AsyncMethod()
{
throw new Exception("Exception from async");
}
}

Is it possible to fail triggering the AppDomain's unhandled exception handler from a Task continuation?

I have a Task which runs asynchronously and handles exceptions using a task continuation task.ContinueWith(t => ..., CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, taskScheduler).
This works great for handling exceptions I know how to handle (e.g. WebException), but what if something like NullReferenceException is thrown, which I can't handle correctly?
I want to be able to handle it using the AppDomain's unhandled exception handler (which in our case logs the error, pops up a dialog notifying the user, and exits the application), but so far I have not found a good way to do this.
Simply rethrowing the exception from the continuation results in the unhandled exception handler being called when the task is finalized (because the rethrown exception is unobserved). This is not good because it may be seconds or even minutes after I know the application should crash.
Calling Environment.FailFast() does crash the application but does not call the unhandled exception handler.
Calling Thread.CurrentThread.Abort() calls the unhandled exception handler but only displays information/the stack trace from the ThreadAbortException. Plus it just seems like a bad idea to use this method.
Calling Application.OnThreadException() actually does exactly what I want, except that it requires referencing System.Windows.Forms and handling Application.ThreadException which will not work with, for example, a service with no UI.
Calling task.Wait() does not make sense because this in our case there's no place to call it from. If the task succeeds the result is processed using a success continuation, if it fails the exception continuation is called, and we can't block the thread creating the task (that's the whole point of the task).
I could call the unhandled exception handler directly except that in at least one case that would require adding a dependency I don't want to add in my application.
Am I missing an obvious way to do this?
I didn't find any BCL class which does similar work. So, I can suggest you to use some hand-written static class:
static class Crusher
{
private static readonly AutoResetEvent CrushEvent;
private static Exception _exception;
static Crusher()
{
CrushEvent = new AutoResetEvent(false);
}
public static Thread GetCrushWaitingThread()
{
return new Thread(WaitForCrush);
}
static void WaitForCrush()
{
CrushEvent.WaitOne();
throw _exception;
}
public static void Crush(Exception exception)
{
_exception = exception;
CrushEvent.Set();
}
}
You should just init it at your application startup:
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
Crusher.GetCrushWaitingThread().Start();
...
and then - you can call it from continuations:
task.ContinueWith(Continuation, TaskContinuationOptions.OnlyOnFaulted);
...
private static void Continuation(Task obj)
{
Crusher.Crush(obj.Exception);
}
But it's not as nice as real rethrown unhandled exception (it has addition information about Crusher.WaitForCrush() method in stacktrace).
We used Application.OnThreadException() to solve our specific case.
Just use the same logic that you implemented for AppDomain.UnhandledException and call it explicitly when you hit an exception in your continuation. Here is an example crash handler I use:
public static class CrashHandler
{
public static void Setup()
{
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
}
public static Task CrashOnUnhandledException(this Task t)
{
t.ContinueWith(x => HandleException(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
return t;
}
private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
HandleException(e.ExceptionObject);
}
private static void HandleException(object ex)
{
ShowErrorMessage(ex);
Environment.Exit(-1);
}
private static void ShowErrorMessage(object ex)
{
// your crash dialog goes here
}
}
The extension method CrashOnUnhandledException can be used like this to save you some typing and give you additional information about the call site in your stack trace:
task.ContinueWith(t => { /* your logic */ })
.CrashOnUnhandledException();

Categories

Resources