www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Destructing Member Objects

reply Brian White <bcwhite pobox.com> writes:
I understand that you're not supposed to delete member within the 
destructor because, when called via the GC, it's possible that object 
has already been destructed.

Is there a way for the object to determine if it's being destructed as 
part of a GC run or if it's being deleted explicitly?

-- Brian
Apr 05 2008
next sibling parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Brian White" <bcwhite pobox.com> wrote in message 
news:ft8iuk$qvm$1 digitalmars.com...
I understand that you're not supposed to delete member within the 
destructor because, when called via the GC, it's possible that object has 
already been destructed.

 Is there a way for the object to determine if it's being destructed as 
 part of a GC run or if it's being deleted explicitly?

 -- Brian

Suggested, lauded, unread, forgotten. Just like most proposed features for D.
Apr 05 2008
prev sibling next sibling parent reply Lionello Lunesu <lio lunesu.remove.com> writes:
Brian White wrote:
 I understand that you're not supposed to delete member within the 
 destructor because, when called via the GC, it's possible that object 
 has already been destructed.

:O really? Where can I find more information about this? I think it's odd that a member can get deleted before a class that contains it. That class still has a valid pointer to it, right? So the GC could not have finalized it.. ??? L.
Apr 05 2008
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Lionello Lunesu" <lio lunesu.remove.com> wrote in message 
news:ft9350$23n8$1 digitalmars.com...
 Brian White wrote:
 I understand that you're not supposed to delete member within the 
 destructor because, when called via the GC, it's possible that object has 
 already been destructed.

:O really? Where can I find more information about this? I think it's odd that a member can get deleted before a class that contains it. That class still has a valid pointer to it, right? So the GC could not have finalized it.. ??? L.

http://www.digitalmars.com/d/1.0/class.html#destructors "When the garbage collector calls a destructor for an object of a class that has members that are references to garbage collected objects, those references are no longer valid. This means that destructors cannot reference sub objects. This is because that the garbage collector does not collect objects in any guaranteed order, so there is no guarantee that any pointers or references to any other garbage collected objects exist when the garbage collector runs the destructor for an object. This rule does not apply to auto objects or objects deleted with the DeleteExpression, as the destructor is not being run by the garbage collector, meaning all references are valid." The spec doesn't really specify _why_ destruction order is nondeterministic, however. You need nondeterministic destruction in order to break cycles. So if A points to B and B points to A, you have a cycle. In order to break the cycle you have to delete one or the other first. There's no "correct" order to destroy them. So, to solve this, you simply cannot guarantee that when an object's destructor is run, that everything that it points to is still valid. (Note, however, that system resources (file handles, Windows COM interfaces, sockets, things like that) _can_ be referenced since they weren't allocated on the GC heap. The only problem there is _when_ they're referenced, since there's no guarantee that the object containing the reference to the system resource will be collected in any timely fashion.)
Apr 05 2008
next sibling parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Sun, 06 Apr 2008 06:27:52 +0200, Jarrett Billingsley  
<kb3ctd2 yahoo.com> wrote:

 The spec doesn't really specify _why_ destruction order is  
 nondeterministic,
 however.  You need nondeterministic destruction in order to break cycles.
 So if A points to B and B points to A, you have a cycle.  In order to  
 break
 the cycle you have to delete one or the other first.  There's no  
 "correct"
 order to destroy them.  So, to solve this, you simply cannot guarantee  
 that
 when an object's destructor is run, that everything that it points to is
 still valid.

It's worth noting that even if only one object in a relation points to another (i.e. no cycle), the easiest thing for the GC to do is to flag both as trash, then destroy them in some order when it gets to that point. If it were to flag only the mother class and destroy that, its child classes might have to wait until the next GC run before they're deleted. -- Simen
Apr 06 2008
parent reply Christopher Wright <dhasenan gmail.com> writes:
Simen Kjaeraas wrote:
 On Sun, 06 Apr 2008 06:27:52 +0200, Jarrett Billingsley 
 <kb3ctd2 yahoo.com> wrote:
 
 The spec doesn't really specify _why_ destruction order is 
 nondeterministic,
 however.  You need nondeterministic destruction in order to break cycles.
 So if A points to B and B points to A, you have a cycle.  In order to 
 break
 the cycle you have to delete one or the other first.  There's no 
 "correct"
 order to destroy them.  So, to solve this, you simply cannot guarantee 
 that
 when an object's destructor is run, that everything that it points to is
 still valid.

It's worth noting that even if only one object in a relation points to another (i.e. no cycle), the easiest thing for the GC to do is to flag both as trash, then destroy them in some order when it gets to that point. If it were to flag only the mother class and destroy that, its child classes might have to wait until the next GC run before they're deleted. -- Simen

That doesn't work. The collector flags stuff only as not-trash, never as trash. Then it does a linear scan through each block containing aggregate types and calls destructors. Well, maybe not, but that's how I'd implement it, if I cared about efficiency.
Apr 06 2008
parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Mon, 07 Apr 2008 03:46:31 +0200, Christopher Wright  
<dhasenan gmail.com> wrote:

 Simen Kjaeraas wrote:
 On Sun, 06 Apr 2008 06:27:52 +0200, Jarrett Billingsley  
 <kb3ctd2 yahoo.com> wrote:

 The spec doesn't really specify _why_ destruction order is  
 nondeterministic,
 however.  You need nondeterministic destruction in order to break  
 cycles.
 So if A points to B and B points to A, you have a cycle.  In order to  
 break
 the cycle you have to delete one or the other first.  There's no  
 "correct"
 order to destroy them.  So, to solve this, you simply cannot guarantee  
 that
 when an object's destructor is run, that everything that it points to  
 is
 still valid.

another (i.e. no cycle), the easiest thing for the GC to do is to flag both as trash, then destroy them in some order when it gets to that point. If it were to flag only the mother class and destroy that, its child classes might have to wait until the next GC run before they're deleted. -- Simen

That doesn't work. The collector flags stuff only as not-trash, never as trash. Then it does a linear scan through each block containing aggregate types and calls destructors. Well, maybe not, but that's how I'd implement it, if I cared about efficiency.

You're probably right. My point was only that even if there is no circular reference, there's still a reason to have nondeterministic destruction.
Apr 06 2008
prev sibling parent "Lionello Lunesu" <lionello lunesu.remove.com> writes:
"Jarrett Billingsley" <kb3ctd2 yahoo.com> wrote in message 
news:ft9jg0$b3h$1 digitalmars.com...
 http://www.digitalmars.com/d/1.0/class.html#destructors

Thanks for that link, I should have been able to find that myself :S Guess that's a reason MS has decided to create a separate Dispose() for RAII, but I'm sure D could do a better job than that. L.
Apr 07 2008
prev sibling parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Sat, 05 Apr 2008 21:12:18 +0200, Brian White <bcwhite pobox.com> wrote:

 I understand that you're not supposed to delete member within the  
 destructor because, when called via the GC, it's possible that object  
 has already been destructed.

 Is there a way for the object to determine if it's being destructed as  
 part of a GC run or if it's being deleted explicitly?

 -- Brian

class foo { private ~this() { } public kill() { // call any specific code you want here delete this; } } Then use foo.kill() instead of delete foo. Ugly, but works, I think.
Apr 05 2008
parent reply Brian White <bcwhite pobox.com> writes:
 class foo
 {
   private ~this()
   {
   }
 
   public kill()
   {
   // call any specific code you want here
   delete this;
   }
 }
 
 Then use foo.kill() instead of delete foo. Ugly, but works, I think.

I don't think it does work. If you have a "scope" variable, for example, the compiler won't call "kill" when deleting the object. -- Brian
Apr 05 2008
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Brian White" <bcwhite pobox.com> wrote in message 
news:ft9oil$j0a$1 digitalmars.com...
 class foo
 {
   private ~this()
   {
   }

   public kill()
   {
   // call any specific code you want here
   delete this;
   }
 }

 Then use foo.kill() instead of delete foo. Ugly, but works, I think.

I don't think it does work. If you have a "scope" variable, for example, the compiler won't call "kill" when deleting the object.

You'd have to do it yourself. { auto f = new foo(); scope(exit) f.kill(); // ... }
Apr 06 2008