While disposition stuff is on the brain, there’s another disposition topic I’ve been wanting to write about for a while now: handling disposition of objects of nebulous ownership.
The issue of nebulous ownership creeps in whenever you use any sort of complex creational pattern. If you instantiate an object by invoking its constructor directly, it’s pretty clear that you are its owner (unless, of course, you’re deliberately handing it off to other code). If that object happens to be disposable, then you can (and should) safely dispose it when you’re done working with it.
But what if you obtained an object instance via a method call? How can you tell if you’re the owner? If you’re lucky, that method has a well-documented contract that specifies whether you’re getting your own instance or not. Otherwise, you’re pretty much going to have to guess or go code spelunking (and hope that the behaviour doesn’t change in later versions). Luckily, we can often make some pretty decent educated guesses about who owns an object returned from a method call. For example, if the method is called CreateFoo
, we can be pretty sure we’re getting our own Foo
instance. On the other hand, if we’re calling the getter of a static Instance
property, chances are that we’re getting a shared singleton.
There are, however, cases where one is explicitly not supposed to care whether one is using a shared instance or not. Use of the service locator pattern is probably the most obvious example, and it’s the one I’ll focus on for the rest of this post. When you retrieve an instance from a service locator, you can’t tell if it’s a shared instance. Even more importantly, you’re really not supposed to care. You’re getting an object that’s castable to the type you requested, and you should use it as that type, and that’s it. Pretty simple, right? And it is, unless the object happens to be disposable.
Unfortunately, when you retrieve a disposable instance from a service locator, you have no way to tell if you’re getting a shared instance or not. This means that you can’t safely dispose it. I’ve seen a few different approaches to trying to deal with this:
- Don’t ever dispose anything received from a service locator.
- Use a separate lookup table keyed on service registration data (usually type and an optional registration name) to indicate whether instances of any given retrieved service should be disposed.
- Offload instance disposition to the container.
- Build disposition-prevention logic into registered service instances so that they will not be disposed unless the service container is being disposed.
#1 has the obvious disadvantage that some things that ought to be disposed won’t be. This will usually lead to at least poorer performance, but it could also cause some more obvious functional bugs if resources locks are not released in a timely fashion.
#2 is kludgy. It means that you can’t simply call Dispose()
on a serviced object. Instead, you need to either perform the disposability lookup first or invoke a helper method that takes care of both the lookup and the Dispose()
invocation. Either way, you can’t use using
statements, which is massively annoying. You’re also very likely to run into some nasty little bugs if you switch from using a locally newed instance to a serviced instance some time in the future. For example, if you make the following change, how likely are you to notice that you also need to change the disposition code?
Original code:
using (IFoo foo = new Foo()) { foo.DoSomething(); }
New code:
using (IFoo foo = ServiceLocator.GetInstance<IFoo>()) { foo.DoSomething(); }
#3 is the approach used by Autofac and, as far as I can tell, only by Autofac in the .NET space. When I first saw it, I was pretty darn excited – for about 20 seconds. And then that inconvenient little voice in my head started raising objections:
- Disposition is meant to allow “expensive” state to be cleaned up as soon as possible. By deferring disposition of container-sourced objects until the end of a predefined lifetime scope, we will be delaying disposition of individual objects in at least some, if not most, cases.
- Unless the tracking mechanism uses weak references (and I do have to admit that I haven’t examined the Autofac implementation in any depth, so I don’t know whether this is the case for it or not), it is very likely preventing non-shared disposable instances from being garbage collected until the end of their declared lifetime scope. This means that we could end up taking an unexpected memory hit unless we use fairly granular lifetime scopes.
- We shouldn’t dispose any container-sourced objects, which means some potentially difficult-to-diagnose problems if we move from a newed instance to a container-sourced instance (as in the previous example).
- Using this mechanism ties us into using the Autofac container. We can’t swap out a different container without making some pretty major code modifications.
So that leaves approach #4… From the point of view of the instance retriever, it’s very simple: you can safely dispose of any instance you retrieve from the container, just as if it had been newed. That’s really quite elegant, and it’s what I want to be able to do in my own code. However, getting to the point where you can do this is anything but simple. The only reference I’ve been able to find to a similar approach leaks implementation details up to the instance users, which is inconvenient in practical terms and, well, more than a little icky from a design point-of-view. So what can be done to avoid this?
Well, whatever we do, we’re going to need a wrapper type that actually controls the disposition. It has three key features:
- It wraps an instance of an interface, with that instance being provided to the wrapper constructor.
- It implements that focal interface, directly forwarding all interface member invocations to the wrapped instance, except for
IDisposable.Dispose()
. - For
IDisposable.Dispose()
, it checks whether disposition is allowed before invoking theDispose()
method of the wrapped instance.
On the surface, that looks pretty simple but, as is so often the case, the devil is lurking in the details… Let’s start with the question of how the wrapper class knows whether disposition of the wrapped instance should be allowed. Regardless of how we implement this, we’ll need to modify our service container to toggle some “disposition allowed” flag. However, I’d prefer to avoid forcing the container type to interact directly with our disposition-prevention mechanism. Instead, let’s make the container implement a more general-purpose interface:
public interface ITrackingDisposable : IDisposable { DispositionState DispositionState { get; } }
where DispositionState
is an enum with the following definition:
public enum DispositionState { Undisposed = 0, Disposing = 1, Disposed = 2 }
(There are a few potentially interesting enhancements to both the interface and the enum, but they’re not relevant to the disposition-prevention mechanism, so we’ll leave them out of the current discussion.)
In order to implement ITrackingDisposable
, we’ll need to make a couple of changes to the service container. Adding the DispositionState
property is pretty trivial:
public DispositionState DispositionState { get; private set; }
Changing the Dispose
implementation is pretty trivial too, as long as you’re not trying to make any distinctions between successful and unsuccessful disposition:
public void Dispose() { this.DispositionState = DispositionState.Disposing; try { this.Dispose(true); GC.SuppressFinalize(this); } finally { this.DispositionState = DispositionState.Disposed; } }
Once our service container implements the ITrackingDisposable
interface, we can pass a reference to the container to our wrapper class, and let it decide whether the wrapped instance should be disposed based on the current DispositionState
of the container.
That just leaves us with one other picky detail: how do we end up with a wrapper type that forwards the focal interface implementation to the wrapped instance? First, we’ll create an abstract base class for our wrappers:
public abstract class DispositionPreventer<TComponent> : IDisposable where TComponent : IDisposable { private readonly TComponent _component; private ITrackingDisposable _owner; protected DispositionPreventer(TComponent component, ITrackingDisposable owner) { Contract.Requires(component != null); Contract.Requires(owner != null); this._component = component; this._owner = owner; } protected TComponent Component { get { return this._component; } } public void Dispose() { switch (this._owner.DispositionState) { case DispositionState.Disposing: case DispositionState.Disposed: this._component.Dispose(); break; } } }
Now, we’ll need to create a concrete subclass for each interface for which we wish to register an instance with the service container. For example, if we have an interface IFoo
defined as follows:
public interface IFoo : IDisposable { event EventHandler Fooing; bool IsFooed { get; } void DoSomething(); }
we’ll need to create a disposition-preventer implementation like the following:
public sealed class FooDispositionPreventer : DispositionPreventer<IFoo>, IFoo { public FooDispositionPreventer(IFoo component, ITrackingDisposable owner) : base(component, owner) { } public event EventHandler Fooing { add { this.Component.Fooing += value; } remove { this.Component.Fooing -= value; } } public bool IsFooed { get { return this.Component.IsFooed; } } public void DoSomething() { this.Component.DoSomething(); } }
That’s not so bad for a little interface like IFoo
, but it can turn into hours of drudgery for larger interfaces. Unfortunately, given the lack of support for easy design by composition in .NET, you’re either going to have to write this code manually or build your own code generation tool to create the wrapper classes. (I recently created a T4 template for this at the day job and, while authoring the template itself was a somewhat annoying exercise, at least I won’t have to write (or, even worse, code review) the wrapper classes themselves.)
Once you’ve got the wrapper classes set up, all that’s left is to actually use them. This is done simply by wrapping our focal instance immediately before registering it with the service container. e.g.:
container.RegisterInstance<IFoo>(new FooDispositionPreventer(new Foo()));
To help detect a failure to wrap a disposable instance before registration, I use a custom FxCop rule that looks for RegisterInstance
calls where the registered instance is disposable but does not inherit from DispositionPreventer<>
.
And that’s pretty much it. The only really hard part of this is implementing the individual disposition wrapper classes. However, if you’re willing to invest a bit in that part of the puzzle, you’ll end up with a container-independent disposition-prevention mechanism that won’t intrude in any way into your container-consuming code. And, in case you missed it, the above mechanism can be made to work with pretty much any nebulous ownership scenario where interfaces are used for implementation decoupling since all you need to do is have the real “owner” pass around disposition-preventing wrappers instead of “naked” instances. If interface-based decoupling is not being used, a subclassing approach is also possible as long as the focal class is unsealed.
But it would still be nice if implementing the wrapper classes wasn’t quite so much work…
Very nice post. I would be interested to see your T4 template for this.
@Eric: I’d love to be able to share the template. Unfortunately, given that it was written on my employer’s dime, I can’t. However, I can tell you that it uses an underlying approach similar to that described at http://www.olegsych.com/2007/12/how-to-use-t4-to-generate-decorator-classes/, with a bunch of extra work to handle stuff like generics, multiple interface implementation, and accomodation of all relevant member types. If you’re interested in trying to roll your own, you may find that attempting to wrap System.ServiceModel.IClientChannel makes for an interesting test case… 😉
Autofac shares its disposition behavior at least with Windsor, which will also dispose of any disposable components when you release the root – that is, if the disposable component’s lifetime is up.
On another note, I consider IDisposable interfaces as leaky abstraction, as it leaks out that you are having a particular implementation in mind.
You may be interested in reading Nicholas Blumhardt’s blog post on the Relationship Zoo, and particularly his thought about Owned(of T): http://nblumhardt.com/2010/01/the-relationship-zoo/
We are probably going to discuss it at some point in the future over at the CCA discussion list, so you might want to join: http://cca.codeplex.com/Thread/List.aspx
@Mark: I’d actually read about the Windsor implementation, but I had assumed the “root” in question was an object requested from the service container, and that the container is only tracking instances that it injects into that root object. If I’m not mistaken about this, one still needs to dispose the root object manually, and one still has no idea if it’s a shared instance or not. If the “root” is actually something other than a retrived instance, please let me know, and I’ll amend the post.
WRT IDisposable as a leaky abstraction, I agree at a theoretical level. However, given the deferred finalization approach adopted in .NET, I can’t really see any way to avoid some sort of “I’m finished with this object” method to trigger early clean-up. Of course, one can make a decision to ignore that method, thereby trading off performance against elegance, but I’m not willing to do that. (This might just be because I’ve already traded off performance against security and no longer have any spare performance to give up.)
Thanks for pointing out Nicholas Blumhardt’s Relationship Zoo post. It sounds like Owned is probably meant to be a generic implementation of approach #4. Unfortunately, there are a couple of things about Owned of which I’m not overly fond. The first is the same issue regarding the generic approach that I mentioned in the original post above: it leaks IoC stuff into dependent code since one has to use the Value property to get at the actual dependency. The second is the name, which makes consuming code read as if it is declaring that it has its own instance of the target type, which is presumably not the actual behaviour.
There is still a problem with #4 and that is that it violates one of the basic rules of ownership: You don’t own an object that is given to you. This is a very normal pattern in the .NET framework and developers tend to be dazzled when code violates the pattern (for instance the StreamReader).
In your post you very well show how IDisposable can complicate things tremendously. Because of all this, I see the registration of disposable types in IoC framework as an anti-pattern. The cure is to only register types that don’t implement IDisposable. If you need an object that needs a lifetime that must be controlled, inject a factory that returns the IDisposable type.
Cheers
When I have an IDisposable service that I want to inject, I usually inject a factory instead. Unity makes this easy with its automatic factories – just declare the constructor parameter as a generic Func, and Unity will create the delegate. The class that consumes the service simply uses a using block around the factory when it wants to call the service.
@Bill: What role does the factory play in disposition of the target service? Is it controlling disposition based on whether the target is a shared instance or not? If so, what practical benefit does this have over something like Owned?
You should never dispose of something that you are not sure is yours to dispose.
When IDispose is properly implemented (as in the pattern, not just the interface), it will dispose of itself when it gets garbage collected => undeterministic behavior – still better than potentially breaking the application, though.
Not ideal but the only option aside from implementing IShared or similar prevention functionality. And they are just workarounds around badly written and documented code anyway.
You can avoid all of this “simply” by not using badly written and documented assemblies (in some cases impossible, I know).