Derelict SFML destructor crashes

Mike Parker aldacron at gmail.com
Mon Dec 17 05:51:45 PST 2012


On Monday, 17 December 2012 at 10:39:56 UTC, Jeremy DeHaan wrote:
>>
> I tried putting destroy in the destructor like the code I 
> wrote. Like I said, it gave me no memory errors, but I don't 
> actually know if it even does anything. I honestly think that 
> it is weird to not be able to rely on destructors like this. To 
> me, it makes sense that the destructor would be a part of the 
> garbage collection, but again I don't have any sort of 
> experience with memory management. I find it kind of silly to 
> have something like a destructor built into a language, but it 
> not being reliable and more or less something to be completely 
> ignored altogether. I do understand that in most cases this can 
> be so, as the GC will free up the memory itself, but in some 
> cases it could be useful to know your destructor will free up 
> the resources the GC can't.

I've not yet used destroy and am not familiar with everything it 
does, but I do know that it isn't going to help you with an 
sfImage because the memory for that was not allocated by the GC.

What I do know about destroy is that if you do want some control 
over when destructors are called then destroy is what you would 
use to do it. destroy(myClassInstance) will call the destructor 
of the object. But, it will not deallocate the object's memory. 
Unlike the deprecated delete, object destruction and memory 
deallocation have been decoupled with destroy.

However, if you are going to take this route, then you have to 
make sure that every object which releases resources in its 
destructor is manually destroyed. This is no different than what 
I do with my term() methods. But I like my approach better. All 
objects have destructors, but only those that need to release 
resources have a term() method, so I'm less likely to make silly 
mistakes at 3 am after several hours of coding.

At any rate, unreliable destructors are usually what you get when 
you are using a GCed language (or none at all, like in Java). 
They're not completely useless, though.

>
>
>> The scope(exit) in the main method ensures that this 
>> termination chain is run every time the app exits regardless 
>> of the reason
>
> This is good to know. I read about this, but haven't used it 
> yet. Wouldn't the destructors get called at this point?

No. Whatever code you have in the scope block gets called.

import std.stdio;

scope(exit) writeln("Foo");

scope(success)
{
    writeln("Bar");
    writeln("Baz");
}

All that's happening here is that the code you've included in the 
scope blocks will be called. The first block will execute no 
matter how the scope exits. The second block will only execute 
when the scope exits normally (and not due to an exception). The 
output will be:

Bar
Baz
Foo

Because the scope blocks are always executed in the reverse order 
they are encountered. scope(failure) will run when exiting due to 
an error.

So with these statements, there's nothing going on under the hood 
to call destructors or release memory.

>
>
>
> Also, what about something like this?
>
> void main(string[] args)
> {
>    {
>       init();
>       gameLoop();
>    }<-Wouldn't the destructors get called here? Or would the GC 
> run first?
> }<-And what would happen here? Would GC be run again after the 
> scope is left?

The main method in your D module is not the program entry point. 
The real entry point is in DRuntime. See the dmain2.d module in 
the Druntime source. At line 367 (in the current master) you'll 
see the entry point.

https://github.com/D-Programming-Language/druntime/blob/master/src/rt/dmain2.d#L367

extern (C) int main(int argc, char **argv)
{
     return _d_run_main(argc, argv, &_Dmain);
}

Looking in _d_run_main, you'll see a bit of platform-specific set 
up code for each platform, then a series of inner functions. At 
the bottom of _d_run_main is this line:

tryExec(&runAll);

runAll is an innder function inside _d_run_main, defined just 
above this tryExec call. It looks like so:

     void runAll()
     {
         gc_init();
         initStaticDataGC();
         rt_moduleCtor();
         rt_moduleTlsCtor();
         if (runModuleUnitTests())
             tryExec(&runMain);
         else
             result = EXIT_FAILURE;
         rt_moduleTlsDtor();
         thread_joinAll();
         rt_moduleDtor();
         gc_term();
     }

It initializes the GC, then calls the module constructors, then 
runs unit tests, then calls your main function. When your main 
function returns, module destructors are run, then gc_term is 
called to finalize the GC. It is here that any objects still 
hanging around in the GC that have not be destructed will have 
their destructors called and then all the memory is released.



>
>
> I am just trying to understand the language better. So far it 
> seems that the only way to prevent memory leaks when memory is 
> being allocated outside of D is to manually delete those 
> objects yourself and to not rely on the destructors. I will 
> make sure this happens in the future!

It's not the only way, but it's the easiest way.

>
> Sorry if I say silly things. I am but a young programmer trying 
> to gain knowledge!


More information about the Digitalmars-d-learn mailing list