Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Effects made accessible in watchdog handlers #2359

Merged
merged 10 commits into from
Jul 13, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,42 @@ private Collection<KNode> transformReactorNetwork(
}
}

// Connect watchdogs
Set<WatchdogInstance> watchdogs = new HashSet<>();
watchdogs.addAll(watchdogSources.keySet());
watchdogs.addAll(watchdogDestinations.keySet());

for (WatchdogInstance watchdog : watchdogs) {
KNode node = associateWith(_kNodeExtensions.createNode(), watchdog.getDefinition());
NamedInstanceUtil.linkInstance(node, watchdog);
_utilityExtensions.setID(node, watchdog.uniqueID());
nodes.add(node);
setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE);
Pair<KPort, KPort> ports = _linguaFrancaShapeExtensions.addWatchdogFigureAndPorts(node);
setAnnotatedLayoutOptions(watchdog.getDefinition(), node);
if (watchdog.getTimeout() != null) {
_kLabelExtensions.addOutsideBottomCenteredNodeLabel(
node, String.format("timeout: %s", watchdog.getTimeout().toString()), 7);
}
Set<TriggerInstance<?>> iterSet =
watchdog.effects != null ? watchdog.effects : new HashSet<>();
for (TriggerInstance<?> effect : iterSet) {
if (effect instanceof ActionInstance) {
actionSources.put((ActionInstance) effect, ports.getValue());
}
}

// connect source
for (KPort source : watchdogSources.get(watchdog)) {
connect(createDelayEdge(watchdog), source, ports.getKey());
}

// connect targets
for (KPort target : watchdogDestinations.get(watchdog)) {
connect(createDelayEdge(watchdog), ports.getValue(), target);
}
}

// Connect actions
Set<ActionInstance> actions = new HashSet<>();
actions.addAll(actionSources.keySet());
Expand Down Expand Up @@ -1258,34 +1294,6 @@ private Collection<KNode> transformReactorNetwork(
}
}

// Connect watchdogs
Set<WatchdogInstance> watchdogs = new HashSet<>();
watchdogs.addAll(watchdogSources.keySet());
watchdogs.addAll(watchdogDestinations.keySet());

for (WatchdogInstance watchdog : watchdogs) {
KNode node = associateWith(_kNodeExtensions.createNode(), watchdog.getDefinition());
NamedInstanceUtil.linkInstance(node, watchdog);
_utilityExtensions.setID(node, watchdog.uniqueID());
nodes.add(node);
setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE);
Pair<KPort, KPort> ports = _linguaFrancaShapeExtensions.addWatchdogFigureAndPorts(node);
setAnnotatedLayoutOptions(watchdog.getDefinition(), node);
if (watchdog.getTimeout() != null) {
_kLabelExtensions.addOutsideBottomCenteredNodeLabel(
node, String.format("timeout: %s", watchdog.getTimeout().toString()), 7);
}
// connect source
for (KPort source : watchdogSources.get(watchdog)) {
connect(createDelayEdge(watchdog), source, ports.getKey());
}

// connect targets
for (KPort target : watchdogDestinations.get(watchdog)) {
connect(createDelayEdge(watchdog), ports.getValue(), target);
}
}

// Transform connections.
// First, collect all the source ports.
List<PortInstance> sourcePorts = new LinkedList<>(reactorInstance.inputs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public void handleIcon(KContainerRendering rendering, ReactorDecl reactor, boole
// if (error != null) {
// var errorText = _kContainerRenderingExtensions.addText(rendering,
// "Icon not found!\n"+error);
// _kRenderingExtensions.setForeground(errorText, Colors.RED);
// _kRenderingExtensions.setForeground(errorText, Colors.BLUE);
edwardalee marked this conversation as resolved.
Show resolved Hide resolved
// _kRenderingExtensions.setFontBold(errorText, true);
// _kRenderingExtensions.setSurroundingSpaceGrid(errorText, 8, 0);
// }
Expand Down
22 changes: 21 additions & 1 deletion core/src/main/java/org/lflang/generator/WatchdogInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
package org.lflang.generator;

import org.lflang.TimeValue;
import org.lflang.lf.Watchdog;
import org.lflang.lf.*;

import java.util.LinkedHashSet;
import java.util.Set;

/**
* Instance of a watchdog. Upon creation the actual delay is converted into a proper time value. If
Expand All @@ -33,6 +36,17 @@ public WatchdogInstance(Watchdog definition, ReactorInstance reactor) {
this.name = definition.getName().toString();
this.definition = definition;
this.reactor = reactor;
for (VarRef effect : definition.getEffects()) {
Variable variable = effect.getVariable();
if (variable instanceof Action) {
// Effect is an Action.
var actionInstance = reactor.lookupActionInstance((Action) variable);
if (actionInstance != null) this.effects.add(actionInstance);
} else {
// Effect is either a mode or an unresolved reference.
// Do nothing, transitions will be set up by the ModeInstance.
}
}
}

//////////////////////////////////////////////////////
Expand All @@ -59,6 +73,12 @@ public String toString() {
return "WatchdogInstance " + name + "(" + timeout.toString() + ")";
}

//////////////////////////////////////////////////////
//// Public fields.

/** The ports or actions that this reaction may write to. */
public Set<TriggerInstance<? extends Variable>> effects = new LinkedHashSet<>();

//////////////////////////////////////////////////////
//// Private fields.

Expand Down
26 changes: 20 additions & 6 deletions core/src/main/java/org/lflang/generator/c/CWatchdogGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,12 @@
package org.lflang.generator.c;

import java.util.List;

import org.lflang.MessageReporter;
import org.lflang.ast.ASTUtils;
import org.lflang.generator.CodeBuilder;
import org.lflang.generator.ReactorInstance;
import org.lflang.lf.Mode;
import org.lflang.lf.ModeTransition;
import org.lflang.lf.Reactor;
import org.lflang.lf.VarRef;
import org.lflang.lf.Variable;
import org.lflang.lf.Watchdog;
import org.lflang.lf.*;
edwardalee marked this conversation as resolved.
Show resolved Hide resolved
import org.lflang.util.StringUtil;

/**
Expand Down Expand Up @@ -227,6 +223,8 @@ private static String generateInitializationForWatchdog(
+ name
+ " not a valid mode of this reactor.");
}
} else if (variable instanceof Action) {
watchdogInitialization.pr(generateActionVariablesInHandler((Action)variable, tpr));
}
}
}
Expand All @@ -243,6 +241,18 @@ private static String generateInitializationForWatchdog(
return code.toString();
}

/**
* Generate action variables for the watchdog handler.
* @param action The action.
*/
private static String generateActionVariablesInHandler(Action action, TypeParameterizedReactor tpr) {
String structType = CGenerator.variableStructType(action, tpr, false);
CodeBuilder builder = new CodeBuilder();
builder.pr("// Expose the action struct as a local variable whose name matches the action name.");
builder.pr(structType + "* " + action.getName() + " = &self->_lf_" + action.getName() + ";");
return builder.toString();
}

/**
* Do heavy lifting to generate the watchdog handler function
*
Expand All @@ -268,6 +278,8 @@ private static String generateFunction(
function.pr(header + " {");
function.indent();
function.pr(init);
function.pr("{"); // Limit scope.
function.indent();
function.pr("environment_t * __env = self->base.environment;");
function.pr("LF_MUTEX_LOCK(&__env->mutex);");
function.pr("tag_t tag = {.time =" + watchdog.getName() + "->expiration , .microstep=0};");
Expand All @@ -280,6 +292,8 @@ private static String generateFunction(
function.pr("_lf_schedule_at_tag(__env, " + watchdog.getName() + "->trigger, tag, NULL);");
function.pr("lf_cond_broadcast(&__env->event_q_changed);");
function.pr("LF_MUTEX_UNLOCK(&__env->mutex);");
function.unindent();
function.pr("}");
function.prSourceLineNumber(watchdog.getCode(), suppressLineDirectives);
function.pr(ASTUtils.toText(watchdog.getCode()));
function.prEndSourceLineNumber(suppressLineDirectives);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.lflang.lf.Reactor;
import org.lflang.lf.ReactorDecl;
import org.lflang.lf.VarRef;
import org.lflang.lf.Watchdog;

/**
* This class enforces custom rules. In particular, it resolves references to parameters, ports,
Expand Down Expand Up @@ -273,6 +274,11 @@ private RefType getRefType(VarRef variable) {
} else if (conn.getRightPorts().contains(variable)) {
return RefType.CRIGHT;
}
} else if (variable.eContainer() instanceof Watchdog) {
var watchdog = (Watchdog) variable.eContainer();
if (watchdog.getEffects().contains(variable)) {
return RefType.EFFECT;
}
}
return RefType.NULL;
}
Expand Down
74 changes: 74 additions & 0 deletions test/C/src/concurrent/WatchdogAction.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Test watchdog. This test starts a watchdog timer of 1500ms every 1s. Half the time, it then
* sleeps after starting the watchdog so that the watchdog expires. There should be a total of two
* watchdog expirations. This version uses an action instead of a reaction to the watchdog.
* @author Benjamin Asch
* @author Edward A. Lee
*/
target C {
timeout: 11000 ms
}

reactor Watcher(timeout: time = 1500 ms) {
// Offset ameliorates startup time.
edwardalee marked this conversation as resolved.
Show resolved Hide resolved
timer t(1 s, 1 s)
// Period has to be smaller than watchdog timeout. Produced if the watchdog triggers.
output d: int
state count: int = 0
logical action a

watchdog poodle(timeout) -> a {=
instant_t p = lf_time_physical_elapsed();
lf_print("******** Watchdog timed out at elapsed physical time: " PRINTF_TIME, p);
self->count++;
lf_schedule(a, 0);
=}

reaction(t) -> poodle, d {=
lf_watchdog_start(poodle, 0);
lf_print("Watchdog started at physical time " PRINTF_TIME, lf_time_physical_elapsed());
lf_print("Will expire at " PRINTF_TIME, lf_time_logical_elapsed() + self->timeout);
lf_set(d, 42);
=}

reaction(a) -> d {=
lf_print("Reaction poodle was called.");
lf_set(d, 1);
=}

reaction(shutdown) -> poodle {=
lf_watchdog_stop(poodle);
// Watchdog may expire in tests even without the sleep, but it should at least expire twice.
if (self->count < 2) {
lf_print_error_and_exit("Watchdog expired %d times. Expected at least 2.", self->count);
}
=}
}

main reactor {
logical action a
state count: int = 0

w = new Watcher()

reaction(startup) {=
if (NUMBER_OF_WATCHDOGS != 1) {
lf_print_error_and_exit("NUMBER_OF_WATCHDOGS was %d", NUMBER_OF_WATCHDOGS);
}
=}

reaction(w.d) {=
lf_print("Watcher reactor produced an output. %d", self->count % 2);
self->count++;
if (self->count % 4 == 0) {
lf_print(">>>>>> Taking a long time to process that output!");
lf_sleep(MSEC(1600));
}
=}

reaction(shutdown) {=
if (self->count < 12) {
lf_print_error_and_exit("Watchdog produced output %d times. Expected at least 12.", self->count);
}
=}
}
Loading