TDPL: Manual invocation of destructor

Steven Schveighoffer schveiguy at yahoo.com
Tue Aug 10 05:59:12 PDT 2010


On Tue, 10 Aug 2010 08:27:19 -0400, bearophile <bearophileHUGS at lycos.com>  
wrote:

> Andrei Alexandrescu:
>> class File {
>>      FILE * fp;
>>      this() { fp = fopen("/tmp/temporary"); }
>>      ~this() { fclose(fp); }
>> }
>
> I am not expert enough about all this topic, so I try to keep myself out  
> of this discussion. Recently I have read this:
>
>> issues with finalizers. The main problem with finalizers and garbage  
>> collection is that finalizers generally try to reclaim a non-memory  
>> resource (such as a specific file, a file handle, or a network socket)  
>> based purely on a memory reclamation policy (generally triggered by  
>> later memory allocations). Generally those other resource exhaust well  
>> before memory does, then you're out of the resource stuck waiting for  
>> garbage collection to notice a particular finalizer needs to be run. A  
>> better general rule of thumb is "don't ever use finalizers". Instead  
>> always call close inside a finally.<
>
> This means that putting something like a fclose(fp) inside the ~this()  
> is wrong, you need to add a specific method to that File class to ask  
> for the closing of the file, because you generally can't rely on the GC  
> for this, because a GC is free to even never collect objects, if there  
> is enough RAM.
>
> In my opinion it's correct to put something like a fclose(fp) inside the  
> ~this() only if you are sure this struct/class will be always allocated  
> on the stack, so its destructor will always be called deterministically  
> when the scope ends (a reference counting strategy too seems acceptable,  
> because it is deterministic).
>
> In a GC-based language you can't assume your destructors are run, so  
> your destructors usually need to be empty, and you need to add other  
> methods to free explicitly and manually (or with a scope(exit)) all the  
> resources that aren't RAM.
>
> Please take this cum grano salis, I am not an expert on this.

destructors are for the purpose of clearing out resources that are not  
provided by the GC.  Not having it in the destructor is a bad thing.

The thing is, if you *don't* call it in the destructor, and require  
someone to manually call your explicit method, then someone will forget to  
make that call, and your resource stays open forever.  It also means you  
have to call your method when going out of scope.

By clearing that resource in the destructor, and allowing a way to  
manually call that destructor, the class works in all three situations:   
manual resource management (via clear), scoped destruction, and GC  
destruction.

I think in time, the GC may be associated with it's own thread, and may  
run on a schedule vs. having to wait for a memory allocation to run.  When  
this happens, it's more likely you can rely on the GC to free the  
resources in a reasonable amount of time.

-Steve


More information about the Digitalmars-d mailing list