Jump to page: 1 2 3
Thread overview
Destructor Bug?
Jul 31, 2005
AJG
Jul 31, 2005
Uwe Salomon
Jul 31, 2005
AJG
Jul 31, 2005
Uwe Salomon
Jul 31, 2005
AJG
Aug 01, 2005
Uwe Salomon
Jul 31, 2005
Uwe Salomon
Jul 31, 2005
AJG
Jul 31, 2005
Dave
Jul 31, 2005
AJG
Aug 01, 2005
Manfred Nowak
Aug 01, 2005
AJG
Aug 01, 2005
Manfred Nowak
Aug 01, 2005
Regan Heath
Aug 01, 2005
AJG
Aug 01, 2005
Manfred Nowak
Aug 01, 2005
Ben Hinkle
Ordered Destruction was: Re: Destructor Bug?
Aug 01, 2005
Russ Lewis
Aug 02, 2005
Walter
Aug 02, 2005
AJG
Aug 02, 2005
Uwe Salomon
Aug 02, 2005
AJG
Aug 02, 2005
Uwe Salomon
Aug 02, 2005
AJG
Aug 02, 2005
Uwe Salomon
Aug 02, 2005
Ben Hinkle
Aug 02, 2005
Holger
July 31, 2005
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
> 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
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
> 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
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
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
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
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
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
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