RE: [boost] Re: [Threads] Simple active object wrapper, take 2

-----Original Message----- From: Matthew Vogt [mailto:mvogt@juptech.com] Sent: Wednesday, 3 March 2004 8:44 AM To: boost@lists.boost.org Subject: [boost] Re: [Threads] Simple active object wrapper, take 2
scott <scottw <at> qbik.com> writes:
Yep. All straight now. I know understand your breakdown of calls to callbacks is very similar to that inherent in SDL. And yes, you/we/it are fully asynchronous.
I'm assuming you prefer this to the reactive object version? Which does a pretty good job of hiding the comms Oh yeah, and doesnt require selection of the method to call in either the receipient or the client (on response), i.e. at the point of making a method call.
Maybe we're down to a matter of taste?
Yes, I guess it depends on whether you're familiar/comfortable with the message passing paradigm, or whether you're more comfortable with C++ shenanigans to obscure the messy details.
It may be the sign of a warped mind but queueing some form of bound functor-like object has almost always seemed to be (broadly) what I wanted to do in any message passing scheme, whether or not fancy machinery existed to do it. However, there are times when it is useful to have the messaging defined independently of either the sender or the receiver, with both being written to conform to the messaging interface. I've tended to think of the tightly/early bound, just queue a functor, and the message passsing + switch case as 2 completely independent patterns, but maybe they should be built on a common framework (at a level above the detail that they both ivolve queuing some sort of object). The AO pattern doesn't require that you identify the object you want to invoke the method on in the proxy, because you send the proxy to that object, and only that object. So one could drop the "this" parameter from the proxy. That would potentially allow the proxy to be routed/forwarded in a more general reactive object model (I think this was one of Scotts requirements)? I'm not 100% sure how useful this is, but it might be nice for the class of problem where you do want concurrent execution - of as many completely independent instances of the object as is "useful". The client/master doesn't care which instance - the next available one will do nicely. Further, it would seem possible to exclude any binding to a particular member function from the proxy itself and instead allow "free proxies" to be defined. To avoid confusion, I'm going to drop the work proxy for these "free proxies" and call them "messages". These would just define a function signature to match, not a particular (member) function. It is not clear to that the type of the "this" parameter need be defined by the message - excluding it would offer better separation between message sender and receiver. The sender need only know that it wants to notify or invoke something by sending the message. Such message types (just a tuple) could be defined with no reference to the wrapper or receiving object type at all. As you have said, the downside is that there is some form of type-switch implied in the receiver (eg the receiver wrapper would accept a boost::variant<message_type_1, message_type_2...> from the activation list and apply a generic visitor to do the dispatch). Maybe this can be avoided if there is a separate queue per message type? The additonal queue/scheduler complexity would probably wipe out any potential advantage though. So a receiver wrapper generator would need to, given the set of message types and corresponding member function pointers to handle them, generate the variant and corresponding visitor. I certainly like this better than a hand coded switch, and it is something like a simplified/limited form of the compile time (actually code generation time) generation of message demux code that is part of scotts existing framework (I think - I only skimmed scotts descriptions and possibly guessed too much from vaguely similar, but more primitive things I've used before).
I would postulate, however, that wrapping the details to allow binding at the call site (or message post) is slightly superior, due to the removal of the dispatch. Of course, this minor gain may easily be outweighed by the contortions required to make it work, and the reduction in the transparency of the code...
The tightly/call site bound case can be written as a simple layer on top of the lose/late bound system. Message type becomes variant<proxy> and the visitor for the one and only type dispatches to a wrapper function that just does the stuff that was done in the thread_function loop in your AO code. I'm not sure that for a reasonably small (any sane?) interface the performance difference due to the demux will be terribly large anyway, so maybe the specialisation isn't needed for that alone. However I've so far ignored the return value in this - the return type could obviously be defined in the message, but what should define how/where it should be delivered? It seems that the caller should define how/where it wants results delivered at the time of invoking the request - which it can do by passing some sort of result handler object in the message instance. Of course this is what the synch object in your proxy is. Perhaps its interface can be made more generic to allow its operation to be policy based. Broadening the definition from "return value" to "message delivery notification" with the sender supplied message delivery notifier object passed the return value from the handler doesn't feel too weird to include at the low level, and shouldn't cost anything for a void return without notification. Interestingly, the option to generate a a notification that a message with a handler returning void has been processed doesn't seem like a strange feature when looked at this way (a void future is a bit odd though).
I hadn't realised how integral the state machine was to your design until I read your other post. Still, if you are doing dispatch on both message code and object state, it can be simplified to bind the message to a per-message code dispatch function which then dispatches on object state.
Characterizing of reactive objects as a framework for state machines would be a little bit sad. I'm not sure it was your intention to say that so just in case others are listening, I will elaborate from my POV.
Sorry, I read too much into the example code in the other message. I assumed the messaging would be an independent layer, but that the design you were using must have the communicating objects modelled as state machines. Thanks for clearing that up.
This observer didn't see the state machine aspect as the exclusive use, more an important use case reflected in the design. Would that be fair to say?
<snip>
The messaging facillity has _no understanding_ of what the recipient does with a message. Proxies and state machines were intended to highlight the benefits of that design.
To summarize; the Reactive Objects that I currently consider our target would communicate via a similar mechansim and there would be no assumption that a Reactive Object was a state machine, or a proxy, or anything except an object that accepts messages.
My questions that I've been thinking out loud about (always dangerous in public) were: What is a message? What is acceptance? Sorry this was so long - am I barking up the wrong tree here or not?
Yes, that all sounds good.
Cheers, Scott
Matt
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/bo> ost
########################################################################## This e-mail is for the use of the intended recipient(s) only. If you have received this e-mail in error, please notify the sender immediately and then delete it. If you are not the intended recipient, you must not use, disclose or distribute this e-mail without the author's prior permission. We have taken precautions to minimise the risk of transmitting software viruses, but we advise you to carry out your own virus checks on any attachment to this message. We cannot accept liability for any loss or damage caused by software viruses. ##########################################################################

[mailto:boost-bounces@lists.boost.org]On Behalf Of Darryl Green Sent: Wednesday, March 03, 2004 11:38 PM
I've tended to think of the tightly/early bound, just queue a functor, and the message passsing + switch case as 2 completely independent patterns, but maybe they should be built on a common framework (at a level above the detail that they both ivolve queuing some sort of object).
Yes. I agree with your distinguishing of the two forms. There is some relief to be found in that. While I can acknowledge the existence of async messaging where the sender has type knowledge of the receiver, I also think its dangerous. Given the two approaches, most developers head down the "fake method" route. This inevitably results in receipient-type knowledge and that precludes some really cool active objects (please refer to other messages if this fails to allude to anything). Also I think its right to say that there is nothing method-based that cant be done message- based; but is the converse true? There is some more fundamental reason why message-based is right that is probably rooted in theory (sets?). There has to be good reason why protocols are so "right". Everyone appreciates the content of RFCs and telephony signaling specifications but we persist with method-based messaging. <sigh> Its an area of personal wonder.
The AO pattern doesn't require that you identify the object you want to invoke the method on in the proxy, because you send the proxy to that object, and only that object. So one could drop the "this" parameter from the proxy. That would potentially allow the proxy to be routed/forwarded in a more general reactive object model (I think this was one of Scotts requirements)? I'm not 100% sure how useful this is, but it might be nice for the class of problem where you do want concurrent execution - of as many completely independent instances of the object as is "useful". The client/master doesn't care which instance - the next available one will do nicely.
IIUC, this is a de-generate form of interaction that has been touched on previously. At least in my work it imposes an unacceptable design constraint; there needs to be some identification of the receiving active object. Most significantly (?) there could not be multiple servants per scheduler (i.e. thread).
is. Perhaps its interface can be made more generic to allow its operation to be policy based. Broadening the definition from "return value" to "message delivery notification" with the sender supplied message delivery notifier object passed the return value from the handler doesn't feel too weird to include at the low level, and shouldn't cost anything for a void return without notification. Interestingly, the option to generate a a notification that a message with a handler returning void has been processed doesn't seem like a strange feature when looked at this way (a void future is a bit odd though).
Followed everything up to the point where you start discussing "return values". On one hand you do discuss this as simply another message. OTOH you do persist with some method-based concepts. Maybe this is not significant.
This observer didn't see the state machine aspect as the exclusive use, more an important use case reflected in the design. Would that be fair to say?
Absolutely.
My questions that I've been thinking out loud about (always dangerous in public) were: What is a message? What is acceptance?
Yes, essential questions :-) Respondez du jour? A message is an object that is self-discriminating (within the world of known messages) and carries associated parameters. It is always a member of a family, i.e. a protocol. A protocol and its messages is defined independent of all active objects. Acceptance I'm not so sure of. The word carries a sense of righteousness whereas the worst that active objects seem to do is discard. Protocols may include messages along the lines of "that last message was so, like; wrong" but then that exchange would itself be defined in the protocol. Maybe messages are simply processed. Or consumed. What a wimp-out.
Sorry this was so long - am I barking up the wrong tree here or not?
Its so hard to be sure. I've got my wrong tree - you find your own :-)

scott <scottw <at> qbik.com> writes:
On Behalf Of Darryl Green
I've tended to think of the tightly/early bound, just queue a functor, and the message passsing + switch case as 2 completely independent patterns, but maybe they should be built on a common framework (at a level above the detail that they both ivolve queuing some sort of object).
Yes. I agree with your distinguishing of the two forms. There is some relief to be found in that.
While I can acknowledge the existence of async messaging where the sender has type knowledge of the receiver, I also think its dangerous.
In what way do you think it is dangerous? Provided that the type knowledge is correct, then it merely provides an assurance to the sender (which they may or may not care about) that the recipient claims to deal with a particular type of message. (and a small opportunity for optimisation based on the type knowledge)
Given the two approaches, most developers head down the "fake method" route. This inevitably results in receipient-type knowledge and that precludes some really cool active objects (please refer to other messages if this fails to allude to anything).
Are you referring to the 'forwarding proxy' example (proxy in an object sense, not a method sense)? This could be done with a method interface, but it would a maintenance nightmare :) Actually, I think this could be done, but in a different way to the message-only version. An object of the proxied type would still have its method stubs invoked to create fully-bound functor messages, but those messages would be queued to a different object - the one that would forward these (essentially typeless) messages on to the real object recipient. The difference would be in the proxies - they would bind to member function addresses, but not on the current instance, and they would enqueue the message to a non-local queue.
Also I think its right to say that there is nothing method-based that cant be done message- based; but is the converse true?
Given that messages would provide the implementation of the method emulation, I agree. I suspect the converse may be true, but it might take a lot of (unrewarded) effort...
There is some more fundamental reason why message-based is right that is probably rooted in theory (sets?). There has to be good reason why protocols are so "right". Everyone appreciates the content of RFCs and telephony signaling specifications but we persist with method-based messaging.
But a method-based interface would still be implemented as message-passing. How can an underlying mechanism have fundamental advantages over something which is layered above it?
<sigh> Its an area of personal wonder.
The AO pattern doesn't require that you identify the object you want to invoke the method on in the proxy, because you send the proxy to that object, and only that object. So one could drop the "this" parameter from the proxy. That would potentially allow the proxy to be routed/forwarded in a more general reactive object model (I think this was one of Scotts requirements)?
I don't think that I've really followed this. The active object pattern specifies that there may be many identical servants servicing method requests concurrently, but this implies that the servants can't be stateful. I think this is a special case, as far as rective objects are concerned.
I'm not 100% sure how useful this is, but it might be nice for the class of problem where you do want concurrent execution - of as many completely independent instances of the object as is "useful". The client/master doesn't care which instance - the next available one will do nicely.
IIUC, this is a de-generate form of interaction that has been touched on previously. At least in my work it imposes an unacceptable design constraint; there needs to be some identification of the receiving active object. Most significantly (?) there could not be multiple servants per scheduler (i.e. thread).
is. Perhaps its interface can be made more generic to allow its operation to be policy based. Broadening the definition from "return value" to "message delivery notification" with the sender supplied message delivery notifier object passed the return value from the handler doesn't feel too weird to include at the low level, and shouldn't cost anything for a void return without notification. Interestingly, the option to generate a a notification that a message with a handler returning void has been processed doesn't seem like a strange feature when looked at this way (a void future is a bit odd though).
Followed everything up to the point where you start discussing "return values". On one hand you do discuss this as simply another message. OTOH you do persist with some method-based concepts. Maybe this is not significant.
Return values can be seen as a syntactic nicety. In implementation then it is just another message, but if a sender knows that the logical outcome of the processing of the message he is sending will be another message received from the other object, then you can remove two switches by binding both the addresses. This would particularly be of benefit if a message had dynamic dispatching, where a different handler was invoked depending on the current state of the receiver. Aside: the void future was a consequence of wrapping active behaviour around 'normal' object interactions. Say you had the following interaction: struct Engine { enum State { Idle, Running }; State state; Engine() : state(Idle) {} void start(void) { state = Running; } State get_state(void) { return state; } }; struct Controller { Engine& engine; Controller(Engine& e) : engine(e) {} void begin(void) { engine.begin() }; } int main(int, char**) { Engine engine; Controller controller(engine); controller.begin(); assert(e.get_state() == Engine::Running); } This would be fine if all objects were normal objects with synchronous behaviour, however, if the Engine and Controller were wrapped to become active, then there would be no guarantee that Controller::begin() would be processed before Engine::get_state(), unless the caller of Controller::begin() was forced to wait for the processing of that message to complete before continuing. I don't think a void future has any relevance for objects which are designed to be active. Matt
participants (3)
-
Darryl Green
-
Matthew Vogt
-
scott