digitalmars.D - Closure/Stack-to-Heap Semantics
- dsimcha (22/22) Sep 24 2008 If I write a loop, and want to submit each iteration of that loop to its...
- Frits van Bommel (32/40) Sep 25 2008 The only way I know of to get D (presumably v2.x) to copy the stack
- downs (16/41) Sep 25 2008 This is how I'd do it with tools/1.0:
- bearophile (9/10) Sep 25 2008 That's a bit ugly. Ruby uses ... to denote an interval closed on the rig...
- Bill Baxter (12/15) Sep 25 2008 That would make a lot of sense if it is what Ruby actually did, but
- Bruno Medeiros (25/50) Oct 03 2008 Your code is correct and should work as you expect. It doesn't only
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?
Sep 24 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.
Sep 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)
Sep 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
Sep 25 2008
On Thu, Sep 25, 2008 at 10:04 PM, bearophile <bearophileHUGS lycos.com> wrote:downs: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 """ """ And it's also true that it's probably too easy to overlook the difference. --bbforeach (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.
Sep 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?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
Oct 03 2008