Building a Leak-Proof Eventing Model

One of the main features of the .NET Framework is to provide automatic memory management via garbage collection.  You might think this would solve the problem of memory leaks in managed applications, but the effectiveness of the garbage collector can be hampered by your code.  The GC will not release memory from objects that have root references, which can be local or static variables.  If you maintain these references, the GC will not collect them.  If you create more instances and keep references to them, memory consumption will continue to grow, resulting in a leaky application.

Note: You can download the code for this blog post here.

Sometimes it’s not so apparent exactly where you’re holding a reference.  This is the case with delegates, which are basically type-safe function pointers. If you fire up Reflector and take a look at a delegate definition, such as Action or EventHandler, you’ll see that the constructor takes two parameters.  The second is a native integer that basically points to the memory address of the target method.  But the first parameter is a System.Object pointing to the instance that owns the target method. (It’s set to null when pointing to a static method.)

public class System.EventHandler : Delegate
{
    public EventHandler(object @object, IntPtr method) { }
}

In other words, a reference to the delegate is actually a reference to the instance where the target method is defined.  If an event publisher holds the delegate reference, then you can’t simply set the subscriber instance to null and expect it to be garbage collected.  The best thing would be for the subscriber to unregister from the event before it gets disposed.  But that means having to implement IDisposable or, worse yet, implementing a finalizer.  This is error-prone and far from fool-proof.  Trust me, you don’t want to go there.

hard-del

What we need is a way for a publisher to expose an event while allowing subscribers to be garbage collected.  Enter the WeakReference, which is a reference that won’t prevent the referenced object from being garbage collected.  A publisher can weakly reference an event subscriber and only fire the event if the subscriber has not been garbage collected, which it can verify by checking the IsAlive property of the weak reference.

The question is, how do you get a reference to the subscriber’s delegate without referencing the delegate?  One idea is to have a weak reference to the delegate itself.  But this won’t work because there’s nothing to prevent the delegate from being garbage collected, even if the subscriber it points to is still alive.

weak-del

If the publisher weakly references the delegate and the delegate gets garbage collected, there’s no way for the publisher to fire the event.  If the publisher weakly references the subscriber, but maintains a hard reference to the delegate, the subscriber won’t be garbage collected when it needs to be, resulting in a potential memory leak.

One solution is for the publisher to weakly reference the subscriber and re-create the delegate when it needs to fire the event.  That way, the subscriber can be collected, because the publisher does not reference the delegate, but it can fire the target method by getting a pointer to it only if the subscriber is still alive.

create-del

Here is what that code would look like:

// Note: Don’t use this code with Silverlight
class WeakDelegate
{
    private readonly WeakReference subscriber;
    private readonly string methodName;
    private readonly Type delegateType;

    public WeakDelegate(Delegate targetMethod)
    {
        this.subscriber = new WeakReference(targetMethod.Target);
        this.methodName = targetMethod.Method.Name;
        this.delegateType = targetMethod.GetType();
    }

    public bool IsAlive
    {
        get { return subscriber != null && subscriber.IsAlive; }
    }

    public Delegate GetDelegate()
    {
        if (IsAlive)
        {
            return Delegate.CreateDelegate(delegateType, 
                subscriber.Target, methodName);
        }
        return null;
    }
}

In full .NET (and consequently WPF) this code works like a charm.  However, if you were to try it on Silverlight or Windows Phone, you’ll soon be banging your head against a wall (at least figuratively).  Those flavors of .NET put some restrictions on Delegate.CreateDelegate.  First of all, the target method needs to be public or you’ll get a MethodAccessException.  It has to be of form Action or Action<T> or you’ll get a MissingMethodException.  It has to be an instance method or you’ll get either a MissingMethodException or an ArgumentException. Yikes!

While it may seem like we’re between a rock and a hard place, there is a solution.  The key is for the publisher to use a proxy to fire events on its behalf.  The proxy, which has delegates referencing the subscriber, is referenced weakly by the publisher and strongly by the subscriber.  The subscriber can be garbage collected because the delegate pointing to it is only referenced by the subscriber itself.  Because the subscriber has a reference to the publisher proxy, the delegate won’t be garbage collected until the subscriber is. Best of all, this approach works across the board with full .NET / WPF, Silverlight and Windows Phone. Yay!

proxy-del

So what does the code look like for this pattern?  First, we will need an interface that contains events that the subscriber can subscribe to.  We’ll call it IPublisher.

interface IPublisher
{
    event EventHandler SomeEvent;
}

Notice we’re exposing an event of type EventHandler.  This is just an example.  You can expose any number of events that suit your fancy, including various forms of EventHandler<T>.  For illustration purposes I’m trying to keep it simple.  The subscriber accepts an IPublisher to its constructor so that it can subscribe to the event as it normally would.

class Subscriber
{
    IPublisher publisher;

    public Subscriber(IPublisher publisher)
    {
        this.publisher = publisher;
        publisher.SomeEvent += OnSomeEvent;
    }

    private void OnSomeEvent(object sender, EventArgs e)
    {
        Console.WriteLine("SomeEvent was fired");
    }
}

There’s nothing unusual here.  Now for the publisher proxy.  For that we’ll need another interface: IPublisherProxy, which extends IPublisher by adding a method that fires the event.  If you added more events to IPublisher, you would add corresponding methods to IPublisherProxy to fire those events.

interface IPublisherProxy : IPublisher
{
    void FireEvent(object sender, EventArgs e);
}

The subscriber needs to expose an IPublisherProxy to the publisher, so that it can weakly reference it and also call FireEvent.  For that we need yet another interface that has a property of type IPublisherProxy.  We’ll call it ISubscriber.

interface ISubscriber
{
    IPublisherProxy PublisherProxy { get; }
}

The subscriber will need to implement this interface and maintain a hard reference to the publisher proxy so that it will only be garbage collected when the subscriber is GC’d.  Implementing it explicitly will be cleaner because only the publisher is interested in it.

class LeakProofSubscriber : ISubscriber
{
    IPublisher publisher = null;

    public LeakProofSubscriber(IPublisher publisher)
    {
        // Subscribe to events
        this.publisher = publisher;
        this.publisher.SomeEvent += OnSomeEvent;
    }

    // Explicitly implement IPublisherProxy
    IPublisherProxy publisherProxy = new PublisherProxy();
    IPublisherProxy ISubscriber.PublisherProxy
    {
        get { return publisherProxy; }
    }

    private void OnSomeEvent(object sender, EventArgs e)
    {
        Console.WriteLine("SomeEvent was fired");
    }
}

Now the fun begins: building a leak-proof publisher.  First we’ll need a List<WeakReference> with weakly referenced publisher proxies.  Then, in order to add proxies, we implement the event manually by providing our own add and remove constructs.  The compiler simply translates these to Add and Remove methods that accept a delegate.  We’ll pull out the subscriber from the Target property of the delegate, cast it to ISubscriber, read the PublisherProxy property, then add or remove the incoming delegate to its event.  Lastly, we’ll add or remove the proxy from our list of weak references.  When we want to fire the event, we simply check to see if the proxy is alive and then call FireEvent.

class LeakProofPublisher : IPublisher
{
    // List of weakly referenced proxies
    private List<WeakReference> weakProxies 
        = new List<WeakReference>();

    // Subscribers to add and remove themselves from subscriptions
    public event EventHandler SomeEvent
    {
        add
        {
            // Get subscriber
            ISubscriber subscriber = value.Target as ISubscriber;
            if (subscriber != null)
            {
                // Add handler to proxy
                IPublisherProxy proxy = subscriber.PublisherProxy;
                proxy.SomeEvent += value;

                // Add to list of weakly referenced proxies
                weakProxies.Add(new WeakReference(proxy));
            }
        }
        remove
        {
            // Get subscriber
            ISubscriber subscriber = value.Target as ISubscriber;
            if (subscriber != null)
            {
                // Remove handler from proxy
                IPublisherProxy proxy = subscriber.PublisherProxy;
                proxy.SomeEvent -= value;

                // Remove from list of weakly referenced proxies
                for (int i = weakProxies.Count - 1; i >= 0; i--)
                {
                    var weakProxy = weakProxies[i];
                    var existing = GetProxy(weakProxy);
                    if (existing != null && 
                        object.ReferenceEquals(existing, proxy))
                        weakProxies.Remove(weakProxy);
                }
            }
        }
    }

    // Ask proxy to fire event
    public void FireEvent()
    {
        foreach (var weakProxy in weakProxies)
        {
            var proxy = GetProxy(weakProxy);
            if (proxy != null)
            {
                proxy.FireEvent(this, EventArgs.Empty);
            }
        }
    }

    private IPublisherProxy GetProxy(WeakReference weakProxy)
    {
        if (weakProxy != null && weakProxy.IsAlive)
        {
            IPublisherProxy proxy = weakProxy.Target 
                as IPublisherProxy;
            if (proxy != null)
            {
                return proxy;
            }
        }
        return null;
    }
}

There you go!  I didn’t say the solution would be easy, but the problem is solvable.  The nice part is that, while the publisher does the heavy lifting, the only extra code that the subscriber needs is the PublisherProxy property.  However, because we implement it explicitly, it doesn’t pollute the subscriber’s appearance.  You can download the full source code here.

Although I wouldn’t recommend making all your events leak-proof in this manner, there are scenarios where making events leak-proof is an essential requirement.  An example would be a message bus (also called an event aggregator or event mediator), which I’ve incorporated into my Simple MVVM Toolkit (version 2 will support leak-proof events and drop the requirement to unregister for messages).  You wouldn’t want subscribers to not get garbage collected simply because they registered to received messages and failed to unregister.

As you can see, weak events can be a thorny problem to solve.  Hopefully this approach will help if you have the need for them.  Happy coding!

About Tony Sneed

Sr. Software Solutions Architect, Hilti Global Application Software
This entry was posted in Technical and tagged , , . Bookmark the permalink.

11 Responses to Building a Leak-Proof Eventing Model

  1. ronidaho says:

    Does this not depend on the subscription being made straight after the LeakProofPublisher is created? If the LeakProofPublisher is created and there are no subscriptions until after one or more garbage collections have occurred, the PublisherProxy might be garbage collected and any subsequent attempt to create a subscription will fail.

    • Tony Sneed says:

      @ronidaho: Thanks for your question. Let me see if I can clarify the relation between the publisher, publisher proxy and subscriber. Each subscriber has its very own copy of a publisher proxy, so the lifetime of the proxy is tied to that of the subscriber in which it lives. Because a subscriber creates its own publisher proxy and has a hard reference to it, there is no way a proxy can be garbage collected so long as the subscriber is alive. When a subscriber wants to subscribe to an event exposed by the publisher, it hands the publisher its proxy, and the publisher adds the subscriber’s callback method (OnSomeEvent) to the proxy’s delegate (SomeEvent). When the publisher fires the event, it goes through its list of weakly referenced proxies and, if they have not been garbage collected, calls the FireEvent method on the proxy, which in turn calls back the subscriber’s OnSomeEvent method. Creating additional subscriptions will not fail because the subscriber is the one that owns its publisher proxy. The proxies are alive because the subscriber is alive. if the subscriber gets garbage collected it’s not possible for it to subscribe to anything. I hope this explanation answers your question. Please let me know if I can explain it better. 🙂

      Cheers,
      Tony

  2. Silent Winter says:

    If you require extra coding work for the subscriber, why don’t you just use Microsoft’s Weak Event Pattern?
    http://msdn.microsoft.com/en-us/library/aa970850.aspx
    (assuming that you are using .NET 3.0 or later)

    • Tony Sneed says:

      @Silent Winter: The Weak Event Pattern you mention is a feature of WPF (full .NET) and is not, to my knowledge, compatible with Silverlight. Hence, the need for my approach. 🙂

      Tony

  3. Richard says:

    Small grammar correction:
    First of all, the target method need to be public or you’ll get a MethodAccessException
    You need an “s” on the end of “need”

  4. mitkodi says:

    I think* it could be simplified and the only what we need could be an ISubscriber interface exposing a List property where publishers could inject strong references to passed Delegate in their Event’s add implementations. Moreover we can make a WeakEvent class to do all the publisher’s work, so the only extra thing that a publisher has to do could be something like this:

    // normal event
    public event EventHandler TestEvent;

    private WeakEvent _weakTestEvent;
    // memory leak-proof event
    public event EventHandler WeakTestEvent {
    add {
    _weakTestEvent.Event += value;
    }
    remove {
    _weakTestEvent.Event -= value;
    }
    }

    * If I’m not wrong I succeeded to write an example of using only an ISubscriber interface exposing list of delegates and a WeakEvent class implementing all of the publisher’s work.

  5. mitkodi says:

    … in addition to may previous post, if the Target is a DependencyObject we don’t need to be an ISubscriber, because we can inject the mentioned list (LIst) through an attached dependency property. This can be usefull when we don’t have control over the subscriber – for example in command binding where subscribers to ICommand.CanExecuteChanged are ButtonBase controls.

  6. mitkodi says:

    … sorry, I didn’t notice the problem with < >, so “List” = “List<Delegate>”

  7. Tony Sneed says:

    @mitkodi: If you send me your code, I’ll be glad to have a look at it: tony@tonysneed.com. The important thing is that the proxy that has a delegate only be referenced by the subscriber, effectively tying the lifetime of the delegate to that of the subscriber. The publisher weakly references the proxy, so that the publisher does not prevent the proxy (and the delegate it contains) from getting garbage collected. I’m curious to see if your proposed design meets these requirements.
    Cheers,
    Tony

  8. Art Majors says:

    A simpler solution is to ensure the Action contains no references to the subscriber. We can do that by ensuring it is a static method or a lambda (more conveniently) with no associated closure. This can be enforced by throwing an exception if action.Method.IsStatic is false.

    public class WeakAction : WeakAction
    {
    private readonly Action _action;
    public WeakAction(object target, Action action) : base(target, null) { _action = action; }

    Now, the objection should be that static methods are too restrictive and you really want to invoke the action on the subscriber instance. There is a simple workaround for that: pass the action the subscriber instance! So, instead of WeakAction you have WeakAction where is the the subscriber instance.

  9. Pingback: Simple MVVM Toolkit versus MVVM Light Toolkit - .NET Code Geeks

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.