|
It is currently Tue Feb 07, 2012 8:20 pm
|
View unanswered posts | View active topics
| Welcome |
|
Welcome to RHAPSODY4YOU
You are currently viewing our boards as a guest, which gives you limited access to view most discussions and access our other features. By joining our free community, you will have access to post topics, respond to polls, upload and download content, and access many other special features. Registration is fast, simple, and absolutely free, so please, register to join our community today. |
 |
|
 |
|
| Author |
Message |
|
bitblit
|
Post subject: Calling triggered operations Posted: Tue Jun 23, 2009 1:45 am |
Joined: Wed Dec 10, 2008 10:51 pm Posts: 138
|
|
What is the correct way to call a triggered operation? Specifically, what is the C++ line of code to do it? I thought I was doing it correctly, but I ran into an odd problem that makes me think I'm not.
I queue reception events with the GEN macro:
GEN(eventName);
I thought I called triggered operations with just the function call, like it was a primitive operation:
eventName();
But, what I'm running into now is that if I call two triggered operations, the second one doesn't actually cause a transition. Only if I let the framework process the event queue between two triggered operations, does the second one work. I've attached a sample model where it fails to transition.
|
|
|
|
 |
|
bitblit
|
Post subject: Posted: Tue Jun 23, 2009 3:53 am |
Joined: Wed Dec 10, 2008 10:51 pm Posts: 138
|
|
Again, I solved my own problem. Here's what I determined, for those interested.
We found a problem when using Triggered Operations. Basically, when you enter state #1, transition to state #2 with a triggered operation, then transition to state #3 with another triggered operation, the second transition will fail silently. No error. It just will not actually transition. I figured out why it does this from reading the framework source code, and it makes perfect sense.
Asynchronous events allow you to go from state to state without traversing a hierarchical call stack. The only things on the call stack are the event queue manager popping and executing events, and the actions queuing events. If you call 1000 function calls, you'll have internal variables and passed parameters for 1000 function calls on your call stack. If you use the asynchronous events pattern, you can call an infinite number of functions as "state transitions" with virtually no call stack. This is because the action (or function call) always runs to completion (except for the few function calls in the framework). The function call itself is pushed and popped before it transitions to the next state, calling the next function call. This is implemented by queuing event objects on a stack, and periodically calling a function to pop events off the stack. We can see the event queue directly in the Rhapsody tool. Normally, even complex models only have a few events in the stack at a time. The actual OS call stack needed is fairly small.
The problem is when you allow synchronous events. A synchronous event behaves like a function call, in that it is completely blocking. So, the action, or function call, does not run to completion before the transition occurs. You can see the behavior by putting printf statements before and after your triggered operation. It'll appear to start state #1, execute state #2, then go back and complete state #1. Left as it is, even a simple ping-pong state machine invoked with triggered operations would quickly blow the stack. It would keep calling function calls, and none of them would ever run to completion. Basically, this is strictly forbidden in Embedded C++. No recursion allowed.
To allow synchronous events, but prevent the user from blowing the stack or violating the No Recursion rule, the Rhapsody framework puts sort of a mutex on the triggered operation event handler. When you transition from state #1 to state #2, it sets the mutex, then executes the entry code for state #2. When it runs into the second triggered operation, it checks the mutex and fails silently by just dropping the event. This allows state #2 to run to completion, clear the mutex, pop the call stack back to state #1, and let it run to completion.
Fascinating.
|
|
|
|
 |
|
Farquad
|
Post subject: Posted: Tue Jun 23, 2009 8:45 am |
Joined: Thu Sep 13, 2007 7:34 pm Posts: 397 Location: London
|
|
Yep, basically the statechart's processEvent() operation is not re-entrant.
A busy flag is set and if it is true when a second event comes along then that event is ignored. As a reactive object can only have one thread (activeContext) the situation, where the statechart is busy when processing an event, can only occur when triggered operations are used.
Events in a UML statechart are regarded as run to completion. The TriggeredOperation is something that you need to fully understand before utilising. I used to take Rhapsody training classes. I would explain this to people but they rarely got it until it actually happened to them.
Summary: Triggered Ops seem like a good idea until you understand their limitations. Once you understand those limitations you'll rarely use them. If you don't understand the limitations you should never use them.
|
|
|
|
 |
|
Skywalker
|
Post subject: Recursions in triggered operations Posted: Thu Jun 25, 2009 8:47 pm |
Joined: Wed Oct 31, 2007 3:09 pm Posts: 58 Location: Milkyway, classic 9-planet solar system
|
|
Just to add my 2 cents...
The second transition will not really fail... the framework contains an anchor to catch such situations. But the default anchor will - as already recognized - rather silently discard the trigger.
But this is not a must... it is just the default behavior...
If you simply overload OMReactive::handleNotConsumed(IOxfReactive::EventNotConsumedReason) in your statemachine class, then you can easily add your own handler for "accidently" discarded statemachine triggers.
The operation distinguishes between two different EventNotConsumedReason's for a trigger not being handled:
"EventNotHandledByStatechart" vs. "StateMachineBusy" (the 2nd is your case).
Hope this helps understanding the framework mechanisms.
Best regards,
Luke.
P.S. It is possible to implement a customized mechanism for handling synchronous triggers, which e.g. saves a recursive 2nd trigger event and then handles it just after completing the current transition before returning from the 1st rigger event... as an alternate solution to using triggered operations
_________________ May the force be with you ...
|
|
|
|
 |
|
Farquad
|
Post subject: Posted: Thu Jun 25, 2009 10:06 pm |
Joined: Thu Sep 13, 2007 7:34 pm Posts: 397 Location: London
|
|
Where have you been Luke? Great input as always.
|
|
|
|
 |
|
Skywalker
|
Post subject: Posted: Sun Jun 28, 2009 5:47 pm |
Joined: Wed Oct 31, 2007 3:09 pm Posts: 58 Location: Milkyway, classic 9-planet solar system
|
Farquad wrote: Where have you been Luke? Great input as always.
Dagobah... talking to Yoda.
_________________ May the force be with you ...
|
|
|
|
 |
|
|
 |
|
 |
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot post attachments in this forum
|
|