www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Object destruction versus finalization

reply "Florian" <santa nortpole.org> writes:
I played around a little and figured out, that destructors in D 
work quite similarily to destructors in C++. They are invoked, 
after the members of the instance being destructed have been 
destroyed themselfes (or at least have been brought into an 
invalid state). Therefore, these members cannot be accessed 
savely from inside the destructor.

In contrast, Java and C# offer a concept called finalizer. When 
the finalizer is called, all members of the instance itself are 
still valid and are guaranteed to be freely accessible. This 
behaviour can be useful for some cleanup operations.

Managed C++ offers both, C#-like finalizers and conventional C++ 
destructors, where the destructor is called ~className and the 
finalizer is called !className. Is their a best practice to 
mimikry this behaviour, i.e. to call a certain block of code when 
an object is being garbage collected, but before its contents 
render invalid?

Any hints would be greatly appreciated.
Nov 12 2013
next sibling parent reply "Dicebot" <public dicebot.lv> writes:
On Tuesday, 12 November 2013 at 20:15:02 UTC, Florian wrote:
 I played around a little and figured out, that destructors in D 
 work quite similarily to destructors in C++. They are invoked, 
 after the members of the instance being destructed have been 
 destroyed themselfes (or at least have been brought into an 
 invalid state). Therefore, these members cannot be accessed 
 savely from inside the destructor.
What made you think so? It must be other way around. Destructor is expected to release resources held by object, it is necessary that those resources are still valid at destructor call point. It would have been completely against the mode of operations of garbage collector.
Nov 12 2013
parent reply "Florian" <santa nortpole.org> writes:
On Tuesday, 12 November 2013 at 20:29:13 UTC, Dicebot wrote:
 On Tuesday, 12 November 2013 at 20:15:02 UTC, Florian wrote:
 I played around a little and figured out, that destructors in 
 D work quite similarily to destructors in C++. They are 
 invoked, after the members of the instance being destructed 
 have been destroyed themselfes (or at least have been brought 
 into an invalid state). Therefore, these members cannot be 
 accessed savely from inside the destructor.
What made you think so? It must be other way around. Destructor is expected to release resources held by object, it is necessary that those resources are still valid at destructor call point. It would have been completely against the mode of operations of garbage collector.
Let me explain this by discussing the example below. I created method stubs, which write single lines to the console, so it is quite easy to follow the control flow. The main method creates an instance of a type "Session", which itself has a member of type "Connection". Let us assume, that I want to properly shutdown the "Connection" instance by invoking its methods, when the "Session" is torn down. The example below prints the following output: ~Connection ~Session segmentation fault This means, the destructor of "Connection" is invoked *BEFORE* the destructor of "Session" is called. This yields to an invalid reference for the call of the shutdown() method, leading to the segmentation fault. Of course, in a scenario as simple as that, it would be possible to move the shutdown() sequence into the destructor of the "Connection" class. However, doing so is not desirable. Just picture, that we want to set some timeout parameters or the like for the shutdown depending on the state of the "Session". This would become really messy. And be assured, I have a more complex scenario, where I definitely want to invoke methods on memeber instances when an instance is destroyed. I am convinced there is a reason for finalizing objects like Java, C# and managed C++ do. Example: import std.stdio; class Connection { this() { } void shutdown() { writeln("shutdown"); } ~this() { writeln("~Connection"); } } class Session { Connection connection; this() { connection = new Connection(); } ~this() { writeln("~Session"); connection.shutdown(); // -> segmentation fault } } void main() { auto session = new Session(); }
Nov 12 2013
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, November 12, 2013 23:40:24 Florian wrote:
 it would be possible to move the shutdown() sequence into the
 destructor of the "Connection" class.
Classes in D do not have destructors. Only structs to. ~this is a destructor in a struct, but it's a finalizer in a class. Finalizers are not guaranteed to be run, and they can't do anything with GC memory (be it allocating it, deallocating it, or using it) unless they're begging for trouble, because the GC is free to collect any GC-allocated objects before calling any finalizers (which avoids circular reference problems). As such, class finalizers are really only good for managing non-GC resources. - Jonathan M Davis
Nov 12 2013
parent reply "Florian" <santa nortpole.org> writes:
I understood very well, that the garbage collector is not 
guaranteed to run. However, it does not explain the segmentation 
fault in my example, does it?
Nov 12 2013
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, November 13, 2013 00:07:12 Florian wrote:
 I understood very well, that the garbage collector is not
 guaranteed to run. However, it does not explain the segmentation
 fault in my example, does it?
You're getting a segfault, because you're using something which is on the GC heap - namely the Connection that you allocated with new. You can't do that in finalizer, because the GC can choose to free it before the finalizer even runs (this avoids issues with circular references). You can only access stuff in the finalizer which is part of the class (and not on the GC heap aside from the fact that the instance of the class is itself on the GC heap) or which is not managed by the GC at all (e.g. it was malloced, or it was some other sort of system resource that the GC doesn't manage). If you want your Connection object to shutdown when it's collected, you're going to need to do that in its own finalizer, not in the finalizer of a class that's using it. - Jonathan M Davis
Nov 12 2013
next sibling parent "Dicebot" <public dicebot.lv> writes:
On Tuesday, 12 November 2013 at 23:18:11 UTC, Jonathan M Davis 
wrote:
 You can't do that in
 finalizer, because the GC can choose to free it before the 
 finalizer even runs
 (this avoids issues with circular references).
Ah, damn, have forgotten about it. Disregard previous post.
Nov 12 2013
prev sibling parent "Florian" <santa nortpole.org> writes:
Jonathan,
Dicebot,

thank you very much for your response. So you are confirming my 
conclusion, that finalizers/destructors in D work pretty much 
like in C++ and there is no way to do Java/C#/Managed C++-like 
finalization.

For the records, since Dicebot asked: I am using DMD32 v2.063.2 
on Debian 7.2

All the best,

Florian.
Nov 13 2013
prev sibling parent "Dicebot" <public dicebot.lv> writes:
On Tuesday, 12 November 2013 at 22:40:26 UTC, Florian wrote:
 The example below prints the following output:
     ~Connection
     ~Session
     segmentation fault
Same example prints this for me (no segfault): ~Session shutdown ~Connection 2.064.2 linux-64 What is your system / compiler? Output does not seem right at all.
Nov 12 2013
prev sibling parent reply "Michael" <pr m1xa.com> writes:
Additional discussions
http://forum.dlang.org/thread/jpl93e$1mns$1 digitalmars.com
and
http://forum.dlang.org/thread/l3dj7b$2tvc$1 digitalmars.com
Nov 13 2013
parent "Florian" <santa nortpole.org> writes:
Michael,

thank you for these links.

Regards, Florian.
Nov 14 2013