January 21, 2010
----- Original Message ----

> From: Andrei Alexandrescu <andrei at erdani.com>
>
> Sean Kelly wrote:
> > On Jan 21, 2010, at 12:35 PM, Steve Schveighoffer wrote:
> >> Maybe in the event that the threads exit in the order you join them, but the
> end result is the main thread resumes immediately after the last thread exits. Calling join on a finished thread does not require any context switching or messages, it simply returns immediately with the exit code.
> > 
> > Yeah exactly.  Joining in a loop shouldn't be noticeably slower than joining
> all in parallel.
> 
> Not if they want to close sockets gracefully or best-effort-gracefully as in your destructor example. Actually your own destructor example ruins your point because it has a relatively high latency!!!

Yeah, but joining one thread does not mean "Hey OS, only run this one thread until it exits."  All the other threads can shut down in parallel (as they will because they just got the shutdown message prior to the join loop).  Then once your really long thread exits, the rest of the joins are relatively instantaneous.  In other words, the effect of the loop waits until the thread that takes the longest terminates.  It's the same effect as an OS primitive that does the same, except as Michel pointed out, the scheduler may pull some tricks to make it insignificantly faster.

-Steve




January 21, 2010

Steve Schveighoffer wrote:
> ----- Original Message ----
> 
>> From: Andrei Alexandrescu <andrei at erdani.com>
>>
>> Sean Kelly wrote:
>>> On Jan 21, 2010, at 12:35 PM, Steve Schveighoffer wrote:
>>>> Maybe in the event that the threads exit in the order you join them, but the
>> end result is the main thread resumes immediately after the last thread exits. Calling join on a finished thread does not require any context switching or messages, it simply returns immediately with the exit code.
>>> Yeah exactly.  Joining in a loop shouldn't be noticeably slower than joining
>> all in parallel.
>>
>> Not if they want to close sockets gracefully or best-effort-gracefully as in your destructor example. Actually your own destructor example ruins your point because it has a relatively high latency!!!
> 
> Yeah, but joining one thread does not mean "Hey OS, only run this one thread until it exits."  All the other threads can shut down in parallel (as they will because they just got the shutdown message prior to the join loop).  Then once your really long thread exits, the rest of the joins are relatively instantaneous.  In other words, the effect of the loop waits until the thread that takes the longest terminates.  It's the same effect as an OS primitive that does the same, except as Michel pointed out, the scheduler may pull some tricks to make it insignificantly faster.

You're right. I was arguing on the code:

broadcastShutdown();
foreach (thread; threads) join(thread);

but somehow I had in mind:

foreach (thread; threads) {
     notifyShutdown(thread);
     join(thread);
}

Apologies.

Nonetheless, Michel makes a good point about boosting thread priorities. How about that? :o)


Andrei
January 21, 2010
----- Original Message ----

> From: Andrei Alexandrescu <andrei at erdani.com>
> Steve Schveighoffer wrote:
> > 
> > Yeah, but joining one thread does not mean "Hey OS, only run this one thread
> until it exits."  All the other threads can shut down in parallel (as they will because they just got the shutdown message prior to the join loop).  Then once your really long thread exits, the rest of the joins are relatively instantaneous.  In other words, the effect of the loop waits until the thread that takes the longest terminates.  It's the same effect as an OS primitive that does the same, except as Michel pointed out, the scheduler may pull some tricks to make it insignificantly faster.
> 
> You're right. I was arguing on the code:
> 
> broadcastShutdown();
> foreach (thread; threads) join(thread);
> 
> but somehow I had in mind:
> 
> foreach (thread; threads) {
>     notifyShutdown(thread);
>     join(thread);
> }
> 
> Apologies.

OK, no problem.  I'd rather be sure than drop the argument, which is why I continued it :)

> Nonetheless, Michel makes a good point about boosting thread priorities. How about that? :o)

It might be a time saver, but probably it's insignificant (as Michel also mentions).  A thread not marked runnable doesn't run no matter how high it's priority is :)  In other words, if you call WaitForMultipleObjects on your list of threads, your thread isn't going to be marked runnable until they all exit anyway, so inverting the priority doesn't do much.  I'd be wary of altering the priorities of those waited threads above other threads.

But besides that, having the function doesn't hurt, calling joinAll(InputRange!Tid) is at least easier to write than a foreach loop, and it does provide an entry point for OSes that support a wait for multiple threads.  I'm not opposed to the function itself, I just was confused when you said it was way faster than a loop :)

-Steve




January 21, 2010
On Thu, Jan 21, 2010 at 3:14 AM, Andrei Alexandrescu <andrei at erdani.com>wrote:

> Kevin Bealer wrote:
>
>> So lets say that threads will normally fall into the three camps, that I'll call producers, filters and processors.
>>
> [snip]
>
> Main could check if it has any dependents, if desired... (these last bits
>> is kind of off the cuff, so maybe there are huge problems with them.)
>>
>
> This makes sense, but at this point I'd avoid classifications that don't have a precedent. If Erlang made it without thread kinds, we should too.
>
>
>
> Andrei
>

Just to clarify, I wasn't proposing that threads be labelled as to what type they are and handled differently by the language or system.  I'm just defining some terms so that I can reason about their behavior.  My argument was just that a user writing main typically needs to keep track of the producers in some way to insure that his design behaved predictably.

Now that I mention it, this really includes any object that might produce an important flow of data without being triggered to do so by an incoming message.  Message originators might be a better term than Producers.

Kevin
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/dmd-concurrency/attachments/20100121/b46f36dd/attachment-0001.htm>
January 21, 2010
Le 2010-01-21 ? 15:14, Andrei Alexandrescu a ?crit :

> I agree we shouldn't put too much faith in the default shutdown method. I just want to choose a default that's reasonable.
> 
> Let me make a counterexample: pthreads have terrible defaults, i.e. it tears down everything without warning when main() exits. Virtually every example in Butenhof's book must use calls that override that crappy default. Look e.g. here:
> 
> http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html
> 
> /* Wait till threads are complete before main continues. Unless we  */ /* wait we run the risk of executing an exit which will terminate   */ /* the process and all threads before the threads have completed.   */
> 
>   pthread_join( thread1, NULL);
>   pthread_join( thread2, NULL);
> 
>   exit(0);
> 
> Not to mention that that Einstein calls exit(0) as the last line in main().

That's C. In C there is no static module destructor running at the end. It looks like a way to make the point explicitly clear that all threads get trashed after this. Calling "exit(0)" in main isn't so bad, it's the same "return 0;". But I agree that it sets a bad example for the general case where you are not in main.


> So I'd be happy to define a reasonable default that does allow simple, meaningful applications to be written, without disallowing people who write servers from doing what they want to do.

Well, you don't have much choice. Either you kill the threads or you wait for them to terminate. We could add an optional timeout to the shutdown procedure, but I think the default should be to never timeout.

I think my Thread Termination Protocol covers termination in a scalable manner by sending termination messages to all threads. For threads not listening to messages, you need another shutdown mechanism. Sometime you can use a sudden termination flag. Here is how it could work:

	void someEventHandlingThread() {
		while (1) {
			allowSuddenTermination = true;
			auto event = waitForSomeEvent();

			// avoid terminating the thread in the middle of event handling.
			allowSuddenTermination = false;
			witch (event.type) {
				case DONE:
					return;
				case HELLO:
					runHelloHandshakeWithNeighbor();
					break;
			}

		}
	}

Pretty easy to use: you just set a flag to true while the thread can be suddenly terminated and false when you don't want it to be interrupted. Assuming it doesn't hold any lock while sudden termination is allowed, the shutdown procedure can just freeze the thread before calling safely the module destructors.

Other threads that need to be shutdown includes threads spawn by external libraries, not all written in D. For those written in D, they should answer to the Terminate message. For those not written in D, you'll always need custom cleanup code, although a D wrapper could make them respond to a Terminate message.

Concentrating on the shutdown procedure as a mean to make the application quit faster has its limit if you want to keep things clean. I'll say that the best way to make sure an app closes rapidly is to make sure each thread can terminate rapidly. We should concentrate on making it easy to write threads that terminate rapidly by making blocking APIs that do the right thing. But that's probably another subject for another thread, a mailing list thread I mean. :-)

-- 
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/



February 09, 2010
On Jan 21, 2010, at 12:37 PM, Andrei Alexandrescu wrote:

> Sean Kelly wrote:
>> On Jan 21, 2010, at 12:26 PM, Steve Schveighoffer wrote:
>>> If a thread has exited, it takes probably a few hundred cycles to return, probably not 100ms.  If it has not exited, no amount of parallelism is going to save you from waiting for the thread to exit.
>>> 
>>> A join sends no messages or anything, it simply waits until the thread has exited and deposited it's return code, then returns the return code.  While you are joining a slow-to-exit thread, all your other threads have exited, so in essence the parallelism occurs because the broadcast of the shutdown gets all the threads ready to be joined.  I don't see any benefit to joinAll (except to avoid having to write a loop).
>> It might certainly be useful to have a join() routine with a timeout.  But to support that universally we'd have to roll our own, and coordinate using an event variable.
> 
> Yah, join(timeout) would be great.

Alrighty.  Let me know if this should be a pre-TDPL thing.  It will mean a shared mutex and condvar for each thread I think.
February 09, 2010
It doesn't need to be pre-TDPL because TDPL does not explain the details of how shutdown is carried.

Andrei

Sean Kelly wrote:
> On Jan 21, 2010, at 12:37 PM, Andrei Alexandrescu wrote:
> 
>> Sean Kelly wrote:
>>> On Jan 21, 2010, at 12:26 PM, Steve Schveighoffer wrote:
>>>> If a thread has exited, it takes probably a few hundred cycles to return, probably not 100ms.  If it has not exited, no amount of parallelism is going to save you from waiting for the thread to exit.
>>>>
>>>> A join sends no messages or anything, it simply waits until the thread has exited and deposited it's return code, then returns the return code.  While you are joining a slow-to-exit thread, all your other threads have exited, so in essence the parallelism occurs because the broadcast of the shutdown gets all the threads ready to be joined.  I don't see any benefit to joinAll (except to avoid having to write a loop).
>>> It might certainly be useful to have a join() routine with a timeout.  But to support that universally we'd have to roll our own, and coordinate using an event variable.
>> Yah, join(timeout) would be great.
> 
> Alrighty.  Let me know if this should be a pre-TDPL thing.  It will mean a shared mutex and condvar for each thread I think.
> _______________________________________________
> dmd-concurrency mailing list
> dmd-concurrency at puremagic.com
> http://lists.puremagic.com/mailman/listinfo/dmd-concurrency
1 2 3 4 5 6 7 8
Next ›   Last »