This page is showing examples in the target language CC++PythonTypeScriptRust. You can change the target language in the left sidebar.
Tag vs. Time
The model of time in Lingua Franca is a bit more sophisticated than we have hinted at. Specifically, a superdense model of time is used. In particular, instead of a timestamp, LF uses a tag, which consists of a logical time t and a microstep m.
A logical action may have a <min_delay>
of zero, and the <offset>
argument to the schedule()
function may be zero. In this case, the call to schedule()
appears to be requesting that the action trigger at the current logical time. Here is where superdense time comes in. The action will indeed trigger at the current logical time, but one microstep later. Consider the following example:
main reactor { state count:int(1); logical action a; reaction(startup, a) -> a }
main reactor { state count:int(1); logical action a; reaction(startup, a) -> a }
main reactor { state count(1); logical action a; reaction(startup, a) }
main reactor { state count:number(1) logical action a reaction(startup, a) -> a }
main reactor { state count:u32(1); logical action a; reaction(startup, a) -> a }
Executing this program will yield something like this:
1. Logical time is 1649607749415269000. Microstep is 0. 2. Logical time is 1649607749415269000. Microstep is 1. 3. Logical time is 1649607749415269000. Microstep is 2. 4. Logical time is 1649607749415269000. Microstep is 3. 5. Logical time is 1649607749415269000. Microstep is 4.
Notice that the logical time is not advancing, but the microstep is (the logical time, in this case, gives the number of nanoseconds that have elapsed since January 1, 1970). The general rule is that every call to schedule()
advances the tag by at least one microstep.
Logical Simultaneity
Two events are logically simultaneous only if both the logical time and the microstep are equal. The following example illustrates this:
reactor Destination { input x:int; input y:int; reaction(x, y) } main reactor { logical action repeat; d = new Destination(); reaction(startup) -> d.x, repeat reaction(repeat) -> d.y }
reactor Destination { input x:int; input y:int; reaction(x, y) } main reactor { logical action repeat; d = new Destination(); reaction(startup) -> d.x, repeat reaction(repeat) -> d.y }
reactor Destination { input x; input y; reaction(x, y) } main reactor { logical action repeat; d = new Destination(); reaction(startup) -> d.x, repeat reaction(repeat) -> d.y }
reactor Destination { input x:number input y:number reaction(x, y) } main reactor { logical action repeat d = new Destination() reaction(startup) -> d.x, repeat reaction(repeat) -> d.y }
reactor Destination { input x:u32; input y:u32; reaction(x, y) } main reactor { logical action repeat; d = new Destination(); reaction(startup) -> d.x, repeat reaction(repeat) -> d.y }
The Destination
reactor has two inputs, x
and y
, and it reports in a reaction to either input what is the logical time, the microstep, and which input is present. The main reactor reacts to startup by sending data to the x
input of Destination
. It then schedules a repeat
action with an <offset>
of zero. The repeat
reaction is invoked strictly later, one microstep later. The output printed, therefore, will look like this:
Time since start: 0, microstep: 0 x is present. Time since start: 0, microstep: 1 y is present.
The reported elapsed logical time has not advanced in the second reaction, but the fact that x
is not present in the second reaction proves that the first reaction and the second are not logically simultaneous. The second occurs one microstep later.
Alignment of Logical and Physical Times
Recall that in Lingua Franca, logical time “chases” physical time, invoking reactions at a physical time close to their logical time. For that purpose, the microstep is ignored.