From dac7871fcdbd2ba45205266bf922c1513be46888 Mon Sep 17 00:00:00 2001 From: "Edward A. Lee" Date: Thu, 12 Dec 2024 12:04:17 -0800 Subject: [PATCH] Added min spacing option for actions with only defer policy so far. --- include/reactor-uc/action.h | 4 +- include/reactor-uc/macros.h | 12 +++--- .../lflang/generator/uc/UcActionGenerator.kt | 2 +- src/action.c | 38 +++++++++++++------ test/lf/src/MinSpacingAction.lf | 20 ++++++++++ 5 files changed, 57 insertions(+), 19 deletions(-) create mode 100644 test/lf/src/MinSpacingAction.lf diff --git a/include/reactor-uc/action.h b/include/reactor-uc/action.h index a6184b84..fe6b2884 100644 --- a/include/reactor-uc/action.h +++ b/include/reactor-uc/action.h @@ -15,6 +15,8 @@ struct Action { Trigger super; // Inherit from Trigger ActionType type; interval_t min_offset; // The minimum offset from the current time that an event can be scheduled on this action. + interval_t min_spacing; // The minimum spacing between two events scheduled on this action. + instant_t last_event_time; // Logical time of most recent event scheduled on this action. void *value_ptr; TriggerEffects effects; // The reactions triggered by this Action. TriggerSources sources; // The reactions that can write to this Action. @@ -28,7 +30,7 @@ struct Action { lf_ret_t (*schedule)(Action *self, interval_t offset, const void *value); }; -void Action_ctor(Action *self, ActionType type, interval_t min_offset, Reactor *parent, Reaction **sources, +void Action_ctor(Action *self, ActionType type, interval_t min_offset, interval_t min_spacing, Reactor *parent, Reaction **sources, size_t sources_size, Reaction **effects, size_t effects_size, Reaction **observers, size_t observers_size, void *value_ptr, size_t value_size, void *payload_buf, bool *payload_used_buf, size_t event_bound); diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index c3e5784b..64722486 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -402,24 +402,24 @@ #define LF_DEFINE_ACTION_CTOR(ReactorName, ActionName, ActionType, EffectSize, SourceSize, ObserverSize, \ MaxPendingEvents, BufferType) \ - void ReactorName##_##ActionName##_ctor(ReactorName##_##ActionName *self, Reactor *parent, interval_t min_delay) { \ - Action_ctor(&self->super, ActionType, min_delay, parent, self->sources, (SourceSize), self->effects, (EffectSize), \ + void ReactorName##_##ActionName##_ctor(ReactorName##_##ActionName *self, Reactor *parent, interval_t min_delay, interval_t min_spacing) { \ + Action_ctor(&self->super, ActionType, min_delay, min_spacing, parent, self->sources, (SourceSize), self->effects, (EffectSize), \ self->observers, ObserverSize, &self->value, sizeof(BufferType), (void *)&self->payload_buf, \ self->payload_used_buf, (MaxPendingEvents)); \ } #define LF_DEFINE_ACTION_CTOR_VOID(ReactorName, ActionName, ActionType, EffectSize, SourceSize, ObserverSize, \ MaxPendingEvents) \ - void ReactorName##_##ActionName##_ctor(ReactorName##_##ActionName *self, Reactor *parent, interval_t min_delay) { \ - Action_ctor(&self->super, ActionType, min_delay, parent, self->sources, (SourceSize), self->effects, (EffectSize), \ + void ReactorName##_##ActionName##_ctor(ReactorName##_##ActionName *self, Reactor *parent, interval_t min_delay, interval_t min_spacing) { \ + Action_ctor(&self->super, ActionType, min_delay, min_spacing, parent, self->sources, (SourceSize), self->effects, (EffectSize), \ self->observers, ObserverSize, NULL, 0, NULL, NULL, (MaxPendingEvents)); \ } #define LF_ACTION_INSTANCE(ReactorName, ActionName) ReactorName##_##ActionName ActionName; -#define LF_INITIALIZE_ACTION(ReactorName, ActionName, MinDelay) \ +#define LF_INITIALIZE_ACTION(ReactorName, ActionName, MinDelay, MinSpacing) \ self->_triggers[_triggers_idx++] = (Trigger *)&self->ActionName; \ - ReactorName##_##ActionName##_ctor(&self->ActionName, &self->super, MinDelay) + ReactorName##_##ActionName##_ctor(&self->ActionName, &self->super, MinDelay, MinSpacing) #define LF_SCOPE_ACTION(ReactorName, ActionName) \ ReactorName##_##ActionName *ActionName = &self->ActionName; \ diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcActionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcActionGenerator.kt index f7ccd328..a473c67d 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcActionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcActionGenerator.kt @@ -70,7 +70,7 @@ class UcActionGenerator(private val reactor: Reactor) { return code; } - private fun generateReactorCtorCode(action: Action) = "LF_INITIALIZE_ACTION(${reactor.codeType}, ${action.name}, ${action.minDelay.orZero().toCCode()});" + private fun generateReactorCtorCode(action: Action) = "LF_INITIALIZE_ACTION(${reactor.codeType}, ${action.name}, ${action.minDelay.orZero().toCCode()}, ${action.minSpacing.orZero().toCCode()});" private fun generateReactorCtorCodeStartup() = "LF_INITIALIZE_STARTUP(${reactor.codeType});" private fun generateReactorCtorCodeShutdown() = "LF_INITIALIZE_SHUTDOWN(${reactor.codeType});" diff --git a/src/action.c b/src/action.c index 47557804..abfdb7dd 100644 --- a/src/action.c +++ b/src/action.c @@ -54,19 +54,10 @@ lf_ret_t Action_schedule(Action *self, interval_t offset, const void *value) { } if (self->events_scheduled >= self->max_pending_events) { - LF_ERR(TRIG, "Scheduled action to often bound: %i", self->max_pending_events); + LF_ERR(TRIG, "Too many pending events. Capacity is %i", self->max_pending_events); return LF_ERR; } - if (value != NULL) { - ret = self->super.payload_pool->allocate(self->super.payload_pool, &payload); - if (ret != LF_OK) { - return ret; - } - - memcpy(payload, value, self->payload_pool.size); - } - tag_t base_tag = ZERO_TAG; interval_t total_offset = lf_time_add(self->min_offset, offset); @@ -77,6 +68,25 @@ lf_ret_t Action_schedule(Action *self, interval_t offset, const void *value) { } tag_t tag = lf_delay_tag(base_tag, total_offset); + + if (self->min_spacing > 0LL) { + instant_t earliest_time = lf_time_add(self->last_event_time, self->min_spacing); + if (earliest_time > tag.time) { + LF_DEBUG(TRIG, "Deferring event on action %p from %lld to %lld.", self, tag.time, earliest_time); + tag.time = earliest_time; + tag.microstep = 0; + } + } + + if (value != NULL) { + ret = self->super.payload_pool->allocate(self->super.payload_pool, &payload); + if (ret != LF_OK) { + return ret; + } + + memcpy(payload, value, self->payload_pool.size); + } + Event event = EVENT_INIT(tag, (Trigger *)self, payload); ret = sched->schedule_at_locked(sched, &event); @@ -86,12 +96,16 @@ lf_ret_t Action_schedule(Action *self, interval_t offset, const void *value) { env->platform->new_async_event(env->platform); } + if (ret == LF_OK) { + self->last_event_time = tag.time; + } + env->leave_critical_section(env); return ret; } -void Action_ctor(Action *self, ActionType type, interval_t min_offset, Reactor *parent, Reaction **sources, +void Action_ctor(Action *self, ActionType type, interval_t min_offset, interval_t min_spacing, Reactor *parent, Reaction **sources, size_t sources_size, Reaction **effects, size_t effects_size, Reaction **observers, size_t observers_size, void *value_ptr, size_t value_size, void *payload_buf, bool *payload_used_buf, size_t event_bound) { @@ -104,6 +118,8 @@ void Action_ctor(Action *self, ActionType type, interval_t min_offset, Reactor * self->type = type; self->value_ptr = value_ptr; self->min_offset = min_offset; + self->min_spacing = min_spacing; + self->last_event_time = NEVER; self->max_pending_events = event_bound; self->events_scheduled = 0; self->schedule = Action_schedule; diff --git a/test/lf/src/MinSpacingAction.lf b/test/lf/src/MinSpacingAction.lf new file mode 100644 index 00000000..d0950924 --- /dev/null +++ b/test/lf/src/MinSpacingAction.lf @@ -0,0 +1,20 @@ +target uC { + platform: native, + build-type: release +} +main reactor { + logical action a(0, 100 ms) + state count:int = 0; + reaction(startup, a) -> a {= + if (self->count > 10) { + env->request_shutdown(env); + } else { + lf_schedule(a, 0); // Defer policy is the default. + } + printf("**** %lld\n", env->get_elapsed_logical_time(env)); + if (self->count > 0) { + validate((self->count - 1) * MSEC(100) == env->get_elapsed_logical_time(env)); + } + self->count++; + =} +} \ No newline at end of file