RSS 2.0

Personal Info:

Joe Send mail to the author(s) is the lead developer and architect for Parallel Extensions to .NET, tinkers with type systems, and is an author and speaker.

Blogroll:
Other
News
 C|Net
 Kuro5hin
 The Register
Technology
 <?xmlhack?>
 Daily WTF
 DevX
 Hacknot
 Java Today
 Microsoft Top 10 Downloads
 MSDN
 MSDN: "Longhorn"
 MSDN: XML Developer Center
 Slashdot
 Techdirt
 theserverside.com
 W3C
 Web Pages That Suck
 XML Cover Pages
 XML Journal
 xml.com
Technology Blogs
 Aaron Skonnard [PluralSight]
 Adam Bosworth [Google]
 Andy Rich [MS/C++]
 Arpan Desai [MS/XML]
 BCL Team [MS]
 Bill Clementson [Lisp]
 Bill de hÓra
 Bruce Eckel [J]
 Bruce Tate [J]
 Casey Chestnut
 Cedric Beust [Google]
 Chris Anderson [MS/Avalon]
 Chris Lyon [MS]
 Christian Weyer
 Clemens Vasters [newtelligence]
 Craig Andera [PluralSight]
 Dan Sugalski [Parrot]
 Daniel Cazzulino
 Dave Chappel
 Dave Roberts [Lisp]
 Dave Thomas [PragProg]
 Dave Winer
 Dion Almaer [J]
 Don Demsak
 Doug Purdy [MS/Indigo]
 Drew Marsh
 Eric Gunnerson [MS]
 Eric Rudder [MS]
 Eric Sink
 Fritz Onion [PluaralSight]
 Gavin King [J/Hibernate]
 Grady Booch [IBM]
 Hervey Wilson [MS/Indigo]
 Hillel Cooperman [MS/Shell]
 Howard Lewis Ship [J/Apache]
 Ingo Rammer [PluralSight]
 James Gosling [J/Sun]
 James Strachan [J/Groovy]
 Jason Matusow [MS/OSS]
 Jeffrey Schlimmer [MS/Indigo]
 Joe Beda [Google]
 Joel Spoelsky
 Jon Udell
 Josh Ledgard [MS/Evang]
 Joshua Allen [MS]
 Lambda
 Larry Osterman [MS]
 Maoni Stephens [MS/CLR]
 Mark Fussell [MS/XML]
 Martin Fowler
 Martin Gudgin [MS/Indigo]
 Me
 Michael Howard [MS]
 Miguel de Icaza [Mono]
 Mike Clark
 Omri Gazitt [MS/Indigo]
 Pat Helland [MS/PAG]
 Pinku Surana
 Raymond Chen [MS]
 Rich Lander [MS/CLR]
 Rob Howard
 Rob Relyea [MS/Avalon]
 Robert Cringely
 S. Somasegar [MS/DevDiv]
 Sam Gentile
 Scoble [MS/Evang]
 Scott Guthrie [MS/WebNet]
 Scott Hanselman
 Sean McGrath [J]
 Simon Fell
 Stanley Lippman [MS/C++]
 Steve Maine
 Steve Swartz [MS/Indigo]
 Steve Vinoski
 Steven Clarke [MS/Usability]
 Stuart Halloway
 Ted Leung
 Ted Neward [DM]
 Tim Bray [Sun]
 Tim Ewald [Mindreef]
 Tim O'Reilly
 Werner Vogels [Amazon]
 Wintellect
 Yasser Shohoud [MS/Indigo]
Top 20
 Brad Abrams [MS/CLR]
 Chris Brumme [MS/CLR]
 Chris Sells [MS/Ultra]
 Cyrus Najmabadi [MS/C#]
 Dominic Cooney [MS/XAF]
 Don Box [MS/Ultra]
 Don Syme [MS/R]
 Guido van Rossum [Python]
 Herb Sutter [MS/C++]
 Ian Griffiths
 Jason Zander [MS/CLR]
 Jim Hugunin [MS/CLR]
 Joel Pobar [MS/CLR]
 Krzysztof Cwalina [MS/CLR]
 Patrick Logan
 Paul Graham
 Rico Mariani [MS/CLR]
 Rory Blyth [MS/DN]
 Sam Ruby
 Wesner Moise
VC/Business Blogs
 Ed Sim
 Fred Wilson
 Jonathan Schwartz [J/Sun]
 Lawrence Lessig [Stanford]
 Mark Cuban
 Michael Hyatt
 Pierre Omidyar
 Ross Mayfield
 VentureBlog
 Weekly Read
Wine, Food & Tea
 The Silk Road of Wine
 Vinography: a wine blog
 Wine Whys

Disclaimer:
The content of this site are my own personal opinions and do not represent my employer's view in anyway.

© 2009, Joe Duffy

 
 Monday, February 28, 2005

Based on a recent question from Brad, I decided to write up a little post on the topic of event handlers. Specifically, that objects registered to receive events are rooted, meaning that they won't be eligible for GC even if you drop them on the floor. Through some trickery with weak references, you can work around this (if you need to).

So, what's the issue?

Once you register an object as a sink for events in C#, the source object generating the events will keep a reference to it. When the source becomes unreachable, your sink will also become unreachable (assuming that's the only live reference to the sink). An alternative way to make your sink unreachable is to manually unregister it, asking that the source give up its reference. But this means that until either one of these things happen, your object won't be eligible for garbage collection. This probably won’t be surprising for most folks, but for finalizable objects which own resources it does mean you need to be very careful to unregister your sink when it needs to die. Even for objects which don't hold on to such resources, you might be building up a list of unused objects after a while, akin to stashing away references in a collection and forgetting to prune it when you're done.

For example, given source and sink set of types:

class Source

{

    public event EventHandler<EventArgs> Tick;

 

    public void OnTick()

    {

        EventHandler<EventArgs> t = Tick;

        if (t != null)

            t(this, new EventArgs());

    }

}

 

interface ISink

{

    void HandleTick(object sender, EventArgs e);

}

And client code which defines a sink implementation:

class MySink : ISink, IDisposable

{

    private IntPtr someHandle;

 

    static int counter;

    int id = counter++;

 

    public MySink()

    {

        // create someHandle

    }

 

    ~MySink()

    {

        // clean up someHandle

        Console.WriteLine("!{0} finalized", id);

    }

 

    public void Dispose()

    {

        // clean up someHandle

        GC.SuppressFinalize(this);

        Console.WriteLine("~{0} disposed", id);

    }

 

    public void HandleTick(object sender, EventArgs e)

    {

        // use someHandle

        Console.WriteLine("{0}: Received", id);

    }

}

The owner of an instance of MySink might not realize what consequences registering the sink for messages will have, namely that it will cause it to become rooted. For example, this simple code does the trick:

Source src = /*...*/;

MySink sk = new MySink();

src.Tick += sk.HandleTick;

Given that this type owns unmanaged resources, the user really needs to remember to unregister it when they’re done with it.

And this is a problem because…?

This actually seems like it's the desired/intended behavior for somebody registering for events in most cases. Frequently, a sink object will be created, registered for an event, and then its reference dropped and left on its own to process the incoming messages. I.e. its only purpose in life is to respond to events. It might seem surprising that somebody would register an object and forget to ever unregister it, but I suspect this happens all the time. This is one of those subtle bugs which could manifest over time in a long running application (e.g. a web app), where unmanaged memory just continues to rise and rise until ASP.NET recycles itself. Especially if there’s some AppDomain-wide event registration and routing mechanism.

So, for those situations where we don’t want the event source to keep us alive, what are we to do?

Enter “Weak Event Handlers”

We have this nifty feature called weak references (exposed by the WeakReference class), enabling you to hold on to a reference without keeping it pinned from GC collection. If there are no strong references to an object and the GC runs, it'll sweep these up. This means that the underlying object referred to by the weak reference won't be valid any longer. (For those from the Java camp, be careful—one of the first mistakes I made when moving to the CLR was thinking that weak references == soft references. Soft references are only collected as a last resort, making them perfect for caching. Weak references are always collected if no strong references exist at the time of a GC, so you might have to think carefully about using them. This is one great reason to avoid GC.Collect() in production applications (among many others).)

We can go ahead and use this feature to wrap up our event handler delegates. Then we just hand the wrapper instead of the raw delegate off to event registration, thus preventing the event source from keeping our object alive. So, let’s get started.

First we introduce a general purpose WeakEventHandler<T> class:

class WeakEventHandler<T> where T : EventArgs

{

 

    private WeakReference weakRef;

    private EventHandler<T> handler;

 

    public WeakEventHandler(EventHandler<T> d)

    {

        weakRef = new WeakReference(d);

        handler = new EventHandler<T>(Invoke);

    }

 

    public EventHandler<T> Handler

    {

        get { return handler; }

    }

 

    private void Invoke(object sender, T e)

    {

        EventHandler<T> d = (EventHandler<T>)weakRef.Target;

        if (d != null)

            d(sender, e);

        else

            // hmm! what to do here?

    }

 

}

So now somebody can construct a weak event handler, and register that instead. Once the target of the delegate loses its roots, it’s eligible for GC. For example, the three line snippet from above becomes:

Source src = /*...*/;

MySink sk = new MySink();

src.Tick += new WeakEventHandler<EventArgs>(sk.HandleTick).Handler;

(Note that I initially wanted to subclass Delegate to make a WeakDelegate type, and simply override the internal storage in order to make a general purpose “weak delegate.” Unfortunately, Delegate and MulticastDelegate are treated specially by the CLI (see Partition II), meaning that I couldn’t do this straightforwardly. I’m going to look into it further, but I’m not sure exactly what this would entail.)

But now won't my WeakEventHandler stay alive?

Yep, it does. This is admittedly leaps and bounds better than keeping a finalizable object rooted, but we can probably clean things up a little. Notice the “// hmm! what to do here?” line above?—we can actually put some code in here that intelligently unregisters the weak handler from the source. It requires a bit of generalization, but here’s a shot at it. (Note that this approach requires an event to be triggered to initiate the cleanup, so for events with particularly long delays in between instances, this might not be a great solution.) 

First, let’s update our WeakEventHandler<T> class to accept an unregister delegate. This will be invoked in cases where our underlying object has been collected, and hence the weak wrapper should be removed from the event source’s registration list; from this point forward, our previous code just ignores all future requests to fire the event anyhow:

delegate void UnregisterHandler<T>(WeakEventHandler<T> handler) where T : EventArgs;

 

class WeakEventHandler<T> where T : EventArgs

{

 

    private WeakReference weakRef;

    private EventHandler<T> handler;

    private UnregisterHandler<T> unregister;

 

    public WeakEventHandler(EventHandler<T> d) : this(d, null)

    {

    }

 

    public WeakEventHandler(EventHandler<T> d, UnregisterHandler<T> u)

    {

        weakRef = new WeakReference(d);

        handler = new EventHandler<T>(Invoke);

        unregister = u;

    }

 

    public EventHandler<T> Handler

    {

        get { return handler; }

    }

 

    private void Invoke(object sender, T e)

    {

        EventHandler<T> d = (EventHandler<T>)weakRef.Target;

        if (d != null)

            d(sender, e);

        else if (unregister != null)

            unregister(this);

    }

 

}

Then, we just update our source to have an Unregister method. As a matter of fact, now that we have this weak handler goop, let’s encapsulate it inside a new Register method. This means that (unfortunately) we won’t be using the C# syntax for event registration, but compared with having users grok the weak handler mechanism, I personally find it much more palatable:

class Source

{

    public event EventHandler<EventArgs> Tick;

 

    public void OnTick()

    {

        EventHandler<EventArgs> t = Tick;

        if (t != null)

            t(this, new EventArgs());

    }

 

    public void Register(ISink sk)

    {

        Tick += new WeakEventHandler<EventArgs>(sk.HandleTick, Unregister).Handler;

    }

 

    private void Unregister(WeakEventHandler<EventArgs> eh)

    {

        Tick -= eh.Handler;

    }

}

Now the three lines of code change to:

Source src = /*...*/;

MySink sk = new MySink();

src.Register(sk);

Should I actually do this?

Just because you can use this doesn't mean you should. :) It seems like a reasonable strategy to make sure you don't shoot yourself in the foot too badly, but if you're relying on weak wrappers to do the unregistration and cleanup for you, it seems like all you're really doing is working around bugs. This is a bit like forgetting to call Dispose on an object and having a finalizer kick in just to make sure you don't leak. Further, you probably shouldn't use this if all you do is register for an event and drop the reference. In this case, the GC could kick in as early as the nanosecond after you drop the reference, meaning your registration was useless.

With that said, I think it's perfectly reasonable to use this to debug and detect bugs in your code. For example, if you fired an assert instead of unregistering the weak handler, you might be able to detect where in your program you forget to unregister sink objects. Just some thoughts. I'd love to hear any feedback on where you might find this useful.

Testing, Testing, 1, 2, 3...

This little snippet of test code demonstrates the whole thing. Here’s the full source in case anybody wants to play around with it without having to scour this post to pull together all the code.

class Program

{

 

    static void Main()

    {

        Program p = new Program();

        p.Execute();

    }

 

    private Source src = new Source();

 

    public void Execute()

    {

        for (int j = 0; j < 1000; j++)

        {

            for (int i = 0; i < 1000; i++)

            {

                src.Register(new MySink());

            }

 

            src.OnTick();

            GC.Collect();

        }

    }

 

}