View mode: basic / threaded / horizontal-split · Log in · Help
July 31, 2005
Destructor Bug?
Hi,

I took me a while to find and reduce this to a minimal example. I was going nuts
thinking it was my database abstraction layer. I think it's a bug in D.

# // test.d 
# class Test {
#     Object obj = null;
#     this () { obj = new Object(); }
#     ~this() { obj.toString();     }
# }
#
# void main() { new Test(); }

Segmentation Fault w/ DMD 0.128 on linux.

Cheers,
--AJG.
July 31, 2005
Re: Destructor Bug?
> I took me a while to find and reduce this to a minimal example. I was  
> going nuts
> thinking it was my database abstraction layer. I think it's a bug in D.
>
> # // test.d
> # class Test {
> #     Object obj = null;
> #     this () { obj = new Object(); }
> #     ~this() { obj.toString();     }
> # }
> #
> # void main() { new Test(); }
>
> Segmentation Fault w/ DMD 0.128 on linux.

http://www.digitalmars.com/d/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."

So this is documented and correct behaviour.

Ciao
uwe
July 31, 2005
Re: Destructor Bug?
Hi,

>http://www.digitalmars.com/d/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."
>
>So this is documented and correct behaviour.

Is there any way around this? I'd like to close a database connection upon
destruction, but this won't let me.

In addition, what's the point of a destructor that's can't access sub-objects?
How would you do clean-up operations that are not strictly memory-related? Like
clearing a mutex, or releasing a lock on a shared resource (such as a hardware
device), or performing database finalization (closing connections, committing
changes, etc.)?

Is there a preferred way of doing that right now?

Thanks for the help!
--AJG.
July 31, 2005
Re: Destructor Bug?
> Is there any way around this? I'd like to close a database connection  
> upon destruction, but this won't let me.

I had a similar problem when implementing signals&slots. You can create  
the sub-object on the C heap with malloc(). Or close the connection in the  
sub-object's destructor.

This problem can happen if there are difficult relations between objects,  
but mostly it shows imperfect design, i think. If a class owns a child,  
and it has to call finalization routines of it when being destructed, that  
somehow means that the child does not fully take care of its own  
destruction, right? So it does not fully protect its invariant. Either  
that should be changed, or if that is not possible a sub-struct should be  
used, and the parent class takes care of protecting the invariant.

Ciao
uwe
July 31, 2005
Re: Destructor Bug?
To answer your other question fully:

> In addition, what's the point of a destructor that's can't access  
> sub-objects?

It would make the job of the garbage collector more difficult, i guess.

> How would you do clean-up operations that are not strictly  
> memory-related? Like
> clearing a mutex, or releasing a lock on a shared resource (such as a  
> hardware
> device), or performing database finalization (closing connections,  
> committing changes, etc.)?

I can only think of the following solutions:

* Allocate the objects somewhere else than the GC heap.
* Use structs. This is somehow similar to the next one:
* Change the design. Example: a file object could have a "autoClose"  
property that is set to true by the parent if necessary, and the file  
destructor automatically closes the attached handle in this case.

The third one should be the preferred, of course.

Ciao
uwe
July 31, 2005
Re: Destructor Bug?
In article <dcj0gn$c7r$1@digitaldaemon.com>, AJG says...
>
>Hi,
>
>>http://www.digitalmars.com/d/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."
>>
>>So this is documented and correct behaviour.
>
>Is there any way around this? I'd like to close a database connection upon
>destruction, but this won't let me.
>
>In addition, what's the point of a destructor that's can't access sub-objects?
>How would you do clean-up operations that are not strictly memory-related? Like
>clearing a mutex, or releasing a lock on a shared resource (such as a hardware
>device), or performing database finalization (closing connections, committing
>changes, etc.)?
>
>Is there a preferred way of doing that right now?
>
>Thanks for the help!
>--AJG.
>
>

Would something like this cause a complete re-write for you?

#class DBConn
#{
#    this() { ... }
#    ~this() { close(); }
#    void open() { ...  }
#    void close() { ... }
#}

#class Test
#{
#    DBConn obj;
#    this () { obj = new DBConn(); }
#    ~this() { delete obj; }
#}

#void main() { new Test(); }

I would think this would be the 'preferred' pattern since (IMHO) an object like
DBConn should always clean-up after itself anyhow.

Calling delete on the object is allowed, the object just can't be dereferenced
in the parent dtor.

- Dave
July 31, 2005
Re: Destructor Bug?
Hi,

>Would something like this cause a complete re-write for you?
>
>#class DBConn
>#{
>#    this() { ... }
>#    ~this() { close(); }
>#    void open() { ...  }
>#    void close() { ... }
>#}
>
>#class Test
>#{
>#    DBConn obj;
>#    this () { obj = new DBConn(); }
>#    ~this() { delete obj; }
>#}
>
>#void main() { new Test(); }
>
>I would think this would be the 'preferred' pattern since (IMHO) an object like
>DBConn should always clean-up after itself anyhow.

The problem is that the object I'm using is not my own. I'm using Jeremy
Cowgar's DDBI, which has explicit connect() and close() functions, and no
destructor.

What I had before worked because I inherited from the object, and I could call
close() as my own function in my own destructor. But this forced me to inherit
from a specific DB, like MySQL.

To enable support for any DB type (PG, SQLite, etc.), I need to contain the
database as a sub-object, and then create it depending on a parameter. When I
made this change, the segmentation faults started because of the problem in the
destructor.

I could hack DDBI to auto-close the connection, but I'd rather not, since that's
not its interface. In my opinion destructors are broken, and very limited. We
should be able to reference sub-objects that don't do automatic cleanup
precisely for cases like this.

Thanks for the help.
--AJG.
July 31, 2005
Re: Destructor Bug?
Hi,

>> Is there any way around this? I'd like to close a database connection  
>> upon destruction, but this won't let me.
>
>I had a similar problem when implementing signals&slots. You can create  
>the sub-object on the C heap with malloc(). Or close the connection in the  
>sub-object's destructor.

Hm... maybe this could work. What's the equivalent code using malloc? I mean,
how do you call the object's constructor(s) using this method?

>This problem can happen if there are difficult relations between objects,  
>but mostly it shows imperfect design, i think. If a class owns a child,  
>and it has to call finalization routines of it when being destructed, that  
>somehow means that the child does not fully take care of its own  
>destruction, right? So it does not fully protect its invariant. Either  
>that should be changed, or if that is not possible a sub-struct should be  
>used, and the parent class takes care of protecting the invariant.

It very well might be a problem in one class' design. However, just because this
is the case, doesn't mean we shouldn't be allowed to do it. We should be
allowed, precisely to fix this kind of problem when the code is not ours. What
about legacy code and libraries? Sometimes these things don't follow ideal OO
design, and as it stands, we can't properly fix it in the destructor.

In addition, what about when clean-up isn't that simple? What if there are
options that need to be specified? As a trivial example, you could have:

void DB::close(bool commit) {
if (commit) m_Conn.commit();
m_Conn.close();
}

Sure, you could move those parameters to the constructor as AutoCommit, but why
move finalization logic to the constructor? IMO that's why we have destructors,
to fully take care of cleanup for objects that don't do it easily and
automatically.

Thanks for the help,
--AJG.
July 31, 2005
Re: Destructor Bug?
Hi,

>To answer your other question fully:
>
>> In addition, what's the point of a destructor that's can't access  
>> sub-objects?
>
>It would make the job of the garbage collector more difficult, i guess.

I wonder whether it makes it trivially more difficult or not. It could also be
that Walter specifially doesn't want that kind of behaviour in the destructor
(for philosophical reasons, a la opAssign). Although that would just be mean. ;)

If I'm not mistaken both C++ and C# allow for sub-object references in the
destructor, right?

>> How would you do clean-up operations that are not strictly  
>> memory-related? Like
>> clearing a mutex, or releasing a lock on a shared resource (such as a  
>> hardware
>> device), or performing database finalization (closing connections,  
>> committing changes, etc.)?
>
>I can only think of the following solutions:
>
>* Allocate the objects somewhere else than the GC heap.

Yeah, this could be an option. I posted a question about this on the other
message.

>* Use structs. This is somehow similar to the next one:
>* Change the design. Example: a file object could have a "autoClose"  
>property that is set to true by the parent if necessary, and the file  
>destructor automatically closes the attached handle in this case.
>The third one should be the preferred, of course.

Unfortunately, I can't do such a thing because it's not my code.

Thanks for the help,
--AJG.
August 01, 2005
Re: Destructor Bug?
AJG <AJG_member@pathlink.com> wrote:

[...]
> In my opinion destructors are broken, and very limited. We
> should be able to reference sub-objects that don't do automatic
> cleanup precisely for cases like this. 

Why is it not possible to embed the foreign object into a class with 
a subclass, whose destrcutor calls the cleanup procedure of the 
foreign object?

Then because the subclass' destructor is executed before the 
embedding class' destructor the cleanup procedure will be executed 
before the foreign object is destroyed.

-manfred
« First   ‹ Prev
1 2 3
Top | Discussion index | About this forum | D home