4

I was working on a class in which I had to dispose of managed and unmanged resources which looked something like this (only the disposing part, obviously):

class MyDisposingExample : IDisposable
{
    public void Dispose()
    {
        Dispose(true);
    }

    private void Dispose(bool callFromDispose)
    {
        // free unmanaged resources here
        if (callFromDispose)
        {
            // free managed resources here
            GC.SuppressFinalize(this);
        }
    }

    ~MyDisposingExample()
    {
        Dispose(false);
    }
}

And then a thought occurred to me. Why bother with the second Dispose function? Why can't I just do this:

class MyDisposingExample2 : IDisposable
{
    public void Dispose()
    {
        // free unmanaged resources here
        // free managed resources here
        GC.SuppressFinalize(this);
    }

    ~MyDisposingExample2()
    {
        Dispose();
    }
}

The usual disposing pattern addresses several problems, all of which seem to be solved by this example, without the additional method.

1) If the method Dispose() is explicitly called, it frees the managed resources, the unmanaged resources and suppresses the finalizer so that the resources won't be freed again later.

2) If the method Dispose() was not explictly called, the finalizer will run Dispose(), free managed resources, free unmanaged resources and suppress the finalizer (we'll get back to this point later).

On the face of it, this pattern provides everything the previous pattern does. I can dispose of my object explicitly if I don't require it anymore, my object is implicitly disposed if I forget to dispose of it myself and there is no fear of resources being freed twice.

The only fly in the ointment seems to be that I am calling GC.SuppressFinalize() from within the finalizer itself, but... why not? According to MSDN:

This method sets a bit in the object header of obj, which the runtime checks when calling finalizers.

So all I'm doing is setting a bit. This shouldn't affect the finalizer once it's running as the bit check must occur before the finalizer is called.

As I haven't seen this pattern anywhere, I must assume there is something wrong with it. What am I missing?

shai tibber
  • 154
  • 11
  • 2
    You shouldn't free managed resources from a finalizer, that's what is wrong with it. – Lasse V. Karlsen Mar 31 '16 at 21:51
  • 4
    You should not free managed resources from the finalizer. However, the standard pattern the MSDN teaches is not that good of one either. Go have a read of the article "[IDisposable: What Your Mother Never Told You About Resource Deallocation](http://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About)" written by Stephen Cleary, it is a real eye-opener. To summarize the article, in modern applications you should never need to write a finalizer and should be using a custom `SafeHandle` to manage it instead. – Scott Chamberlain Mar 31 '16 at 21:53
  • 2
    You did not implement the disposable pattern according to the MSDN sample code. You probably would not have asked this question if you did. Your replacement has a glaring bug but you hid the bad code. Do use the MSDN sample. – Hans Passant Mar 31 '16 at 21:53
  • 5
    Your supposition is that the unnecessary call to SuppressFinalize is the error in your plan. That's not the problem; the problem is the disposal of *managed* resources on the finalizer thread. Recall to your mind that finalizers run on their own thread, and that managed resources can be thread-affinitized, and now start to imagine the horrors that could result. Moreover: finalizers run in arbitrary order. A managed object disposed on the finalizer thread might have already been finalized; now you are possibly running finalization logic twice on one object; is it robust to that scenario? – Eric Lippert Mar 31 '16 at 21:58
  • 1
    Writing a correct finalizer is extraordinarily difficult and I recommend against you trying *ever*, ideally, but definitely hold off until you understand the pattern better. If you are not sufficiently scared yet, my series of articles on the subject might put more fear into you: http://ericlippert.com/2015/05/18/when-everything-you-know-is-wrong-part-one/ – Eric Lippert Mar 31 '16 at 21:59
  • If you extract the unmanaged resources into a separate handle class all these issues go away. Almost all unmanaged resources are handle like so you can use the SafeHandle infrastructure. – usr Mar 31 '16 at 22:14
  • 1
    Oh, yet another fell for the disposable anti-pattern. – CodesInChaos Apr 01 '16 at 10:46
  • Great answer Eric Lippert, thanks! Incidentally, by pure coincidence I was asked this exact question in a job interview a few days ago and gave your answer. – shai tibber Apr 14 '16 at 08:43
  • @EricLippert, just if you could be so kind as to answer explicitly, because I'm having a hard time finding an answer anywhere: is it true that calling `GC.SuppressFinalize(this)` from (code called by) a finalizer is _in itself_ not a problem? (You did say above it was not _the_ problem in the given case - I just want to make sure that, in itself, it's simply not a problem.) – Tom Jul 07 '19 at 12:22
  • 1
    @Tom: The question is "I'm using the dispose pattern completely wrong; is this particular part of what I'm doing wrong?" No, the *whole thing is wrong from the very first sentence*. You don't use `Dispose` to dispose managed resources, and you certainly don't use a finalizer for that. That's the problem here. Is there anything wrong, per se, with calling SuppressFinalize from a finalizer? Well, it will work, but *there should not be a situation in which that is the correct thing to do*, so whether it works or not should be irrelevant. – Eric Lippert Jul 07 '19 at 14:17
  • @Tom: Also, why do you call SuppressFinalize in the first place? Only because it is a performance optimization. But under what circumstances is it an optimization *when called from the finalizer thread?* Only when you've *failed* to make that optimization from the main thread! That's the place to do that optimization! – Eric Lippert Jul 07 '19 at 14:24
  • Thanks, @EricLippert, and sure, the optimization is only relevant from the main thread, but if it's not a problem per se when called from the finalizer thread then it can lead to slightly simpler code in case _only **unmanaged** resources are at play_. It's getting a bit too big for a comment, so I'm making a proper question out of it: please see https://stackoverflow.com/questions/56924439/can-the-c-sharp-disposable-pattern-be-simplified-when-only-unmanaged-resources. – Tom Jul 07 '19 at 17:19
  • @Tom Having only now seen the very specific question you just posed, it does however appear that [I've had a little more success](https://stackoverflow.com/questions/58044679/is-it-harmless-to-call-gc-suppressfinalize-within-the-finalizer) at getting its answer. I commend your attempt, but in the end I think I owe my success to (1.) no small amount of luck, and (2.) an annoying amount of effort pre-inoculating the question's language and phrasing against the backlash that would inevitably and sadly erupt. – Glenn Slayden Sep 23 '19 at 21:56
  • @Tom I especially appreciate your insight, in response to the `SafeHandle` clamor, that that feels like simply moving the problem somewhere else. It's also not necessarily just a cosmetic difference either, because each `SafeHandle` is itself a GC object, meaning that, whenever deriving from `SafeHandle` is precluded by other considerations, each containment of a `SafeHandle` instance, versus `IntPtr`, ostensibly adds to the GC object load. – Glenn Slayden Sep 23 '19 at 22:26
  • Thanks for your kind words, @Glenn - and for succeeding to get an answer and for sharing! – Tom Sep 27 '19 at 09:03
  • @Tom I sensed a kindred spirit. – Glenn Slayden Sep 27 '19 at 09:54

0 Answers0