Thread overview
Re: Bug in phobos Thread module?
May 13, 2007
Regan Heath
OT: Calling delete on 'this' from member function
May 13, 2007
Regan Heath
May 13, 2007
Vladimir Panteleev
May 13, 2007
Daniel Keep
May 14, 2007
Vladimir Panteleev
May 14, 2007
Daniel Keep
May 13, 2007
Thought you might want some test code to demonstrate the problem.

# import std.thread, std.c.time, std.c.windows.windows;
#
# class Threader : Thread {
# 	int id;
#
# 	this(int _id) { id = _id; printf("thread %d\n",id); }
# 	~this() {printf("deconstructing %d\n",id);}
#
# 	int run() {
# 		msleep(1000);
# 		CloseHandle(hdl);
# 		delete this;
# 		return 0;
# 	}
# }
#
# void main() {
# 	for(uint i = 0; i < 4000; i++) {
# 		(new Threader(i)).start;
# 		msleep(50);
# 	}
# }

If you remove the call to CloseHandle and configure task manager to show handles and threads you should see the number of threads at a constant, around 17, and the number of handles steadily climbing.

The addition of CloseHandle causes the number of handles to remain steady also, at approximately 2x the number of threads.

The correct location for the call to CloseHandle, I believe, is at the end of threadstart, as shown in my other reply.

Can someone run this same code on linux (without CloseHandle etc) and use 'top' or similar to watch the thread and handle counts, I suspect no fix is required for linux but I may be mistaken.
May 13, 2007
One further question, is it actually valid to call delete this in a non-static method of a class as shown below.  It seems to work but I would not have thought it possible...

> # class Threader : Thread {
> # 	int run() {
> # 		delete this;
> # 		return 0;
> # 	}
> # }

Regan Heath
May 13, 2007
On Sun, 13 May 2007 18:23:13 +0300, Regan Heath <regan@netmail.co.nz> wrote:

> One further question, is it actually valid to call delete this in a non-static method of a class as shown below.  It seems to work but I would not have thought it possible...

I don't see why this wouldn't work, with the condition that you don't access class fields or virtual methods after deleting the object (this includes invariants). The above might work in very specific circumstances, but you should never rely on accessing data marked as deallocated.

-- 
Best regards,
  Vladimir                          mailto:thecybershadow@gmail.com
May 13, 2007

Vladimir Panteleev wrote:
> On Sun, 13 May 2007 18:23:13 +0300, Regan Heath <regan@netmail.co.nz> wrote:
> 
>> One further question, is it actually valid to call delete this in a non-static method of a class as shown below.  It seems to work but I would not have thought it possible...
> 
> I don't see why this wouldn't work, with the condition that you don't access class fields or virtual methods after deleting the object (this includes invariants). The above might work in very specific circumstances, but you should never rely on accessing data marked as deallocated.

The problem I can see with this is that *something* must still have a
reference to the object, otherwise the method couldn't have been called.
 And if that's the case, that reference is now left dangling.  (Plus,
you can't use this on any class with invariants or they'll blow up.)

I suppose, if you were careful, it would be OK.  But frankly, it just seems wrong... a bit like returning pointers to local variables based on the assumption they get used before calling something else; technically feasible, but just *asking* for problems.

	-- Daniel

-- 
int getRandomNumber()
{
    return 4; // chosen by fair dice roll.
              // guaranteed to be random.
}

http://xkcd.com/

v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP  http://hackerkey.com/
May 14, 2007
On Mon, 14 May 2007 02:11:56 +0300, Daniel Keep <daniel.keep.lists@gmail.com> wrote:

>
>
> Vladimir Panteleev wrote:
>> On Sun, 13 May 2007 18:23:13 +0300, Regan Heath <regan@netmail.co.nz> wrote:
>>
>>> One further question, is it actually valid to call delete this in a non-static method of a class as shown below.  It seems to work but I would not have thought it possible...
>>
>> I don't see why this wouldn't work, with the condition that you don't access class fields or virtual methods after deleting the object (this includes invariants). The above might work in very specific circumstances, but you should never rely on accessing data marked as deallocated.
>
> The problem I can see with this is that *something* must still have a
> reference to the object, otherwise the method couldn't have been called.
>  And if that's the case, that reference is now left dangling.

Not really. The object context, aka "this", is nothing more than a "hidden" function parameter. Classes are just pointers to structs with VMTs. So "delete this" isn't different than deallocating something the reference to which was given to you as a function parameter. The same logic applies - the calling code shouldn't use the object reference after calling the function/method, just like you wouldn't use a reference after you delete it explicitely :)

> I suppose, if you were careful, it would be OK.  But frankly, it just seems wrong... a bit like returning pointers to local variables based on the assumption they get used before calling something else; technically feasible, but just *asking* for problems.

Returning pointers to local variables is comparable to this situation only when you actually DO use the class's fields or virtual methods after you delete it. Just like with the stack, memory has been deallocated but usually not overwritten. In pre-multi-tasking OSes (MS-DOS specifically), using data left in the callees' stacks had a real risk of being overwritten by hardware interrupt handlers.

-- 
Best regards,
  Vladimir                          mailto:thecybershadow@gmail.com
May 14, 2007

Vladimir Panteleev wrote:
> On Mon, 14 May 2007 02:11:56 +0300, Daniel Keep <daniel.keep.lists@gmail.com> wrote:
> 
>>
>> Vladimir Panteleev wrote:
>>> On Sun, 13 May 2007 18:23:13 +0300, Regan Heath <regan@netmail.co.nz> wrote:
>>>
>>>> One further question, is it actually valid to call delete this in a non-static method of a class as shown below.  It seems to work but I would not have thought it possible...
>>> I don't see why this wouldn't work, with the condition that you don't access class fields or virtual methods after deleting the object (this includes invariants). The above might work in very specific circumstances, but you should never rely on accessing data marked as deallocated.
>> The problem I can see with this is that *something* must still have a
>> reference to the object, otherwise the method couldn't have been called.
>>  And if that's the case, that reference is now left dangling.
> 
> Not really. The object context, aka "this", is nothing more than a "hidden" function parameter. Classes are just pointers to structs with VMTs. So "delete this" isn't different than deallocating something the reference to which was given to you as a function parameter. The same logic applies - the calling code shouldn't use the object reference after calling the function/method, just like you wouldn't use a reference after you delete it explicitely :)

I *know* how classes work; I wrote assembler code a while back to figure out how the D ABI worked back before it was documented :P

>> I suppose, if you were careful, it would be OK.  But frankly, it just seems wrong... a bit like returning pointers to local variables based on the assumption they get used before calling something else; technically feasible, but just *asking* for problems.
> 
> Returning pointers to local variables is comparable to this situation only when you actually DO use the class's fields or virtual methods after you delete it. Just like with the stack, memory has been deallocated but usually not overwritten. In pre-multi-tasking OSes (MS-DOS specifically), using data left in the callees' stacks had a real risk of being overwritten by hardware interrupt handlers.

My problem with this is basically the same reason Joel asserts that exceptions are evil: it's not that it necessarily introduces bugs or bad behaviour, but it sure as hell helps mask it!

For instance:

foo.run();

What about that line indicates that "foo" is no longer a valid reference?  Absolutely nothing; you could argue that "the docs should state that the object deletes itself", but let's face it: reading the documentation is the *last* thing most people do.  Programmers are lazy by their very definition (if we *weren't* lazy, we wouldn't be spending all this time and effort trying to get computers to do stuff for us.)

Hell, I know I've looked at code before that did something sneaky and completely missed what it was doing, causing me no end of grief: and that's on code I wrote myself!

On the other hand:

foo.runThenDelete();

Is marginally better, but it's still obfuscating the deletion; what's it deleting?

foo.run();
delete foo;

Is not that much more typing, and is explicit about what's going on.

Anyway, just my AU$0.02 :)

	-- Daniel

-- 
int getRandomNumber()
{
    return 4; // chosen by fair dice roll.
              // guaranteed to be random.
}

http://xkcd.com/

v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP  http://hackerkey.com/