I have this event handler:
private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e)
{
var canShutdown = lifetime.CanShutdown();
e.Cancel = !canShutdown;
}
Now, due to design decisions the CanShutdown method has changed from bool to Task<bool>
Task<bool> CanShutDown()
{
//...
}
So, I need to modify the event handler like this:
private async void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e)
{
var canShutdown = await lifetime.CanShutdown();
e.Cancel = !canShutdown;
}
I've read many times that async void methods have many problems, like unhandled exceptions being thrown directly inside the SynchronizationContext. But one of the valid usages for them are event handlers. This is an event handler. Isn't it?
BUT, I wonder if that code is free of undesired consequences after the "migration".
My concern is that the handler modifies the value of e.Cancel.
A colleague has told me that this will happen:
After await, the caller to that method isn't awaiting. It assumes synchronous execution, so it immediately reads
e.Cancel, and that hasn't been set yet. That is a problem inside event handler: You realize as soon as the await keyword is hit, the code that calledShutdownRequested.Invoke()immediately returns. The value it will read might not be up-to-date.
I'm afraid my colleague has his point. So, it seems this approach is broken. But I still don't see how to fix that.
How to deal with the EventArgs being shared by sync and async code?