Jump to page: 1 2
Thread overview
another gc question
Sep 15, 2004
Toaster
Sep 15, 2004
Ben Hinkle
Sep 15, 2004
Arcane Jill
Sep 15, 2004
Toaster
Sep 15, 2004
Stewart Gordon
Sep 15, 2004
Toaster
Sep 15, 2004
Stewart Gordon
Sep 15, 2004
Toaster
Sep 15, 2004
Sean Kelly
Sep 15, 2004
Toaster
Sep 15, 2004
Sean Kelly
Sep 16, 2004
Toaster
September 15, 2004
Hello,

I have a top-to-bottom structure and I need to make sure, when one object is deleted, the object will delete all objects downstream in the structure. I implemented this kind of linked list:


class Top {

	Middle list;

	~this()
	{
		while(list) {
			Middle m = list;
			list = null;
			delete m;
		}
	}
}

class Middle {

	Top parent;

	Middle next;
	Middle previous;

	Bottom list;

	this(Top parent)
	{
		this.parent = parent;
		next = parent.list;
		if(next) next.previous = this;
		parent.list = this;
	}

	~this()
	{
		while(list) {
			Bottom b = list;
			list = null;
			delete b;
		}

		if(previous) {
			previous.next = next;
			if(next) next.previous = previous;
		} else {
			parent.list = next;
			if(next) next.previous = null;
		}
	}
}



class Bottom {

	Middle  parent;

	Bottom next;
	Bottom previous;

	this(Middle parent)
	{
		this.parent = parent;
		next = parent.list;
		if(next) next.previous = this;
		parent.list = this;
	}

	~this()
	{
		if(previous) {
			previous.next = next;
			if(next) next.previous = previous;
		} else {
			parent.list = next;
			if(next) next.previous = null;
		}
	}
}


I hope this makes sure for:

- No Middle objects and Bottom objects are collectable as long as their Top object has a valid reference because they all have references in the parent's list

- With the Top object out of scope, when the gc kicks in it picks one unpredictable object out of the structure and calls it's destructor (that's how I understood it), the destructor will delete recursively the whole part of the structure that depends on it, then it removes itself from the parent's list. If the gc collects the Top object first, the whole structure is gone.

I tried it out and appearently it works. I am still asking because I am not aware of the internal workings of «delete» and the gc. For instance, would there be any problem for the gc if a destructor deletes manually an object the gc has flagged collectable before calling that destructor? Any comments would be appreciated.



September 15, 2004
"Toaster" <wb@sapo.pt> wrote in message news:phdgk0h6akqeuu33lfkc0jnn40k67q761m@4ax.com...
>
> Hello,
>
> I have a top-to-bottom structure and I need to make sure, when one object is deleted, the object will delete all objects downstream in the structure. I implemented this kind of linked list:
>
>
> class Top {
>
> Middle list;
>
> ~this()
> {
> while(list) {
> Middle m = list;
> list = null;
> delete m;
> }
> }
> }
>
> class Middle {
>
> Top parent;
>
> Middle next;
> Middle previous;
>
> Bottom list;
>
> this(Top parent)
> {
> this.parent = parent;
> next = parent.list;
> if(next) next.previous = this;
> parent.list = this;
> }
>
> ~this()
> {
> while(list) {
> Bottom b = list;
> list = null;
> delete b;
> }
>
> if(previous) {
> previous.next = next;
> if(next) next.previous = previous;
> } else {
> parent.list = next;
> if(next) next.previous = null;
> }
> }
> }
>
>
>
> class Bottom {
>
> Middle  parent;
>
> Bottom next;
> Bottom previous;
>
> this(Middle parent)
> {
> this.parent = parent;
> next = parent.list;
> if(next) next.previous = this;
> parent.list = this;
> }
>
> ~this()
> {
> if(previous) {
> previous.next = next;
> if(next) next.previous = previous;
> } else {
> parent.list = next;
> if(next) next.previous = null;
> }
> }
> }
>
>
> I hope this makes sure for:
>
> - No Middle objects and Bottom objects are collectable as long as their Top object has a valid reference because they all have references in the parent's list
>
> - With the Top object out of scope, when the gc kicks in it picks one unpredictable object out of the structure and calls it's destructor (that's how I understood it), the destructor will delete recursively the whole part of the structure that depends on it, then it removes itself from the parent's list. If the gc collects the Top object first, the whole structure is gone.
>
> I tried it out and appearently it works. I am still asking because I am not aware of the internal workings of «delete» and the gc. For instance, would there be any problem for the gc if a destructor deletes manually an object the gc has flagged collectable before calling that destructor? Any comments would be appreciated.
>

I don't understand the motivation for worrying about this. I wouldn't have any destructors at all. If the list becomes garbage all the nodes will be collected in due time - not just one randomly chosen node. The GC calls destructors on anything about to be collected and the random part is that the nodes get collected in random order.


September 15, 2004
In article <phdgk0h6akqeuu33lfkc0jnn40k67q761m@4ax.com>, Toaster says...
>
>
>Hello,
>
>I have a top-to-bottom structure and I need to make sure, when one object is deleted, the object will delete all objects downstream in the structure.

I realise this is a really dumb question, but ... have you tried just not bothering to delete anything at all, ever? I only ask because deletion seems to be a rare requirement in D.

Jill


September 15, 2004
>I realise this is a really dumb question, but ... have you tried just not bothering to delete anything at all, ever? I only ask because deletion seems to be a rare requirement in D.
>

Sorry, I forgot about the purpose of this:  the real objects holds
windows handles, and the destructor of each class calls the API to
free that handle. But, if the Top object's handle is freed by the API
call, all Middle and Bottom handles get invalidated. So, any call to a
Middle destructor AFTER a call to it's parent Top destructor means
"Access violation" because the API doesn't check if the handle is
valid.
So I need to make sure that a Bottom destructor never gets called
after it's parents have been destroyed. I do this by making sure that
the Top or Middle destructors delete ALL dependent destructors before
freeing their own handle.


So the middle constructor would look like this:

	~this()
	{
		while(list) {
			Bottom b = list;
			list = null;
			delete b;
			/* child destructors free all child handles */
		}

		freeHandle();
		/* it's safe to free the own handle now */

		if(previous) {
			previous.next = next;
			if(next) next.previous = previous;
		} else {
			parent.list = next;
			if(next) next.previous = null;
		}
	}




September 15, 2004
Toaster wrote:
<snip>
> Sorry, I forgot about the purpose of this:  the real objects holds
> windows handles, and the destructor of each class calls the API to
> free that handle. But, if the Top object's handle is freed by the API
> call, all Middle and Bottom handles get invalidated.

Surely, if the handles are invalidated then Windows has automatically freed them at that point.  What are they handles of, for that matter?

> So, any call to a
> Middle destructor AFTER a call to it's parent Top destructor means
> "Access violation" because the API doesn't check if the handle is
> valid. So I need to make sure that a Bottom destructor never gets called
> after it's parents have been destroyed. I do this by making sure that
> the Top or Middle destructors delete ALL dependent destructors before
> freeing their own handle.

A possibility I can see is to use std.gc.addRange and removeRange to pin down each object's parent.

Another is to do all freeing of handles in the Top destructor, and do away with the others.

Stewart.

-- 
My e-mail is valid but not my primary mailbox.  Please keep replies on the 'group where everyone may benefit.
September 15, 2004
On Wed, 15 Sep 2004 16:28:38 +0100, Stewart Gordon <smjg_1998@yahoo.com> wrote:

>
>Surely, if the handles are invalidated then Windows has automatically freed them at that point.  What are they handles of, for that matter?

they are odbc handles henv, hdbc, hstmt

>A possibility I can see is to use std.gc.addRange and removeRange to pin down each object's parent.
>
>Another is to do all freeing of handles in the Top destructor, and do away with the others.
>

I would like to stick with that implementation because besides freeing all handles in the correct order by gc it allows for this:

Top top = new Top;

Middle m1 = new Middle(top);
Middle m2 = new Middle(top);

Bottom b1_1 = new Bottom(middle);

/* create a whole bunch of objects and their associated handles and work with them */

delete m4; /* delete m4 and all b4_* objects and free their handles in the correct order */

delete top; /*delete all objects and  free all handles in the correct order */

/* all objects are deleted, but after deleting top I don't need them anyway. */

My only concern is if some day before release 1.0 the manual won't say "delete cannot be used from inside a class destructor".



September 15, 2004
Toaster wrote:

<snip>
> they are odbc handles henv, hdbc, hstmt

Well, I know practically nothing about ODBC, so I can't really comment.

<snip>
> I would like to stick with that implementation because besides freeing
> all handles in the correct order by gc it allows for this:
<snip>
> My only concern is if some day before release 1.0 the manual won't say
> "delete cannot be used from inside a class destructor".

To be honest, I'm not sure about your use of delete.  I don't know if the GC is allowed to queue objects for destruction, to the effect that delete on an already queued object would have no effect.

Possibly better would be to create a .dispose() method in each class, which would call .dispose on the child objects, free its own handle and then set a 'disposed' flag.  Each class's destructor would, of course, call its own .dispose.  Of course, you'd check the flag - whether you'd do this in .dispose or in the destructor would depend depend on whether you want a slight increase in efficiency or protection against disposing something twice.

Stewart.

-- 
My e-mail is valid but not my primary mailbox.  Please keep replies on the 'group where everyone may benefit.
September 15, 2004
On Wed, 15 Sep 2004 18:02:59 +0100, Stewart Gordon <smjg_1998@yahoo.com> wrote:


>Possibly better would be to create a .dispose() method in each class, which would call .dispose on the child objects, free its own handle and then set a 'disposed' flag.  Each class's destructor would, of course, call its own .dispose.  Of course, you'd check the flag - whether you'd do this in .dispose or in the destructor would depend depend on whether you want a slight increase in efficiency or protection against disposing something twice.
>

I see what you mean, you can propagate downstream in another function that just frees the handle, that would be the best choice IF delete is not allowed in destructors. If delete can be used in destructors, then there's no point of doing so:  You would have to do the checks you described and clutter up the code; the lists would have to be maintained anyway, and the objects sit waiting for collection.

I still want to stick to the implementation:

- I know, if the object exists, the handle is still valid. On bug, instead of a reference to an object with a handle invalidated by the parent, I have a reference to a deleted object and get it debugged at once.

- I don't want to write more code if I don't have to.

Currently I delete one object and this deletes a whole tree of objects rendered useless (not just the handles), giving back the memory to the gc that can use it right away for new objects without having to run a collection. Isn't that an advantage?

Since I found nothing in documentation about delete not being allowed in destructors, I keep it for now. I just hoped somebody could tell me for sure.


September 15, 2004
In article <6gpgk0difrkkrlr0djhkkakjegdme8hfha@4ax.com>, Toaster says...
>
>On Wed, 15 Sep 2004 16:28:38 +0100, Stewart Gordon <smjg_1998@yahoo.com> wrote:
>>
>>Surely, if the handles are invalidated then Windows has automatically freed them at that point.  What are they handles of, for that matter?
>
>they are odbc handles henv, hdbc, hstmt

Why allow the destruction of a connection object to invalidate all query objects open through that connection?  I would be more inclined to do something like this:

# class ConnHandle {
# public:
#     this( HDBC h ) { handle = h; }
#     ~this() { CloseHandle( handle ); }
#     HDBC handle;
# }
#
# class Statement {
# public:
#     this( ConnHandle h ) { conn = h; }
# private:
#     ConnHandle conn;
# }
#
# class Connection {
# public:
#     this( ... ) { handle = new ConnHandle( ... ); }
#     Statement open() { return new Statement( handle ); }
# private:
#     ConnHandle handle;
# }

So the connection object can be deleted, but the connection itself is held open until all statements using that connection are deleted as well.  This may mean connections lingering for an indeterminate amount of time, but this shouldn't be a serious issue.  The alternative would be to have ConnHandle be reference counted, but the basic design would be the same.

Sean


September 15, 2004
On Wed, 15 Sep 2004 18:57:31 +0000 (UTC), Sean Kelly <sean@f4.ca>
wrote:

>Why allow the destruction of a connection object to invalidate all query objects open through that connection?  I would be more inclined to do something like this:

I don't do that. You can invalidate any query (bottom) object without
invalidating the connection (middle) object.

To be specific, if a connection object's destructor is called, it first disconnect()s from the data source, then it frees the hdbc.

If I don't finish and free the hstmt's prior to that, the disconnect() may or may not be sucessful; if not the hdbc stays connected even after destruction of it's container object, probably forever until the program exits.

But by disconnecting and freeing the hdbc, all derived hstmt handles become invalid. Now, if the gc destructs the connection object first, then the result object, the result object's hstmt will have been invalidated by the API - access violation. This can't happen if I delete all result objects prior, getting all hstmt freed, and free the hdbc then. I think it's appropriate to do so, I just copy the behaviour of the API to the classes: closing a connection renders all statements useless.


« First   ‹ Prev
1 2