Thread overview
Closure/Stack-to-Heap Semantics
Sep 25, 2008
dsimcha
Sep 25, 2008
Frits van Bommel
Sep 25, 2008
downs
Sep 25, 2008
bearophile
Sep 25, 2008
Bill Baxter
Oct 03, 2008
Bruno Medeiros
September 25, 2008
If I write a loop, and want to submit each iteration of that loop to its own thread, or to a thread pool or something, how do I get D to copy the current stack frame to the heap each time I use the delegate?  For example:

import std.thread, std.stdio, std.c.time;

void main() {
    Thread[] myThreads = new Thread[100];
    foreach(i; 0..100) {
        auto T = new Thread({
            sleep(1);  //Give i time to be incremented to 100.
            writeln(i);
            return 0;
        });
        T.start;
        myThreads[i] = T;
    }
    foreach(T; myThreads) {
        T.wait;
    }
}

This program prints out all 100's because by the time each thread is done sleeping, i = 100.  How do I make D copy the stack context each time I submit a delegate to a thread, in similar fashion to a closure?
September 25, 2008
dsimcha wrote:
> If I write a loop, and want to submit each iteration of that loop to its own
> thread, or to a thread pool or something, how do I get D to copy the current
> stack frame to the heap each time I use the delegate?  For example:
> 
[snip]
> 
> This program prints out all 100's because by the time each thread is done
> sleeping, i = 100.  How do I make D copy the stack context each time I submit
> a delegate to a thread, in similar fashion to a closure?

The only way I know of to get D (presumably v2.x) to copy the stack frame to the heap is to create a delegate using it that would otherwise outlast the stack frame in question. Only one such copy is made per stack frame, however; the solution is therefore to create a separate stack frame per iteration through the loop:
-----
import std.thread, std.stdio, std.c.time;

int delegate() threadfn(int i) {
    return {
            sleep(1);  //Give i time to be incremented to 100.
            writeln(i);
            return 0;
        };
}

void main() {
    Thread[] myThreads = new Thread[100];
    foreach(i; 0..100) {
        auto T = new Thread(threadfn(i));
        T.start;
        myThreads[i] = T;
    }
    foreach(T; myThreads) {
        T.wait;
    }
}
-----
If you need to access other variables from main() in the threads, either pass a copy (or pointer) to threadfn(), or change threadfn() into a nested function in main. Remember though: any variable not accessed as a copy on threadfn's stackframe will be shared between all the threads.
September 25, 2008
dsimcha wrote:
> If I write a loop, and want to submit each iteration of that loop to its own thread, or to a thread pool or something, how do I get D to copy the current stack frame to the heap each time I use the delegate?  For example:
> 
> import std.thread, std.stdio, std.c.time;
> 
> void main() {
>     Thread[] myThreads = new Thread[100];
>     foreach(i; 0..100) {
>         auto T = new Thread({
>             sleep(1);  //Give i time to be incremented to 100.
>             writeln(i);
>             return 0;
>         });
>         T.start;
>         myThreads[i] = T;
>     }
>     foreach(T; myThreads) {
>         T.wait;
>     }
> }
> 
> This program prints out all 100's because by the time each thread is done sleeping, i = 100.  How do I make D copy the stack context each time I submit a delegate to a thread, in similar fashion to a closure?

This is how I'd do it with tools/1.0:

void main() {
  auto tp = new Threadpool(100);
  auto sem = new Semaphore;
  foreach (i; Range[0..100].endIncl) {
    tp.addTask(i /apply/ (int i) {
      sleep(1);
      writefln(i);
      sem.release;
    });
  }
  foreach (i; Range[0..100].endIncl)
    sem.acquire;
}

 (Untested)
September 25, 2008
downs:
>   foreach (i; Range[0..100].endIncl) {

That's a bit ugly. Ruby uses ... to denote an interval closed on the right and .. to denote an interval open on the right. But it's easy to miss the difference when you read code, so that too is bad design.

Python always uses intervals open on the right, avoiding problems. D slices and D2 foreach interval syntax too use the same semantics.

In my d libs I use something more readable:

foreach (i; xrange(100+1)) {

If you really really want to iterate on an interval closed on the right you can use an iterable with a different name, this helps the person that reads your code spot the difference quickly, for example:

foreach (i; xinterval(100)) {

Bye,
bearophile
September 25, 2008
On Thu, Sep 25, 2008 at 10:04 PM, bearophile <bearophileHUGS@lycos.com> wrote:
> downs:
>>   foreach (i; Range[0..100].endIncl) {
>
> That's a bit ugly. Ruby uses ... to denote an interval closed on the right and .. to denote an interval open on the right. But it's easy to miss the difference when you read code, so that too is bad design.

That would make a lot of sense if it is what Ruby actually did, but unfortunately the actual roles of ... and .. are reversed from what you just said.

E.g. from here:
http://www.techotopia.com/index.php/Ruby_Ranges
"""
  1..10    # Creates a range from 1 to 10 inclusive
  1...10   # Creates a range from 1 to 9
"""

And it's also true that it's probably too easy to overlook the difference.

--bb
October 03, 2008
dsimcha wrote:
> If I write a loop, and want to submit each iteration of that loop to its own
> thread, or to a thread pool or something, how do I get D to copy the current
> stack frame to the heap each time I use the delegate?  For example:
> 
> import std.thread, std.stdio, std.c.time;
> 
> void main() {
>     Thread[] myThreads = new Thread[100];
>     foreach(i; 0..100) {
>         auto T = new Thread({
>             sleep(1);  //Give i time to be incremented to 100.
>             writeln(i);
>             return 0;
>         });
>         T.start;
>         myThreads[i] = T;
>     }
>     foreach(T; myThreads) {
>         T.wait;
>     }
> }
> 
> This program prints out all 100's because by the time each thread is done
> sleeping, i = 100.  How do I make D copy the stack context each time I submit
> a delegate to a thread, in similar fashion to a closure?

Your code is correct and should work as you expect. It doesn't only because of a bug: http://d.puremagic.com/issues/show_bug.cgi?id=2043
The workaround is to envelop the foreach body in a function, as explained in the report.

Strangely, you'll run into another bug: writefln is not thread-safe!? You will have to envelop it with a synchronized statement. So the code looks like this:

    foreach(i; 0..100) {
        (int i) {
            writeln("(BEGIN) i: ", i, " &i: ", &i);
            auto T = new Thread({
                sleep(1);  //Give i time to be incremented to 100.
                synchronized {
                    writeln("i: ", i, " &i: ", &i);
                }
                return 0;
            });
            T.start;
            myThreads[i] = T;
        } (i);
    }




-- 
Bruno Medeiros - Software Developer, MSc. in CS/E graduate
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D