A place for spare thoughts

16/08/2012

Weakly referenced Decorator: meet the ephemerons in .NET

Filed under: patterns — Ivan Danilov @ 01:07

Sometimes we have long-living class that should notify other classes with potentially short lifetime about some event. In such case WeakReference is the obvious solution to prevent memory leaks. Well, something like that, supposing long-lived class named Service and short-living is Client (for simplicity sake I’ve limited Service‘s handling abilities to just one client):

    public interface IClient {
        void Notify();
    }

    public class Service {
        private WeakReference _clientRef;

        public void AddClient(IClient client) {
            _clientRef = new WeakReference(client);
        }

        public void NotifyClientIfAlive() {
            var client = (IClient)_clientRef.Target;
            if (client != null) client.Notify();
        }
    }

    public class Client : IClient {
        public void Notify() { /* not important */ }
    }

So far, so good. You’ve wrote such code and it’s working beautifully. But now yet one task comes: suppose it is logging, or something similar and you decide to implement that via the Decorator pattern. Well, it is very handy we have that IClient interface, isn’t it? Just a piece of cake:

    public class ClientDecorator : IClient {
        private readonly IClient _realClient;

        public ClientDecorator(IClient realClient) {
            _realClient = realClient;
        }

        public void Notify() {
            // do what is needed
            _realClient.Notify();
        }
    }

Now we can just pass instance of ClientDecorator to the Service and be off.
Well, not really. If you will construct IClient just from the start with decorator and work with decorated instance all the time – it is ok.
But what if you want to implement decoration of calls from this service only? Or, you receive already-made instance of IClient from somewhere. Then you have a problem: if you wrap your instance and ‘forget’ strong reference to newly created decorator – it would be garbage collected at the very next GC pass. Moreover, it will break not only your logging, but the message will never reach real client in the first place. Let me illustrate the problem with small unit test:

        [Test]
        public void DecoratorIsNotGCedWhenRefsToRealClientExist() {
            var client = new Client();
            var decorator = new ClientDecorator(client);
            var service = new Service();
            service.AddClient(decorator);

            // create weak refs to watch if these are alive
            var weakClient = new WeakReference(client);
            var weakDecorator = new WeakReference(decorator);

            // nullify strong ref to decorator, but leave to client
            decorator = null;

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();

            Assert.IsTrue(weakClient.IsAlive);
            Assert.IsTrue(weakDecorator.IsAlive);
        }

Oops. Red test. Not good. Well, of course, we could have made Service‘s refs strong, but than both client and decorator will live at least as long as the Service, i.e. we will introduce memory leak this way. Let’s write another unit test and requirement:

        [Test]
        public void ClientDecoratorIsGCedWhenNoClientRefsAreAlive() {
            var client = new Client();
            var decorator = new ClientDecorator(client);
            var service = new Service();
            service.AddClient(decorator);

            // create weak refs to watch if these are alive
            var weakClient = new WeakReference(client);
            var weakDecorator = new WeakReference(decorator);

            // nullify strong refererences
            client = null;
            decorator = null;

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();

            Assert.IsFalse(weakClient.IsAlive);
            Assert.IsFalse(weakDecorator.IsAlive);
        }

It is almost the same except that both strong refs to decorator and client dying, and after GC – they should be swept, simulating client death when service is still alive and demonstrating that no memory leaks is present.

Another option is to make service aware of decorator’s presence: then service can have strong ref to decorator and weak – to real client, decorator will have weak reference to client and when notification is there – service can check real client and remove decorator as well. It is complex even to read, not only to understand. And totally defeats purpose of decorator. And if you will have to add two, three or four decorators to the single client… oops, you have a real problem in this case with such an approach.

So, can we do better? Yes, we can. What we need is to invent a way to keep decorator’s instance alive not as long as someone have strong refs to itself, but as long as someone have strong refs to other instance, namely to real client instance. That task is precise description of ephemeron. Since .NET 4.0 they are supported by CLR in the form of ConditionalWeakTable<TKey, TValue> class.

Here is the code:

    public class ClientDecorator : IClient {
        private static readonly ConditionalWeakTable<IClient, ClientDecorator> _keepaliveMap = new ConditionalWeakTable<IClient, ClientDecorator>();
        private readonly IClient _realClient;

        public ClientDecorator(IClient realClient) {
            _realClient = realClient;
            _keepaliveMap.Add(realClient, this);
        }

        public void Notify() {
            // do what is needed
            _realClient.Notify();
        }
    }

Basically adding to _keepaliveMap is equivalent to making this instance non-eligible for GC while realClient is not going to be GCed.

Both tests are green, hooray! But we can do even better, we can move out required logic to base class and enable any decorator to use it without code duplication, like that:

    public class WeaklyReferencedDecorator<T> where T : class {
        private static readonly ConditionalWeakTable<T, WeaklyReferencedDecorator<T>> _keepaliveMap = new ConditionalWeakTable<T, WeaklyReferencedDecorator<T>>();
        protected readonly T Instance;

        public WeaklyReferencedDecorator(T instance) {
            Instance = instance;
            _keepaliveMap.Add(instance, this);
        }
    }

    public class ClientDecorator : WeaklyReferencedDecorator<IClient>, IClient {
        public ClientDecorator(IClient realClient) : base(realClient) {}

        public void Notify() {
            // do what is needed
            Instance.Notify();
        }
    }

It seems, with base class amount of code… huh, LOCs number even decreased as we have Instance variable on base class now. Looks good, isn’t it?

Just to be sure everything is OK, lets write tests to simulate several stacking decorators one on top of another:

        [Test]
        public void StackingTwoDecoratorsGCedCorrectly() {
            var client = new Client();
            var service = new Service();
            var clientDecorator1 = new ClientDecorator(client);
            var clientDecorator2 = new ClientDecorator(clientDecorator1);
            service.AddClient(clientDecorator2);

            var weakDecorator1 = new WeakReference(clientDecorator1);
            var weakDecorator2 = new WeakReference(clientDecorator1);
            var weakClient = new WeakReference(client);
            clientDecorator1 = clientDecorator2 = null;
            client = null;

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();

            Assert.IsFalse(weakClient.IsAlive);
            Assert.IsFalse(weakDecorator1.IsAlive);
            Assert.IsFalse(weakDecorator2.IsAlive);
        }

        [Test]
        public void StackingTwoDecoratorsNotGCedIfRefToRealClientExist() {
            var client = new Client();
            var service = new Service();
            var clientDecorator1 = new ClientDecorator(client);
            var clientDecorator2 = new ClientDecorator(clientDecorator1);
            service.AddClient(clientDecorator2);

            var weakDecorator1 = new WeakReference(clientDecorator1);
            var weakDecorator2 = new WeakReference(clientDecorator1);
            var weakClient = new WeakReference(client);
            clientDecorator1 = clientDecorator2 = null;

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();

            Assert.IsTrue(weakClient.IsAlive);
            Assert.IsTrue(weakDecorator1.IsAlive);
            Assert.IsTrue(weakDecorator2.IsAlive);
        }

All of the tests pass, so it seems we’re done.

And a last word about where it could be possibly useful except mentioned scenario with some notifying service (which will probably have a collection of WeakReference’s is real world). Here is my answer to related question. In short, with addition of collectible assemblies for dynamic type generation in .NET 4.0, you can’t safely cache reflection data in something alike Dictionary<Type, MyReflectionResult> because doing so will prevent GC from collecting such dynamic types even if no one needs them anymore because your cache will keep them alive. Bad thing, especially if you’re writing some general-purpose lib or framework.

Hope this post was interesting to someone 🙂

Advertisements

1 Comment »

  1. thanks for great idea

    Comment by Panchenko Dmytro — 16/08/2012 @ 14:34


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: