How to properly manage the collection of unmanaged resources from the finalizer?

Here's an example I'm not sure about:

public class SomeClass : IDisposable {

    ~SomeClass() {
        Dispose(false);
    }

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private bool _disposed;

    protected virtual void Dispose(bool disposing) {
        if (!_disposed) {
            if (disposing) {
                // TODO: Release any managed resources here...
            }

            // ?! Is it safe to enumerate the dictionary here ?!
            foreach (var resource in _resources.Values)
                ReleaseBuffer(resource);
            _resources = null;

            _disposed = true;
        }
    }

   private Dictionary<string, IntPtr> _resources;

    ...

}

      

Is it possible to enumerate a managed dictionary to free up unmanaged resources?

Is the presence of an undefined dictionary, since the order in which the finalizer is called is undefined?

Here is a quote taken from MSDN that I am confusing [1]:

  • The finalizers of two objects are not guaranteed to be in any particular order, even if one object refers to the other. That is, if object A has a reference to object B, and both have finalizers, object B may already have completed when object A's finalizer begins.
+3


source to share


2 answers


Instead of having a dictionary of unmanaged resources, I would suggest having a dictionary of independent wrapper objects, each of which is responsible for guarding a single unmanaged resource. If the object containing the dictionary is abandoned and no other references exist in the wrapper objects, all wrapper objects will be completed without the need to involve the dictionary itself in the process. Using this approach will make it easier to safely handle cases in which an exception is thrown when constructing an object, and at least somewhat intelligently handles situations where an object is resurrected between the time it was queued for finalization and the time when the finalizer runs [the code generally cannot be expected "correctly" in such cases, but should avoid corrupting the state of the rest of the system].



For example, code that uses a handle might acquire the lock during use and check the "disposeObjectASAP" flag after use; if set, re-purchase the lock and delete the item. The finalizer itself must set the flag and then try to acquire the lock; if it successfully acquires the lock, it must destroy the object. If it fails, the fact that it set the flag should mean that the code that has the lock is meant to check the flag and clean up the object, so the finalizer doesn't need that. If the finalizer runs ahead of schedule, it can free resources that need another thread, causing actions on that other thread to fail, but the finalizer will not release resources while the other thread uses them or disposes of them.because freeing resources in these situations can cause massive damage to the system.

+2


source


According to Dispose Injection, the code you show is not safe.

The example code shows:

protected virtual void Dispose(bool disposing)
{
   if (disposed)
      return; 

   if (disposing) {
      // Free any other managed objects here. 
      //
   }

   // Free any unmanaged objects here. 
   //
   disposed = true;
}

      



Your example shows you freeing unmanaged resources in a conditional block. The MSDN example shows that you must release managed resources in a conditional block.

As the text says (under the heading "Dispose (Boolean) overload"):

If a method call comes from a finalizer (that is, if it is recycled false

), only the code that releases the unmanaged resources is executed. Because the order in which the garbage collector destroys managed objects during finalization is undefined, calling this overload Dispose

with a value false

prevents the finalizer from attempting to release managed resources that may have already been patched.

0


source







All Articles