Skip to content

Commit 4441984

Browse files
committed
Early detection
1 parent f6ab862 commit 4441984

File tree

3 files changed

+133
-6
lines changed

3 files changed

+133
-6
lines changed

blog/2025-08-13-deadlines.md

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,91 @@ Some care is needed in this case because the code generator inserts a `NetworkSe
170170
This is a code-generated Lingua Franca file for the `spa` federate alone.
171171
In this case, the reaction in the `NetworkSender` will have the same level as the reaction in the `Processor` and hence may get delayed, causing the deadline to be missed at the `pa` federate.
172172

173+
## Early Deadline Violation Detection
174+
175+
A deadline violation handler is invoked when a reaction is to be _started_ late.
176+
Above, we explained how to react to a late _completion time_ of a reaction, but that reaction is not invoked until the reaction actually completes.
177+
What if you need to react as soon as you know that the completion time will be late?
178+
Here we describe three complementary mechanisms that can react sooner.
179+
180+
### Reactions that Monitor Their Execution Time
181+
182+
The [`lf_check_deadline`](https://www.lf-lang.org/reactor-c/group__API.html#gab3a04dd0a1581844829b28686b6b3c53) function in the [reactor API](https://www.lf-lang.org/reactor-c/group__API.html) can be used to write a reaction that monitors its own execution time against a deadline and invokes its deadline violation handler as soon as it detects that the execution time has exceeded the deadline.
183+
This mechanism works when the reaction is _started_ on time, but when we want it to terminate its execution when it cannot _complete_ on time.
184+
A nice example of this is given in the [AnytimePrime.lf](https://github.com/lf-lang/playground-lingua-franca/blob/main/examples/C/src/deadlines/AnytimePrime.lf) example in the [deadline collection](https://github.com/lf-lang/playground-lingua-franca/blob/main/examples/C/src/deadlines/README.md) of the [LF playground repo](https://github.com/lf-lang/playground-lingua-franca/tree/main).
185+
It computes as many prime numbers as it can before exceeding a time budget and then aborts.
186+
187+
### Watchdogs
188+
189+
An experimental `watchdog` mechanism is available in LF and is described by
190+
[Asch, et al., Software-Defined Watchdog Timers for Cyber-Physical Systems](https://ieeexplore.ieee.org/document/10693560).
191+
A `watchdog` specifies a handler that is invoked if, after the watchdog is started using the [`lf_watchdog_start`](https://www.lf-lang.org/reactor-c/group__API.html#ga82bf2c7bd91fdf03b357914cf875dbb9) function in the [reactor API](https://www.lf-lang.org/reactor-c/group__API.html), the watchdog is not stopped or restarted within the specified amount of physical time.
192+
193+
### Federates as Watchdogs
194+
195+
The [decentralized coordinator](https://www.lf-lang.org/docs/next/writing-reactors/distributed-execution#decentralized-coordination) for federated execution gives a convenient mechanism for creating a form of watchdog that runs in a separate process or even on a separate machine.
196+
This can give a more robust detection of a failure because the watchdog monitor can be put on a separate machine from the process being monitored.
197+
198+
Consider the following example:
199+
200+
![FederatedWatchdog diagram](../static/img/blog/FederatedWatchdog.svg)
201+
202+
The code for this is:
203+
204+
```lf-c
205+
target C {
206+
coordination: decentralized
207+
}
208+
import Sensor, Processor, Actuator from "SensorProcessorActuator.lf"
209+
210+
reactor Monitored(exec = 10 ms) {
211+
output complete:int
212+
s = new Sensor()
213+
p = new Processor(exec = exec)
214+
a = new Actuator()
215+
s.out -> p.inp
216+
p.out -> a.inp
217+
p.out -> complete
218+
}
219+
220+
reactor Watchdog(STA: time = 50 ms) {
221+
input inp:int
222+
timer t(0, 200 ms)
223+
224+
reaction(t, inp) {=
225+
if (!inp->is_present) {
226+
lf_print("%s: ******* Failed to receive input on time at logical time " PRINTF_TIME,
227+
lf_reactor_name(self), lf_time_logical_elapsed());
228+
} else {
229+
lf_print("%s: Monitor OK at logical time " PRINTF_TIME,
230+
lf_reactor_name(self), lf_time_logical_elapsed());
231+
}
232+
=} STAA(0) {=
233+
lf_print("%s: ******* Monitor received late input.", lf_reactor_name(self));
234+
=}
235+
}
236+
237+
federated reactor {
238+
@label("exec = 60 ms")
239+
m = new Monitored(exec = 60 ms)
240+
@label("STA = 50 ms")
241+
w = new Watchdog()
242+
m.complete -> w.inp
243+
}
244+
```
245+
246+
The `Monitored` reactor is simply a federate containing the sensor-processor-actuator chain.
247+
It is just like above except that it also copies the output of the processor to its own `complete` output.
248+
249+
The `Watchdog` federate has a timer that exactly the `Sensor` timer in offset and period.
250+
It expects an input from the `Monitored` at each tick of this timer.
251+
The [`STA` parameter](https://www.lf-lang.org/docs/next/writing-reactors/distributed-execution#safe-to-advance-sta) (**safe to advance**) specifies that it is safe to advance the federate's logical time to the logical time of the timer tick when physical time exceeds that logical time plus the `STA`.
252+
The `STA` is set to 50 ms, so, at physical times 50 ms, 250 ms, 450 ms, etc. after the start time, if an input has not arrived, then the input will be assumed to be absent and the `Watchdog`'s reaction will be invoked.
253+
The reaction, therefore, just has to check whether the input is present.
254+
If it is, then the `Monitored` federate is alive and well and its processor output was received by the `Watchdog` within 50 ms.
255+
Otherwise, something has gone wrong that has led to a delay greater than 50 ms.
256+
257+
173258
## Ongoing Research
174259

175260
Several significant efforts are under way to improve the real-time behavior of LF and to guide scheduling using deadlines.
@@ -256,13 +341,11 @@ In this case, as with the federate execution, the deadline will be systematicall
256341
As with federated execution, there is a subtlety because an `EnclaveCommunication` reactor is inserted on the communication path to the enclave.
257342
It has two reactions separated by a logical action, so, for this particular structure, the reaction that sends data to `pa` will have the same level as the reaction in `p1`.
258343

259-
The goal of enclaves is to achieve the same decoupling as with federates, but with all enclaves executing in the same process and communicating via shared memory.
260-
261-
### Early Deadline Violation Detection
344+
Enclaves realize the equivalent of centralized coordination, which makes sense because they all run in the same process.
345+
However, this means that the `FederatedWatchdog` example cannot be converted as-is to use enclaves.
346+
A similar monitor, however, can be created using a physical connection.
262347

263-
Watchdogs.
264-
265-
[Asch, et al., Software-Defined Watchdog Timers for Cyber-Physical Systems](https://ieeexplore.ieee.org/document/10693560)
348+
The goal of enclaves is to achieve the same decoupling as with federates, but with all enclaves executing in the same process and communicating via shared memory.
266349

267350
## Conclusions
268351

blog/src/FederatedWatchdog.lf

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Use of the decentralized coordinator to implement a watchdog.
3+
*/
4+
target C {
5+
timeout: 1 s,
6+
coordination: decentralized
7+
}
8+
import Sensor, Processor, Actuator from "SensorProcessorActuator.lf"
9+
10+
reactor Monitored(exec = 10 ms) {
11+
output complete:int
12+
s = new Sensor()
13+
p = new Processor(exec = exec)
14+
a = new Actuator()
15+
s.out -> p.inp
16+
p.out -> a.inp
17+
p.out -> complete
18+
}
19+
20+
reactor Watchdog(STA: time = 50 ms) {
21+
input inp:int
22+
timer t(0, 200 ms)
23+
24+
reaction(t, inp) {=
25+
if (!inp->is_present) {
26+
lf_print("%s: ******* Failed to receive input on time at logical time " PRINTF_TIME ".",
27+
lf_reactor_name(self), lf_time_logical_elapsed());
28+
} else {
29+
lf_print("%s: Monitor OK at logical time " PRINTF_TIME ".",
30+
lf_reactor_name(self), lf_time_logical_elapsed());
31+
}
32+
=} STAA(0) {=
33+
lf_print("%s: ******* Monitor received late input.", lf_reactor_name(self));
34+
=}
35+
}
36+
37+
federated reactor {
38+
@label("exec = 60 ms")
39+
m = new Monitored(exec = 60 ms)
40+
@label("STA = 50 ms")
41+
w = new Watchdog()
42+
m.complete -> w.inp
43+
}

static/img/blog/FederatedWatchdog.svg

Lines changed: 1 addition & 0 deletions
Loading

0 commit comments

Comments
 (0)