The Component Burden

9 commentsWritten on December 14th, 2008 by
Categories: Castle Windsor, Memory Management

In my previous post i showed you how important it is to properly release the components you've resolved through the Windsor IoC container. At the end of that post, i showed an example where a disposable dependency of a component had to be disposed by the component. Basically something like this:

    public class Controller : IController
    {
        public IDependency Dependency { get; private set; }
 
        public Controller(IDependency myDependency)
        {
            Dependency = myDependency;
        }
 
        public void Dispose()
        {
            Dependency.Dispose();
        }
    }

This prompted the following comment by Bryan Watts:

So Controller is responsible for disposing of something it did not explicitly create? That’s a little presumptuous isn’t it?
> if you own a reference to an IDisposable instance, you are responsible for properly disposing of that instance.
I agree (and I’ve read your other article). However, in this case, Controller clearly does not own its dependency.

And Bryan is right. For a lot of languages, memory management rules can be summarized (somewhat simplified) like this:

  • If you create an instance of a class, you are responsible for releasing/freeing/disposing it.
  • If a factory creates an instance of a class, the factory is not responsible for releasing/freeing/disposing it. This burden falls upon the object which requested the object from the factory
  • If you receive an instance of an object, you should not release/free/dispose it, unless you received the instance from a factory

So where does this leave us with the example code shown above? Clearly, we did not create the instance of the disposable dependency, so in theory, we are not allowed to dispose it. Then again, if we weren't using Dependency Injection we would have probably instantiated the disposable dependency in our constructor (or when we first need it). In that case, we would have been responsible for disposing the disposable dependency.

You could argue that the IoC container is in some way a factory which creates and supplies the dependency to our class. So according to the rules stated above, it would be allowed to dispose the dependency. The downside to this is that this means that our class (the Controller) has knowledge of the lifecycle of the dependency. We know that the dependency has been configured with a Transient lifecycle (which means the container creates a new instance whenever an instance of its class is needed) so we can safely dispose it. The problem is that this knowledge is implicit. It is nowhere visible in the code of the Controller class. The only place where this lifecycle is configured explicitly, is in the configuration of the IoC container so it would be wrong for the Controller to assume anything about the lifecycle of its dependencies.

After all, suppose someone makes a change to the code of the dependency which makes it possible to be used with a different lifecycle. Suppose someone makes the change to the dependency and then configures the IoC container to always return a singleton instance of this dependency. If nobody thinks about the fact that the Controller still assumes a Transient lifecycle of the dependency, the disposal of the dependency will not be removed from the Controller class. Once a Controller instance is created, and then disposed, it will try to dispose the instance of its dependency, which will cause problems because that instance is supposed to be used by other objects as well!

So what do we do? Do we dispose of the dependency in the Controller class because we know it's safe (until someone changes the dependency and/or its configuration with the container)? Or do we simply not dispose it (since we're not really allowed to do so without some kind of implicit knowledge of the 'outside world')?

Neither option sounds very appealing. The first option could lead to problems depending on possible future changes outside of the Controller class. The second option is already problematic because a disposable dependency is not being disposed of as fast as it should be. The disposal would basically be postponed until the instance is collected by the garbage collector.

Another alternative is to have the container inject a factory which can create transient instances of the dependency, instead of having the container inject the transient instance directly. This would fix all of the discussed problems: the controller would not need to dispose the injected dependency, and can safely dispose the instantiated disposable instances. However, having to create factories simply to avoid this problem seems a bit cumbersome. The implementation of the factory could lead to other discussions as well because of how the factory should create the actual instance and its dependencies, which is exactly the reason why we wanted to use an IoC container in the first place. True, the factory could resolve the instance directly through the IoC container, but then you're just moving the problem to a different class without actually fixing it properly.

This entire problem has been described as the Component Burden by Hammet, the original creator of the Windsor IoC container. Obviously, this is something that the container should solve for us instead of us having to worry about it. Ideally, the container would keep track of all of the disposable dependencies it has created to satisfy the creation of requested components. Then when the requested component would be released, the container could safely dispose of each transient disposable dependency it created to create the requested component. Luckily, the Castle developers have implemented this behavior recently, so if you're using a recent build of the Windsor IoC container, this problem should no longer occur and you shouldn't be forced to dispose of dependencies that were injected into your objects.

The reason why i'm not using it yet is because there were many attempts at implementing this, and most attempts had considerable downsides to them. This one has been committed to the trunk, so it's probably a good solution, but i'm not going to use this solution in production systems until it has had some more time to prove itself. So for now, i'm sticking with my approach where i manually dispose the injected dependencies. I know it's not the best approach, but until the real solution in the container is somewhat more proven to work without downsides, manually disposing the dependencies seems to be the lesser of multiple evils.

Keep in mind that i'm only talking about the Windsor IoC container. I have no experience with StructureMap, NInject or Unity, so i have no idea how these containers deal with this problems. If anyone with knowledge about these container could shed some light on how they deal with this, i'd be very interested in reading it :)

  • http://hammett.castleproject.org hammett

    Great stuff. I’d also recommend skepticism towards the latest implementation, but so far I’ve only heard about one minor side effect.

  • Bryan Watts

    Thank you for your in-depth reply. This is indeed a difficult logical issue.

    You addressed the main implication of my comment, which is that components must have contextual knowledge of their use. This effectively couples components to particular DI paradigms.

    It seems intuitive that an IoC container, which creates instances, would also dispose of them. Have you seen Autofac Deterministic Disposal”? What is your opinion of that approach?

  • http://davybrion.com Davy Brion

    it’s interesting, although i’m not sure what to think of the inner-container based approach. It surely makes it easier for autofac to keep track of created references and properly disposing of them.

    I have no experience with autofac, so i’m not sure if you’re always supposed to use nested containers or not, but if you’re able to resolve components both through the root container and nested containers, it seems a bit ‘dangerous’ to me. Ideally, i’d like my code to know as little as possible about the IoC container. The solution that the Castle guys implemented seems to make that make that possible without having to decide between root containers or nested containers.

  • Bryan Watts

    Windsor demands diligence, as highlighted by your original post. It certainly doesn’t drop you in the pit of success.

    “Nested containers” is an overloaded concept. A “unit of work” is a richer perspective – it is a natural scope for transient instances. In the same way, a root container is a natural scope for singletons.

    The structure of the dependencies must make sense (i.e. not be “dangerous”), but the cleanliness of the solution leads me to believe it has merit.

  • http://yoot.be Steve Degosserie

    Another interesting article, although MEF-related, on the subject:
    http://hammett.castleproject.org/?p=326

  • Pingback: Dew Drop - December 15, 2008 | Alvin Ashcraft's Morning Dew

  • http://hammett.castleproject.org hammett

    @Bryan,
    Not sure I follow: ‘demands diligence’? As much as disposable types demands to be disposed.

    I work with Nick from Autofac, and as much as I understand the child/scoped containers approach it has pros and cons as everything. There’s no such thing as the perfect design.

  • Bryan Watts

    @Hammett,

    I appreciate your response. I had a chance to talk with Nick at PDC; we discussed lifetimes and container hierarchies and he intrigued me with the possibilities.

    By “demands diligence” I simply meant there is an extra step which is easy to forget. I agree the same issue exists with disposable types, though the contours are different.

    When you directly create your dependencies, you’re most likely to control their lifetimes because they are variables. There is no ambiguity: wrap that shiny new object in a using statement, or dispose it alongside yourself.

    Explicit release requires knowledge of the container. Components shouldn’t know about the container, so that gap must be bridged somewhere. This is the point at which I am still fuzzy, and would appreciate guidance.

    The issue seems to be scope: variables have well-defined scopes, but components in a container do not. That disparity is addressed by the unit-of-work, a natural scope.

    I realize more thought has been put into this by the MEF team than could possibly be quantified. The points above are where I get stuck.

  • http://hammett.castleproject.org hammett

    Containers are the natural scope of components. Calling release will allow early disposal of a graph – it is a shortcut to reclaim resources early – and this should only be called by whomever deals with the container directly, not deep in the graph…

    Given that any application will at some point have a reference to the container, get something from it, do some work and know that the component is not needed anymore, calling Release is natural for the app code (not domain or services, though). Sure, easy to forget. But as easy as forgetting to scope containers… The use of using a syntax sugar that might be worthwhile to add to windsor too. Will consider that.

    Agreed that if you’re composing manually you know what is disposable or not. IoC containers could do the same thing by transferring ownership of dependencies to defendants which would force you to virally make your own types in the graph disposable if you happen to have a disposable type down in the graph. That is where I think IoC container provide two key benefits

    - it manages the ownership on your behalf
    - given that an interface/contract does not implement IDisposable but an implementation does, the container would be the middle man that know this otherwise hidden detail. bring proxies to the table and it gets even more interesting

    The same child container would also work with windsor, but would require a different strategy to register the right types on the right points of the hierarchy – which is something that PicoContainer does too.

    Yep, the MEF lifetime team (which I belong to) has put a lot of effort to balance all the options and provide solutions for key scenarios. The code should be available soon, hope you all like it :-)