Skip to content

Commit 29b0728

Browse files
Reaction Deadline Handlers (#148)
* add deadline handlers * add forward declaration * Add code-gen support for deadlines * Remove some code duplication --------- Co-authored-by: erlingrj <[email protected]>
1 parent 9092780 commit 29b0728

File tree

8 files changed

+156
-11
lines changed

8 files changed

+156
-11
lines changed

include/reactor-uc/macros.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,13 +291,30 @@
291291
#define DEFINE_REACTION_BODY(ReactorName, ReactionName) \
292292
void ReactorName##_Reaction_##ReactionName##_body(Reaction *_self)
293293

294-
#define DEFINE_REACTION_CTOR(ReactorName, ReactionName, Priority) \
294+
#define DEFINE_REACTION_DEADLINE_HANDLER(ReactorName, ReactionName) \
295+
void ReactorName##_Reaction_##ReactionName##_deadline_handler(Reaction *_self)
296+
297+
#define DEFINE_REACTION_CTOR_WITHOUT_DEADLINE(ReactorName, ReactionName, Priority) \
295298
DEFINE_REACTION_BODY(ReactorName, ReactionName); \
296299
void ReactorName##_Reaction_##ReactionName##_ctor(ReactorName##_Reaction_##ReactionName *self, Reactor *parent) { \
297300
Reaction_ctor(&self->super, parent, ReactorName##_Reaction_##ReactionName##_body, self->effects, \
298-
sizeof(self->effects) / sizeof(self->effects[0]), Priority); \
301+
sizeof(self->effects) / sizeof(self->effects[0]), Priority, NULL, 0); \
299302
}
300303

304+
#define DEFINE_REACTION_CTOR_WITH_DEADLINE(ReactorName, ReactionName, Priority, Deadline) \
305+
DEFINE_REACTION_DEADLINE_HANDLER(ReactorName, ReactionName); \
306+
void ReactorName##_Reaction_##ReactionName##_ctor(ReactorName##_Reaction_##ReactionName *self, Reactor *parent) { \
307+
Reaction_ctor(&self->super, parent, ReactorName##_Reaction_##ReactionName##_body, self->effects, \
308+
sizeof(self->effects) / sizeof(self->effects[0]), Priority, \
309+
ReactorName##_Reaction_##ReactionName##_deadline_handler, (Deadline)); \
310+
}
311+
312+
#define GET_ARG3(arg1, arg2, arg3, arg4, arg5, ...) arg5
313+
#define DEFINE_REACTION_CTOR_CHOOSER(...) \
314+
GET_ARG3(__VA_ARGS__, DEFINE_REACTION_CTOR_WITH_DEADLINE, DEFINE_REACTION_CTOR_WITHOUT_DEADLINE)
315+
316+
#define DEFINE_REACTION_CTOR(...) DEFINE_REACTION_CTOR_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
317+
301318
#define DEFINE_STARTUP_STRUCT(ReactorName, EffectSize, ObserversSize) \
302319
typedef struct { \
303320
BuiltinTrigger super; \

include/reactor-uc/reaction.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ typedef struct Trigger Trigger;
1111
struct Reaction {
1212
Reactor *parent;
1313
void (*body)(Reaction *self);
14+
void (*deadline_handler)(Reaction *self);
15+
interval_t deadline;
1416
int level; // Negative level means it is invalid.
1517
size_t index;
1618
Trigger **effects;
@@ -21,6 +23,6 @@ struct Reaction {
2123
};
2224

2325
void Reaction_ctor(Reaction *self, Reactor *parent, void (*body)(Reaction *), Trigger **effects, size_t effects_size,
24-
size_t index);
26+
size_t index, void (*deadline_handler)(Reaction *), interval_t deadline);
2527

2628
#endif

lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ class UcReactionGenerator(private val reactor: Reactor) {
1515
val Reaction.index
1616
get(): Int = priority - 1
1717

18+
19+
20+
private val Reaction.ctorDeadlineArg
21+
get() = if (deadline != null) ", ${deadline.delay.toCCode()}" else ""
22+
1823
private val Reaction.allUncontainedTriggers
1924
get() = triggers.filterNot { it.isEffectOf(this) || it.isContainedRef }
2025
private val Reaction.allUncontainedEffects
@@ -29,6 +34,8 @@ class UcReactionGenerator(private val reactor: Reactor) {
2934
private val Reaction.allContainedSources
3035
get() = sources.filter { !it.isEffectOf(this) && it.isContainedRef }
3136

37+
private val reactionsWithDeadline = reactor.allReactions.filter {it.deadline != null}
38+
3239
// Calculate the total number of effects, considering that we might write to
3340
// a contained input port
3441
private val Reaction.totalNumEffects
@@ -151,7 +158,7 @@ class UcReactionGenerator(private val reactor: Reactor) {
151158
}
152159

153160
private fun generateReactionCtor(reaction: Reaction) =
154-
"DEFINE_REACTION_CTOR(${reactor.codeType}, ${reaction.codeName}, ${reaction.index});"
161+
"DEFINE_REACTION_CTOR(${reactor.codeType}, ${reaction.codeName}, ${reaction.index} ${reaction.ctorDeadlineArg});"
155162

156163
private fun generateSelfStruct(reaction: Reaction) =
157164
"DEFINE_REACTION_STRUCT(${reactor.codeType}, ${reaction.codeName}, ${reaction.totalNumEffects});"
@@ -185,15 +192,37 @@ class UcReactionGenerator(private val reactor: Reactor) {
185192
postfix = "\n"
186193
) { generateReactionBody(it) }
187194

188-
private fun generateReactionBody(reaction: Reaction) = with(PrependOperator) {
189-
"""
190-
|DEFINE_REACTION_BODY(${reactor.codeType}, ${reaction.codeName}) {
191-
| // Bring self struct, environment, triggers, effects and sources into scope.
195+
fun generateReactionDeadlineHandlers() =
196+
reactionsWithDeadline.joinToString(
197+
separator = "\n",
198+
prefix = "// Reaction deadline handlers\n",
199+
postfix="\n"
200+
) { generateReactionDeadlineHandler(it) }
201+
202+
private fun generateReactionScope(reaction: Reaction) = with(PrependOperator) {
203+
""" |// Bring self struct, environment, triggers, effects and sources into scope.
192204
| SCOPE_SELF(${reactor.codeType});
193205
| SCOPE_ENV();
194206
${"| "..generateTriggersEffectsAndSourcesInScope(reaction)}
195207
${"| "..generateContainedTriggersAndSourcesInScope(reaction)}
208+
""".trimMargin()
209+
}
210+
211+
private fun generateReactionDeadlineHandler(reaction: Reaction) = with(PrependOperator) {
212+
"""
213+
|DEFINE_REACTION_DEADLINE_HANDLER(${reactor.codeType}, ${reaction.codeName}) {
214+
${"| "..generateReactionScope(reaction)}
196215
| // Start of user-witten reaction body
216+
${"| "..reaction.deadline.code.toText()}
217+
|}
218+
""".trimMargin()
219+
}
220+
221+
private fun generateReactionBody(reaction: Reaction) = with(PrependOperator) {
222+
"""
223+
|DEFINE_REACTION_BODY(${reactor.codeType}, ${reaction.codeName}) {
224+
${"| "..generateReactionScope(reaction)}
225+
| // Start of user-witten reaction deadline handler
197226
${"| "..reaction.code.toText()}
198227
|}
199228
""".trimMargin()

lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ class UcReactorGenerator(private val reactor: Reactor, fileConfig: UcFileConfig,
170170
|#include "${headerFile}"
171171
${" |"..preambles.generateReactorPrivatePreamble()}
172172
${" |"..reactions.generateReactionBodies()}
173+
${" |"..reactions.generateReactionDeadlineHandlers()}
173174
${" |"..reactions.generateReactionCtors()}
174175
${" |"..actions.generateCtors()}
175176
${" |"..timers.generateCtors()}

src/reaction.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ size_t Reaction_calculate_level(Reaction *self) {
101101
}
102102

103103
void Reaction_ctor(Reaction *self, Reactor *parent, void (*body)(Reaction *self), Trigger **effects,
104-
size_t effects_size, size_t index) {
104+
size_t effects_size, size_t index, void (*deadline_handler)(Reaction *), interval_t deadline) {
105105
self->body = body;
106106
self->parent = parent;
107107
self->effects = effects;
@@ -111,4 +111,6 @@ void Reaction_ctor(Reaction *self, Reactor *parent, void (*body)(Reaction *self)
111111
self->get_level = Reaction_get_level;
112112
self->index = index;
113113
self->level = -1;
114+
self->deadline_handler = deadline_handler;
115+
self->deadline = deadline;
114116
}

src/schedulers/dynamic/scheduler.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,17 @@ void Scheduler_run_timestep(Scheduler *untyped_self) {
140140
while (!self->reaction_queue.empty(&self->reaction_queue)) {
141141
Reaction *reaction = self->reaction_queue.pop(&self->reaction_queue);
142142
assert(reaction);
143-
LF_DEBUG(SCHED, "Executing %s->reaction_%d", reaction->parent->name, reaction->index);
144-
reaction->body(reaction);
143+
144+
if (reaction->deadline_handler == NULL) {
145+
LF_DEBUG(SCHED, "Executing %s->reaction_%d", reaction->parent->name, reaction->index);
146+
reaction->body(reaction);
147+
} else if (self->env->get_physical_time(self->env) > (self->current_tag.time + reaction->deadline)) {
148+
LF_WARN(SCHED, "Deadline violation detected for %s->reaction_%d", reaction->parent->name, reaction->index);
149+
reaction->deadline_handler(reaction);
150+
} else {
151+
LF_DEBUG(SCHED, "Executing %s->reaction_%d", reaction->parent->name, reaction->index);
152+
reaction->body(reaction);
153+
}
145154
}
146155
}
147156

test/lf/src/Deadline.lf

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
target uC {
2+
// platform: Native
3+
}
4+
5+
6+
main reactor {
7+
8+
state check:bool = false;
9+
10+
reaction(startup) {=
11+
env->platform->wait_for(env->platform, MSEC(100));
12+
=}
13+
14+
reaction(startup) {=
15+
16+
=} deadline(100 msec) {=
17+
self->check = true;
18+
=}
19+
20+
reaction(shutdown) {=
21+
validate(self->check);
22+
=}
23+
24+
}

test/unit/deadline_test.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#include "reactor-uc/reactor-uc.h"
2+
#include "unity.h"
3+
4+
#include <unistd.h>
5+
6+
DEFINE_TIMER_STRUCT(TimerTest, t, 1, 0)
7+
DEFINE_REACTION_STRUCT(TimerTest, reaction, 0)
8+
9+
typedef struct {
10+
Reactor super;
11+
REACTION_INSTANCE(TimerTest, reaction);
12+
TIMER_INSTANCE(TimerTest, t);
13+
REACTOR_BOOKKEEPING_INSTANCES(1,1,0);
14+
int cnt;
15+
} TimerTest;
16+
17+
DEFINE_REACTION_BODY(TimerTest, reaction) {
18+
SCOPE_SELF(TimerTest);
19+
SCOPE_ENV();
20+
TEST_ASSERT_EQUAL(self->cnt * MSEC(1), env->get_elapsed_logical_time(env));
21+
printf("Hello World @ %ld\n", env->get_elapsed_logical_time(env));
22+
self->cnt++;
23+
sleep(self->cnt);
24+
}
25+
26+
DEFINE_REACTION_DEADLINE_HANDLER(TimerTest, reaction) {
27+
SCOPE_SELF(TimerTest);
28+
SCOPE_ENV();
29+
30+
TEST_ASSERT(self->cnt == 2);
31+
printf("Deadline Violated!!!\n");
32+
env->request_shutdown(env);
33+
}
34+
35+
DEFINE_TIMER_CTOR(TimerTest, t, 1, 0)
36+
DEFINE_REACTION_CTOR(TimerTest, reaction, 0, SEC(2))
37+
38+
REACTOR_CTOR_SIGNATURE(TimerTest) {
39+
REACTOR_CTOR_PREAMBLE();
40+
REACTOR_CTOR(TimerTest);
41+
INITIALIZE_REACTION(TimerTest, reaction);
42+
INITIALIZE_TIMER(TimerTest, t, MSEC(0), MSEC(1));
43+
TIMER_REGISTER_EFFECT(self->t, self->reaction);
44+
}
45+
46+
TimerTest my_reactor;
47+
Environment env;
48+
void test_simple() {
49+
Environment_ctor(&env, (Reactor *)&my_reactor);
50+
env.scheduler->duration = MSEC(100);
51+
TimerTest_ctor(&my_reactor, NULL, &env);
52+
env.assemble(&env);
53+
env.start(&env);
54+
Environment_free(&env);
55+
}
56+
57+
int main() {
58+
UNITY_BEGIN();
59+
RUN_TEST(test_simple);
60+
return UNITY_END();
61+
}

0 commit comments

Comments
 (0)