www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to write a proper class destructor?

reply Bradley Smith <digitalmars-com baysmith.com> writes:
According to the documentation, a class destructor is "expected to 
release any resources held by the object."[1] However, if resources to 
be released are in objects to be garbage collected, "those references 
are no longer valid."[1]

How is a class supposed to release something for which it no longer has 
a valid reference?


   Bradley



[1] http://www.digitalmars.com/d/class.html#destructors.
Jan 24 2007
next sibling parent reply kris <foo bar.com> writes:
Bradley Smith wrote:
 According to the documentation, a class destructor is "expected to 
 release any resources held by the object."[1] However, if resources to 
 be released are in objects to be garbage collected, "those references 
 are no longer valid."[1]
 
 How is a class supposed to release something for which it no longer has 
 a valid reference?
 
 
   Bradley
 
 
 
 [1] http://www.digitalmars.com/d/class.html#destructors.

Yeah, that's a good one :) Tango has a good resolution for this, that stemmed from long discussion on the NG. Those changes never made it into phobos, IIRC
Jan 25 2007
parent reply Bradley Smith <digitalmars-com baysmith.com> writes:
kris wrote:
 Bradley Smith wrote:
 According to the documentation, a class destructor is "expected to 
 release any resources held by the object."[1] However, if resources to 
 be released are in objects to be garbage collected, "those references 
 are no longer valid."[1]

 How is a class supposed to release something for which it no longer 
 has a valid reference?


   Bradley



 [1] http://www.digitalmars.com/d/class.html#destructors.

Yeah, that's a good one :) Tango has a good resolution for this, that stemmed from long discussion on the NG. Those changes never made it into phobos, IIRC

Isn't the destructor behavior dictated by the language? How can it be fixed with a library? Replace or bypass the GC? Thanks, Bradley
Jan 25 2007
next sibling parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Bradley Smith wrote:
 kris wrote:
 Bradley Smith wrote:
 According to the documentation, a class destructor is "expected to 
 release any resources held by the object."[1] However, if resources 
 to be released are in objects to be garbage collected, "those 
 references are no longer valid."[1]

 How is a class supposed to release something for which it no longer 
 has a valid reference?


 [1] http://www.digitalmars.com/d/class.html#destructors.

Yeah, that's a good one :) Tango has a good resolution for this, that stemmed from long discussion on the NG. Those changes never made it into phobos, IIRC

Isn't the destructor behavior dictated by the language? How can it be fixed with a library? Replace or bypass the GC?

*Behavior* is dictated by the language, yes. But the *order* in which they're invoked is determined by the library (specifically, the GC) and the program (if 'delete' is used). So yes, this may be fixable by replacing the GC.
Jan 25 2007
prev sibling parent kris <foo bar.com> writes:
Bradley Smith wrote:
 kris wrote:
 
 Bradley Smith wrote:

 According to the documentation, a class destructor is "expected to 
 release any resources held by the object."[1] However, if resources 
 to be released are in objects to be garbage collected, "those 
 references are no longer valid."[1]

 How is a class supposed to release something for which it no longer 
 has a valid reference?


   Bradley



 [1] http://www.digitalmars.com/d/class.html#destructors.

Yeah, that's a good one :) Tango has a good resolution for this, that stemmed from long discussion on the NG. Those changes never made it into phobos, IIRC

Isn't the destructor behavior dictated by the language? How can it be fixed with a library? Replace or bypass the GC?

Aye -- tango has a modified GC with all kinds of goodies.
Jan 25 2007
prev sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Bradley Smith" <digitalmars-com baysmith.com> wrote in message 
news:ep9joi$1mr2$1 digitaldaemon.com...
 According to the documentation, a class destructor is "expected to release 
 any resources held by the object."[1] However, if resources to be released 
 are in objects to be garbage collected, "those references are no longer 
 valid."[1]

 How is a class supposed to release something for which it no longer has a 
 valid reference?

Huuaaaahhh! I remember someone suggesting that 'scope' be a valid modifier for class instance variables, and it'd basically mean "when this class gets destroyed, destroy this other instance as well." Like auto-deletion when you use a scope reference in a function: class ResourceHolder { scope SomeResource mRsrc; this(char[] name) { mRsrc = new SomeResource(name); } } .. { scope holder = new ResourceHolder("a.file"); ... } // holder is destroyed here, and so is its mRsrc member. Another solution would be to change the behavior of the GC so that deletion on program end _is_ deterministic. That is, it'd basically run a collection sweep, call finalizers on objects which don't have references to them, and keep doing that until nothing is left. That way, references to other classes would always be valid in destructors, since the GC wouldn't collect those references until everything that points to them is destroyed.
Jan 25 2007
next sibling parent reply Bradley Smith <digitalmars-com baysmith.com> writes:
Jarrett Billingsley wrote:
 "Bradley Smith" <digitalmars-com baysmith.com> wrote in message 
 news:ep9joi$1mr2$1 digitaldaemon.com...
 According to the documentation, a class destructor is "expected to release 
 any resources held by the object."[1] However, if resources to be released 
 are in objects to be garbage collected, "those references are no longer 
 valid."[1]

 How is a class supposed to release something for which it no longer has a 
 valid reference?

Huuaaaahhh! I remember someone suggesting that 'scope' be a valid modifier for class instance variables, and it'd basically mean "when this class gets destroyed, destroy this other instance as well." Like auto-deletion when you use a scope reference in a function:

Is scope a valid modifier for a data member? The documentation states "scope cannot be applied to globals, statics, data members, inout or out parameters".[2] In my tests, using scope on a data member compiles, but doesn't change the behavior.
 
 class ResourceHolder
 {
     scope SomeResource mRsrc;
 
     this(char[] name)
     {
         mRsrc = new SomeResource(name);
     }
 }
 
 ..
 
 {
     scope holder = new ResourceHolder("a.file");
     ...
 } // holder is destroyed here, and so is its mRsrc member.
 
 Another solution would be to change the behavior of the GC so that deletion 
 on program end _is_ deterministic.  That is, it'd basically run a collection 
 sweep, call finalizers on objects which don't have references to them, and 
 keep doing that until nothing is left.  That way, references to other 
 classes would always be valid in destructors, since the GC wouldn't collect 
 those references until everything that points to them is destroyed. 
 

Are you saying that a proper class destructor can't be written without modifying the language? Thanks, Bradley [2] http://www.digitalmars.com/d/attribute.html#scope
Jan 25 2007
next sibling parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Bradley Smith wrote:
 Jarrett Billingsley wrote:
 I remember someone suggesting that 'scope' be a valid modifier for 
 class instance variables, and it'd basically mean "when this class 
 gets destroyed, destroy this other instance as well."  Like 
 auto-deletion when you use a scope reference in a function:

Is scope a valid modifier for a data member?

No, that paragraph was about a suggestion that it *should* be :).
 Another solution would be to change the behavior of the GC so that 
 deletion on program end _is_ deterministic.  That is, it'd basically 
 run a collection sweep, call finalizers on objects which don't have 
 references to them, and keep doing that until nothing is left.  That 
 way, references to other classes would always be valid in destructors, 
 since the GC wouldn't collect those references until everything that 
 points to them is destroyed.

Are you saying that a proper class destructor can't be written without modifying the language?

I think he's saying that it can be fixed by modifying the GC (which isn't so much part of the language as it is of the standard library).
Jan 25 2007
prev sibling next sibling parent Kevin Bealer <kevinbealer gmail.com> writes:
Bradley Smith wrote:
 

 
 Are you saying that a proper class destructor can't be written without 
 modifying the language?
 
 
 Thanks,
   Bradley
 
 
 [2] http://www.digitalmars.com/d/attribute.html#scope

Some cases like closing a socket or a file opened with the unix open(2) are not affected, because the file is identified with an integer, which is stored in your class -- you can safely close it. But, even if the object is a heap object, there exists a fairly simple way to do this *without* modifying the GC algorithms. You can delay the destruction until the next GC cycle using a global table -- in the example below, the Foo object is never deleted until it is removed from the global table, which is done from the destructor, *after* the cleanup is done. Notes: 1. Don't do this for a linked list's next pointer or similar, or you will only remove one linked list element per GC run. 2. Look out for cycles -- this is essentially a reference counting technique, so cyclical data structures will never be claimed. 3. Don't reorder the lines marked A and B, or use a 'delete f' in the destructor because those actions are not safe if the GC runs while your destructor is running -- this could happen if this object is manually deleted or declared with scope, etc. class Foo { // must be called cleanup(); } class Bar { static bool[Foo] pity_the_foo; Foo f; this() { f = new Foo; pity_the_foo[f] = true; } ~this() { f.cleanup(); // A pity_the_foo.remove(f); // B // delete f; -- Don't do this } } Kevin
Jan 28 2007
prev sibling parent Kevin Bealer <kevinbealer gmail.com> writes:
Just an addendum to my previous reply - the technique I mention can also 
be used to implement 'weak pointers'.

Kevin
Jan 28 2007
prev sibling parent reply Jonas Kivi <satelliittipupu yahoo.co.uk> writes:
 "Bradley Smith" <digitalmars-com baysmith.com> wrote in message 

 How is a class supposed to release something for which it no longer has a 
 valid reference?


Jarrett Billingsley wrote:
 I remember someone suggesting that 'scope' be a valid modifier for class 
 instance variables, and it'd basically mean "when this class gets destroyed, 
 destroy this other instance as well."  Like auto-deletion when you use a 
 scope reference in a function:
 
 class ResourceHolder
 {
     scope SomeResource mRsrc;
 
     this(char[] name)
     {
         mRsrc = new SomeResource(name);
     }
 }
 
 ..
 
 {
     scope holder = new ResourceHolder("a.file");
     ...
 } // holder is destroyed here, and so is its mRsrc member.
 
 Another solution would be to change the behavior of the GC so that deletion 
 on program end _is_ deterministic.  That is, it'd basically run a collection 
 sweep, call finalizers on objects which don't have references to them, and 
 keep doing that until nothing is left.  That way, references to other 
 classes would always be valid in destructors, since the GC wouldn't collect 
 those references until everything that points to them is destroyed. 
 
 

Hmm, I think I've run into this problem just a few days ago, as my own (very basic) LinkedList implementation was getting a little bit heavy on the GC. So, I thought I might be able to make it a little bit quicker by adding a destructor and deleting the Nodes that point to the actual data (the Nodes that the LinkedList has created). But suddenly I wasn't able to iterate through the Nodes while in the destructor, and so, I wasn't able to delete them. I'm not really sure if the GC is causing the one second pauses that I get when I'm on a really heavy load doing thousands of LinkedLists and releasing them. Maybe I should try using pure D dynamic arrays instead of my own container... Or try Tango when it's released. Do the dynamic arrays in D, or the Tango containers use GC? And about the scope keyword on a class member suggestion: What about the "auto" keyword. What's the difference between the auto and the scope keywords? class Any { auto SomeOther m_someOther; this() { m_someOther = new SomeOther(); } ~this() { //automatically deletes m_someOther... } }
Jan 26 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Jonas Kivi wrote:
 And about the scope keyword on a class member suggestion: What about the 
 "auto" keyword. What's the difference between the auto and the scope 
 keywords?

"scope" is for automatic deletion. "auto" is for type inference. "auto" used to also be for automatic deletion, but its dual purpose was annoying, especially when you wanted to have both of its meanings apply (which wasn't possible). "auto" may still do automatic deletion when a type is also supplied, I'm not sure, but if so that's just for backwards compatibility. Don't use it for that in new code.
Jan 26 2007
next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Frits van Bommel" <fvbommel REMwOVExCAPSs.nl> wrote in message 
news:epcrvr$hen$1 digitaldaemon.com...

 "auto" is for type inference.

No ;) 'auto' is the default (hidden) storage class for local variables in a function. So the following declarations are equivalent: int x; auto int x; 'auto' is in the same family as 'static', 'const', and 'scope'. 'auto' does not do type inference. Type inference is triggered when there is a storage class, but no type. So these all trigger type inference: auto x = 5; static y = 10; const z = 15; scope f = new Foo(); 'auto' just happens to be the most common of the bunch, and so you see it a lot more. But it doesn't mean "infer the type."
 "auto" may still do automatic deletion when a type is also supplied, I'm 
 not sure, but if so that's just for backwards compatibility. Don't use it 
 for that in new code.

Nope.
Jan 26 2007
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Jarrett Billingsley wrote:
 "Frits van Bommel" <fvbommel REMwOVExCAPSs.nl> wrote in message 
 news:epcrvr$hen$1 digitaldaemon.com...
 
 "auto" is for type inference.

No ;) 'auto' is the default (hidden) storage class for local variables in a function. So the following declarations are equivalent: int x; auto int x; 'auto' is in the same family as 'static', 'const', and 'scope'. 'auto' does not do type inference. Type inference is triggered when there is a storage class, but no type. So these all trigger type inference: auto x = 5; static y = 10; const z = 15; scope f = new Foo(); 'auto' just happens to be the most common of the bunch, and so you see it a lot more. But it doesn't mean "infer the type."

Right, forgot about all that for a moment[1]. However, that is how it *should* be. And if you follow those rules, it works just fine, so... [1] Which is weird, since the code I'm currently working on uses 'const' for type inference wherever possible ;)
Jan 26 2007
prev sibling parent reply Jonas Kivi <satelliittipupu yahoo.co.uk> writes:
Frits van Bommel kirjoitti:
 Jonas Kivi wrote:
 And about the scope keyword on a class member suggestion: What about 
 the "auto" keyword. What's the difference between the auto and the 
 scope keywords?

"scope" is for automatic deletion. "auto" is for type inference. "auto" used to also be for automatic deletion, but its dual purpose was annoying, especially when you wanted to have both of its meanings apply (which wasn't possible). "auto" may still do automatic deletion when a type is also supplied, I'm not sure, but if so that's just for backwards compatibility. Don't use it for that in new code.

Ahh, thanks for clearing that up. I changed all the auto-keywords in my own projects code to scope. There were some 50 of them. I'm not sure. It looked like it had some effect on my program, so maybe the auto keyword doesn't even work for it's old purpose anymore? Don't know. But thanks, now I only have my destructors unfunctional.
Jan 26 2007
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Jonas Kivi wrote:
 Frits van Bommel kirjoitti:
 Jonas Kivi wrote:
 And about the scope keyword on a class member suggestion: What about 
 the "auto" keyword. What's the difference between the auto and the 
 scope keywords?

"scope" is for automatic deletion. "auto" is for type inference. "auto" used to also be for automatic deletion, but its dual purpose was annoying, especially when you wanted to have both of its meanings apply (which wasn't possible). "auto" may still do automatic deletion when a type is also supplied, I'm not sure, but if so that's just for backwards compatibility. Don't use it for that in new code.

Ahh, thanks for clearing that up. I changed all the auto-keywords in my own projects code to scope. There were some 50 of them. I'm not sure. It looked like it had some effect on my program, so maybe the auto keyword doesn't even work for it's old purpose anymore? Don't know. But thanks, now I only have my destructors unfunctional.

Well, one way to make sure a class never gets GC'ed is to make it a 'scope class'. Then you can only keep references as scope variables, and they always get explicitly deleted. The only problem is that you're then not allowed to keep them in member variables, just function-local vars.
Jan 26 2007