Defensive event publishing in .NET – Part I
This article was inspired by Juval Lowy’s Advanced .NET master class (Copyrights © IDesign 2005) and contains code excerpts from Programming .NET Components (1st Edition) By O’Reilly 2003.
Summary
Events and delegates in the .NET Framework present an interesting case of apparent simplicity with abundant complexity underneath. Unfortunately, we *have* to take into account the complexity underneath if we are to use events correctly in our applications. What I’m suggesting here is that most developers today are *not* raising events correctly. Even most of the “advanced” developers out there are not doing all the necessary steps in order to make sure the events are published in a safe manner.
The case of the innocent coder
Take the following code as a starting point:
public event EventHandler Clicked;
private void FireClickedEvent()
{
Clicked(this,EventArgs.Empty);
}
“This isn’t good code” most of you will say immediately. “You’re not checking to make sure the event is not null. And that’s true. An event with no subscriber is set to null by the runtime. That means that running this code will result in an exception. Consider the following code then:
private void FireClickedEvent()
{
if(Clicked!=null)
Clicked(this,EventArgs.Empty);
}
Attack of the event subscribers
“That’s better”, most of you will say and continue on your merry way as if the code is fine and life is good. But, are they really? Picture a state in which there are 10 event subscribers in your application. By design, your event is raised to each of them on the same thread. What happens if the 3rd subscriber receives the event and throws an exception? Yep. You event will stop bubbling to the rest of the subscribers. That means that 7 clients will not receive your event and blow you up in the process. So what do you need to do? You need to manually go into the list of subscribers that your event will publish to and iterate over each one, manually activating the event for each of them and doing all this inside a try-catch block. To get into the subscriber list you go into your event’s “Invocation list”. An event is simply a delegate which is decorated with the “event” keyword. All delegates have an invocation list which exposes the list of subscribers. Here’s how the code looks now:
private void FireClickedEvent()
{
if(Clicked != null)
{
Delegate[] list = Clicked.GetInvocationList();
foreach (Delegate del in list)
{
try
{
EventHandler handler = (EventHandler)del;
handler(this, EventArgs.Empty);
}
catch { }
}
}
}
Refactoring: Extract Utility Class
Better. But now consider the amount of code you need to write for each event firing code. What’s missing here is a more generic way of throwing the events. Let’s make an EventsHelper class that fires the event for us. To do that we need the ability to invoke any delegate in a generic way, regardless of the parameters that it expects.
Help arrives in the form of Delegate.DynamicInvoke(). Dynamic invocation allows us to send any parameters into a delegate invocation. The upside is that now you can write generic code that looks like this:
public class EventsHelper
{
public static void Fire(Delegate del,params object[] args)
{
if (del == null)
{
return;
}
Delegate[] delegates = del.GetInvocationList();
foreach (Delegate sink in delegates)
{
InvokeDelegate(sink,args);
}
}
private static void InvokeDelegate(Delegate sink,object[] args)
{
try
{
sink.DynamicInvoke(args);
}
catch
{}
}
}
Which means that now we can write code that looks like this in our original class:
private void FireClickedEvent()
{
EventsHelper.Fire(Clicked,this,EventArgs.Empty);
}
But there’s a downside as well: invoking the delegate just became a non-type safe activity. That means that the user of the events helper may be able to send in the wrong parameters in code to invoke an event, but the compiler won’t complain about this. We’ll see how we solve this problem in .NET 2.0 when we discuss generics in part II of this article. For now, we have even more important potential issues we need to worry about.
Return of the killer event subscribers
Consider the earlier scenario of multiple subscribers. Now imagine that no subscriber throws an exception, but instead, now the 3rd subscriber does very lengthy processing. Since all events are sent in a synchronous manner, the 4th subscriber and so on may receive the events in a delay which may render the event meaningless (for example, events that mark the passing of a single second but which take 3 seconds to process).
The way to overcome this is of course to invoke the delegate asynchronously on a thread different than the calling thread. That way we don’t care how long it takes for each event subscriber to process the event, we just keep going through our invocation list without worries, never blocking for any subscriber. To that end we can use the delegate’s BeginInvoke() method,
which automatically will invoke the delegate on a different thread. The plan: We’re still going to call the “InvokeDelegate” method, only we’re going to use our own delegate to call it from within EventsHelper, and we’re going to call it asynchronously for each of the delegates in the invocation list (changed lines are highlighted):
public class EventsHelper
{
delegate void AsyncInvokeDelegate(Delegate del, params object[] args);
public static void FireAsync(Delegate del, params object[] args)
{
if (del == null)
{
return;
}
Delegate[] delegates = del.GetInvocationList();
AsyncInvokeDelegate invoker = new AsyncInvokeDelegate(InvokeDelegate);
foreach (Delegate sink in delegates)
{
invoker.BeginInvoke(sink,args,null,null);
}
}
The BeginInvoke method of a delegate will automatically run the target method on a thread from the thread pool. Notice the last two parameters. These are used in cases where we want to be able to get a callback when the target method finishes execution. Since in this scenario we don’t really care (the whole idea of firing events is “fire and forget”) we’ll leave these alone to specific articles on those issues. For now, this will suffice in letting us raise events in an asynchronous way.
Are we done yet?
The answer is, of course, “Hell no!”.
The case of the hidden resource leak
One of the things we need to watch out for is resource leaks. It’s a little known fact that whenever you invoke a delegate asynchronously, it keeps aside some information about the call, arguments, errors, returned value, signaling event, state and so on, for purposes of callback and other things. Consider what’s going to happen if our application calls the EventsHelper 5 million times a day for a month, without restarting. Can you guess what will happen to the computer’s memory and resources?
Because we don’t really care for all that saved information, we can make the delegate ignore it and not save it, by placing a [OneWay] attribute on the method that the delegate invokes. We can’t trust our event subscribers to do this, can we? Luckily, we already have a method which is a perfect candidate for this attribute – you guessed it – the “InvokeDelegate” method. Because we call it asynchronously using our own internal delegate, we can simply signal that method as [OneWay] and be done with it:
[OneWay]
private static void InvokeDelegate(Delegate sink,object[] args)
{
try
{
//execute delegate invocation
Are we done already? Not really, but we’ve sure come a long way. We now have a generic class now that you can use in your applications. You can download the full version of this class from the IDesign website here (along with many other useful code snippets and solutions!)
Unfortunately, I’ll have to stop this article mid way; the reason being that some of the stuff that was shown in Juval’s class was excerpted from his new book, “Programming .NET Components, 2nd edition”, that which will only be published in April 2005. Since there are still copyright issues in showing that intellectual property, part II of this article will have to wait until then.