using GC needs particular skills?

Mike Parker aldacron at gmail.com
Tue Jul 17 18:00:26 PDT 2012


On 7/18/2012 12:45 AM, Alexandr Druzhinin wrote:
> 17.07.2012 18:34, Mike Parker пишет:
>> On 7/17/2012 1:27 AM, Alexandr Druzhinin wrote:
>>>
>>> The reason were bindings to GeographicLib C++ library written in
>>> analogue to Derelict bindings (I used it in my projects too).
>>> Now I'm trying to make simple test cases for my trouble.
>>
>> Bindings based on Derelict will release the shared libraries in a static
>> module destructor. So if you are calling any bound functions from inside
>> class destructors and letting your objects be cleaned up by the GC, then
>> you are guaranteed to get a segfault at exit.
>>
>> That's usually the cause of the problem you're seeing. And if that is
>> indeed the root of your problem, you should never rely on class
>> destructors to clean up system resources. You cannot control when they
>> will be called.
>>
>
> I think you hit the problem. When I removed resources releasing from
> destructors in my bindings the error disappeared too. But frankly I
> didn't understand why it happened :( and how I should free resources
> now. What is I can rely on to clean up system resources?
> Definitly I need read the TDPL again...)
> Is it because GC may mess calling object destructors and static module
> destructors? Don't GC make difference between them and don't call their
> desctructors in predefined order (module destructors before other
> destructors for example or vice versa)?

Destructors are unreliable. There is no guarantee that a destructor will 
be called before the garbage collector is terminated. When the program 
exits, the runtime will call gc_term which will then call destructors on 
any objects that haven't yet been cleaned up. But the order in which 
those destructors are called is unpredictable. This is a recipe for all 
sorts of problems.

Static class destructors and module destructors are more reliable in 
that you know they will be called in a particular order. But, they are 
called before the gc is terminated.

Your particular problem is this. Derelict-style bindings load shared 
libraries dynamically via system calls. That means that every bound 
function is actually a function pointer. The shared library is then 
unloaded in a static module destructor. When DRuntime exits, it calls 
all the module destructors *before* calling gc_term. So what's happening is:

1. The module destructors are run
2. Derelict unloads the shared library, thereby causing all of the 
function pointers into that library to become invalid.
3. gc_term is run
4. The destructor of one of your objects is called and it tries to call 
a function from the Derelict binding, but since that function pointer is 
no longer valid, you get a segfault.

When cleaning up resources in D, you should generally not rely on class 
destructors to do so. You'll want to include some sort of process to 
clean up everything yourself. What I tend to do is something like this:

========
void term()
{
     // initiate cleanup here
}

void main()
{
     scope(exit) term();
     init();
     run();
}
========

The scope(exit) will ensure that the cleanup is run regardless of how 
the program exits. Every subsystem in my program will have term() 
function or method that substitutes for a destructor. This works fine 
and I have no problems with it.

Of course, you can still use destructors for scoped object instances in 
cases where you want RAII inside a particular scope.


More information about the Digitalmars-d-learn mailing list