www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - new thread with nested function

reply bobef <bobef_member pathlink.com> writes:
I don't know if this is a bug so I post it here. If new thread is started with
nested function as delegate it sees it's parents variables and class variables
(if parent is class member) but they are all 'dead'...
Apr 06 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 6 Apr 2005 20:58:28 +0000 (UTC), bobef <bobef_member pathlink.com>  
wrote:
 I don't know if this is a bug so I post it here. If new thread is  
 started with
 nested function as delegate it sees it's parents variables and class  
 variables
 (if parent is class member) but they are all 'dead'...

Do you exit the parent function after starting the thread? What is meant by "dead" exactly? Regan
Apr 06 2005
parent reply bobef <bobef_member pathlink.com> writes:
Yes I exit the parent function. And by 'dead' I mean I do not get compiler
errors but memory contained in these vairables is unsusable (corrupted, moved or
deallocated or something like that). I suppose parent's variables are released
when it gets out of scope or something like that but I think then it should be
forbidden to pass nested functions as parameters for threads or something like
that...

In article <opsoufcrkw23k2f5 nrage.netwin.co.nz>, Regan Heath says...
On Wed, 6 Apr 2005 20:58:28 +0000 (UTC), bobef <bobef_member pathlink.com>  
wrote:
 I don't know if this is a bug so I post it here. If new thread is  
 started with
 nested function as delegate it sees it's parents variables and class  
 variables
 (if parent is class member) but they are all 'dead'...

Do you exit the parent function after starting the thread? What is meant by "dead" exactly? Regan

Apr 07 2005
next sibling parent "Ben Hinkle" <ben.hinkle gmail.com> writes:
"bobef" <bobef_member pathlink.com> wrote in message 
news:d33574$1s6j$1 digitaldaemon.com...
 Yes I exit the parent function. And by 'dead' I mean I do not get compiler
 errors but memory contained in these vairables is unsusable (corrupted, 
 moved or
 deallocated or something like that). I suppose parent's variables are 
 released
 when it gets out of scope or something like that but I think then it 
 should be
 forbidden to pass nested functions as parameters for threads or something 
 like
 that...

Disallowing passing nested functions would be pretty harsh. It would be like disallowing passing pointers to local variables to functions because the pointers could be held onto and dereferenced after the local variable is no longer valid. If the documentation on threading or nested functions could include a little blurb about the dangers of passing nested functions to threads (or for that matter doing anything that runs a nested function after the stack is gone).
Apr 07 2005
prev sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 7 Apr 2005 11:23:16 +0000 (UTC), bobef <bobef_member pathlink.com>  
wrote:
 Yes I exit the parent function. And by 'dead' I mean I do not get  
 compiler
 errors but memory contained in these vairables is unsusable (corrupted,  
 moved or
 deallocated or something like that). I suppose parent's variables are  
 released
 when it gets out of scope or something like that

I suspect that's exactly what is happening.
 but I think then it should be
 forbidden to pass nested functions as parameters for threads or  
 something like
 that...

I'm with Ben on this one. Here is an example highlighting the issue: (windows only, sorry linux guys) import std.c.windows.windows; import std.stdio; import std.thread; import std.stream; struct stest { int a = 5; int run() { writefln("s",a); Sleep(1000); writefln("s",a); Sleep(1000); writefln("s",a); } } class ctest { int a = 6; int run() { writefln("c",a); Sleep(1000); writefln("c",a); Sleep(1000); writefln("c",a); } } void main() { fire(); Sleep(10000); } void fire() { Thread t; ctest b = new ctest(); stest a; t = new Thread(&a.run); t.start(); t = new Thread(&b.run); t.start(); Sleep(1000); } Regan
Apr 07 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
I forgot to mention. This example was actually my test to see what happens  
with structs vs classes. What I was trying to answer was:

1. does passing a struct delegate to a thread stop the struct from being  
free'd.
2. does passing a class delegate to a thread stop the class from being  
free'd.

The answer to #1 seems to be no. No surprises really, it's a value type  
stored on the stack, not a reference.

I wasn't sure I had answered #2, to be sure I added a fullCollect (see  
code below).

It appears passing a class delegate, prevents the class from being free'd,  
which is good, I wondered whether the delegate pointer would do that, or  
if it had been overlooked.

Have I overlooked something?

Regan

On Fri, 08 Apr 2005 00:40:33 +1200, Regan Heath <regan netwin.co.nz> wrote:
 Here is an example highlighting the issue:
 (windows only, sorry linux guys)

 import std.c.windows.windows;
 import std.stdio;
 import std.thread;
 import std.stream;

 struct stest
 {
 	int a = 5;
 	
 	int run()
 	{
 		writefln("s",a);
 		Sleep(1000);
 		writefln("s",a);
 		Sleep(1000);
 		writefln("s",a);
 	}
 }

 class ctest
 {
 	int a = 6;
 	
 	int run()
 	{
 		writefln("c",a);
 		Sleep(1000);
 		writefln("c",a);
 		Sleep(1000);
 		writefln("c",a);
 	}
 }

 void main()
 {
 	fire();

fullCollect();
 	Sleep(10000);
 }
 void fire()
 {
 	Thread t;
 	ctest b = new ctest();
 	stest a;
 	
 	t = new Thread(&a.run);
 	t.start();
 	
 	t = new Thread(&b.run);
 	t.start();
 	
 	Sleep(1000);
 }

 Regan

Apr 07 2005
next sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
If you allocated the struct on the stack, then it should not be GC'd 
until the delegate goes away.

Regan Heath wrote:
 I forgot to mention. This example was actually my test to see what 
 happens  with structs vs classes. What I was trying to answer was:
 
 1. does passing a struct delegate to a thread stop the struct from 
 being  free'd.
 2. does passing a class delegate to a thread stop the class from being  
 free'd.
 
 The answer to #1 seems to be no. No surprises really, it's a value type  
 stored on the stack, not a reference.
 
 I wasn't sure I had answered #2, to be sure I added a fullCollect (see  
 code below).
 
 It appears passing a class delegate, prevents the class from being 
 free'd,  which is good, I wondered whether the delegate pointer would do 
 that, or  if it had been overlooked.
 
 Have I overlooked something?
 
 Regan
 
 On Fri, 08 Apr 2005 00:40:33 +1200, Regan Heath <regan netwin.co.nz> wrote:
 
 Here is an example highlighting the issue:
 (windows only, sorry linux guys)

 import std.c.windows.windows;
 import std.stdio;
 import std.thread;
 import std.stream;

 struct stest
 {
     int a = 5;
     
     int run()
     {
         writefln("s",a);
         Sleep(1000);
         writefln("s",a);
         Sleep(1000);
         writefln("s",a);
     }
 }

 class ctest
 {
     int a = 6;
     
     int run()
     {
         writefln("c",a);
         Sleep(1000);
         writefln("c",a);
         Sleep(1000);
         writefln("c",a);
     }
 }

 void main()
 {
     fire();

Sleep(1000); fullCollect();
     Sleep(10000);
 }
 void fire()
 {
     Thread t;
     ctest b = new ctest();
     stest a;
     
     t = new Thread(&a.run);
     t.start();
     
     t = new Thread(&b.run);
     t.start();
     
     Sleep(1000);
 }

 Regan


Apr 07 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 07 Apr 2005 09:52:28 -0700, Russ Lewis  
<spamhole-2001-07-16 deming-os.org> wrote:
 If you allocated the struct on the stack, then it should not be GC'd  
 until the delegate goes away.

So, you think I have found a bug below? Regan
 Regan Heath wrote:
 I forgot to mention. This example was actually my test to see what  
 happens  with structs vs classes. What I was trying to answer was:
  1. does passing a struct delegate to a thread stop the struct from  
 being  free'd.
 2. does passing a class delegate to a thread stop the class from being   
 free'd.
  The answer to #1 seems to be no. No surprises really, it's a value  
 type  stored on the stack, not a reference.
  I wasn't sure I had answered #2, to be sure I added a fullCollect  
 (see  code below).
  It appears passing a class delegate, prevents the class from being  
 free'd,  which is good, I wondered whether the delegate pointer would  
 do that, or  if it had been overlooked.
  Have I overlooked something?
  Regan
  On Fri, 08 Apr 2005 00:40:33 +1200, Regan Heath <regan netwin.co.nz>  
 wrote:

 Here is an example highlighting the issue:
 (windows only, sorry linux guys)

 import std.c.windows.windows;
 import std.stdio;
 import std.thread;
 import std.stream;

 struct stest
 {
     int a = 5;
         int run()
     {
         writefln("s",a);
         Sleep(1000);
         writefln("s",a);
         Sleep(1000);
         writefln("s",a);
     }
 }

 class ctest
 {
     int a = 6;
         int run()
     {
         writefln("c",a);
         Sleep(1000);
         writefln("c",a);
         Sleep(1000);
         writefln("c",a);
     }
 }

 void main()
 {
     fire();

fullCollect();
     Sleep(10000);
 }
 void fire()
 {
     Thread t;
     ctest b = new ctest();
     stest a;
         t = new Thread(&a.run);
     t.start();
         t = new Thread(&b.run);
     t.start();
         Sleep(1000);
 }

 Regan



Apr 07 2005
next sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
EEK!  Sorry, I mistyped.  What I meant to say was that you allocate it 
on the *HEAP*, then it should not be GC'd until the delegate goes away. 
  Structs on the stack definitely *do* become invalid the moment that 
you return from the function.

Regan Heath wrote:
 On Thu, 07 Apr 2005 09:52:28 -0700, Russ Lewis  
 <spamhole-2001-07-16 deming-os.org> wrote:
 
 If you allocated the struct on the stack, then it should not be GC'd  
 until the delegate goes away.

So, you think I have found a bug below? Regan
 Regan Heath wrote:

 I forgot to mention. This example was actually my test to see what  
 happens  with structs vs classes. What I was trying to answer was:
  1. does passing a struct delegate to a thread stop the struct from  
 being  free'd.
 2. does passing a class delegate to a thread stop the class from 
 being   free'd.
  The answer to #1 seems to be no. No surprises really, it's a value  
 type  stored on the stack, not a reference.
  I wasn't sure I had answered #2, to be sure I added a fullCollect  
 (see  code below).
  It appears passing a class delegate, prevents the class from being  
 free'd,  which is good, I wondered whether the delegate pointer 
 would  do that, or  if it had been overlooked.
  Have I overlooked something?
  Regan
  On Fri, 08 Apr 2005 00:40:33 +1200, Regan Heath 
 <regan netwin.co.nz>  wrote:

 Here is an example highlighting the issue:
 (windows only, sorry linux guys)

 import std.c.windows.windows;
 import std.stdio;
 import std.thread;
 import std.stream;

 struct stest
 {
     int a = 5;
         int run()
     {
         writefln("s",a);
         Sleep(1000);
         writefln("s",a);
         Sleep(1000);
         writefln("s",a);
     }
 }

 class ctest
 {
     int a = 6;
         int run()
     {
         writefln("c",a);
         Sleep(1000);
         writefln("c",a);
         Sleep(1000);
         writefln("c",a);
     }
 }

 void main()
 {
     fire();

Sleep(1000); fullCollect();
     Sleep(10000);
 }
 void fire()
 {
     Thread t;
     ctest b = new ctest();
     stest a;
         t = new Thread(&a.run);
     t.start();
         t = new Thread(&b.run);
     t.start();
         Sleep(1000);
 }

 Regan




Apr 07 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 07 Apr 2005 16:03:28 -0700, Russ Lewis  
<spamhole-2001-07-16 deming-os.org> wrote:
 EEK!  Sorry, I mistyped.  What I meant to say was that you allocate it  
 on the *HEAP*, then it should not be GC'd until the delegate goes away.  
   Structs on the stack definitely *do* become invalid the moment that  
 you return from the function.

Yep. That's what I thought. Regan
Apr 07 2005
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <opsovvsbcn23k2f5 nrage.netwin.co.nz>, Regan Heath says...
On Thu, 07 Apr 2005 09:52:28 -0700, Russ Lewis  
<spamhole-2001-07-16 deming-os.org> wrote:
 If you allocated the struct on the stack, then it should not be GC'd  
 until the delegate goes away.

So, you think I have found a bug below?

I would expect your sample code to (probably) output: 5 6 At this point the app may print something and it may just explode, as I expect 'a' would have gone out of scope. Best case would be for it to print 6 twice more and either 5, 0, or something random for the other two lines. But none of this is guaranteed, since nothing says the new threads will actually start in the 1 second before fire() exits. Sean
Apr 07 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 7 Apr 2005 23:14:17 +0000 (UTC), Sean Kelly <sean f4.ca> wrote:
 In article <opsovvsbcn23k2f5 nrage.netwin.co.nz>, Regan Heath says...
 On Thu, 07 Apr 2005 09:52:28 -0700, Russ Lewis
 <spamhole-2001-07-16 deming-os.org> wrote:
 If you allocated the struct on the stack, then it should not be GC'd
 until the delegate goes away.

So, you think I have found a bug below?

I would expect your sample code to (probably) output: 5 6 At this point the app may print something and it may just explode, as I expect 'a' would have gone out of scope. Best case would be for it to print 6 twice more and either 5, 0, or something random for the other two lines. But none of this is guaranteed, since nothing says the new threads will actually start in the 1 second before fire() exits.

You are correct :) Regan
Apr 07 2005
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <opsou4v7p323k2f5 nrage.netwin.co.nz>, Regan Heath says...
I forgot to mention. This example was actually my test to see what happens  
with structs vs classes. What I was trying to answer was:

1. does passing a struct delegate to a thread stop the struct from being  
free'd.
2. does passing a class delegate to a thread stop the class from being  
free'd.

The answer to #1 seems to be no. No surprises really, it's a value type  
stored on the stack, not a reference.

Right.
I wasn't sure I had answered #2, to be sure I added a fullCollect (see  
code below).

It appears passing a class delegate, prevents the class from being free'd,  
which is good, I wondered whether the delegate pointer would do that, or  
if it had been overlooked.

Right. Any GCed object won't be cleaned up until all references to it are gone. The thread these references are held in is not an issue. Sean
Apr 07 2005
parent reply David Medlock <nospam nospam.com> writes:
 From reading the issues, it appears the problem is delegates are just a 
way of referencing local variables where the function was called rather 
than true closures.

For those interested, true closures allow a function to 'capture' values 
at the time the function is created(not declared), and the values it 
takes along with it are called upvalues.

A nice example of this is Paul Grahams challenge to create an accumulator:

http://store.yahoo.com/paulgraham/accgen.html

Which is possible with classes and templates, but it fairly messy.

Lua properly handles upvalues, its design is quite interesting:

http://www.tecgraf.puc-rio.br/~lhf/ftp/doc/sblp2005.pdf

-David


Sean Kelly wrote:

 In article <opsou4v7p323k2f5 nrage.netwin.co.nz>, Regan Heath says...
 
I forgot to mention. This example was actually my test to see what happens  
with structs vs classes. What I was trying to answer was:

1. does passing a struct delegate to a thread stop the struct from being  
free'd.
2. does passing a class delegate to a thread stop the class from being  
free'd.

The answer to #1 seems to be no. No surprises really, it's a value type  
stored on the stack, not a reference.

Right.
I wasn't sure I had answered #2, to be sure I added a fullCollect (see  
code below).

It appears passing a class delegate, prevents the class from being free'd,  
which is good, I wondered whether the delegate pointer would do that, or  
if it had been overlooked.

Right. Any GCed object won't be cleaned up until all references to it are gone. The thread these references are held in is not an issue. Sean

Apr 07 2005
next sibling parent Sean Kelly <sean f4.ca> writes:
In article <d34eoo$t6m$1 digitaldaemon.com>, David Medlock says...
 From reading the issues, it appears the problem is delegates are just a 
way of referencing local variables where the function was called rather 
than true closures.

They are. True closures have been discussed but are not currently a part of D. Sean
Apr 07 2005
prev sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Right.  I have been an advocate of being able to copy the stack 
variables onto the heap, so that you can create (something like?) true 
closures.  It hasn't gotten in, yet.

I was just thinking today about a complexity that I hadn't considered 
before, though.  What happens if a function has out or inout parameters? 
  If we're trying to create something where the function can do use 
(copies of) the local variables after the function has returned, then 
how do we handle these types of parameters?

For now, I'm using structs, but as you said, it's ugly.

David Medlock wrote:
  From reading the issues, it appears the problem is delegates are just a 
 way of referencing local variables where the function was called rather 
 than true closures.
 
 For those interested, true closures allow a function to 'capture' values 
 at the time the function is created(not declared), and the values it 
 takes along with it are called upvalues.
 
 A nice example of this is Paul Grahams challenge to create an accumulator:
 
 http://store.yahoo.com/paulgraham/accgen.html
 
 Which is possible with classes and templates, but it fairly messy.
 
 Lua properly handles upvalues, its design is quite interesting:
 
 http://www.tecgraf.puc-rio.br/~lhf/ftp/doc/sblp2005.pdf
 
 -David
 
 
 Sean Kelly wrote:
 
 In article <opsou4v7p323k2f5 nrage.netwin.co.nz>, Regan Heath says...

 I forgot to mention. This example was actually my test to see what 
 happens  with structs vs classes. What I was trying to answer was:

 1. does passing a struct delegate to a thread stop the struct from 
 being  free'd.
 2. does passing a class delegate to a thread stop the class from 
 being  free'd.

 The answer to #1 seems to be no. No surprises really, it's a value 
 type  stored on the stack, not a reference.

Right.
 I wasn't sure I had answered #2, to be sure I added a fullCollect 
 (see  code below).

 It appears passing a class delegate, prevents the class from being 
 free'd,  which is good, I wondered whether the delegate pointer would 
 do that, or  if it had been overlooked.

Right. Any GCed object won't be cleaned up until all references to it are gone. The thread these references are held in is not an issue. Sean


Apr 07 2005