Home » Eclipse Projects » Papyrus for Real Time » defer/recall
|defer/recall [message #1783633]
||Wed, 14 March 2018 17:31
| Juergen Dingel
Registered: January 2015
we've been looking at code involving defer/recall of a message with a parameter that should work (we think), but does not.
Please consider the following situation:
1) Instance N receives message 'm(1), i.e., a message 'm' with parameter '1', on some port p and defers it
2) when entering a new state S (with an outgoing transition t with trigger 'm'), N recalls messages on p, i.e., N executes 'recall' as part of the entry code of S
3) while in S, N receives another message 'm(2)'
One would expect that t is taken with '1' delivered as parameter, right?
However, it appears that '2' is delivered. However, if 'recallFront' is used instead of 'recall' or 'recallAll', then things work fine.
|Re: defer/recall [message #1783645 is a reply to message #1783633]
||Wed, 14 March 2018 20:31
| Ernesto Posse
Registered: March 2011
If I understand correctly, the situation that you describe is like the one in the attached model.
Here we have a Top capsule with a part a of type A. A has ports p and q typed Protocol1 and Protocol2 respectively, each of which is connected to ports p~ and q~ of Top respectively. Protocol1 has a single input message m with an int parameter 'data'. Protocol2 has a single message k.
Top behaves as follows: it sends m(1) through p~ and then sets a timer. After the timeout, it sends, in the same action, k() through q~ and then m(2) through p~.
A behaves as follows: in state S1, it waits for m. When m arrives, it goes to state S2 where it defers the message. It also sets a timer and has two outgoing transitions, one triggered by the timeout that goes to S3, and one triggered by q.k that goes to S5. In S3's entry action, it recalls a message from p. S3 has a transition triggered by m that goes to S4 where it prints the data received in m. S5 is almost identical to S3: it recalls a message from p and has a transition triggered by m to S6 where it prints the data.
The sample model can be run from the command-line as follows:
./TopMain -u <Top-delay> <A-delay>
So when you run it as
you observe the following output:
Controller "DefaultController" running.
[Top](S1) sending m(1)
[Top](S1) waiting for 1 s; 0 ms;
[A](S1) waiting for m
[A](S2) received m with data = 1
[A](S2) message deferred
[A](S2) waiting for 2 s; 0 ms;
[Top](S2) sending k, then m(2)
[A](S5) recalling m
[A](S5) one message recalled from p
[A](S6) received m with data = 2
Unexpected message to capsule instance Top.a role a on port p protocol Protocol1 signal m
Unexpected message to capsule instance Top.a role a on port timing protocol Timing signal timeout
As you can see, it reaches state S6 and prints the data 2, which is the payload of the second message m, instead of the first message which was recalled.
This is expected. The reason is this: in a controller there are two separate queues: the "main" queue, where all incoming messages for capsule instances handled by the controller arrive, and the "deferred" queue. When the message currently being processed by a state machine is defered, it is put into the back of the defered queue (for the sake of simplicity I'm ignoring priorities). Then, when an action recalls a message from a port, it removes it from the defered queue (wherever it is on that queue), and puts it at the *back* of the main queue.
So in the scenario above this is what happens, assuming all capsules are on the same controller (if they were not, the behaviour would be different, yet another reason why thread assignment is semantically meaningful)
1) Top sends m(1), which puts m(1) at the back of the *main* queue
2) The controller processes all pending events until it reaches m(1), which is sent to Top.a
3) Top.a performs the run-to-completion step which takes the transition to S2 and executes its entry action, which defers the message, i.e. it moves it to the back of the *defer* queue
4) After some time, Top's transition is taken and it goes to S2 where in its entry action it performs two actions: send k through q~ and then send m(2) through p~. This results in adding k() and m(2) to the back of the controller's *main* queue in that order (again, equal priorities).
5) The controller processes events until it reaches k(), which is sent to Top.a.
6) Top.a makes the transition from S2 to S5, where it executes the entry action, which recalls a message from p. This results in moving m(1) from the defered queue to the *back* of the *main* queue. But since m(2) was already in the main queue, then m(1) will go after it:
7) The controller processes the next event in the main queue, which is m(2), not m(1), so it results in Top.a taking the transition from S5 to S6 where the message is m(2).
If you replace "recall" with "recallFront", then when recalling, the message will be moved from wherever it is in the deferred queue, to the *front* of the *main* queue. So in this scenario it would result in m(1) being processed before m(2).
|Re: defer/recall [message #1783795 is a reply to message #1783645]
||Sat, 17 March 2018 19:24
| Juergen Dingel
Registered: January 2015
this makes perfect sense and is clarifying the issue.
I thank you very much for your clear and precise response!
The mistake that I made was to think that as long as we had
self-transitions deferring 'm(2)' around every state in which
'm(2)' was unexpected, then if 'm(2)' was sent while the
system was in any of these states, then 'm(2)' would get deferred
and put behind 'm(1)' in the defer queue leaving the arrival order
intact when both messages were recalled.
I realize now that this is not the case.
If 'm(2)' arrives when the system is in a state with a self-transition
defering 'm(2)', but there is *another* message 'k' in the message
queue triggering some other transition 't', then 't' will get taken
and *not* the self-transition deferring 'm(2)', meaning 'm(2)' stays
in the message queue and never gets deferred.
Now, as the system enters the target state of 't' and messages are
recalled, the recalled message 'm(1)' ends up behind 'm(2)' in the
message queue, leading to the unexpected behaviour (which is
avoided by 'recallFront').
I will have to update not only my thinking about defer/recall
but also my teaching material to reflect this ;-)
Thanks again, you've been a GREAT help!
Current Time: Sat Jun 06 15:13:11 GMT 2020
Powered by FUDForum
. Page generated in 0.01601 seconds