January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Le 2010-01-25 ? 11:29, Andrei Alexandrescu a ?crit : > Michel Fortin wrote: >> Personally I'd tend to just not define any priority and deal with things in a FIFO manner to keep things simple. If you want to add complexity (priority), it should be justified by use cases. So which use case do you have in mind for priority? > > Upon further thinking, I realized there's need for two orthogonal things: > > 1. Priority messages of any type. They'll be put to the front of the queue. I thought originally that you wanted to classify messages by priority, but that's not quite what you're proposing. You're proposing to be able to insert them in front of the queue. That's simpler than I was thought when you said "priority". Perhaps "priority" isn't the right word, as to me it implies that "priority messages" would be scheduled still in FIFO order between themselves. That is not the case if you just insert on front, so perhaps we could use another name: "sendFront"? > 2. "Must handle" messages that unblock any call to receive(). They may or may not have priority. These may be derived from Exception. I'd say those should be just "tasks". Sending one would execute that task in the given thread, inside receive. Tasks could be shared delegates, functions, or structs and objects defining opCall. For instance: void genericWorker() { while (1) receive(); } auto soundThread = spawn(&genericWorker); soundThread.dispatch({ playSoundBlocking("hello.wav"); }); soundThread.dispatch({ playSoundBlocking("world.wav"); }); soundThread.dispatchFront({ throw new Exception("Don't play any more sound. Thanks."); }); > It's never simple is it :o/. Looks simple to me now. ;-) Two functions to send messages: send & sendFront. Two functions to dispatch tasks: dispatch & dispatchFront. I think with that we have all the primitives we might need on the sender end. -- Michel Fortin michel.fortin at michelf.com http://michelf.com/ |
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin |
----- Original Message ----
> From: Michel Fortin <michel.fortin at michelf.com>
> > 2. "Must handle" messages that unblock any call to receive(). They may or may
> not have priority. These may be derived from Exception.
>
> I'd say those should be just "tasks". Sending one would execute that task in the given thread, inside receive. Tasks could be shared delegates, functions, or structs and objects defining opCall.
How does that scale to inter-process communication? One thing that was nice about the message passing is it was seamless as far as inter-process since the messages could be serialized to be sent via IPC. Executing arbitrary code can only be feasible within the same process.
-Steve
|
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Le 2010-01-25 ? 12:31, Andrei Alexandrescu a ?crit : > That's perfect as far as priority messages go, and should be part of the API. Unfortunately, I realized that thread termination is _not_ a priority message - it has "handle me" written all over itself, but not "handle me before anything else". Depends on the case. If the termination is due to an error condition then you might want it to be handled faster. But obviously, in the case where it isn't an error you want to process it serially. So if a thread terminates normally, it should send a Terminate to all the threads it owns. If a thread terminates with an exception, it could send Terminate as a priority message instead. Although in that case it should be a special kind of Terminate that get caught as an exception. That exception could be used to perform cleanup work like deleting a partially copied file. That said, if you were sending a web page to a browser instead of writing to a file, you'd probably want it to abort exactly where the problem is, not before. So it's not universal that you want errors to be propagated faster. So probably the best thing to do is that thread termination should always send events serially. This behaviour is easier to predict and thus less risky of creating races in the program's logic. If you want to handle errors faster, you can always fast-track messages and bypass sequential order explicitly. -- Michel Fortin michel.fortin at michelf.com http://michelf.com/ |
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin | On Mon, Jan 25, 2010 at 12:21 PM, Michel Fortin <michel.fortin at michelf.com>wrote: > Le 2010-01-25 ? 11:36, Kevin Bealer a ?crit : > > > Are you also considering multiple levels of priority? In the reader / processor / writer case, I can think of several priorities the processor might want: > > > > 1. Normal -- here is another buffer to write. > > 2. Medium -- the file read failed, stop with this file. > > 3. Medium#2 -- too much information too fast, please stop sending until > > further notice. > > 4. High -- The system is going down, pack up and get out of dodge > > > > I'm not sure if #2 and #3 need to be at different levels, but it seems > like > > 1, (2 or 3), and 4 should be distinct. E.g. you don't care about either Medium if there is a High in the pipe and you want to process Normals > after > > any High or Medium is handled. > > > > This argues for a many-level approach, which might as well be an integer > or > > short. > > I think only one queue where you can put messages directly on the front is enough. The more priorities you have, the more overhead it adds. > > Seriously, why should 4 be distinct from 2? First, they both should be rare enough that it won't make a difference in which order you execute them. And second: what 2 makes the thread do (stopping writing a file) you must do it for 4 anyway, so it's not like you're really wasting time by handling 2 before 4. > > And 3 should be handled by setting a maximum capacity for queues. Even if you needed a message for that, you could just make it a priority message too. Stopping sending messages should be pretty quick to handle. > > -- > Michel Fortin > michel.fortin at michelf.com > http://michelf.com/ > Yes, as you point out, each of the non-lowest priority messages are easy to handle quickly in this particular example. But it's easy to construct examples where more than 2 are a good idea, you need only assume that a non-lowest-priority message could be a common case or take a longer time to handle. For example, certain deadlock patterns arise from using a query/response idioms. The deadlock can be avoided by making some kinds of messages act with higher priority than others. But it's fine with me to wait until more experience accrues before solving this -- maybe there is a better way to solve the query/response question than message priorities. Kevin -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.puremagic.com/pipermail/dmd-concurrency/attachments/20100125/cc70158b/attachment.htm> |
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin | Michel Fortin wrote:
> So probably the best thing to do is that thread termination should always send events serially. This behaviour is easier to predict and thus less risky of creating races in the program's logic. If you want to handle errors faster, you can always fast-track messages and bypass sequential order explicitly.
I agree.
Andrei
|
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Schveighoffer | Steve Schveighoffer wrote:
>
>
>
> ----- Original Message ----
>> From: Michel Fortin <michel.fortin at michelf.com>
>>> 2. "Must handle" messages that unblock any call to receive(). They may or may
>> not have priority. These may be derived from Exception.
>>
>> I'd say those should be just "tasks". Sending one would execute that task in the given thread, inside receive. Tasks could be shared delegates, functions, or structs and objects defining opCall.
>
> How does that scale to inter-process communication? One thing that was nice about the message passing is it was seamless as far as inter-process since the messages could be serialized to be sent via IPC. Executing arbitrary code can only be feasible within the same process.
That is correct. There will be differences between send(tid, stuff) and send(pid, stuff). There's stuff that you can only send across threads, such as shared data and (now under discussion) pointers to functions.
Andrei
|
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Schveighoffer | Le 2010-01-25 ? 12:55, Steve Schveighoffer a ?crit : >> I'd say those should be just "tasks". Sending one would execute that task in the given thread, inside receive. Tasks could be shared delegates, functions, or structs and objects defining opCall. > > How does that scale to inter-process communication? One thing that was nice about the message passing is it was seamless as far as inter-process since the messages could be serialized to be sent via IPC. Executing arbitrary code can only be feasible within the same process. Obviously, delegates and function pointers won't work with inter-process communication. But if you can serialize and unserialize an object with an opCall member, then it'll work. The object could contain a script, or execute some predefined code. There's already some precedents: sending a reference to a shared object should work across threads. But across processes that's more dubious. Sending a Tid to another thread in the same process makes sense, but not to another process. -- Michel Fortin michel.fortin at michelf.com http://michelf.com/ |
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | ----- Original Message ----
> From: Andrei Alexandrescu <andrei at erdani.com>
>
> Steve Schveighoffer wrote:
> >
> >
> >
> > ----- Original Message ----
> >> From: Michel Fortin
> >>> 2. "Must handle" messages that unblock any call to receive(). They may or
> may
> >> not have priority. These may be derived from Exception.
> >>
> >> I'd say those should be just "tasks". Sending one would execute that task in
> the given thread, inside receive. Tasks could be shared delegates, functions, or structs and objects defining opCall.
> >
> > How does that scale to inter-process communication? One thing that was nice
> about the message passing is it was seamless as far as inter-process since the messages could be serialized to be sent via IPC. Executing arbitrary code can only be feasible within the same process.
>
> That is correct. There will be differences between send(tid, stuff) and send(pid, stuff). There's stuff that you can only send across threads, such as shared data and (now under discussion) pointers to functions.
OK, that makes sense (Michel's response too). I was under the impression from early postings to this list that the differences between sending messages to threads and external processes would be abstracted.
Is there a planned hierarchy for Tid/Pid/etc. ? Is it still going to be possible to abstract the commonalities? I think this was an attractive proposition.
One library I've used for building easy-to-use network protocols is .NET remoting, which essentially allows you to call methods on remote objects as if they were local objects. To myself, I thought it might be cool if we could build such a library on top of D's message passing scheme.
-Steve
|
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steve Schveighoffer | Steve Schveighoffer wrote: > Is there a planned hierarchy for Tid/Pid/etc. ? Is it still going to be possible to abstract the commonalities? I think this was an attractive proposition. Yah. My hope is to gloss over much detail regarding Tid so we save ourselves flexibility in implementation later. Sean and I agreed it's not realistic to implement IPC soon so the book will only emphasize ITC and mention IPC as a future direction. > One library I've used for building easy-to-use network protocols is .NET remoting, which essentially allows you to call methods on remote objects as if they were local objects. To myself, I thought it might be cool if we could build such a library on top of D's message passing scheme. That would be a great litmus test indeed. Andrei |
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Le 2010-01-25 ? 12:35, Andrei Alexandrescu a ?crit : > Michel Fortin wrote: >> tid.send({ throw new Exception(); }); > > I think this is a very interesting idea to consider, thanks Michel. Passing a function from one thread to another could simply mean "call this function instead of blocking in receive()". The idea comes from libdispatch. Combined with Apple's block extension to C, you can do pretty much the same with it: int global; void func() { dispatch_async(dispatch_get_main_queue(), ^{ global = 10; }); } This example shows how to update a global variable always in the main thread, avoiding the need for synchronization. The main thread event loop will execute the block upon reception. It's pretty much the same really. I think another idea worth taking from libdispatch is the concurrent queues. Although I wonder how efficient they might be without kernel support. > That function in particular has signature void(), so it does not need to be shared. A function pointer never needs to be shared, whatever its signature. The only thing it points to is code, and code is always immutable. The thread-local arguments you pass to it at the call site are guarantied to stay thread-local even if the function pointer comes from another thread. -- Michel Fortin michel.fortin at michelf.com http://michelf.com/ |
Copyright © 1999-2021 by the D Language Foundation