Skip to main content
Version: 0.6.0

Causality Loops

This article has examples in the following target languages:

Cycles​

The interconnection pattern for a collection of reactors can form a cycle, but some care is required. Consider the following example:

target C; reactor A { input x:int; output y:int; reaction(x) -> y {= // ... something here ... =} } reactor B { input x:int; output y:int; reaction(x) {= // ... something here ... =} reaction(startup) -> y {= // ... something here ... =} } main reactor { a = new A(); b = new B(); a.y -> b.x; b.y -> a.x; }

This program yields the following diagram:

Lingua Franca diagram: CycleReactor contains cyclic dependencies!Show CycleFilter CycleCycleAxyB12xy

The diagram highlights a causality loop in the program. At each tag, in reactor B, the first reaction has to execute before the second if it is enabled, a precedence indicated with the red dashed arrow. But the first can't execute until the reaction of A has executed, and that reaction cannot execute until the second reaction B has executed. There is no way to satisfy these requirements, so the tools refuse to generated code.

Cycles with Delays​

One way to break the causality loop and get an executable program is to introduce a logical delay into the loop, as shown below:

target C; reactor A { input x:int; output y:int; reaction(x) -> y {= // ... something here ... =} } reactor B { input x:int; output y:int; reaction(x) {= // ... something here ... =} reaction(startup) -> y {= // ... something here ... =} } main reactor { a = new A(); b = new B(); a.y -> b.x after 0; b.y -> a.x; }
Lingua Franca diagram: CycleWithDelayCycleWithDelayAxyB12xy0

Here, we have used a delay of 0, which results in a delay of one microstep. We could equally well have specified a positive time value.

Reaction Order​

Frequently, a program will have such cycles, but you don't want a logical delay in the loop. To get a cycle without logical delays, the reactions need to be reordered, as shown below:

target C; reactor A { input x:int; output y:int; reaction(x) -> y {= // ... something here ... =} } reactor B { input x:int; output y:int; reaction(startup) -> y {= // ... something here ... =} reaction(x) {= // ... something here ... =} } main reactor { a = new A(); b = new B(); a.y -> b.x; b.y -> a.x; }
Lingua Franca diagram: CycleReorderedCycleReorderedAxyB12xy

There is no longer any causality loop.