Re: Overloading Based on Constraints

2015-07-23 Thread Steven Schveighoffer via Digitalmars-d-learn

On 7/23/15 5:58 PM, jmh530 wrote:

I was looking at
http://dlang.org/concepts.html
where it discusses overloading templates based on constraints. I wanted
to try to overload a template function with another version that does
everything in place. So basically, one has a return and the other
doesn't. However, when I run the code, it only calls the first function.
The second one is ignored. I tried a number of adjustments, but the only
thing that worked is to re-name the function something else, remove the
U's, and just have it be a void function.


Just so you know, *every* template is tried for every call. So the if 
statements are not dependent on each other. Something I've wanted for a 
long time is an if-else mechanism for template constraints.


What ends up happening is you have to repeat your constraints on every 
overload, but negate the previous ones.


In any case, to your code:




import std.stdio : writeln;
import std.traits;

T test(T)(T x)
 if (isNumeric!(T))
{
 writeln(calling test without void);
 T y = x;
 y += 1;
 return y;
}

U test(T, U)(ref T x)
 if (isNumeric!(T)  is(U == void))


this will NEVER be called via IFTI, because U cannot be determined from 
the parameters. Return type (or how you use the result) does not play 
into IFTI at all.


This is likely why it's never used, because it fails to instantiate.

And note that regular overloading does not work like this. You can't 
overload on return type. Overloading on ref works, but only makes it so 
you can dictate how to handle rvalues vs. lvalues differently.


-Steve


Re: Converting uint[] slice to string for the purpose of hashing?

2015-07-23 Thread Enjoys Math via Digitalmars-d-learn

On Thursday, 23 July 2015 at 11:49:05 UTC, cym13 wrote:

On Thursday, 23 July 2015 at 11:15:46 UTC, Enjoys Math wrote:

1.  Is the best way to hash a uint[] slice

2.  How do you do it?


IIRC, std.digest functions take ubyte[] as input, so to hash a 
uint[] I would do the following:


void main(string[] args)
{
import std.conv;
import std.digest.md;

int[] a   = [1, 2, 3, 4, 5];
auto  md5 = new MD5Digest();

md5.put(a.to!(ubyte[]));

auto hash = md5.finish();
writeln(hash);
}


Thanks.  That worked.   Here's my code:

module hashtools;
import std.conv;
import std.digest.md;

string uintSliceToHash(const uint[] slice) {
auto  md5 = new MD5Digest();
md5.put(slice.to!(ubyte[]));
return md5.finish().to!(string);
}

unittest {
import std.stdio;
uint[] slice = [1,2,3,4];
writeln(uintSliceToHash(slice));
}



Re: Converting uint[] slice to string for the purpose of hashing?

2015-07-23 Thread Enjoys Math via Digitalmars-d-learn

On Thursday, 23 July 2015 at 12:10:04 UTC, Enjoys Math wrote:

On Thursday, 23 July 2015 at 11:49:05 UTC, cym13 wrote:

[...]


Thanks.  That worked.   Here's my code:

module hashtools;
import std.conv;
import std.digest.md;

string uintSliceToHash(const uint[] slice) {
auto  md5 = new MD5Digest();
md5.put(slice.to!(ubyte[]));
return md5.finish().to!(string);
}

unittest {
import std.stdio;
uint[] slice = [1,2,3,4];
writeln(uintSliceToHash(slice));
}


Actually, uint[] seems to be hashable:

import std.stdio;
int[uint[]] aa;
aa[[1,2,3]] = 5;
writeln(aa[[1,2,3]]);

WORKS


Converting uint[] slice to string for the purpose of hashing?

2015-07-23 Thread Enjoys Math via Digitalmars-d-learn

1.  Is the best way to hash a uint[] slice

2.  How do you do it?




Re: Converting uint[] slice to string for the purpose of hashing?

2015-07-23 Thread cym13 via Digitalmars-d-learn

On Thursday, 23 July 2015 at 11:15:46 UTC, Enjoys Math wrote:

1.  Is the best way to hash a uint[] slice

2.  How do you do it?


IIRC, std.digest functions take ubyte[] as input, so to hash a 
uint[] I would do the following:


void main(string[] args)
{
import std.conv;
import std.digest.md;

int[] a   = [1, 2, 3, 4, 5];
auto  md5 = new MD5Digest();

md5.put(a.to!(ubyte[]));

auto hash = md5.finish();
writeln(hash);
}



Re: Converting uint[] slice to string for the purpose of hashing?

2015-07-23 Thread Temtaime via Digitalmars-d-learn
All types are hashable and for your own structs and classes you 
can redefine opHash


How do you make a copy TO and object when you're INSIDE of it?

2015-07-23 Thread Enjoys Math via Digitalmars-d-learn

Here's my code:

module grammar;

class Grammar(T : ulong) {
this(const T[] str) {
auto grammar = str in grammarCache;

if (grammar) {
this = grammar.dup;
} else {
this = approximateSmallestGrammar(str);
grammarCache[str] = this.dup;
}
}

static Grammar approximateSmallestGrammar(const T[] str) {
return new Grammar();
}

@property Grammar dup() {

}

private:
this() {}
static Grammar[T[]] grammarCache;
};


Compiler says 'this' is not an lvalue.  How would I accomplish 
what I want?




Re: How do you make a copy TO and object when you're INSIDE of it?

2015-07-23 Thread Steven Schveighoffer via Digitalmars-d-learn

On 7/23/15 9:30 PM, Enjoys Math wrote:

Here's my code:

module grammar;

class Grammar(T : ulong) {
 this(const T[] str) {
 auto grammar = str in grammarCache;

 if (grammar) {
 this = grammar.dup;
 } else {
 this = approximateSmallestGrammar(str);
 grammarCache[str] = this.dup;
 }
 }

 static Grammar approximateSmallestGrammar(const T[] str) {
 return new Grammar();
 }

 @property Grammar dup() {

 }

private:
 this() {}
 static Grammar[T[]] grammarCache;
};


Compiler says 'this' is not an lvalue.  How would I accomplish what I want?



You're approaching this wrong. Do the lookup before deciding whether to 
instantiate a new object:


static Grammar getGrammar(const T[] str) {
 if(auto x = str in grammarCache)
 return *x;
 else
 {
 auto g = new Grammar;
 grammarCache[str] = g;
 return g;
 }
}

If you always want to dup, then do it outside the lookup. Don't do it in 
the constructor, you already have an object by then.


-Steve


Re: How do you make a copy TO and object when you're INSIDE of it?

2015-07-23 Thread Enjoys Math via Digitalmars-d-learn
On Friday, 24 July 2015 at 03:12:43 UTC, Steven Schveighoffer 
wrote:

On 7/23/15 9:30 PM, Enjoys Math wrote:

[...]


You're approaching this wrong. Do the lookup before deciding 
whether to instantiate a new object:


static Grammar getGrammar(const T[] str) {
 if(auto x = str in grammarCache)
 return *x;
 else
 {
 auto g = new Grammar;
 grammarCache[str] = g;
 return g;
 }
}

If you always want to dup, then do it outside the lookup. Don't 
do it in the constructor, you already have an object by then.


-Steve


Thanks.  That sounds like a good approach


Re: Measuring Execution time

2015-07-23 Thread Jonathan M Davis via Digitalmars-d-learn
On Thursday, July 23, 2015 13:59:11 Steven Schveighoffer via 
Digitalmars-d-learn wrote:
 On 7/22/15 5:23 AM, Clayton wrote:
  How does one represent Duration in only Micro-seconds, or milliseconds.
  Trying to measure the execution time of an algorithm and I get 4 ms,
  619 μs, and 8 hnsecs , I want to sum all these and get total hnsecs or
  μs .
 
  I would also  appreciate advise on  whether this is the best way to
  measure the execution time of an algorithm.
 
 
 
  import std.datetime;
  import std.stdio;
 
  void algorithm( ){
   writeln(Hello!);
  }
  void main(){
 
   auto stattime = Clock.currTime();
   algorithm( );
   endttime = Clock.currTime();
 
   auto duration = endttime - stattime;
 
   writeln(Hello Duration == , duration);
 
  }

 I know John identified Stopwatch, but just an FYI, Duration has the
 method total: http://dlang.org/phobos/core_time.html#.Duration.total

 I think doing:

 writeln(Hello Duration == , duration.total!usecs);

 would also work.

Yes, you could do that, but doing timing with the realtime clock is
fundamentally wrong, because the clock can change on you while you're
timing. That's why using a monotonic clock is better, since it's guaranteed
to never move backwards. Unfortunately, while StopWatch does use a monotonic
clock, it currently does that by using TickDuration for that rather than
MonoTime, so its result is a TickDuration rather than a Duration, so it's a
bit harder to use than would be nice, but it is more correct to use
StopWatch than to subtract SysTimes. Alternatively, you could just use
MonoTime directly. e.g.

auto startTime = MonoTime.currTime;
// do stuff
auto endTime = MonoTime.currTime;

audo duration = endTime - startTime;
writeln(Hello Duration == , duration.total!usecs);

in which case you get a Duration just like with subtract SysTimes, and the
suggestion of using total works just fine.

I need to put together replacements for the benchmarking functions in
std.datetime (probably in std.benchmark) which use MonoTime and Duration
rather than TickDuration so that we can deprecate the ones in std.datetime
which use TickDuration (and deprecate TickDuration itself).

- Jonathan M Davis




Re: How do you make a copy TO and object when you're INSIDE of it?

2015-07-23 Thread Jonathan M Davis via Digitalmars-d-learn
On Friday, July 24, 2015 01:30:55 Enjoys Math via Digitalmars-d-learn wrote:
 Here's my code:

 module grammar;

 class Grammar(T : ulong) {
  this(const T[] str) {
  auto grammar = str in grammarCache;

  if (grammar) {
  this = grammar.dup;
  } else {
  this = approximateSmallestGrammar(str);
  grammarCache[str] = this.dup;
  }
  }

  static Grammar approximateSmallestGrammar(const T[] str) {
  return new Grammar();
  }

  @property Grammar dup() {

  }

 private:
  this() {}
  static Grammar[T[]] grammarCache;
 };


 Compiler says 'this' is not an lvalue.  How would I accomplish
 what I want?

Assign to the members individually rather than the whole object at once.

- Jonathan M Davis



Overloading Based on Constraints

2015-07-23 Thread jmh530 via Digitalmars-d-learn

I was looking at
http://dlang.org/concepts.html
where it discusses overloading templates based on constraints. I 
wanted to try to overload a template function with another 
version that does everything in place. So basically, one has a 
return and the other doesn't. However, when I run the code, it 
only calls the first function. The second one is ignored. I tried 
a number of adjustments, but the only thing that worked is to 
re-name the function something else, remove the U's, and just 
have it be a void function.



import std.stdio : writeln;
import std.traits;

T test(T)(T x)
if (isNumeric!(T))
{
writeln(calling test without void);
T y = x;
y += 1;
return y;
}

U test(T, U)(ref T x)
if (isNumeric!(T)  is(U == void))
{
writeln(calling test with void);
x += 2;
}

void main()
{
int a = 1;
int b = test(a);
writeln(b);
int c = b;
test(c);
writeln(c);
}


Re: Converting uint[] slice to string for the purpose of hashing?

2015-07-23 Thread Jonathan M Davis via Digitalmars-d-learn
On Thursday, July 23, 2015 12:56:13 Temtaime via Digitalmars-d-learn wrote:
 All types are hashable and for your own structs and classes you
 can redefine opHash

It's toHash, actually, but yeah.

- Jonathan M Davis



Re: How to get *32mscoff libraries for phobos?

2015-07-23 Thread Taylor Hillegeist via Digitalmars-d-learn

On Thursday, 23 July 2015 at 01:43:56 UTC, Mike Parker wrote:

On Thursday, 23 July 2015 at 01:39:05 UTC, Mike Parker wrote:


post at [1] where Rainer shared the relevant bits of a batch


Gah, hate it when I forget the links.

[1] http://forum.dlang.org/post/m456t5$2jc4$1...@digitalmars.com


IT worked! Placing this Batch file in the dmd2\src Folder.

-- BEGIN FILE: BUILD.bat
set dm_make=C:\D\dmd2\windows\bin\make.exe
set DMD=C:\D\dmd2\windows\bin\dmd.exe
set cl32=C:\Program Files (x86)\Microsoft Visual Studio 
12.0\VC\bin\cl.exe
set ar32=C:\Program Files (x86)\Microsoft Visual Studio 
12.0\VC\bin\lib.exe


cd druntime
del /q errno_c.obj complex.obj
%dm_make% -f win64.mak DMD=%DMD% MODEL=32mscoff CC=\%cl32%\
if errorlevel 1 goto xit
cd ..

cd phobos
cd etc\c\zlib
%dm_make% -f win64.mak clean
cd ..\..\..
%dm_make% -f win64.mak DMD=%DMD% MODEL=32mscoff CC=\%cl32%\ 
MAKE=%dm_make% AR=\%ar32%\

if errorlevel 1 goto xit
cd ..

-- END FILE

I had to reinstall dmd (I think I messed up my dmd2\src folder 
somehow)

I used visual studio 2013 community edition.

SO. Where do i put it now that its built? I placed it in the 
dmd2\windows\lib and i still got LINK : fatal error LNK1104: 
cannot open file 'phobos32mscoff.lib'


... perhaps i need to edit the sc.ini file...


Re: Sending an immutable object to a thread

2015-07-23 Thread Frank Pagliughi via Digitalmars-d-learn

On Thursday, 23 July 2015 at 09:05:12 UTC, Marc Schütz wrote:


It is not safe, but for a different reason: `mt` is already a 
_reference_ to the actual object (that's how classes behave in 
D). This reference is located in a register or on the stack, 
and `mt` is therefore a pointer into the stack.


It's illegal to return that pointer from the function, because 
it will become invalid once the function is left. Fortunately, 
the compiler can detect simple cases like this one, and will 
refuse to compile it:


Object* foo() {
Object o;
return o;
}

xx.d(3): Error: escaping reference to local o


Very interesting. You see, I am trying to resolve the distinction 
be a value type and a reference type in D.


If Object were declared as a struct, this would make sense to 
me. The object would be created on the stack as a temporary, and 
it would disappear when the function exited. So returning a 
pointer to it would be a very, very bad thing.


But a class object is allocated in the GC heap. I would have 
guessed that, since a class reference is just essentially a 
hidden pointer, that the address-of operator '' for a class 
object would return the address into the heap... not the address 
of the reference itself! Just a little syntactic sugar.


But that's not the case. I thought this was true:
  class MyThing { ... };

  MyThing a = new MyThing,
  b = a;
  assert(a == b);  // Fails

In a weird way, that makes total sense to me, and no sense at all.

So, passing a pointer to a stack-based reference from one thread 
is another is not necessarily a good thing to do, as the original 
reference might disappear while the thread is using it.


Is there a way to get the address of the actual heap object from 
a class reference? Or am I drifting to far into the realm of 
unsafe.


This all goes back to my original question of passing an 
immutable object from one thread to another. It is simple with 
arrays, since there is a clear distinction between the array 
reference and its contents. You can easily create a mutable 
reference to immutable contents with an array.


But it seems rather convoluted with class objects.

So, in the end, it seems best to send a rebindable reference to 
the other thread, and perhaps hide the mild ugliness behind a 
library API that takes an immutable object and then sends the 
rebindable version, like:


  void send_message(Tid tid, immutable(Message) msg) {
send(tid, thisTid(), rebindable(msg));
  }

That seems easy enough.

Thanks much for all the help.



Re: C bindings: typedef struct conflicts with method

2015-07-23 Thread Jacob Carlborg via Digitalmars-d-learn

On 2015-07-23 03:57, Mike Parker wrote:


In your case, rd_kafka_metadata is the name of the struct, but in C
instances would need to be declared like so:

struct rd_kafka_metadata instance;


Since the struct is declared directly in the typedef, is the struct name 
actually available?


--
/Jacob Carlborg


Re: How to get *32mscoff libraries for phobos?

2015-07-23 Thread Taylor Hillegeist via Digitalmars-d-learn
On Thursday, 23 July 2015 at 15:39:15 UTC, Taylor Hillegeist 
wrote:
On Thursday, 23 July 2015 at 15:23:07 UTC, Taylor Hillegeist 
wrote:

I found this nugget in the sc.ini file!

[Environment32mscoff]
LIB=%@P%\..\lib32mscoff

Apparently i need to create a lib32mscoff folder in 
C:\D\dmd2\windows\


Well if its not one thing its always another :)

LINK : fatal error LNK1104: cannot open file 'LIBC.lib'

This file is not on my computer... In recent versions of VC it 
has been replaced with LIBCMT.lib (multi-thread). So not really 
sure what the right thing is to do here.


LOL... I copied LIBCMT.lib and renamed it LIBC.lib and it 
worked... so if it works? Thanks everyone!


Re: How to get *32mscoff libraries for phobos?

2015-07-23 Thread Taylor Hillegeist via Digitalmars-d-learn
On Thursday, 23 July 2015 at 14:56:48 UTC, Taylor Hillegeist 
wrote:

On Thursday, 23 July 2015 at 01:43:56 UTC, Mike Parker wrote:

[...]


IT worked! Placing this Batch file in the dmd2\src Folder.

-- BEGIN FILE: BUILD.bat
set dm_make=C:\D\dmd2\windows\bin\make.exe
set DMD=C:\D\dmd2\windows\bin\dmd.exe
set cl32=C:\Program Files (x86)\Microsoft Visual Studio 
12.0\VC\bin\cl.exe
set ar32=C:\Program Files (x86)\Microsoft Visual Studio 
12.0\VC\bin\lib.exe


[...]


I found this nugget in the sc.ini file!

[Environment32mscoff]
LIB=%@P%\..\lib32mscoff

Apparently i need to create a lib32mscoff folder in 
C:\D\dmd2\windows\


Re: How to get *32mscoff libraries for phobos?

2015-07-23 Thread Taylor Hillegeist via Digitalmars-d-learn
On Thursday, 23 July 2015 at 15:23:07 UTC, Taylor Hillegeist 
wrote:

I found this nugget in the sc.ini file!

[Environment32mscoff]
LIB=%@P%\..\lib32mscoff

Apparently i need to create a lib32mscoff folder in 
C:\D\dmd2\windows\


Well if its not one thing its always another :)

LINK : fatal error LNK1104: cannot open file 'LIBC.lib'

This file is not on my computer... In recent versions of VC it 
has been replaced with LIBCMT.lib (multi-thread). So not really 
sure what the right thing is to do here.


Re: Measuring Execution time

2015-07-23 Thread Clayton via Digitalmars-d-learn

On Wednesday, 22 July 2015 at 09:32:15 UTC, John Colvin wrote:

On Wednesday, 22 July 2015 at 09:23:36 UTC, Clayton wrote:

[...]


The normal way of doing this would be using 
std.datetime.StopWatch:


StopWatch sw;
sw.start();
algorithm();
long exec_ms = sw.peek().msecs;


Am wondering how possible is to restrict that all algorithms get 
run on a specific core( e.g. CPU 0 ) since I wanted my test run 
on the same environment.


Re: Passing struct and struct[] into a template

2015-07-23 Thread Gary Willoughby via Digitalmars-d-learn

On Wednesday, 22 July 2015 at 04:29:26 UTC, Taylor Gronka wrote:

Hi,

I have a template function, and I want it to do something if 
the input variable is a list of structs, and something else if 
the input is a struct.


[...]


Take a look at this thread, the poster had the same question:
http://forum.dlang.org/thread/djxzatqdwplocaazm...@forum.dlang.org


Re: Measuring Execution time

2015-07-23 Thread Steven Schveighoffer via Digitalmars-d-learn

On 7/22/15 5:23 AM, Clayton wrote:

How does one represent Duration in only Micro-seconds, or milliseconds.
Trying to measure the execution time of an algorithm and I get 4 ms,
619 μs, and 8 hnsecs , I want to sum all these and get total hnsecs or
μs .

I would also  appreciate advise on  whether this is the best way to
measure the execution time of an algorithm.



import std.datetime;
import std.stdio;

void algorithm( ){
 writeln(Hello!);
}
void main(){

 auto stattime = Clock.currTime();
 algorithm( );
 endttime = Clock.currTime();

 auto duration = endttime - stattime;

 writeln(Hello Duration == , duration);

}


I know John identified Stopwatch, but just an FYI, Duration has the 
method total: http://dlang.org/phobos/core_time.html#.Duration.total


I think doing:

writeln(Hello Duration == , duration.total!usecs);

would also work.

-Steve


Re: C bindings: typedef struct conflicts with method

2015-07-23 Thread Mike Parker via Digitalmars-d-learn

On Thursday, 23 July 2015 at 06:26:28 UTC, Jacob Carlborg wrote:

On 2015-07-23 03:57, Mike Parker wrote:

In your case, rd_kafka_metadata is the name of the struct, but 
in C

instances would need to be declared like so:

struct rd_kafka_metadata instance;


Since the struct is declared directly in the typedef, is the 
struct name actually available?


Yes. It's just short hand for this:

typedef struct foo foo_t;
struct foo {}


Re: Getting this to work similar to self in Python

2015-07-23 Thread Jacob Carlborg via Digitalmars-d-learn

On 2015-07-23 00:22, nurfz wrote:

I think you got overly complicated answers.


I guess I'm confused as to why the D code isn't acting similar to the
Python code in the sense that you would expect this to reference the
speed property of the current instance and not statically reference
the parent.  Am I having these issues because these attributes are being
initialized statically?


No, it's not because they're statically initialized. It's because fields 
are not polymorphic.



Would using constructors be the way to go about this? I suppose I'm just
trying to find a way to implement fairly clean and intuitive object
oriented inheritance that isn't crippled by getters/setters, is resolved
at compile time, and doesn't impose any kind of runtime cost other than
what you would assume is associated with fundamental level OOP.


Either you can set the value in the constructor or turn speed in to a 
method/property. I think it's easiest to set it in the constructor:


class Airplane : Vehicle
{
this()
{
speed = 100;
}
}

--
/Jacob Carlborg


Re: Getting this to work similar to self in Python

2015-07-23 Thread John Colvin via Digitalmars-d-learn

On Wednesday, 22 July 2015 at 22:52:22 UTC, John Colvin wrote:

On Wednesday, 22 July 2015 at 22:22:02 UTC, nurfz wrote:

[...]


Fields of classes are not in any way polymorphic in D (this is 
the same as C++ and I think java too). Base class members can 
be accessed like so:


class Vehicle {
int speed;
void printSpeed() {
writeln(this.speed);
}
}

class Airplane: Vehicle {
this()
{
Vehicle.speed = 100;
}
}

or if you really want to use the same variable name:

class Airplane: Vehicle {
alias speed = Vehicle.speed;
this()
{
speed = 100;
}
}

You can even automatically do that sort of thing by various 
means, the shortest/simplest way I can think of would be:


class Airplane: Vehicle {
private @property Vehicle base() { return this; }
alias base this;
this()
{
speed = 100;
}
}


Ok literally ignore all of that, I'm an idiot.

speed is accessible directly, without Vehicle prefix, both from 
inside Airplane and outside. However, if you declare another 
variable Airplane.speed, it will shadow* Vehicle.speed, leading 
to the behaviour you see. Use a constructor instead of declaring 
a new variable with the same name.


*as opposed to override or aliasing


It really shows how little I use classes in D...


Re: Thread pools

2015-07-23 Thread Chris via Digitalmars-d-learn

On Wednesday, 22 July 2015 at 17:01:52 UTC, Marc Schütz wrote:


You can probably simply terminate the main thread, which will 
send an OwnerTerminated message to all dependent threads. The 
threads need to `receive()` this message and terminate.


Not possible here. Main has to run the all the time, it's waiting 
for input and spawns threads accordingly.


Re: Sending an immutable object to a thread

2015-07-23 Thread via Digitalmars-d-learn

On Wednesday, 22 July 2015 at 17:17:17 UTC, Frank Pagliughi wrote:
Or, to put it another way, getting threads out of the equation, 
is this safe?


  class MyThing { ... }

  MyThing* create_a_thing() {
MyThing mt = new MyThing();
do_something_with(mt);
return mt;
  }

  void main() {
MyThing* pmt = create_a_thing();
// ...
  }

The thing will remain alive for the duration of main() ??


It is not safe, but for a different reason: `mt` is already a 
_reference_ to the actual object (that's how classes behave in 
D). This reference is located in a register or on the stack, and 
`mt` is therefore a pointer into the stack.


It's illegal to return that pointer from the function, because it 
will become invalid once the function is left. Fortunately, the 
compiler can detect simple cases like this one, and will refuse 
to compile it:


Object* foo() {
Object o;
return o;
}

xx.d(3): Error: escaping reference to local o


Re: Thread pools

2015-07-23 Thread Chris via Digitalmars-d-learn

On Wednesday, 22 July 2015 at 16:16:36 UTC, John Colvin wrote:



I would send a message to terminate to thread1, which would in 
turn send a similar message to any threads it has started, wait 
until they've all stopped (maybe with a time-out), then return.


I.e. every thread knows how to cleanly terminate itself when 
instructed, so you just send a terminate message down the tree 
of threads and then wait for the effects to bubble back up to 
main.


Thanks. I was thinking the same when I gave it a second thought 
on my way home. Instead of having a central pool, every thread is 
responsible for its own threads. So main only needs to care about 
the initial thread. That's the theory, I'll have to see how this 
works in reality.