user492238
user492238

Reputation: 4084

IDisposable with destructor: requires thread-safe implementation?

This is pretty much only for me to make sure, I got this right:

We have a large resource class implementing the IDisposal pattern. It should (by design) be implemented in a way, that enables it to get called more than one time (even if we try to call it exactly one time of course). We also implement a finalizer, which also calls the Dispose() method - just as backup. If called manually, Dispose() will also call GC.SuppressFinalize(this).

There are several examples of disposal patterns around. Most of them call GC.SuppressFinalize(this) at the end of the disposing code. Some claim, it would be better, to call it at the beginning of the Dispose() method, before any cleaning. Latter argue, this would make sure, the GC doesn't call the finalizer concurrently, while we are still cleaning up.

Question:
It seems, placing GC.SuppressFinalize at the beginning doesn't do any better? We still have a race condition, right? So is it true, that we rather should implement Dispose() in a thread safe way instead?

Upvotes: 4

Views: 1063

Answers (3)

supercat
supercat

Reputation: 81115

While it is sometimes possible for an object to be finalized while a seemingly-live reference exists, that can only happen when nothing else is going to ever refer to the object. The GC.SuppressFinalize(this) actively refers to the present object 'this', thus guaranteeing that it will not be finalized until the GC.SuppressFinalize executes. Further, the fact that the object reference existed to dispose the object, and was available to the Dispose method, guarantees that the finalizer couldn't have been queued before Dispose started running unless the object was dead and a finalizer somewhere (either its own finalizer, or that of some other object) resurrected it.

Because there are some scenarios where an object could be scheduled for finalization and resurrected without ever being aware of it, it may not be a bad idea to protect a dispose and finalize against redundant operation. Microsoft's pattern is not a good one, however. Finalizable objects shouldn't hold references to any objects not needed for finalization. If an object would hold a mixture of managed and unmanaged resources, the unmanaged resources should be moved into their own classes (effectively turning them into managed resources), so then the main object would hold nothing but managed resources.

Upvotes: 3

user180326
user180326

Reputation:

The GC only cleans up objects that are not 'reachable'.

A class in which code is executing is still 'reachable' because it's this pointer is on the stack. So while dispose is executing, the finalizer won't be called.

So it does not matter if you call SuppressFinalize at the begin or end.

As commentors below indicated, the CLR implementation does not appear to guarantee that your object does not get garbage collected/finalized while instance methods are executing. The only possible 'dependable' reference keeping the object alive is the one used to invoke the method on the object, but I don't know enough about the JIT internals to make statements about that, and it's behaviour might change.

I'm leaving the answer here for access to the discussion below.

Upvotes: 4

CodingBarfield
CodingBarfield

Reputation: 3398

Dispose should not throw any exceptions.

I would make sure all the code in Dispose is thread safe so that if it gets called it will not do anything strange. Usually adding checks if variables are null already should do the trick.

In the microsoft examples i've only seen GC.SuppressFinalize at the end of the Dispose function.

Upvotes: 1

Related Questions