Design of output control reactions #608
Replies: 4 comments 3 replies
-
I've been advocating the use of generated
One thought I had was that it might make more sense to perform the required AST transformations on a per-federate basis rather than for the entire program (after which elements non-essential to a particular federate have to be filtered out), but this may require a more substantial redesign of the compiler than you might be willing to carry out. |
Beta Was this translation helpful? Give feedback.
-
I agree. The original explanation meant to convey that if a
That seems like a reasonable solution. There is an initial roadblock to this though. The |
Beta Was this translation helpful? Give feedback.
-
An update on this:We have changed the AST transformation that is done on federated programs. In short, the new AST transformation preserves the dependencies between federates. This new design will facilitate a level assignment to network reactions that more accurately reflects the structure of the federation as a whole. Previously, dependencies between federates were cut during the AST transformation, losing valuable information for level assignment. For example, imagine the following program:
The new AST transformation will create the following program for a I will not go into details about the role of each reaction and action, since the original post here explains these. The thing to focus on here is the highlighted connections. The only change here is that these highlighted connections were previously not there. This would result in incorrect level assignments to the network reactions. Now, these connections are deliberately added during the AST transformation. However, the burden is now on target language developers to account for these connections to make sure that they will not cause any issues in the user programs. Notice that for each highlighted connection, the source is in a different federate than the destination. It might be a dangerous situation, depending on how the target is designed, if a federate containing the source tries to directly write to the destination port that is not in the federate. For the C target, we get around this issue by calling the |
Beta Was this translation helpful? Give feedback.
-
Rather than performing AST transformations, assigning levels, and then (partially) rolling back said AST transformations (as well as how they are reflected If we collect these extra edges, we can also pass them along to the code generators of targets that perform a topological sort at runtime. In any case, I think this approach will be the easiest to take for the TypeScript target, so we could take a stab at implementing it and then comparing notes with the current C code generator. The steps would be:
|
Beta Was this translation helpful? Give feedback.
-
Preamble
Imagine the following federated program:
For a federated program, this is changed to the following via an AST transformation1:
In other words, the generator can produce 4 different kinds of network reactions and an action, each carrying a different responsibility. The generated reactions and the action are selectively generated for each network port depending on its type.
Next is a brief description of what each generated node does:
For each network output port (e.g.,
out
):fed
's)out
port and send it to a destination federatefed
is not going to produce anything onout
at the current tag, send a PORT_ABSENT instead. This reaction is triggered at all valid tags.For each network input port (e.g.,
in
):fed
's input port is neitherpresent
norabsent
wait until a PORT_ABSENT message is received, or until the Safe-To-Assume-Absent (STAA) offset expires, or until it receives a Tag Advance Grant (TAG) with an equal or larger tag from the RTI. This reaction is triggered at all valid tags.fed
receives a message from another federate on portin
.federate.c
when a message arrives forin
from an upstream federate. This action will in turn trigger a Network Receiver.Issue at hand
The listed generated network reactions are all marked unordered to prevent a deadlock from occurring, where a federate is blocked on receiving an input before it can produce an output or vice versa. Nonetheless, this might interfere with their objective.
Network Input Control and Network Receiver are not mutually exclusive, meaning that the execution of one does not interfere with the execution of the other. If a Network Input Control for a given port is executing, it means that the port is still neither present nor absent. This reaction waits until the status of the port has changed. If Network Receiver is executing, that means the port is present. Therefore, sooner or later, the Network Input Control will realize this and exit. Thus, both can remain unordered and execute in an arbitrary order.
The same is not true for Network Sender and Network Output Control. These two reactions have logic in them that is mutually exclusive at a given tag. If Network Output Control is executed at a tag and sends a PORT_ABSENT to the destination, under no circumstances should the corresponding Network Sender execute at the same tag. Otherwise, the destination will be informed that the port is absent at this tag, but then later will receive a value from the same federate for the same tag on the same port, violating the semantics of LF.
One way to enforce this exclusivity is for Network Output Control to have a priority that is lower than all reactions in
fed
that haveout
in their effect (thus, it will execute only after all those reactions, if triggered, are finished). This way, it can directly check the status ofout
, and if it is absent, it can be sure that the corresponding Network Sender will never be triggered at the current tag. I had assumed that declaring a source dependency onfed.out
will achieve this. Therefore, the Network Output Control reaction in our example is defined asreaction(outputControlReactionTrigger) fed.out
.The issue seems to be that since the Network Output Control already has a trigger that is triggered at all valid tags, it appears that it can be executed sometimes without the status of
fed.out
becoming fully known (in other words, without executing all triggered reactions infed
that haveout
in their effect). This is true even iffed.out
is a trigger (not a source) to this control reaction. My surprise is that why doesn't this mechanism interfere with the work of the Network Sender reaction? Imagine the following fed:Why doesn't the first reaction cause the Network Sender to send an output to the destination federate before the second reaction gets to execute? Is it because the Network Sender doesn't have another trigger?
Solution
Since the aforementioned solution does not work, two new approaches are proposed:
fed
or any reactor instantiated withinfed
that produce a value onout
.fed
that contains both the Network Output Control reaction and the Network Sender reaction. This reactor has to be generated for each output port.The downside of the first approach is that it might somewhat interfere with mutations, since deleting a nested reactor will also delete the network output control reaction within it.
For the second approach, there are a few design considerations. First, the C target does not have a generic type. This makes it harder to produce the Network Output Reactor as just one reactor class that adapts to each network output port using parameters. It might be easier to just produce a new reactor class for each network output port. Second, we currently selectively produce network control reactions for each federate. The same principle should have to carry to the network output reactor. Perhaps, the federate instance should keep a record of these generated network output reactor classes, or some sort of string name matching should be performed on the name of these network output reactors.
Footnotes
Here is the source code for the transformed program:
↩Beta Was this translation helpful? Give feedback.
All reactions