/// <summary> /// Throw an exception if the given object isn't already registered. /// </summary> /// <param name="obj">The object to look for.</param> private void CheckObjectIsNotRegistered(IManagedDisposableObject obj) { if (this.dependencies.ContainsKey(obj.Id)) { throw new InvalidOperationException("Object is already registered"); } }
/// <summary> /// Called when a managed disposable object is about to be disposed. This methods /// disposes all dependent ojects. /// </summary> /// <param name="obj">The object being disposed.</param> private void OnObjectDispose(IManagedDisposableObject obj) { // Remove the object callback so that this method isn't called twice obj.Disposed -= this.OnObjectDispose; lock (this.lockObject) { // During process shutdown and AppDomain unloading objects in the // RegisteredForFinalization queue are finalized even though they are still // reachable. This means the objects can be finalized in an unexpected // fashion. To deal with that we ignore these callbacks during shutdown // and AppDomain unload. // // An object can be finalized/disposed multiple times. We have to make // sure this object is still registered. if (!AppDomain.CurrentDomain.IsFinalizingForUnload() && !Environment.HasShutdownStarted && !this.wasDisposed && !this.wasFinalized && this.dependencies.ContainsKey(obj.Id)) { this.DisposeDependents(obj); this.Unregister(obj); } } }
private static void VerifyIManagedDisposableObjectDisposePassesSelfToEvent(IManagedDisposableObject obj) { IManagedDisposableObject arg = null; obj.Disposed += x => arg = x; obj.Dispose(); Assert.AreEqual(obj, arg); }
private static void VerifyIManagedDisposableObjectDisposeFiresEvent(IManagedDisposableObject obj) { bool eventFired = false; obj.Disposed += ignored => eventFired = true; obj.Dispose(); Assert.IsTrue(eventFired); }
/// <summary> /// Call the dispose method of the specified object. /// </summary> /// <param name="obj">The object to dispose.</param> private void DisposeObject(IManagedDisposableObject obj) { // Recursively dispose all dependent objects, deregister // (to avoid the Disposed event) then dispose. this.DisposeDependents(obj); this.Unregister(obj); obj.Dispose(); }
/// <summary> /// Unregister an object. The object must have been registered with /// this manager. /// </summary> /// <param name="obj">The object to remove the registration for.</param> private void Unregister(IManagedDisposableObject obj) { lock (this.lockObject) { this.CheckObjectIsRegistered(obj); this.RemoveEntriesFor(obj.Id); this.CheckObjectIsNotRegistered(obj); } }
/// <summary> /// Register a new managed disposable object. This object isn't dependent /// on anything, but can be depended on. /// </summary> /// <param name="obj">The object to register.</param> public void Register(IManagedDisposableObject obj) { lock (this.lockObject) { this.CheckObjectIsNotRegistered(obj); this.RegisterAsDependent(obj, this); this.CheckObjectIsRegistered(obj); } }
/// <summary> /// Remove all dependencies/dependentOn entries for the given id. /// </summary> /// <param name="objectId">The id to remove entries for.</param> private void RemoveEntriesFor(DisposableObjectId objectId) { // Remove the object from the list of its parent's dependencies IManagedDisposableObject parent = this.dependentOn[objectId]; this.dependencies[parent.Id].RemoveReferenceTo(objectId); this.dependentOn.Remove(objectId); // Delete the list of dependencies for this object this.dependencies.Remove(objectId); }
/// <summary> /// Call the dispose method of all dependent objects. /// </summary> /// <param name="obj"> /// The depended on object. This object will not be disposed, but its /// dependents will be. /// </param> private void DisposeDependents(IManagedDisposableObject obj) { // Disposing an object removes it from the list. To avoid trying to iterate a list // that is being modified, we copy the list first. var referencesToDispose = new WeakReferenceCollection(this.dependencies[obj.Id]); foreach (WeakReferenceOf <IManagedDisposableObject> reference in referencesToDispose) { IManagedDisposableObject objectToDispose = reference.Target; if (null != objectToDispose) { this.DisposeObject(objectToDispose); } else { // The weak reference can't be resolved. Remove all entries. this.dependencies[obj.Id].RemoveReferenceTo(reference.Id); this.RemoveEntriesFor(reference.Id); } } }
/// <summary> /// Register an object as dependent on another object. If the depended-on object is /// disposed then the registered object will be disposed too. /// </summary> /// <param name="child">The object to register.</param> /// <param name="parent"> /// The depended on object. This object must have been registered already. /// </param> public void RegisterAsDependent(IManagedDisposableObject child, IManagedDisposableObject parent) { lock (this.lockObject) { this.CheckObjectIsNotRegistered(child); this.CheckObjectIsRegistered(parent); // Add the child to the list of the parent's dependents this.dependencies[parent.Id].Add(new WeakReferenceOf <IManagedDisposableObject>(child)); // A child starts out with no dependents this.dependencies[child.Id] = new WeakReferenceCollection(); // The child is dependent on the parent this.dependentOn[child.Id] = parent; // We need to know when the child is disposed child.Disposed += this.OnObjectDispose; this.CheckObjectIsRegistered(child); } }