Reputation: 64196
Here is an example about which I am uncertain:
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;
...
}
Will it be safe to enumerate the managed dictionary in order to release the unmanaged resources?
Is availability of the dictionary uncertain since the order in which finalizers are invoked is not defined?
Here is a quote taken from the MSDN which I find confusing [1]:
- The finalizers of two objects are not guaranteed to run in any specific 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 might have already been finalized when the finalizer of Object A starts.
Upvotes: 3
Views: 637
Reputation: 81115
Rather than having a dictionary of unmanaged resources, I would suggest having a dictionary of independent wrapper objects, each of which is responsible for guarding one unmanaged resource. If the object holding the dictionary is abandoned and no other references exist to the wrapper objects, all of the wrapper objects will get finalized without needing to involve the dictionary itself in the process. Using such an approach will make it easier to sanely handle cases in which an exception occurs during object construction, and at least somewhat-sanely deal with situations where an object finds itself resurrected between the time it has been enqueued for finalization and the time the finalizer runs [code generally can't be expected to run "correctly" in such cases, but should avoid corrupting the state of the rest of the system].
For example, code which uses a handle may acquire a lock during its use and, after use, check a "disposeObjectASAP" flag; if set, re-acquire the lock and dispose object. The finalizer itself should set the flag and then try to acquire the lock; if it successfully acquires the lock, it should dispose the object. If unable, the fact that it set the flag should imply that code which has the lock is destined to check the flag and clean up the object, so the finalizer doesn't have to. If the finalizer runs prematurely, it may release resources which another thread is going to need, causing actions on that other thread to fail, but the finalizer won't release resources while another thread is using them or disposing them, since releasing resources in those situations could cause massive system corruption.
Upvotes: 2
Reputation: 133950
According to Implementing a Dispose method, the code you show there is not safe.
The code example 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 sample shows you freeing unmanaged resources in the conditional block. The MSDN example shows that you should free managed resources in the conditional block.
As it says in the text (under the heading, "The Dispose(Boolean) overload"):
If the method call comes from a finalizer (that is, if disposing is
false
), only the code that frees unmanaged resources executes. Because the order in which the garbage collector destroys managed objects during finalization is not defined, calling thisDispose
overload with a value offalse
prevents the finalizer from trying to release managed resources that may have already been reclaimed.
Upvotes: 1