diff --git a/core/src/main/java/org/lflang/generator/c/CActionGenerator.java b/core/src/main/java/org/lflang/generator/c/CActionGenerator.java index 0d89a1f81b..5577edc71a 100644 --- a/core/src/main/java/org/lflang/generator/c/CActionGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CActionGenerator.java @@ -49,11 +49,13 @@ public static String generateInitializers(ReactorInstance instance) { ? CTypes.getInstance().getTargetTimeExpr(minSpacing) : CGenerator.UNDEFINED_MIN_SPACING) + ";"; + var lastTimeInitializer = triggerStructName + ".last_time = NEVER;"; code.addAll( List.of( "// Initializing action " + action.getFullName(), offsetInitializer, - periodInitializer)); + periodInitializer, + lastTimeInitializer)); var mode = action.getMode(false); if (mode != null) { diff --git a/core/src/main/java/org/lflang/generator/c/CGenerator.java b/core/src/main/java/org/lflang/generator/c/CGenerator.java index dd2fc494d5..7eaf3d860d 100644 --- a/core/src/main/java/org/lflang/generator/c/CGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CGenerator.java @@ -1253,7 +1253,7 @@ private void generateInteractingContainedReactors( constructorCode.pr( String.join( "\n", - portOnSelf + "_trigger.last = NULL;", + portOnSelf + "_trigger.last_time = NEVER;", portOnSelf + "_trigger.number_of_reactions = " + triggered.size() + ";")); // Set the physical_time_of_arrival diff --git a/core/src/main/java/org/lflang/generator/c/CReactionGenerator.java b/core/src/main/java/org/lflang/generator/c/CReactionGenerator.java index 0cb4e7cd0b..972ab3a40b 100644 --- a/core/src/main/java/org/lflang/generator/c/CReactionGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CReactionGenerator.java @@ -1024,7 +1024,7 @@ private static void createTriggerT( var varName = variable.getName(); // variable is a port, a timer, or an action. body.pr("trigger_t _lf__" + varName + ";"); - constructorCode.pr("self->_lf__" + varName + ".last = NULL;"); + constructorCode.pr("self->_lf__" + varName + ".last_time = NEVER;"); constructorCode.pr( CExtensionUtils.surroundWithIfFederatedDecentralized( "self->_lf__" @@ -1097,7 +1097,7 @@ public static void generateBuiltinTriggeredReactionsArray( constructorCode.pr( String.join( "\n", - "self->_lf__" + name + ".last = NULL;", + "self->_lf__" + name + ".last_time = NEVER;", "self->_lf__" + name + ".reactions = &self->_lf__" + name + "_reactions[0];", "self->_lf__" + name + ".number_of_reactions = " + reactions.size() + ";", "self->_lf__" + name + ".is_timer = false;")); diff --git a/core/src/main/resources/lib/c/reactor-c b/core/src/main/resources/lib/c/reactor-c index 89aa9b8ef8..7f199bc62b 160000 --- a/core/src/main/resources/lib/c/reactor-c +++ b/core/src/main/resources/lib/c/reactor-c @@ -1 +1 @@ -Subproject commit 89aa9b8ef8642ab105f58915432cae8f462aaf38 +Subproject commit 7f199bc62bdb7fcb1cb9cd33db293d167711da9e diff --git a/test/C/src/LastTimeDefer.lf b/test/C/src/LastTimeDefer.lf new file mode 100644 index 0000000000..2509a5e896 --- /dev/null +++ b/test/C/src/LastTimeDefer.lf @@ -0,0 +1,45 @@ +// Test for https://github.com/lf-lang/reactor-c/issues/145 +// This tests the defer policy, which is the default. +target C { + timeout: 1 s +} + +main reactor { + timer t(0, 100 ms) + logical action a(1 ms, 300 ms): int + state c: int = 0 + state c2: int = 0 // For expected values. + state last: time = 0 + + reaction(startup) {= + // Unfortunately, a time state variable cannot be initialized with NEVER. + // So we do that here. + self->last = NEVER; + =} + + reaction(t) -> a {= + tag_t now = lf_tag(); + instant_t start = lf_time_start(); + lf_print("(Timer) Current: " PRINTF_TIME ", Scheduled: " PRINTF_TIME ", Count: %d", + now.time - start, now.time + MSEC(1) - start, self->c + ); + lf_schedule_int(a, 0, self->c++); + =} + + reaction(a) {= + tag_t now = lf_tag(); + instant_t start = lf_time_start(); + lf_print("(Action) Current: " PRINTF_TIME ", Microstep: %d, Count: %d", + now.time - start, now.microstep, a->value + ); + // Check min_spacing. + if (now.time < self->last + MSEC(300)) { + lf_print_error_and_exit("Minimum spacing of 300ms was violated."); + } + // The value should be equal to the current state c before incrementing. + if (a->value != self->c2++) { + lf_print_error_and_exit("Expected value %d", self->c2 - 1); + } + self->last = now.time; + =} +} diff --git a/test/C/src/LastTimeDrop.lf b/test/C/src/LastTimeDrop.lf new file mode 100644 index 0000000000..2862ac75f0 --- /dev/null +++ b/test/C/src/LastTimeDrop.lf @@ -0,0 +1,44 @@ +// Test for https://github.com/lf-lang/reactor-c/issues/145 +// This tests the drop policy. +target C { + timeout: 1 s +} + +main reactor { + timer t(0, 100 ms) + logical action a(1 ms, 300 ms, "drop"): int + state c: int = 0 + state last: time = 0 + + reaction(startup) {= + // Unfortunately, a time state variable cannot be initialized with NEVER. + // So we do that here. + self->last = NEVER; + =} + + reaction(t) -> a {= + tag_t now = lf_tag(); + instant_t start = lf_time_start(); + lf_print("(Timer) Current: " PRINTF_TIME ", Scheduled: " PRINTF_TIME ", Count: %d", + now.time - start, now.time + MSEC(1) - start, self->c + ); + lf_schedule_int(a, 0, self->c++); + =} + + reaction(a) {= + tag_t now = lf_tag(); + instant_t start = lf_time_start(); + lf_print("(Action) Current: " PRINTF_TIME ", Microstep: %d, Count: %d", + now.time - start, now.microstep, a->value + ); + // Check min_spacing. + if (now.time < self->last + MSEC(300)) { + lf_print_error_and_exit("Minimum spacing of 300ms was violated."); + } + // The value should be equal to the current state c before incrementing. + if (a->value != self->c - 1) { + lf_print_error_and_exit("Expected value %d", self->c - 1); + } + self->last = now.time; + =} +} diff --git a/test/C/src/LastTimeReplace.lf b/test/C/src/LastTimeReplace.lf new file mode 100644 index 0000000000..0d44bb58b5 --- /dev/null +++ b/test/C/src/LastTimeReplace.lf @@ -0,0 +1,44 @@ +// Test for https://github.com/lf-lang/reactor-c/issues/145 +// This tests the replace policy. +target C { + timeout: 1 s +} + +main reactor { + timer t(0, 100 ms) + logical action a(1 ms, 300 ms, "replace"): int + state c: int = 0 + state last: time = 0 + + reaction(startup) {= + // Unfortunately, a time state variable cannot be initialized with NEVER. + // So we do that here. + self->last = NEVER; + =} + + reaction(t) -> a {= + tag_t now = lf_tag(); + instant_t start = lf_time_start(); + lf_print("(Timer) Current: " PRINTF_TIME ", Scheduled: " PRINTF_TIME ", Count: %d", + now.time - start, now.time + MSEC(1) - start, self->c + ); + lf_schedule_int(a, 0, self->c++); + =} + + reaction(a) {= + tag_t now = lf_tag(); + instant_t start = lf_time_start(); + lf_print("(Action) Current: " PRINTF_TIME ", Microstep: %d, Count: %d", + now.time - start, now.microstep, a->value + ); + // Check min_spacing. + if (now.time < self->last + MSEC(300)) { + lf_print_error_and_exit("Minimum spacing of 300ms was violated."); + } + // The value should be equal to the current state c before incrementing. + if (a->value != self->c - 1) { + lf_print_error_and_exit("Expected value %d", self->c - 1); + } + self->last = now.time; + =} +} diff --git a/test/TypeScript/src/federated/DistributedLoopedPhysicalAction.lf b/test/TypeScript/src/federated/DistributedLoopedPhysicalAction.lf index fec7fc0145..54320c2dd7 100644 --- a/test/TypeScript/src/federated/DistributedLoopedPhysicalAction.lf +++ b/test/TypeScript/src/federated/DistributedLoopedPhysicalAction.lf @@ -33,7 +33,7 @@ reactor Receiver(takeBreakAfter: number = 10, breakInterval: time = 550 msec) { state receivedMessages: number = 0 state totalReceivedMessages: number = 0 state breaks: number = 0 - timer t(0, 1 msec) // This will impact the performance + timer t(0, 10 msec) // This will impact the performance // but forces the logical time to advance Comment this line for a more sensible log output. reaction(inp) {=