using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
var someEventRaisingObject = new SomeEventRaisingObject();
someEventRaisingObject.TheEvent += async (s, e) =>
Console.WriteLine("Entering the event handler...");
await TaskThatIsReallyImportantAsync();
Console.WriteLine("Exiting the event handler...");
await someEventRaisingObject.RaiseEventAsync();
Console.WriteLine($"Caught an exception in our handler: {ex.Message}");
Console.WriteLine("Press Enter to exit");
async Task TaskThatIsReallyImportantAsync()
throw new Exception("This is an expected exception");
class SomeEventRaisingObject
public event EventHandler<EventArgs> TheEvent;
public Task RaiseEventAsync()
return InvokeAsync(TheEvent, true, true, this, EventArgs.Empty);
private async Task InvokeAsync(
MulticastDelegate theDelegate,
var taskCompletionSource = new TaskCompletionSource<bool>();
bool taskCompletionSourceCompleted = false;
var delegates = theDelegate.GetInvocationList();
var countOfDelegates = delegates.Length;
var assignedExceptions = new List<Exception>();
var trackedExceptions = new ConcurrentQueue<Exception>();
foreach (var @delegate in theDelegate.GetInvocationList())
var async = @delegate.Method
.GetCustomAttributes(typeof(AsyncStateMachineAttribute), false)
var completed = new Action(() =>
if (Interlocked.Decrement(ref countOfDelegates) == 0)
lock (taskCompletionSource)
if (taskCompletionSourceCompleted)
assignedExceptions.AddRange(trackedExceptions);
if (!trackedExceptions.Any())
taskCompletionSource.SetResult(true);
else if (trackedExceptions.Count == 1)
taskCompletionSource.SetException(assignedExceptions[0]);
taskCompletionSource.SetException(new AggregateException(assignedExceptions));
taskCompletionSourceCompleted = true;
var failed = new Action<Exception>(e =>
trackedExceptions.Enqueue(e);
var context = new EventHandlerSynchronizationContext(completed, failed);
SynchronizationContext.SetSynchronizationContext(context);
@delegate.DynamicInvoke(args);
catch (TargetParameterCountException e)
catch (TargetInvocationException e) when (e.InnerException != null)
failed(e.InnerException);
while (forceOrdering && !waitFlag)
if (stopOnFirstError && trackedExceptions.Any() && !taskCompletionSourceCompleted)
lock (taskCompletionSource)
if (!taskCompletionSourceCompleted && !assignedExceptions.Any())
assignedExceptions.AddRange(trackedExceptions);
if (trackedExceptions.Count == 1)
taskCompletionSource.SetException(assignedExceptions[0]);
taskCompletionSource.SetException(new AggregateException(assignedExceptions));
taskCompletionSourceCompleted = true;
await taskCompletionSource.Task;
private class EventHandlerSynchronizationContext : SynchronizationContext
private readonly Action _completed;
private readonly Action<Exception> _failed;
public EventHandlerSynchronizationContext(
Action<Exception> failed)
public override SynchronizationContext CreateCopy()
return new EventHandlerSynchronizationContext(
public override void Post(SendOrPostCallback d, object state)
if (state is ExceptionDispatchInfo edi)
_failed(edi.SourceException);
public override void Send(SendOrPostCallback d, object state)
if (state is ExceptionDispatchInfo edi)
_failed(edi.SourceException);
public override void OperationCompleted() => _completed();