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 called ShutdownRequested.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?
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.
Yes. In fact, throwing exceptions on the SynchronizationContext is actually deliberate behavior specifically to emulate the behavior of event handlers.
One of the primary problems with async void method is that it's not easy to determine when the async void method has completed. Which is exactly the problem your colleague is pointing out.
This is an event handler. Isn't it?
Sort of.
Take a step back and consider the design patterns being used. The Observer pattern is a way to notify observers of state changes. The Observer pattern is a clear fit for "events" in OOP: any number of observers may subscribe to state change notifications.
This kind of "shutdown" notification is not just a notification, though. It also has a return value. Generally, this is the Strategy pattern. The Strategy pattern is not a good fit for events in OOP. However, many times the Strategy pattern is (mis-)implemented with an event; this is a common design mistake in OOP languages.
So, is it an event handler? Technically, yes. Should it be an event handler? Probably not.
In the synchronous world, implementing the Strategy pattern with events is often "close enough". Sometimes there's some confusion about how the return value should be handled in case there are multiple event subscribers, but in general this kind of design mistake goes unnoticed. Until async comes along, and suddenly the design mistake of using events for the Strategy pattern becomes more apparent.
But I still don't see how to fix that.
I describe a few possibilities on my blog.
If you control OnShutdownRequested and ShutdownRequestedEventArgs, then you can use deferrals. It's a bit complex to set up, but it allows both synchronous and asynchronous handlers (as long as the handlers use a deferral), and the code that raises the event can (asynchronously) wait for all handlers to complete before retrieving the results.
If shutdown is the only event you have to worry about, then one common trick is to always set Cancel to true, do the asynchronous work, and then if the shutdown is permitted, explicitly do a shutdown at the end of that asynchronous work.
Your colleague is correct, the posted example code will most likely not work as intended.
I would suggest making ShutdownRequestedEventArgs contain something like a List<Task<bool>>, so the caller can await all the task to determine if any of them wants to cancel before shutting down. This might also include a timeout, so that some hanged task does not block the process forever. This moves the problem from the handler of the event to the caller, that hopefully is in a better position to deal with the problem.
Another possibility could be to wait for the task synchronously:
private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e)
{
var canShutdown = lifetime.CanShutdown().Result;
e.Cancel = !canShutdown;
}
But this may be dangerous in a UI program. If the ShutdownRequested is requested on the UI thread, and CanShutdown needs to execute any code on the UI thread, then the program will deadlock. And this is fairly likely if CanShutdown is not written specifically to avoid this. See configureAwait for more details .
You might also just revert to the synchronous solution. An event like ShutdownRequested does not sound like it is a good fit for an asynchronous solution. I would have expected that such an event would require a immediate response. But I do not know the background of your application or why the method was changed in the first place, so there might very well be good reasons for it.
Related
Is there any point in wrapping event assignments in anonymous async functions?
Lets say this code is in some adapter class:
public event Action<int> someAction;
The following code is in some other class that sets the action:
Example 1:
_someAdapter.someAction += async (someParameter) =>
{
await HandleAction(someParamter);
};
In this case HandleAction has async Task return type.
Example 2:
_someAdapter.someAction += HandleAction;
In this case HandleAction has async void return type.
The same question applies to button click event handlers etc. I have seen examples of both and was wondering why this gets wrapped sometimes. From my testing there doesn't seem to be any difference.
I believe that using an anonymous handler wrapper you'll be able to let the C# Garbage Collector just "unhook" and destroy the handler on this (on example 1) when it is about to be destroyed.
Event handlers outside the this (it isn't the case in your example) would have special attention and wrap it looks a good choice.
If this will live a long time and there is some possibility of you have to hook that event again you should use another strategy to avoid memory leak like ones discussed on this question
The return types differences are related to exception handling and it's ok to use an async void on top-level methods and async event handlers.
I hope it helps.
In your example, there would be likely be no noticeable difference. This is more about coding standards and best practices. In general, it is not recommended that you create 'async void' functions, if you can avoid it. The purpose of that feature is to allow async code in functions that can't change their signature (like overrides). An 'async void' function has disadvantages like swallowing exceptions and being less composable.
If, for instance, you wanted to call HandleAction from another place/method (often this is the reason for using a named method instead of an anonymous function), you would not be able to await it unless it returns Task.
Also, if HandleAction threw an exception, you would not be able to catch it if you can't await it, so you better be sure it will handle all exceptions internally.
I won't recommend to use an anonymes delegate as an event handler. The reason is that it might come to cycling references and thus the garbage collector won't be able to free up memory - so called memory leak.
Especially in the Xamarin-world, where the managed garbage collector often won't know the real size of an object, developers should have an eye on memory and allocations their applications needs.
By avoiding anonymes delegates you are able to break the cycle simple by removing the event handler assignment.
I can't comment on your adapter handlers, but... seeing as you say "The same question applies to...", the answer might be the same as for button click event handlers, etc, which is (arguably the largest benefit of async in UI applications) - you get to perform expensive async operations in the handlers off the UI thread.
E.g.
_button.Click += async (...) =>
{
int answer = await ExpensiveHttpResultThatTakes10Seconds();
_answer.Text = answer.ToString();
};
If that wasn't async, the UI would freeze up for 10 seconds, not responding to clicks, keys, etc.
Because it is async, the UI remains responsive during those 10 seconds, as the await is non-blocking, processing UI events and even other event handlers.
I have a button which has an async handler which calls awaits on an async method. Here's how it looks like:
private async void Button1_OnClick(object sender, RoutedEventArgs e)
{
await IpChangedReactor.UpdateIps();
}
Here's how IpChangedReactor.UpdateIps() looks:
public async Task UpdateIps()
{
await UpdateCurrentIp();
await UpdateUserIps();
}
It's async all the way down.
Now I have a DispatcherTimer which repeatedly calls await IpChangedReactor.UpdateIps in its tick event.
Let's say I clicked the button. Now the event handler awaits on UpdateIps and returns to caller, this means that WPF will continue doing other things. In the meantime, if the timer fired, it would again call UpdateIps and now both methods will run simultaneously. So the way I see it is that it's similar to using 2 threads. Can race conditions happen? (A part of me says no, because it's all running in the same thread. But it's confusing)
I know that async methods doesn't necessarily run on separate threads. However, on this case, it's pretty confusing.
If I used synchronous methods here, it would have worked as expected. The timer tick event will run only after the first call completed.
Can someone enlighten me?
Since both calls run on the UI thread the code is "thread safe" in the traditional sense of - there wouldn't be any exceptions or corrupted data.
However, can there be logical race conditions? Sure. You could easily have this flow (or any other):
UpdateCurrentIp() - button
UpdateCurrentIp() - Timer
UpdateUserIps() - Timer
UpdateUserIps() - button
By the method names it seems not to really be an issue but that depends on the actual implementation of these methods.
Generally you can avoid these problems by synchronizing calls using a SemaphoreSlim, or an AsyncLock (How to protect resources that may be used in a multi-threaded or async environment?):
using (await _asyncLock.LockAsync())
{
await IpChangedReactor.UpdateIps();
}
In this case though, it seems that simply avoiding starting a new update when one is currently running is good enough:
if (_isUpdating) return;
_isUpdating = true;
try
{
await IpChangedReactor.UpdateIps();
}
finally
{
_isUpdating = false;
}
I can think of a number of ways to handle this issue
1 Do not handle it
Like i3arnon says it might not be a problem to have multiple calls to the methods running at the same time. It all depends on the implementation of the update methods. Just like you write, it's very much the same problem that you face in real, multi-threaded concurrency. If having multiple async operations running at once is not a problem for these methods, you can ignore the reentrancy issues.
2 Block the timer, and wait for running tasks to finish
You can disable the timer, och block the calls to the event handler when you know you have a async task running. You can use a simple state field, or any kind of locking/signaling primitive for this. This makes sure you only have a single operation running at a given time.
3 Cancel any ongoing async operations
If you want to cancel any async operations already running, you can use a cancellationtoken to stop them, and then start a new operation. This is described in this link How to cancel a Task in await?
This would make sense if the operation takes a long time to finish, and you want to avoid spending time to complete an operation that is already "obsolete".
4 Queue the requests
If it's important to actually run all the updates, and you need synchronization you can queue the tasks, and work them off one by one. Consider adding some sort of backpressure-handling if you go down this route...
Consider this code that runs on the UI thread:
dividends = await Database.GetDividends();
if (IsDisposed)
return;
//Do expensive UI work here
earnings = await Database.GetEarnings();
if (IsDisposed)
return;
//Do expensive UI work here
//etc...
Note that every time I await I also check IsDisposed. It's necessary because say I await on a long running Task. Meanwhile the user closes the form before it completes. The Task will finish and run a continuation that attempts to access controls on a disposed form. An exception occurs.
Is there a better way to handle this or simplify this pattern? I use await liberally in UI code and it's both ugly to check for IsDisposed every time and error prone if I forget.
EDIT:
There are a few proposed solutions that don't fit the bill because they change functionality.
Prevent form closing until background tasks complete
This will frustrate the users. And it also still allows potentially expensive GUI work to occur that is a waste of time, hurts performance and is no longer relevant. In the case where I'm almost always doing background work this could prevent the form close for a very long time.
Hide the form and close it once all tasks complete
This has all the problems of preventing the form close except doesn't frustrate users. The continuations that do expensive GUI work will still run. It also adds complexity of tracking when all tasks complete and then closing the form if it's hidden.
Use a CancellationTokenSource to cancel all tasks when the form is closing
This doesn't even address the problem. In fact, I already do this (no point in wasting background resources either). This isn't a solution because I still need to check IsDisposed due to an implicit race condition. The below code demonstrates the race condition.
public partial class NotMainForm : Form
{
private readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
public NotMainForm()
{
InitializeComponent();
FormClosing += (sender, args) => tokenSource.Cancel();
Load += NotMainForm_Load;
Shown += (sender, args) => Close();
}
async void NotMainForm_Load(object sender, EventArgs e)
{
await DoStuff();
}
private async Task DoStuff()
{
try
{
await Task.Run(() => SimulateBackgroundWork(tokenSource.Token), tokenSource.Token);
}
catch (TaskCanceledException)
{
return;
}
catch (OperationCanceledException)
{
return;
}
if (IsDisposed)
throw new InvalidOperationException();
}
private void SimulateBackgroundWork(CancellationToken token)
{
Thread.Sleep(1);
token.ThrowIfCancellationRequested();
}
}
The race condition happens when the task has already completed, the form has closed, and the continuation still runs. You will see InvalidOperationException being thrown occasionally. Cancelling the task is good practice, sure, but it doesn't alleviate me from having to check IsDisposed.
CLARIFICATION
The original code example is exactly what I want in terms of functionality. It's just an ugly pattern and doing "await background work then update GUI" is a quite common use case. Technically speaking I just want the continuation to not run at all if the form is disposed. The example code does just that but not elegantly and is error prone (if I forget to check IsDisposed on every single await I'm introducing a bug). Ideally I want to write a wrapper, extension method, etc. that could encapsulate this basic design. But I can't think of a way to do this.
Also, I guess I must state performance is a first-class consideration. Throwing an exception, for example, is very expensive for reasons I won't get into. So I also don't want to just try catch ObjectDisposedException whenever I do an await. Even uglier code and also hurts performance. It seems like just doing an IsDisposed check every single time is the best solution but I wish there was a better way.
EDIT #2
Regarding performance - yes it is all relative. I understand the vast majority of developers don't care about the cost of throwing exceptions. The true cost of throwing an exception is off-subject. There is plenty of information available on this elsewhere. Suffice to say it's many orders of magnitude more expensive than the if (IsDisposed) check. For me, the cost of needlessly throwing exceptions is unacceptable. I say needless in this case because I already have a solution that doesn't throw exceptions. Again, letting a continuation throw an ObjectDisposedException is not an acceptable solution and exactly what I'm trying to avoid.
I also use IsDisposed to check the state of the control in such situations. Although it is a bit verbose, it is no more verbose than necessary to handle the situation - and it is not confusing at all. A functional language like F# with monads could probably help here - I'm no expert - but this seems as good as it gets in C#.
It should be pretty straightforward to have a CancellationTokenSource owned by your form, and have the form call Cancel when it is closed.
Then your async methods can observe the CancellationToken.
I once solved a similar issue by not closing the form. Instead, I hid it at first and only really closed it when all outstanding work had completed. I had to track that work, of course, in form of Task variables.
I find this to be a clean solution because disposal issues do not arise at all. Yet, the user can immediately close the form.
I have a Windows service that processes a private, local message queue (MSMQ). When it starts, it registers an event handler for PeekCompleted on the queue, and it then calls the asynchronous BeginPeek() to wait for a message to arrive.
protected override void OnStart(string[] args)
{
if (String.IsNullOrWhiteSpace(args[0]))
return;
queue = new MessageQueue(args[0]);
queue.Formatter = new BinaryMessageFormatter();
queue.PeekCompleted += new PeekCompletedEventHandler(OnPeekCompleted);
queue.BeginPeek();
}
Once a message arrives, my goal is to obviously process that message. My code currently has a queue.Receive() method to get the message, contained within a transaction so the message gets put back on the queue in case of errors during processing. BeginPeek() is called again to restart the cycle.
private static void OnPeekCompleted(Object source, PeekCompletedEventArgs asyncResult)
{
try
{
MessageQueue q = (MessageQueue)source;
using (MessageQueueTransaction trans = new MessageQueueTransaction())
{
trans.Begin();
Message msg = q.Receive(trans);
ProcessMessage(msg);
trans.Commit();
}
// Restart the asynchronous peek operation.
q.BeginPeek();
}
catch (MessageQueueException qEx)
{
// TODO: Walk it off.
}
return;
}
Do I, at any point, need to call EndPeek() on the queue ?
Maybe to avoid memory leaks, like this question alludes to ? I'm pretty sure I don't have to, but the documentation isn't very clear on that. It just doesn't feel 100% right to 'begin' something without 'ending' it :)
Btw: I could replace the Receive() line with Message msg = q.EndPeek(asyncResult.AsyncResult), which equally fetches me the message, but it doesn't remove the message from the queue.
Giving a proper answer to this question takes some effort, because the short answer ("no") could be misleading.
Does the API description explicitly say you must call EndPeek() for every call to BeginPeek()? Not in any topic I could find, and not only that, it appears to state the opposite here:
To use BeginPeek, create an event handler that processes the results
of the asynchronous operation, and associate it with your event
delegate. BeginPeek initiates an asynchronous peek operation; the
MessageQueue is notified, through the raising of the PeekCompleted
event, when a message arrives in the queue. The MessageQueue can then
access the message by calling EndPeek(IAsyncResult) or by retrieving
the result using the PeekCompletedEventArgs.
(Emphasis mine.) This seems to say that you can either use .EndPeek() or just directly get the message from the event args with no obligation to call .EndPeek().
Alright, so does the implementation mandate that you call .EndPeek() in order to make things work correctly? At least for the System.Messaging implementation in .NET 4.0, the answer is no. When you call .BeginPeek(), an asynchronous operation is allocated and a callback is registered for completion. The unmanaged resources associated with this operation are partially cleaned up in this callback, and only then is the event handler called. .EndPeek() does not actually do any cleanup -- it merely waits for the operation to complete if it hasn't yet, checks for errors, and returns the message. So it is indeed true that you can either call .EndPeek() or just access the message from the event args, or do nothing at all -- it will all work just as poorly.
Poorly, yes -- note that I said "partially cleaned up". The implementation of MessageQueue has a problem in that it allocates a ManualResetEvent for every asynchronous operation, but never disposes it, leaving this entirely up to the garbage collector -- something .NET developers are often excoriated for doing, but of course Microsoft's own developers aren't perfect either. I haven't tested whether the OverlappedData leak described in this question is still relevant, nor is it immediately obvious from the source, but it would not surprise me.
The API has other warning signs that its implementation may leave something to be desired, most prominently that it does not follow the established .Begin...() / .End...() pattern for asynchronous operations but introduces event handlers in the middle, producing a strange hybrid I've never see anywhere else. Then there's the very dubious decision of making the Message class inherit from Component, which adds considerable overhead to every instance and raises the question of when and how it should be disposed... all in all, not Microsoft's best work.
Now, does all this mean you're not "obliged" to call .EndPeek()? Yes, in the sense that calling it or not calling it makes no functional difference with regards to resource cleanup or correctness. But with all that said, my advice is still to call it anyway. Why? Because anyone who is familiar with how the asynchronous operation pattern works in other .NET classes would expect the call to be there, and not putting it there looks like a bug that could lead to resource leaks. If there is a problem with the application, such a person might reasonably spend some fruitless effort looking at "problem code" that isn't. Given that a call to .EndPeek() has negligible overhead compared to the rest of the machinery, I'd say the savings in programmer surprise more than make up for the costs. A possible alternative is to insert a comment instead, explaining why you're not calling .EndPeek() -- but in all probability this still takes more programmer cycles to grasp than just calling it.
In theory, another reason for calling it is that the semantics of the API could change in the future to make the call to .EndPeek() necessary; in practice, this is very unlikely to happen because Microsoft is traditionally reluctant to make breaking changes like this (code that previously and reasonably did not call .EndPeek() would stop working) and the existing implementation already contravenes established practice.
I'm aware that the best practice is to avoid async void methods for anything but async event handlers, and there is quite strong expert opinion against other use cases. I was however just involved in a brief discussion about usefulness of async void methods and I've got a couple of questions:
How does the Framework keep track of pending async void methods, including event handlers? Is there possibly a way to obtain the current list of them or cancel them (EDITED: tracking is probably possible by installing a custom SynchronizationContext)?
Are they any useful for fire-and-forget logging scenarious? I think they actually may be, as long as the correct time-stamp is preserved at the beginning of the method, while it still executes synchronously.
How does the Framework keep track of pending async void methods, including event handlers?
The framework doesn't do anything special to keep track of async void methods. They're just like any other async method.
Also, your method either has a proper signature or it doesn't; event handlers do not care and have no logic to detect or work with async specifically.
A custom scheduler would be able to keep track of running tasks, but not have any specific knowledge if one is from an async void method or not. I don't think this is the right solution anyway -- if you find yourself needing to keep track of an async void method, you need to rethink your design.
Are they any useful for fire-and-forget logging scenarios? I think they actually may be, as long as the correct time-stamp is preserved
I don't know what you mean by a timestamp?
Async void is fine for any method where the caller will either never need to care about the result of the method call, or where it is being notified of the result elsewhere. These cases should be very exceedingly rare.
Fire-and-forget might be one such scenario, though I feel people often misuse fire-and-forget and end up just hiding important bugs from themselves.
Regarding logging scenarios, here are two scenarios, with async-void being suitable for the first but less suitable for the second.
1) Logging the outcome of a long-running operation:
public static async void LogCompletion(Task operation, string title)
{
try
{
await operation.ConfigureAwait(false);
Log.Info($"{title} completed succesfully");
}
catch (Exception ex)
{
Log.Error($"{title} failed", ex);
}
}
This usage resembles an async event handler, since the completion of an asynchronous operation is conceptually similar to the raising of an event. So this method essentially "handles" the completion "event" of a specific task. Converting this async-void method to an async Task LogCompletionAsync method wouldn't bring many benefits. It is true that an exception inside the LogCompletion method will crash the process, but the only possibility for an exception to occur is if the Log.Error throws. But if your logging framework starts throwing exceptions, your application is not going to stay alive for long anyway. And the sooner you learn about it the better, to start searching ASAP for a better logging framework.
2) Logging per se:
public static async void Log(string message)
{
try
{
await File.AppendAllTextAsync(GetLogFilePath(),
DateTime.Now.ToString() + " " + message + "\r\n");
}
catch { }
}
This usage resembles calling an asynchronous method in a fire-and-forget fashion. Although it's not a terrible usage of async-void, it is a quite primitive and unsophisticated way of implementing logging in general. And it is quite inadvisable to try implementing it in the first place, since there are many high-quality implementations out there, available for free.
Are they any useful for fire-and-forget logging scenarious?
Conceptually, I would say so, but if a task has an exception and it isn't being handled by waiting on it or accessing its Exception property which doesn't happen in an async void method it will tear down your application.
So I would avoid that.