[dmd-concurrency] draft 6

Michel Fortin michel.fortin at michelf.com
Thu Jan 28 06:41:01 PST 2010


I'm curious about something. You say this:

> When receive sees a message of an unexpected type, it doesn’t throw an excep- tion (as receiveOnly does). The message passing subsystem simply saves the non- matching messages in a queue, colloquially known as the thread’s mailbox.

> 
> [...]
> 
> Planting a Variant handler at the bottom of the message handling food chain is a good method to dynamically check for protocol bugs.

So basically you should use receive with a variant at the end when you want to catch protocol bugs (and avoid crowding the queue). My question is this: when do you *not* want to catch protocol bugs?


> Choosing a good buffer size and checking for errors completes a useful (if unoriginal) program.


Hum, choosing a good buffer size is a useful "program"?


> The exception is only thrown if receive has no more match- ing messages and must wait for a new message; as long as receive has something to fetch from the mailbox, it will not throw. In other words, when the owner thread termi- nates, the owned threads’ calls to receive (or receiveOnly for that matter) will throw OwnerTerminated if and only if they would otherwise block waiting for a new message


Didn't we agree this should happen serially, in he same order the messages are added to the queue? Ah, but you're explaining it correctly two paragraphs below. :-)


> The ownership relation is not necessarily unidirectional. In fact, two threads may even own each other; in that case, whichever thread finishes will notify the other.


My idea with the thread termination protocol was that it would prevent circular references, following the owner chain would always lead you back to the main thread. It somewhat breaks the idea that when the main thread terminates all threads end up notified. I don't quite oppose this, but which use case do you have in mind for this?


> the OwnerTerminated exception

So it is going to inherit from Exception after all?


> If a thread exits via an exception, the exception OwnerFailed propagates to all of its owned threads by means of prioritySend.


I though we were in agreement that it'd be better if this was handled serially by default to avoid races in the program's logic?

Also, no mention about what happens if the writer thread exits with an exception. You only explain what happens if it exits normally after catching the exception itself (sending a message to that thread throws).


On to 'shared'...

> The annotation helps the compiler with much more than an indication of where the variable should be allocated


Strange. I though 'shared' has no relation to where the data is allocated. There is no way to implement thread-local memory pools in the D2, since you're allowed to cast non-shared to shared when you know that no one else has a reference to it, and also because of immutable. So what are you trying to say exactly?


> atomicOp!"+="(threadsCount, 1); // fine


Oh, wow. Can't we have a better syntax? :-/


> It’s like saying “I’ll share this wallet with everyone, just please re- member that the money in it ain’t shared.” Claiming the pointer is shared across threads but the pointed-to data is not takes us back to the wonderful programming-by-honor- system paradigm that has failed so successfully throughout history. It’s not the volun- tary malicious uses, it’s the honest mistakes that form the bulk of problems. Software is large, complex, and ever-changing, traits that never go well with maintaining guarantees through convention.

I think you're going a little too far with this. Just saying "if many threads can read a pointer, all those threads can follow this pointer and access the data it points to, so that data cannot be shared" should be enough. It's pretty obvious.

And I'm not even sure how far the wallet analogy goes since const(shared(money*)) should be enough to not have anyone steal your money (you should keep the mutable pointer to yourself, of course).


> The shared constructor undergoes special typechecking, distinct from that of reg- ular functions. The compiler makes sure that during construction the address of the object or of a member of it does not escape to the outside. Only at the end of the con- structor, the object is “published” and ready to become visible to multiple threads.


That sounds wrong. I mean, it's all fine to have a constructor that ensures that no reference escapes, but is 'shared' the right term for this? In all other cases, 'shared' means the 'this' pointer is shared, not that it can't escape.

I think 'scope' would be a better term for 'no escape'.

Also, I'm a little surprised. I though the 'no escape' thing was deemed too difficult to implement a few months ago. Has something changed?


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





More information about the dmd-concurrency mailing list