Thread overview | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
I found a weakness of the priority messaging system. Recall that my plan was to do the following: if a message inherits Exception and is not explicitly expected in receive(), it causes receive() to throw that very message. (There was discussion on whether the right base is Exception/Throwable/Error.) That approach nicely uses an existing mechanism to send out-of-band values into the control flow. I really think that's a Good Thing, but I realized there's a problem. Actually two. 1. What if I want to send an int with priority? Throwing an int is a rather absurd proposition, but sending an int to a thread is a very reasonable thing to ask for. 2. Exceptions are class objects, so the whole issue of undue sharing and consequently marshaling/unmarshaling across threads comes up. So I'd like to change the approach. Instead of doing a type-based priority system (i.e. certain types are priority messages), let's define a per-call based priority system (i.e. if you call prioritySend() instead of send() the message has priority). In this proposed approach, if receive() does not explicitly handle the message sent with priority, it will be packaged as an exception PriorityMessageException!T, where T is the original type sent. The advantage is that the exception will be created and thrown in the receiving thread (no need to be careful about cross-thread aliasing of the exception object. Feedback welcome. Andrei |
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Le 2010-01-25 ? 9:42, Andrei Alexandrescu a ?crit : > I found a weakness of the priority messaging system. Recall that my plan was to do the following: if a message inherits Exception and is not explicitly expected in receive(), it causes receive() to throw that very message. (There was discussion on whether the right base is Exception/Throwable/Error.) > > That approach nicely uses an existing mechanism to send out-of-band values into the control flow. I really think that's a Good Thing, but I realized there's a problem. Actually two. > > 1. What if I want to send an int with priority? Throwing an int is a rather absurd proposition, but sending an int to a thread is a very reasonable thing to ask for. > > 2. Exceptions are class objects, so the whole issue of undue sharing and consequently marshaling/unmarshaling across threads comes up. > > So I'd like to change the approach. Instead of doing a type-based priority system (i.e. certain types are priority messages), let's define a per-call based priority system (i.e. if you call prioritySend() instead of send() the message has priority). > > In this proposed approach, if receive() does not explicitly handle the message sent with priority, it will be packaged as an exception PriorityMessageException!T, where T is the original type sent. The advantage is that the exception will be created and thrown in the receiving thread (no need to be careful about cross-thread aliasing of the exception object. 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? About sending exceptions, I see two possibilities: either you manually send an exception, or a thread is terminated by an exception. In the first case, you'll need the exception to be either immutable, shared, or Unique!Exception. In the second case, which according to my Thread Termination protocol the exception gets sent to the owner thread, the terminating thread can safely cast the thread-local exception to Unique!Exception since all other thread-local references are lost when the thread terminates. About throwing Exceptions in receive... I've been thinking lately and I wonder if this couldn't be generalized a bit more, using a libdispatch-inspired API. What if instead of receive() having a special case for exceptions it had a special case for shared delegates and functions? You could then do this: tid.send({ throw new Exception(); }); The interesting result is that any thread can instantly becomes a worker queue. -- Michel Fortin michel.fortin at michelf.com http://michelf.com/ |
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin | 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? Thread termination! Also forcibly closing any protocol in progress. In an expanded file copy example that reads and writes several files, a read error should immediately send the signal to abort writes, even writes of buffers that are still waiting in the queue. > About sending exceptions, I see two possibilities: either you manually send an exception, or a thread is terminated by an exception. In the first case, you'll need the exception to be either immutable, shared, or Unique!Exception. In the second case, which according to my Thread Termination protocol the exception gets sent to the owner thread, the terminating thread can safely cast the thread-local exception to Unique!Exception since all other thread-local references are lost when the thread terminates. The problem is, my purpose is not to throw an exception. I want to send a priority message that will necessarily (a) be processed before non-priority messages, and (b) that is never ignored by receive(). Exceptions are a mechanism used to do (b). > About throwing Exceptions in receive... I've been thinking lately and I wonder if this couldn't be generalized a bit more, using a libdispatch-inspired API. What if instead of receive() having a special case for exceptions it had a special case for shared delegates and functions? You could then do this: > > tid.send({ throw new Exception(); }); > > The interesting result is that any thread can instantly becomes a worker queue. That is fine as a means to throw exceptions across threads, but I'm looking for priority messages. Andrei |
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin | 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.
2. "Must handle" messages that unblock any call to receive(). They may or may not have priority. These may be derived from Exception.
It's never simple is it :o/.
Andrei
|
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | 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. Kevin On Mon, Jan 25, 2010 at 11:29 AM, Andrei Alexandrescu <andrei at erdani.com>wrote: > 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. > > 2. "Must handle" messages that unblock any call to receive(). They may or may not have priority. These may be derived from Exception. > > It's never simple is it :o/. > > > Andrei > > _______________________________________________ > dmd-concurrency mailing list > dmd-concurrency at puremagic.com > http://lists.puremagic.com/mailman/listinfo/dmd-concurrency > -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.puremagic.com/pipermail/dmd-concurrency/attachments/20100125/6d79809c/attachment.htm> |
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Le 2010-01-25 ? 11:20, 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? > > Thread termination! Also forcibly closing any protocol in progress. In an expanded file copy example that reads and writes several files, a read error should immediately send the signal to abort writes, even writes of buffers that are still waiting in the queue. Ok, I see. Having priority seems a good solution for this use case. I like the "tid.prioritySend(message)" approach best. -- Michel Fortin michel.fortin at michelf.com http://michelf.com/ |
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kevin Bealer | 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/ |
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin | Michel Fortin wrote:
> Le 2010-01-25 ? 11:20, 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?
>> Thread termination! Also forcibly closing any protocol in progress. In an expanded file copy example that reads and writes several files, a read error should immediately send the signal to abort writes, even writes of buffers that are still waiting in the queue.
>
> Ok, I see. Having priority seems a good solution for this use case. I like the "tid.prioritySend(message)" approach best.
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".
Andrei
|
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin | Michel Fortin 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.
I'm with Michel. Arbitrary priorities are a natural extension, but I don't want us to overengineer. Heck, I'd do without prioritySend if I could get away with it.
Andrei
|
January 25, 2010 [dmd-concurrency] priority messages | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin | 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()".
That function in particular has signature void(), so it does not need to be shared.
Andrei
|
Copyright © 1999-2021 by the D Language Foundation