From 2eb940dd88ffcdcfce7deefaf29c467e5ca0dfe3 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 11 Apr 2025 08:11:55 +0200 Subject: [PATCH 01/29] WIP --- include/reactor-uc/environment.h | 3 +++ include/reactor-uc/platform.h | 3 +++ src/environments/unfederated_environment.c | 10 ++++++++++ src/platform/posix/posix.c | 10 ++++++++++ test/lf/src/EnclavedSimple.lf | 19 +++++++++++++++++++ test/lf/src/FederatedConnection.lf | 1 + 6 files changed, 46 insertions(+) create mode 100644 test/lf/src/EnclavedSimple.lf diff --git a/include/reactor-uc/environment.h b/include/reactor-uc/environment.h index e0aab5fcd..4fb1ff8a4 100644 --- a/include/reactor-uc/environment.h +++ b/include/reactor-uc/environment.h @@ -22,6 +22,9 @@ struct Environment { Reactor *main; // The top-level reactor of the program. Scheduler *scheduler; // The scheduler in charge of executing the reactions. Platform *platform; // The platform that provides the physical time and sleep functions. + Environment **enclaved_environments; + pthread_t thread; // FIXME: Move to EnclavedEnvironment + size_t num_enclaved_environments; bool has_async_events; // Whether the program has multiple execution contexts and can receive async events and thus // need critical sections. bool fast_mode; // Whether the program is executing in fast mode where we do not wait for physical time to elapse diff --git a/include/reactor-uc/platform.h b/include/reactor-uc/platform.h index b5b8e76e8..b0cd428c9 100644 --- a/include/reactor-uc/platform.h +++ b/include/reactor-uc/platform.h @@ -4,6 +4,7 @@ #include "reactor-uc/error.h" #include "reactor-uc/tag.h" #include +#include typedef struct Platform Platform; @@ -34,6 +35,8 @@ struct Platform { */ lf_ret_t (*wait_until_interruptible_locked)(Platform *super, instant_t wakeup_time); + lf_ret_t (*create_thread)(Platform *super, pthread_t *thread, void* (*thread_func)(void*), void *arguments); + /** * @brief Signal the occurrence of an asynchronous event. This should wake * up the platform if it is sleeping on `wait_until_interruptible_locked`. diff --git a/src/environments/unfederated_environment.c b/src/environments/unfederated_environment.c index 55e8960b3..eab855c65 100644 --- a/src/environments/unfederated_environment.c +++ b/src/environments/unfederated_environment.c @@ -6,6 +6,12 @@ #include #include +static void *enclave_thread(void *environment_pointer) { + Environment *env = (Environment *)environment_pointer; + env->start(env); + return NULL; +} + static void Environment_validate(Environment *self) { Reactor_validate(self->main); } @@ -22,6 +28,10 @@ static void Environment_assemble(Environment *self) { static void Environment_start(Environment *self) { instant_t start_time = self->get_physical_time(self); LF_INFO(ENV, "Starting program at " PRINTF_TIME " nsec", start_time); + for (size_t i = 0; i < self->num_enclaved_environments; i++) { + self->platform->create_thread(self->platform, self->enclaved_environments[i]->thread, enclave_thread, + (void *)self->enclaved_environments[i]); + } self->scheduler->set_and_schedule_start_tag(self->scheduler, start_time); self->scheduler->run(self->scheduler); } diff --git a/src/platform/posix/posix.c b/src/platform/posix/posix.c index b5f893de4..87aa2d594 100644 --- a/src/platform/posix/posix.c +++ b/src/platform/posix/posix.c @@ -129,6 +129,15 @@ void PlatformPosix_new_async_event(Platform *super) { LF_DEBUG(PLATFORM, "New async event"); } +lf_ret_t PlatformPosix_create_thread(Platform *super, pthread_t *thread, void* (*thread_func)(void*), void *arguments) { + int ret = pthread_create(thread, NULL, thread_func, arguments); + if (ret == 0) { + return LF_OK; + } else { + validate(false); + } +} + void Platform_ctor(Platform *super) { super->enter_critical_section = PlatformPosix_enter_critical_section; super->leave_critical_section = PlatformPosix_leave_critical_section; @@ -138,6 +147,7 @@ void Platform_ctor(Platform *super) { super->initialize = PlatformPosix_initialize; super->wait_until_interruptible_locked = PlatformPosix_wait_until_interruptible; super->new_async_event = PlatformPosix_new_async_event; + super->create_thread = PlatformPosix_create_thread; } Platform *Platform_new(void) { diff --git a/test/lf/src/EnclavedSimple.lf b/test/lf/src/EnclavedSimple.lf new file mode 100644 index 000000000..d9d489e52 --- /dev/null +++ b/test/lf/src/EnclavedSimple.lf @@ -0,0 +1,19 @@ +target uC { + platform: Native +} + + +reactor R(id: int = 0){ + reaction(startup) {= + printf("Hello From Enclave %d\n", self->id); + =} +} + + +main reactor { + @enclave + e1 = new R(id=1) + + @enclave + e2 = new R(id=2) +} \ No newline at end of file diff --git a/test/lf/src/FederatedConnection.lf b/test/lf/src/FederatedConnection.lf index ca363f309..2f067eab5 100644 --- a/test/lf/src/FederatedConnection.lf +++ b/test/lf/src/FederatedConnection.lf @@ -38,6 +38,7 @@ reactor Dst { federated reactor { r1 = new Src(id=42) r2 = new Dst() + r1.out -> r2.in r2.out -> r1.in } \ No newline at end of file From 3af4a8b38ceedda34f892e656c8455afab4b086a Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 11 Apr 2025 15:46:27 +0200 Subject: [PATCH 02/29] WIP --- include/reactor-uc/environment.h | 3 --- .../environments/enclaved_environment.h | 22 +++++++++++++++++++ include/reactor-uc/macros_internal.h | 14 +++++++----- include/reactor-uc/platform.h | 1 - src/environments/unfederated_environment.c | 6 ++--- src/platform/posix/posix.c | 1 + test/lf/src/EnclavedSimple.lf | 3 --- test/lf/src/FedTest.lf | 19 ++++++++++++++++ test/lf/src/FederatedConnection.lf | 1 - 9 files changed, 54 insertions(+), 16 deletions(-) create mode 100644 include/reactor-uc/environments/enclaved_environment.h create mode 100644 test/lf/src/FedTest.lf diff --git a/include/reactor-uc/environment.h b/include/reactor-uc/environment.h index d92e005e1..bbe5a5fe3 100644 --- a/include/reactor-uc/environment.h +++ b/include/reactor-uc/environment.h @@ -22,9 +22,6 @@ struct Environment { Reactor *main; // The top-level reactor of the program. Scheduler *scheduler; // The scheduler in charge of executing the reactions. Platform *platform; // The platform that provides the physical time and sleep functions. - Environment **enclaved_environments; - pthread_t thread; // FIXME: Move to EnclavedEnvironment - size_t num_enclaved_environments; bool has_async_events; // Whether the program has multiple execution contexts and can receive async events and thus // need critical sections. bool fast_mode; // Whether the program is executing in fast mode where we do not wait for physical time to elapse diff --git a/include/reactor-uc/environments/enclaved_environment.h b/include/reactor-uc/environments/enclaved_environment.h new file mode 100644 index 000000000..2f919e196 --- /dev/null +++ b/include/reactor-uc/environments/enclaved_environment.h @@ -0,0 +1,22 @@ +#ifndef REACTOR_UC_ENVIRONMENT_ENCLAVED_H +#define REACTOR_UC_ENVIRONMENT_ENCLAVED_H + +#include "reactor-uc/environment.h" +#include "reactor-uc/network_channel.h" +#include "reactor-uc/startup_coordinator.h" +#include "reactor-uc/clock_synchronization.h" +#include "reactor-uc/physical_clock.h" + +typedef struct EnclavedEnvironment EnclavedEnvironment; + +struct EnclavedEnvironment { + Environment super; + Environment **enclave_environments; + pthread_t *enclave_threads; + size_t num_enclaves; +}; + +void EnclavedEnvironment_ctor(EnclavedEnvironment *self, Reactor *main, Scheduler *scheduler, bool fast_mode); +void EnclavedEnvironment_free(EnclavedEnvironment *self); + +#endif diff --git a/include/reactor-uc/macros_internal.h b/include/reactor-uc/macros_internal.h index 1c3a648fa..4ccc51032 100644 --- a/include/reactor-uc/macros_internal.h +++ b/include/reactor-uc/macros_internal.h @@ -634,7 +634,8 @@ typedef struct FederatedInputConnection FederatedInputConnection; EventQueue super; \ ArbitraryEvent events[(NumEvents)]; \ } Name##_t; \ - static Name##_t Name; + +#define LF_EVENT_QUEUE_INSTANCE(Name) Name##_t Name; #define LF_DEFINE_REACTION_QUEUE(Name, NumReactions) \ typedef struct { \ @@ -642,12 +643,15 @@ typedef struct FederatedInputConnection FederatedInputConnection; Reaction *reactions[(NumReactions)][(NumReactions)]; \ int level_size[(NumReactions)]; \ } Name##_t; \ - static Name##_t Name; -#define LF_INITIALIZE_EVENT_QUEUE(Name, NumEvents) EventQueue_ctor(&Name.super, Name.events, NumEvents); +#define LF_REACTION_QUEUE_INSTANCE(Name) Name##_t Name; + +#define LF_INITIALIZE_EVENT_QUEUE(Name) \ + EventQueue_ctor(&Name.super, Name.events, sizeof(Name.events) / sizeof(Name.events[0])); -#define LF_INITIALIZE_REACTION_QUEUE(Name, NumReactions) \ - ReactionQueue_ctor(&Name.super, (Reaction **)Name.reactions, Name.level_size, NumReactions); +#define LF_INITIALIZE_REACTION_QUEUE(Name) \ + ReactionQueue_ctor(&Name.super, (Reaction **)Name.reactions, Name.level_size, \ + sizeof(Name.level_size) / sizeof(Name.level_size[0])); #define LF_ENTRY_POINT(MainReactorName, NumEvents, NumReactions, Timeout, KeepAlive, Fast) \ static MainReactorName main_reactor; \ diff --git a/include/reactor-uc/platform.h b/include/reactor-uc/platform.h index 6100525dd..0d03e9a87 100644 --- a/include/reactor-uc/platform.h +++ b/include/reactor-uc/platform.h @@ -4,7 +4,6 @@ #include "reactor-uc/error.h" #include "reactor-uc/tag.h" #include -#include typedef struct Platform Platform; typedef struct Mutex Mutex; diff --git a/src/environments/unfederated_environment.c b/src/environments/unfederated_environment.c index da385c345..a778b3f10 100644 --- a/src/environments/unfederated_environment.c +++ b/src/environments/unfederated_environment.c @@ -24,9 +24,9 @@ static void Environment_assemble(Environment *self) { static void Environment_start(Environment *self) { instant_t start_time = self->get_physical_time(self); LF_INFO(ENV, "Starting program at " PRINTF_TIME " nsec", start_time); - for (size_t i = 0; i < self->num_enclaved_environments; i++) { - self->platform->create_thread(self->platform, self->enclaved_environments[i]->thread, enclave_thread, - (void *)self->enclaved_environments[i]); + for (size_t i = 0; i < self->num_enclaves; i++) { + self->platform->create_thread(self->platform, &self->enclave_environments[i]->thread, enclave_thread, + (void *)self->enclave_environments[i]); } self->scheduler->set_and_schedule_start_tag(self->scheduler, start_time); self->scheduler->run(self->scheduler); diff --git a/src/platform/posix/posix.c b/src/platform/posix/posix.c index 91d556209..2803c6016 100644 --- a/src/platform/posix/posix.c +++ b/src/platform/posix/posix.c @@ -106,6 +106,7 @@ void PlatformPosix_notify(Platform *super) { } lf_ret_t PlatformPosix_create_thread(Platform *super, pthread_t *thread, void* (*thread_func)(void*), void *arguments) { + (void)super; int ret = pthread_create(thread, NULL, thread_func, arguments); if (ret == 0) { return LF_OK; diff --git a/test/lf/src/EnclavedSimple.lf b/test/lf/src/EnclavedSimple.lf index d9d489e52..59067e390 100644 --- a/test/lf/src/EnclavedSimple.lf +++ b/test/lf/src/EnclavedSimple.lf @@ -11,9 +11,6 @@ reactor R(id: int = 0){ main reactor { - @enclave e1 = new R(id=1) - - @enclave e2 = new R(id=2) } \ No newline at end of file diff --git a/test/lf/src/FedTest.lf b/test/lf/src/FedTest.lf new file mode 100644 index 000000000..4691fef06 --- /dev/null +++ b/test/lf/src/FedTest.lf @@ -0,0 +1,19 @@ +target uC { + platform: Native +} + + +reactor R(id: int = 0){ + output out: int + input in: int + reaction(startup) {= + printf("Hello From Enclave %d\n", self->id); + =} +} + + +federated reactor { + e1 = new R(id=1) + e2 = new R(id=2) + e1.out -> e2.in +} \ No newline at end of file diff --git a/test/lf/src/FederatedConnection.lf b/test/lf/src/FederatedConnection.lf index 2f067eab5..ca363f309 100644 --- a/test/lf/src/FederatedConnection.lf +++ b/test/lf/src/FederatedConnection.lf @@ -38,7 +38,6 @@ reactor Dst { federated reactor { r1 = new Src(id=42) r2 = new Dst() - r1.out -> r2.in r2.out -> r1.in } \ No newline at end of file From bb1738838481e773a11bee048c0b522b80abf638 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Mon, 14 Apr 2025 15:18:07 +0200 Subject: [PATCH 03/29] Rename FederatedEnvironment to FederateEnvironment --- .../environments/federated_environment.h | 8 ++-- include/reactor-uc/macros_internal.h | 6 +-- .../lflang/generator/uc/UcMainGenerator.kt | 6 +-- src/clock_synchronization.c | 12 ++--- src/environments/federated_environment.c | 44 +++++++++---------- src/federated.c | 2 +- src/platform/riot/coap_udp_ip_channel.c | 2 +- src/startup_coordinator.c | 8 ++-- test/lf/src/ClockSyncAttr2.lf | 4 +- test/lf/src/ClockSyncTargetProperty.lf | 4 +- test/platform/riot/coap_channel_test/main.c | 4 +- test/platform/riot/uart_channel_test/main.c | 4 +- test/unit/tcp_channel_test.c | 4 +- 13 files changed, 54 insertions(+), 54 deletions(-) diff --git a/include/reactor-uc/environments/federated_environment.h b/include/reactor-uc/environments/federated_environment.h index b945ee71c..28f6939ac 100644 --- a/include/reactor-uc/environments/federated_environment.h +++ b/include/reactor-uc/environments/federated_environment.h @@ -7,10 +7,10 @@ #include "reactor-uc/clock_synchronization.h" #include "reactor-uc/physical_clock.h" -typedef struct FederatedEnvironment FederatedEnvironment; +typedef struct FederateEnvironment FederateEnvironment; extern Environment *_lf_environment; // NOLINT -struct FederatedEnvironment { +struct FederateEnvironment { Environment super; PhysicalClock clock; // The physical clock that provides the physical time. bool do_clock_sync; @@ -22,9 +22,9 @@ struct FederatedEnvironment { ClockSynchronization *clock_sync; // A pointer to the clock synchronization module, if the program has one. }; -void FederatedEnvironment_ctor(FederatedEnvironment *self, Reactor *main, Scheduler *scheduler, bool fast_mode, +void FederateEnvironment_ctor(FederateEnvironment *self, Reactor *main, Scheduler *scheduler, bool fast_mode, FederatedConnectionBundle **net_bundles, size_t net_bundles_size, StartupCoordinator *startup_coordinator, ClockSynchronization *clock_sync); -void FederatedEnvironment_free(FederatedEnvironment *self); +void FederateEnvironment_free(FederateEnvironment *self); #endif diff --git a/include/reactor-uc/macros_internal.h b/include/reactor-uc/macros_internal.h index 4ccc51032..6a1ac3d6e 100644 --- a/include/reactor-uc/macros_internal.h +++ b/include/reactor-uc/macros_internal.h @@ -683,7 +683,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; #define LF_ENTRY_POINT_FEDERATED(FederateName, NumEvents, NumSystemEvents, NumReactions, Timeout, KeepAlive, \ NumBundles, DoClockSync) \ static FederateName main_reactor; \ - static FederatedEnvironment env; \ + static FederateEnvironment env; \ Environment *_lf_environment = &env.super; \ static DynamicScheduler scheduler; \ static ArbitraryEvent events[(NumEvents)]; \ @@ -694,7 +694,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; static int level_size[(NumReactions)]; \ static ReactionQueue reaction_queue; \ void lf_exit(void) { \ - FederatedEnvironment_free(&env); \ + FederateEnvironment_free(&env); \ } \ void lf_start() { \ EventQueue_ctor(&event_queue, events, (NumEvents)); \ @@ -702,7 +702,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; ReactionQueue_ctor(&reaction_queue, (Reaction **)reactions, level_size, (NumReactions)); \ DynamicScheduler_ctor(&scheduler, _lf_environment, &event_queue, &system_event_queue, &reaction_queue, (Timeout), \ (KeepAlive)); \ - FederatedEnvironment_ctor( \ + FederateEnvironment_ctor( \ &env, (Reactor *)&main_reactor, &scheduler.super, false, (FederatedConnectionBundle **)&main_reactor._bundles, \ (NumBundles), &main_reactor.startup_coordinator.super, (DoClockSync) ? &main_reactor.clock_sync.super : NULL); \ FederateName##_ctor(&main_reactor, NULL, _lf_environment); \ diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index 75a190389..7aea089f5 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -189,17 +189,17 @@ class UcMainGeneratorFederated( ${" |"..generateIncludeScheduler()} |#include "lf_federate.h" |static ${currentFederate.codeType} main_reactor; - |static FederatedEnvironment lf_environment; + |static FederateEnvironment lf_environment; |Environment *_lf_environment = &lf_environment.super; ${" |"..generateDefineQueues()} ${" |"..generateDefineScheduler()} |void lf_exit(void) { - | FederatedEnvironment_free(&lf_environment); + | FederateEnvironment_free(&lf_environment); |} |void lf_start(void) { ${" | "..generateInitializeQueues()} ${" | "..generateInitializeScheduler()} - | FederatedEnvironment_ctor(&lf_environment, (Reactor *)&main_reactor, scheduler, ${fast()}, + | FederateEnvironment_ctor(&lf_environment, (Reactor *)&main_reactor, scheduler, ${fast()}, | (FederatedConnectionBundle **) &main_reactor._bundles, ${netBundlesSize}, &main_reactor.${UcStartupCoordinatorGenerator.instName}.super, | ${if (clockSyncGenerator.enabled()) "&main_reactor.${UcClockSyncGenerator.instName}.super" else "NULL"}); | ${currentFederate.codeType}_ctor(&main_reactor, NULL, _lf_environment); diff --git a/src/clock_synchronization.c b/src/clock_synchronization.c index e666816c3..12397de5b 100644 --- a/src/clock_synchronization.c +++ b/src/clock_synchronization.c @@ -16,7 +16,7 @@ static void ClockSynchronization_correct_clock(ClockSynchronization *self, Clock interval_t rtt = (timestamps->t4 - timestamps->t1) - (timestamps->t3 - timestamps->t2); interval_t owd = rtt / 2; interval_t clock_offset = owd - (timestamps->t2 - timestamps->t1); - FederatedEnvironment *env_fed = (FederatedEnvironment *)self->env; + FederateEnvironment *env_fed = (FederateEnvironment *)self->env; LF_DEBUG(CLOCK_SYNC, "RTT: " PRINTF_TIME " OWD: " PRINTF_TIME " offset: " PRINTF_TIME, rtt, owd, clock_offset); // The very first iteration of clock sync we possibly step the clock (forwards or backwards) @@ -61,7 +61,7 @@ static void ClockSynchronization_correct_clock(ClockSynchronization *self, Clock /** Send our current clock priority to all connected neighbors. */ static void ClockSynchronization_broadcast_priority(ClockSynchronization *self) { lf_ret_t ret; - FederatedEnvironment *env_fed = (FederatedEnvironment *)self->env; + FederateEnvironment *env_fed = (FederateEnvironment *)self->env; LF_DEBUG(CLOCK_SYNC, "Broadcasting priority %d to all neighbors", self->my_priority); for (size_t i = 0; i < self->num_neighbours; i++) { // Do not send out the priority to the master neighbor, because this is the origin @@ -157,7 +157,7 @@ static void ClockSynchronization_handle_message_callback(ClockSynchronization *s static void ClockSynchronization_handle_priority_request(ClockSynchronization *self, int src_neighbor) { lf_ret_t ret; - FederatedEnvironment *env_fed = (FederatedEnvironment *)self->env; + FederateEnvironment *env_fed = (FederateEnvironment *)self->env; if (src_neighbor == NEIGHBOR_INDEX_SELF) { LF_DEBUG(CLOCK_SYNC, "Send clock sync priority requests to all neighbors"); for (size_t i = 0; i < self->num_neighbours; i++) { @@ -207,7 +207,7 @@ static void ClockSynchronization_handle_priority_update(ClockSynchronization *se /** Handle a DelayRequest message from a slave. Respond with the time at which the request was received. */ static void ClockSynchronization_handle_delay_request(ClockSynchronization *self, SystemEvent *event) { lf_ret_t ret; - FederatedEnvironment *env_fed = (FederatedEnvironment *)self->env; + FederateEnvironment *env_fed = (FederateEnvironment *)self->env; ClockSyncEvent *payload = (ClockSyncEvent *)event->super.payload; int src_neighbor = payload->neighbor_index; LF_DEBUG(CLOCK_SYNC, "Handling delay request from neighbor %d", src_neighbor); @@ -229,7 +229,7 @@ static void ClockSynchronization_handle_delay_request(ClockSynchronization *self /** Handle a SyncResponse from a master. Record the time of arrival and send a DelayRequest. */ static void ClockSynchronization_handle_sync_response(ClockSynchronization *self, SystemEvent *event) { ClockSyncEvent *payload = (ClockSyncEvent *)event->super.payload; - FederatedEnvironment *env_fed = (FederatedEnvironment *)self->env; + FederateEnvironment *env_fed = (FederateEnvironment *)self->env; lf_ret_t ret; SyncResponse *msg = &payload->msg.message.sync_response; int src_neighbor = payload->neighbor_index; @@ -271,7 +271,7 @@ static void ClockSynchronization_handle_delay_response(ClockSynchronization *sel /** Handle a SyncRequest message from a slave. Repond with SyncResponse which contains the time of its transmission. */ static void ClockSynchronization_handle_request_sync(ClockSynchronization *self, SystemEvent *event) { - FederatedEnvironment *env_fed = (FederatedEnvironment *)self->env; + FederateEnvironment *env_fed = (FederateEnvironment *)self->env; ClockSyncEvent *payload = (ClockSyncEvent *)event->super.payload; lf_ret_t ret; int src_neighbor = payload->neighbor_index; diff --git a/src/environments/federated_environment.c b/src/environments/federated_environment.c index 760f932a0..eb37f2827 100644 --- a/src/environments/federated_environment.c +++ b/src/environments/federated_environment.c @@ -9,22 +9,22 @@ #include #include -static void FederatedEnvironment_validate(Environment *super) { - FederatedEnvironment *self = (FederatedEnvironment *)super; +static void FederateEnvironment_validate(Environment *super) { + FederateEnvironment *self = (FederateEnvironment *)super; Reactor_validate(super->main); for (size_t i = 0; i < self->net_bundles_size; i++) { FederatedConnectionBundle_validate(self->net_bundles[i]); } } -static void FederatedEnvironment_assemble(Environment *super) { - FederatedEnvironment *self = (FederatedEnvironment *)super; +static void FederateEnvironment_assemble(Environment *super) { + FederateEnvironment *self = (FederateEnvironment *)super; // Here we enter a critical section which do not leave. // The scheduler will leave the critical section before executing the reactions. // Everything else within the runtime happens in a critical section. validaten(super->main->calculate_levels(super->main)); lf_ret_t ret; - FederatedEnvironment_validate(super); + FederateEnvironment_validate(super); // Establish connections to all neighbors: ret = self->startup_coordinator->connect_to_neighbors_blocking(self->startup_coordinator); @@ -32,14 +32,14 @@ static void FederatedEnvironment_assemble(Environment *super) { self->startup_coordinator->start(self->startup_coordinator); } -static void FederatedEnvironment_start(Environment *super) { +static void FederateEnvironment_start(Environment *super) { // We do not set the start time here in federated mode, instead the StartupCoordinator will do it. // So we just start the main loop and the StartupCoordinator. super->scheduler->run(super->scheduler); } -static lf_ret_t FederatedEnvironment_wait_until(Environment *super, instant_t wakeup_time) { - FederatedEnvironment *self = (FederatedEnvironment *)super; +static lf_ret_t FederateEnvironment_wait_until(Environment *super, instant_t wakeup_time) { + FederateEnvironment *self = (FederateEnvironment *)super; if (wakeup_time <= super->get_physical_time(super) || super->fast_mode) { return LF_OK; } @@ -56,8 +56,8 @@ static lf_ret_t FederatedEnvironment_wait_until(Environment *super, instant_t wa } } -static interval_t FederatedEnvironment_get_physical_time(Environment *super) { - FederatedEnvironment *self = (FederatedEnvironment *)super; +static interval_t FederateEnvironment_get_physical_time(Environment *super) { + FederateEnvironment *self = (FederateEnvironment *)super; return self->clock.get_time(&self->clock); } @@ -70,9 +70,9 @@ static interval_t FederatedEnvironment_get_physical_time(Environment *super) { * @param next_tag * @return lf_ret_t */ -static lf_ret_t FederatedEnvironment_acquire_tag(Environment *super, tag_t next_tag) { +static lf_ret_t FederateEnvironment_acquire_tag(Environment *super, tag_t next_tag) { LF_DEBUG(SCHED, "Acquiring tag " PRINTF_TAG, next_tag); - FederatedEnvironment *self = (FederatedEnvironment *)super; + FederateEnvironment *self = (FederateEnvironment *)super; instant_t additional_sleep = 0; for (size_t i = 0; i < self->net_bundles_size; i++) { FederatedConnectionBundle *bundle = self->net_bundles[i]; @@ -105,8 +105,8 @@ static lf_ret_t FederatedEnvironment_acquire_tag(Environment *super, tag_t next_ } } -static lf_ret_t FederatedEnvironment_poll_network_channels(Environment *super) { - FederatedEnvironment *self = (FederatedEnvironment *)super; +static lf_ret_t FederateEnvironment_poll_network_channels(Environment *super) { + FederateEnvironment *self = (FederateEnvironment *)super; for (size_t i = 0; i < self->net_bundles_size; i++) { if (self->net_bundles[i]->net_channel->mode == NETWORK_CHANNEL_MODE_POLLED) { PolledNetworkChannel *poll_channel = (PolledNetworkChannel *)self->net_bundles[i]->net_channel; @@ -116,16 +116,16 @@ static lf_ret_t FederatedEnvironment_poll_network_channels(Environment *super) { return LF_OK; } -void FederatedEnvironment_ctor(FederatedEnvironment *self, Reactor *main, Scheduler *scheduler, bool fast_mode, +void FederateEnvironment_ctor(FederateEnvironment *self, Reactor *main, Scheduler *scheduler, bool fast_mode, FederatedConnectionBundle **net_bundles, size_t net_bundles_size, StartupCoordinator *startup_coordinator, ClockSynchronization *clock_sync) { Environment_ctor(&self->super, main, scheduler, fast_mode); - self->super.assemble = FederatedEnvironment_assemble; - self->super.start = FederatedEnvironment_start; - self->super.wait_until = FederatedEnvironment_wait_until; - self->super.get_physical_time = FederatedEnvironment_get_physical_time; - self->super.acquire_tag = FederatedEnvironment_acquire_tag; - self->super.poll_network_channels = FederatedEnvironment_poll_network_channels; + self->super.assemble = FederateEnvironment_assemble; + self->super.start = FederateEnvironment_start; + self->super.wait_until = FederateEnvironment_wait_until; + self->super.get_physical_time = FederateEnvironment_get_physical_time; + self->super.acquire_tag = FederateEnvironment_acquire_tag; + self->super.poll_network_channels = FederateEnvironment_poll_network_channels; self->net_bundles_size = net_bundles_size; self->net_bundles = net_bundles; self->startup_coordinator = startup_coordinator; @@ -140,7 +140,7 @@ void FederatedEnvironment_ctor(FederatedEnvironment *self, Reactor *main, Schedu validate(self->startup_coordinator); } -void FederatedEnvironment_free(FederatedEnvironment *self) { +void FederateEnvironment_free(FederateEnvironment *self) { LF_INFO(ENV, "Reactor shutting down, freeing federated environment."); Environment_free(&self->super); for (size_t i = 0; i < self->net_bundles_size; i++) { diff --git a/src/federated.c b/src/federated.c index 91e7a6d40..c1c88adc1 100644 --- a/src/federated.c +++ b/src/federated.c @@ -219,7 +219,7 @@ void FederatedConnectionBundle_handle_tagged_msg(FederatedConnectionBundle *self } void FederatedConnectionBundle_msg_received_cb(FederatedConnectionBundle *self, const FederateMessage *msg) { - FederatedEnvironment *env_fed = (FederatedEnvironment *)self->parent->env; + FederateEnvironment *env_fed = (FederateEnvironment *)self->parent->env; switch (msg->which_message) { case FederateMessage_tagged_message_tag: LF_DEBUG(FED, "Handeling tagged message"); diff --git a/src/platform/riot/coap_udp_ip_channel.c b/src/platform/riot/coap_udp_ip_channel.c index db38ec224..a1e24997f 100644 --- a/src/platform/riot/coap_udp_ip_channel.c +++ b/src/platform/riot/coap_udp_ip_channel.c @@ -70,7 +70,7 @@ static NetworkChannelState _CoapUdpIpChannel_get_state(CoapUdpIpChannel *self) { static CoapUdpIpChannel *_CoapUdpIpChannel_get_coap_channel_by_remote(const sock_udp_ep_t *remote) { CoapUdpIpChannel *channel; - FederatedEnvironment *env = (FederatedEnvironment *)_lf_environment; + FederateEnvironment *env = (FederateEnvironment *)_lf_environment; for (size_t i = 0; i < env->net_bundles_size; i++) { if (env->net_bundles[i]->net_channel->type == NETWORK_CHANNEL_TYPE_COAP_UDP_IP) { channel = (CoapUdpIpChannel *)env->net_bundles[i]->net_channel; diff --git a/src/startup_coordinator.c b/src/startup_coordinator.c index 6b07b07c1..8faea0b17 100644 --- a/src/startup_coordinator.c +++ b/src/startup_coordinator.c @@ -11,7 +11,7 @@ * @brief Open connections to all neighbors. This function will block until all connections are established. */ static lf_ret_t StartupCoordinator_connect_to_neighbors_blocking(StartupCoordinator *self) { - FederatedEnvironment *env_fed = (FederatedEnvironment *)self->env; + FederateEnvironment *env_fed = (FederateEnvironment *)self->env; validate(self->state == StartupCoordinationState_UNINITIALIZED); self->state = StartupCoordinationState_CONNECTING; LF_DEBUG(FED, "%s connecting to %zu federated peers", self->env->main->name, env_fed->net_bundles_size); @@ -106,7 +106,7 @@ static void StartupCoordinator_handle_message_callback(StartupCoordinator *self, /** Handle a request, either local or external, to do a startup handshake. This is called from the runtime context. */ static void StartupCoordinator_handle_startup_handshake_request(StartupCoordinator *self, StartupEvent *payload) { lf_ret_t ret; - FederatedEnvironment *env_fed = (FederatedEnvironment *)self->env; + FederateEnvironment *env_fed = (FederateEnvironment *)self->env; if (payload->neighbor_index == NEIGHBOR_INDEX_SELF) { LF_DEBUG(FED, "Received handshake request from self"); switch (self->state) { @@ -196,7 +196,7 @@ static void StartupCoordinator_handle_startup_handshake_response(StartupCoordina /** Convenience function to send out a start time proposal to all neighbors for a step. */ static void send_start_time_proposal(StartupCoordinator *self, instant_t start_time, int step) { lf_ret_t ret; - FederatedEnvironment *env_fed = (FederatedEnvironment *)self->env; + FederateEnvironment *env_fed = (FederateEnvironment *)self->env; LF_DEBUG(FED, "Sending start time proposal " PRINTF_TIME " step %d to all neighbors", start_time, step); for (size_t i = 0; i < self->num_neighbours; i++) { NetworkChannel *chan = env_fed->net_bundles[i]->net_channel; @@ -212,7 +212,7 @@ static void send_start_time_proposal(StartupCoordinator *self, instant_t start_t /** Handle a start time proposal, either from self or from neighbor. */ static void StartupCoordinator_handle_start_time_proposal(StartupCoordinator *self, StartupEvent *payload) { - FederatedEnvironment *env_fed = (FederatedEnvironment *)self->env; + FederateEnvironment *env_fed = (FederateEnvironment *)self->env; if (payload->neighbor_index == NEIGHBOR_INDEX_SELF) { LF_DEBUG(FED, "Received start time proposal from self"); switch (self->state) { diff --git a/test/lf/src/ClockSyncAttr2.lf b/test/lf/src/ClockSyncAttr2.lf index 55b8682ba..66cd42028 100644 --- a/test/lf/src/ClockSyncAttr2.lf +++ b/test/lf/src/ClockSyncAttr2.lf @@ -8,7 +8,7 @@ reactor Src(id: int = 0) { input in: bool reaction(startup) -> out {= lf_set(out, self->id); - validate(((FederatedEnvironment *)env)->do_clock_sync); + validate(((FederateEnvironment *)env)->do_clock_sync); =} reaction(in) {= @@ -21,7 +21,7 @@ reactor Dst { output out: bool state check: bool = false reaction(startup) {= - validate(!((FederatedEnvironment *)env)->do_clock_sync); + validate(!((FederateEnvironment *)env)->do_clock_sync); =} reaction(in) -> out {= validate(in->value == 42); diff --git a/test/lf/src/ClockSyncTargetProperty.lf b/test/lf/src/ClockSyncTargetProperty.lf index ecaa78c34..d54ffa34a 100644 --- a/test/lf/src/ClockSyncTargetProperty.lf +++ b/test/lf/src/ClockSyncTargetProperty.lf @@ -8,7 +8,7 @@ reactor Src(id: int = 0) { input in: bool reaction(startup) -> out{= lf_set(out, self->id); - validate(!((FederatedEnvironment *)env)->do_clock_sync); + validate(!((FederateEnvironment *)env)->do_clock_sync); =} reaction(in) {= @@ -21,7 +21,7 @@ reactor Dst { output out: bool state check: bool = false reaction(startup) {= - validate(!((FederatedEnvironment *)env)->do_clock_sync); + validate(!((FederateEnvironment *)env)->do_clock_sync); =} reaction(in) -> out {= validate(in->value == 42); diff --git a/test/platform/riot/coap_channel_test/main.c b/test/platform/riot/coap_channel_test/main.c index bfd4e9e6c..e2fb54c49 100644 --- a/test/platform/riot/coap_channel_test/main.c +++ b/test/platform/riot/coap_channel_test/main.c @@ -13,7 +13,7 @@ #define REMOTE_PROTOCOL_FAMILY AF_INET6 Reactor parent; -FederatedEnvironment env; +FederateEnvironment env; Environment *_lf_environment = &env.super; FederatedConnectionBundle bundle; FederatedConnectionBundle *net_bundles[] = {&bundle}; @@ -27,7 +27,7 @@ bool client_callback_called = false; void setUp(void) { /* init environment */ - FederatedEnvironment_ctor(&env, NULL, NULL, false, net_bundles, 1, &startup_coordinator, NULL); + FederateEnvironment_ctor(&env, NULL, NULL, false, net_bundles, 1, &startup_coordinator, NULL); /* init channel */ CoapUdpIpChannel_ctor(&_coap_channel, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); diff --git a/test/platform/riot/uart_channel_test/main.c b/test/platform/riot/uart_channel_test/main.c index 0bbe7ea11..59c1eeb5b 100644 --- a/test/platform/riot/uart_channel_test/main.c +++ b/test/platform/riot/uart_channel_test/main.c @@ -20,7 +20,7 @@ static void delay(void) { UartPolledChannel channel_1; UartPolledChannel channel_2; -FederatedEnvironment env; +FederateEnvironment env; Environment *_lf_environment = &env.super; FederateMessage msg; @@ -35,7 +35,7 @@ void receive_callback(FederatedConnectionBundle *conn, const FederateMessage *me } int main(void) { - FederatedEnvironment_ctor(&env, NULL, NULL, false, NULL, 0,NULL,NULL); + FederateEnvironment_ctor(&env, NULL, NULL, false, NULL, 0,NULL,NULL); _lf_environment = &env.super; UartPolledChannel_ctor(&channel_1, 0, 9600, UC_UART_DATA_BITS_8, UC_UART_PARITY_EVEN, UC_UART_STOP_BITS_2); diff --git a/test/unit/tcp_channel_test.c b/test/unit/tcp_channel_test.c index 17e0c7bef..f7c968c57 100644 --- a/test/unit/tcp_channel_test.c +++ b/test/unit/tcp_channel_test.c @@ -16,7 +16,7 @@ #define PORT 9000 Reactor parent; -FederatedEnvironment env; +FederateEnvironment env; Environment *_lf_environment = &env.super; FederatedConnectionBundle server_bundle; FederatedConnectionBundle client_bundle; @@ -33,7 +33,7 @@ bool client_callback_called = false; void setUp(void) { /* init environment */ - FederatedEnvironment_ctor(&env, NULL, NULL, false, net_bundles, 2, &startup_coordinator, NULL); + FederateEnvironment_ctor(&env, NULL, NULL, false, net_bundles, 2, &startup_coordinator, NULL); /* init server */ TcpIpChannel_ctor(&_server_tcp_channel, HOST, PORT, AF_INET, true); From 0b64df697190e65d0f2fbfc61a14c3c93e8698ea Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 16 Apr 2025 11:57:57 +0200 Subject: [PATCH 04/29] WIP --- CMakeLists.txt | 2 +- include/reactor-uc/connection.h | 19 +++ include/reactor-uc/environment.h | 13 +- ...laved_environment.h => base_environment.h} | 0 .../environments/enclave_environment.h | 18 +++ ...d_environment.h => federate_environment.h} | 0 include/reactor-uc/macros_internal.h | 130 ++++++++++++++-- include/reactor-uc/platform.h | 9 +- include/reactor-uc/platform/posix/posix.h | 6 + include/reactor-uc/port.h | 5 +- include/reactor-uc/reactor-uc.h | 4 + include/reactor-uc/scheduler.h | 26 +++- .../reactor-uc/schedulers/dynamic/scheduler.h | 2 + include/reactor-uc/trigger.h | 1 + .../main/java/org/lflang/LinguaFranca.xtext | 2 +- .../kotlin/org/lflang/ast/AstExtensions.kt | 4 + .../lflang/generator/uc/UcCmakeGenerator.kt | 11 +- .../generator/uc/UcConnectionGenerator.kt | 39 ++++- .../lflang/generator/uc/UcConnectionUtils.kt | 18 ++- .../lflang/generator/uc/UcEnclaveGenerator.kt | 93 ++++++++++++ .../generator/uc/UcFederateGenerator.kt | 4 +- .../generator/uc/UcInstanceGenerator.kt | 28 ++-- .../lflang/generator/uc/UcMainGenerator.kt | 15 +- .../lflang/generator/uc/UcPortGenerator.kt | 2 +- .../lflang/generator/uc/UcReactorGenerator.kt | 27 ++++ src/clock_synchronization.c | 2 +- src/connection.c | 140 ++++++++++++++++++ src/enclaved.c | 0 src/environment.c | 12 +- ...rated_environment.c => base_environment.c} | 48 ++++-- src/environments/enclave_environment.c | 74 +++++++++ ...d_environment.c => federate_environment.c} | 0 src/federated.c | 2 +- src/platform/posix/posix.c | 18 ++- src/port.c | 3 +- src/schedulers/dynamic/scheduler.c | 2 - src/startup_coordinator.c | 2 +- src/util.c | 5 + test/lf/src/EnclavedConnection.lf | 43 ++++++ test/lf/src/EnclavedMaxWait.lf | 39 +++++ test/lf/src/EnclavedMaxWait2.lf | 40 +++++ test/lf/src/EnclavedSimple.lf | 8 +- 42 files changed, 837 insertions(+), 79 deletions(-) rename include/reactor-uc/environments/{enclaved_environment.h => base_environment.h} (100%) create mode 100644 include/reactor-uc/environments/enclave_environment.h rename include/reactor-uc/environments/{federated_environment.h => federate_environment.h} (100%) create mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcEnclaveGenerator.kt create mode 100644 src/enclaved.c rename src/environments/{unfederated_environment.c => base_environment.c} (73%) create mode 100644 src/environments/enclave_environment.c rename src/environments/{federated_environment.c => federate_environment.c} (100%) create mode 100644 test/lf/src/EnclavedConnection.lf create mode 100644 test/lf/src/EnclavedMaxWait.lf create mode 100644 test/lf/src/EnclavedMaxWait2.lf diff --git a/CMakeLists.txt b/CMakeLists.txt index 66b128bc7..a54f6b17e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,7 +81,7 @@ endif() target_compile_definitions(reactor-uc PUBLIC "PLATFORM_${PLATFORM}") # Add compile definition for scheduler used -target_compile_definitions(reactor-uc PRIVATE "SCHEDULER_${SCHEDULER}") +target_compile_definitions(reactor-uc PUBLIC "SCHEDULER_${SCHEDULER}") if(NETWORK_CHANNEL_TCP_POSIX) target_compile_definitions(reactor-uc PRIVATE NETWORK_CHANNEL_TCP_POSIX) diff --git a/include/reactor-uc/connection.h b/include/reactor-uc/connection.h index be6de2b66..c864c0fd5 100644 --- a/include/reactor-uc/connection.h +++ b/include/reactor-uc/connection.h @@ -7,11 +7,13 @@ #include "reactor-uc/reaction.h" #include "reactor-uc/reactor.h" #include "reactor-uc/trigger.h" +#include "reactor-uc/platform.h" typedef struct Connection Connection; typedef struct LogicalConnection LogicalConnection; typedef struct PhysicalConnection PhysicalConnection; typedef struct DelayedConnection DelayedConnection; +typedef struct EnclavedConnection EnclavedConnection; typedef struct Port Port; typedef struct Output Output; @@ -50,4 +52,21 @@ void DelayedConnection_ctor(DelayedConnection *self, Reactor *parent, Port **dow interval_t delay, ConnectionType type, size_t payload_size, void *payload_buf, bool *payload_used_buf, size_t payload_buf_capacity); + +struct EnclavedConnection { + Connection super; + interval_t delay; + ConnectionType type; + EventPayloadPool payload_pool; + void *staged_payload_ptr; + MUTEX_T mutex; + tag_t last_known_tag; + void (*set_last_known_tag)(EnclavedConnection *self, tag_t tag); + tag_t (*get_last_known_tag)(EnclavedConnection *self); +}; + +void EnclavedConnection_ctor(EnclavedConnection *self, Reactor *parent, Port **downstreams, size_t num_downstreams, + interval_t delay, ConnectionType type, size_t payload_size, void *payload_buf, + bool *payload_used_buf, size_t payload_buf_capacity); + #endif diff --git a/include/reactor-uc/environment.h b/include/reactor-uc/environment.h index bbe5a5fe3..4a627dd6c 100644 --- a/include/reactor-uc/environment.h +++ b/include/reactor-uc/environment.h @@ -16,9 +16,14 @@ typedef struct Platform Platform; typedef struct Environment Environment; +typedef enum { + ENVIRONMENT_BASE, ENVIRONMENT_ENCLAVE, ENVIRONMENT_ENCLAVED, ENVIRONMENT_FEDERATE, ENVIRONMENT_ENCLAVED_FEDERATE +} EnvironmentType; + extern Environment *_lf_environment; // NOLINT struct Environment { + EnvironmentType type; Reactor *main; // The top-level reactor of the program. Scheduler *scheduler; // The scheduler in charge of executing the reactions. Platform *platform; // The platform that provides the physical time and sleep functions. @@ -40,6 +45,12 @@ struct Environment { */ void (*start)(Environment *self); + + void (*start_at)(Environment *self, instant_t start_time); + + void (*join)(Environment *self); + + /** * @private * @brief Sleep until the wakeup time. @@ -147,7 +158,7 @@ struct Environment { lf_ret_t (*poll_network_channels)(Environment *self); }; -void Environment_ctor(Environment *self, Reactor *main, Scheduler *scheduler, bool fast_mode); +void Environment_ctor(Environment *self, EnvironmentType type, Reactor *main, Scheduler *scheduler, bool fast_mode); void Environment_free(Environment *self); #endif diff --git a/include/reactor-uc/environments/enclaved_environment.h b/include/reactor-uc/environments/base_environment.h similarity index 100% rename from include/reactor-uc/environments/enclaved_environment.h rename to include/reactor-uc/environments/base_environment.h diff --git a/include/reactor-uc/environments/enclave_environment.h b/include/reactor-uc/environments/enclave_environment.h new file mode 100644 index 000000000..44574113b --- /dev/null +++ b/include/reactor-uc/environments/enclave_environment.h @@ -0,0 +1,18 @@ +#ifndef REACTOR_UC_ENCLAVE_ENVIRONMENT_H +#define REACTOR_UC_ENCLAVE_ENVIRONMENT_H + +#include "reactor-uc/environment.h" + +#include + +typedef struct EnclaveEnvironment EnclaveEnvironment; + +struct EnclaveEnvironment { + Environment super; + THREAD_T thread; +}; + +void EnclaveEnvironment_ctor(EnclaveEnvironment *self, Reactor *main, Scheduler *scheduler, bool fast_mode); +void EnclaveEnvironment_free(EnclaveEnvironment *self); + +#endif diff --git a/include/reactor-uc/environments/federated_environment.h b/include/reactor-uc/environments/federate_environment.h similarity index 100% rename from include/reactor-uc/environments/federated_environment.h rename to include/reactor-uc/environments/federate_environment.h diff --git a/include/reactor-uc/macros_internal.h b/include/reactor-uc/macros_internal.h index 6a1ac3d6e..7cc192f69 100644 --- a/include/reactor-uc/macros_internal.h +++ b/include/reactor-uc/macros_internal.h @@ -156,10 +156,10 @@ ReactorName##_##PortName##_ctor(&self->PortName[i], &self->super, External[i]); \ } -#define LF_INITIALIZE_INPUT(ReactorName, PortName, PortWidth, External) \ +#define LF_INITIALIZE_INPUT(ReactorName, PortName, PortWidth, External, MaxWait) \ for (int i = 0; i < (PortWidth); i++) { \ self->_triggers[_triggers_idx++] = (Trigger *)&self->PortName[i]; \ - ReactorName##_##PortName##_ctor(&self->PortName[i], &self->super, External[i]); \ + ReactorName##_##PortName##_ctor(&self->PortName[i], &self->super, External[i], MaxWait); \ } #define LF_DEFINE_INPUT_STRUCT(ReactorName, PortName, EffectSize, ObserversSize, BufferType, NumConnsOut) \ @@ -183,10 +183,10 @@ #define LF_DEFINE_INPUT_CTOR(ReactorName, PortName, EffectSize, ObserverSize, BufferType, NumConnsOut) \ void ReactorName##_##PortName##_ctor(ReactorName##_##PortName *self, Reactor *parent, \ - InputExternalCtorArgs external) { \ + InputExternalCtorArgs external, interval_t max_wait) { \ Port_ctor(&self->super, TRIG_INPUT, parent, &self->value, sizeof(self->value), self->effects, (EffectSize), \ external.parent_sources, external.parent_sources_size, self->observers, ObserverSize, \ - (Connection **)&self->conns_out, NumConnsOut); \ + (Connection **)&self->conns_out, NumConnsOut, max_wait); \ } #define LF_DEFINE_TIMER_STRUCT(ReactorName, TimerName, EffectSize, ObserversSize) \ @@ -455,11 +455,10 @@ sizeof(self->payload_buf[0]), (void *)self->payload_buf, self->payload_used_buf, \ BufferSize); \ } -// FIXME: Duplicated + #define LF_DELAYED_CONNECTION_INSTANCE(ParentName, ConnName, BankWidth, PortWidth) \ ParentName##_##ConnName ConnName[BankWidth][PortWidth]; -// FIXME: Duplicated #define LF_INITIALIZE_DELAYED_CONNECTION(ParentName, ConnName, Delay, BankWidth, PortWidth) \ for (int i = 0; i < (BankWidth); i++) { \ for (int j = 0; j < (PortWidth); j++) { \ @@ -467,6 +466,40 @@ } \ } +#define LF_DEFINE_ENCLAVED_CONNECTION_STRUCT(ParentName, ConnName, DownstreamSize, BufferType, BufferSize) \ + typedef struct { \ + EnclavedConnection super; \ + BufferType payload_buf[(BufferSize)]; \ + bool payload_used_buf[(BufferSize)]; \ + Port *downstreams[(BufferSize)]; \ + } ParentName##_##ConnName; + +#define LF_DEFINE_ENCLAVED_CONNECTION_STRUCT_ARRAY(ParentName, ConnName, DownstreamSize, BufferType, BufferSize, \ + ArrayLength) \ + typedef struct { \ + EnclavedConnection super; \ + BufferType payload_buf[(BufferSize)][(ArrayLength)]; \ + bool payload_used_buf[(BufferSize)]; \ + Port *downstreams[(BufferSize)]; \ + } ParentName##_##ConnName; + +#define LF_DEFINE_ENCLAVED_CONNECTION_CTOR(ParentName, ConnName, DownstreamSize, BufferSize, IsPhysical) \ + void ParentName##_##ConnName##_ctor(ParentName##_##ConnName *self, Reactor *parent, interval_t delay) { \ + EnclavedConnection_ctor(&self->super, parent, self->downstreams, DownstreamSize, delay, IsPhysical, \ + sizeof(self->payload_buf[0]), (void *)self->payload_buf, self->payload_used_buf, \ + BufferSize); \ + } + +#define LF_ENCLAVED_CONNECTION_INSTANCE(ParentName, ConnName, BankWidth, PortWidth) \ + ParentName##_##ConnName ConnName[BankWidth][PortWidth]; + +#define LF_INITIALIZE_ENCLAVED_CONNECTION(ParentName, ConnName, Delay, BankWidth, PortWidth) \ + for (int i = 0; i < (BankWidth); i++) { \ + for (int j = 0; j < (PortWidth); j++) { \ + ParentName##_##ConnName##_ctor(&self->ConnName[i][j], &self->super, Delay); \ + } \ + } + typedef struct FederatedOutputConnection FederatedOutputConnection; #define LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(ReactorName, OutputName, BufferType) \ typedef struct { \ @@ -629,11 +662,31 @@ typedef struct FederatedInputConnection FederatedInputConnection; self->_children[_child_idx++] = &self->instanceName[i].super; \ } +#define LF_ENCLAVE_INSTANCE(ReactorName, instanceName, BankWidth) \ + Environment_##ReactorName instanceName##_env[BankWidth]; \ + ReactorName instanceName[BankWidth]; + +#define LF_INITIALIZE_ENCLAVE(ReactorName, instanceName, BankWidth) \ + for (int i = 0; i < BankWidth; i++) { \ + Environment_##ReactorName##_ctor(&self->instanceName##_env[i], &self->instanceName[i], env->scheduler->duration, \ + env->fast_mode); \ + ReactorName##_ctor(&self->instanceName[i], &self->super, &self->instanceName##_env[i].super.super); \ + self->_children[_child_idx++] = &self->instanceName[i].super; \ + } + +#define LF_INITIALIZE_ENCLAVE_WITH_PARAMETERS(ReactorName, instanceName, BankWidth, ...) \ + for (int i = 0; i < (BankWidth); i++) { \ + Environment_##ReactorName##_ctor(&self->instanceName##_env[i], &self->instanceName[i], env->scheduler->duration, \ + env->fast_mode); \ + ReactorName##_ctor(&self->instanceName[i], &self->super, &self->instanceName##_env[i].super.super, __VA_ARGS__); \ + self->_children[_child_idx++] = &self->instanceName[i].super; \ + } + #define LF_DEFINE_EVENT_QUEUE(Name, NumEvents) \ typedef struct { \ EventQueue super; \ ArbitraryEvent events[(NumEvents)]; \ - } Name##_t; \ + } Name##_t; #define LF_EVENT_QUEUE_INSTANCE(Name) Name##_t Name; @@ -642,7 +695,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; ReactionQueue super; \ Reaction *reactions[(NumReactions)][(NumReactions)]; \ int level_size[(NumReactions)]; \ - } Name##_t; \ + } Name##_t; #define LF_REACTION_QUEUE_INSTANCE(Name) Name##_t Name; @@ -653,6 +706,61 @@ typedef struct FederatedInputConnection FederatedInputConnection; ReactionQueue_ctor(&Name.super, (Reaction **)Name.reactions, Name.level_size, \ sizeof(Name.level_size) / sizeof(Name.level_size[0])); +#define LF_DEFINE_ENVIRONMENT_STRUCT(Name, NumEvents, NumReactions) \ + typedef struct { \ + Environment super; \ + struct { \ + EventQueue super; \ + ArbitraryEvent events[(NumEvents)]; \ + } event_queue; \ + struct { \ + ReactionQueue super; \ + Reaction *reactions[(NumReactions)][(NumReactions)]; \ + int level_size[(NumReactions)]; \ + } reaction_queue; \ + DynamicScheduler scheduler; \ + } Environment_##Name; + +#define LF_DEFINE_ENVIRONMENT_CTOR(Name) \ + void Environment_##Name##_ctor(Environment_##Name *self, Name *main, interval_t duration, bool keep_alive, \ + bool fast_mode) { \ + EventQueue_ctor(&self->event_queue.super, self->event_queue.events, \ + sizeof(self->event_queue.events) / sizeof(self->event_queue.events[0])); \ + ReactionQueue_ctor(&self->reaction_queue.super, (Reaction **)self->reaction_queue.reactions, \ + self->reaction_queue.level_size, \ + sizeof(self->reaction_queue.level_size) / sizeof(self->reaction_queue.level_size[0])); \ + DynamicScheduler_ctor(&self->scheduler, &self->super, &self->event_queue.super, NULL, &self->reaction_queue.super, \ + duration, keep_alive); \ + Environment_ctor(&self->super, ENVIRONMENT_BASE, &main->super, &self->scheduler.super, fast_mode); \ + } + +#define LF_DEFINE_ENCLAVE_ENVIRONMENT_STRUCT(Name, NumEvents, NumReactions) \ + typedef struct { \ + EnclaveEnvironment super; \ + struct { \ + EventQueue super; \ + ArbitraryEvent events[(NumEvents)]; \ + } event_queue; \ + struct { \ + ReactionQueue super; \ + Reaction *reactions[(NumReactions)][(NumReactions)]; \ + int level_size[(NumReactions)]; \ + } reaction_queue; \ + DynamicScheduler scheduler; \ + } Environment_##Name; + +#define LF_DEFINE_ENCLAVE_ENVIRONMENT_CTOR(Name) \ + void Environment_##Name##_ctor(Environment_##Name *self, Name *main, interval_t duration, bool fast_mode) { \ + EventQueue_ctor(&self->event_queue.super, self->event_queue.events, \ + sizeof(self->event_queue.events) / sizeof(self->event_queue.events[0])); \ + ReactionQueue_ctor(&self->reaction_queue.super, (Reaction **)self->reaction_queue.reactions, \ + self->reaction_queue.level_size, \ + sizeof(self->reaction_queue.level_size) / sizeof(self->reaction_queue.level_size[0])); \ + DynamicScheduler_ctor(&self->scheduler, &self->super.super, &self->event_queue.super, NULL, \ + &self->reaction_queue.super, duration, true); \ + EnclaveEnvironment_ctor(&self->super, &main->super, &self->scheduler.super, fast_mode); \ + } + #define LF_ENTRY_POINT(MainReactorName, NumEvents, NumReactions, Timeout, KeepAlive, Fast) \ static MainReactorName main_reactor; \ static Environment env; \ @@ -683,7 +791,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; #define LF_ENTRY_POINT_FEDERATED(FederateName, NumEvents, NumSystemEvents, NumReactions, Timeout, KeepAlive, \ NumBundles, DoClockSync) \ static FederateName main_reactor; \ - static FederateEnvironment env; \ + static FederateEnvironment env; \ Environment *_lf_environment = &env.super; \ static DynamicScheduler scheduler; \ static ArbitraryEvent events[(NumEvents)]; \ @@ -694,7 +802,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; static int level_size[(NumReactions)]; \ static ReactionQueue reaction_queue; \ void lf_exit(void) { \ - FederateEnvironment_free(&env); \ + FederateEnvironment_free(&env); \ } \ void lf_start() { \ EventQueue_ctor(&event_queue, events, (NumEvents)); \ @@ -702,7 +810,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; ReactionQueue_ctor(&reaction_queue, (Reaction **)reactions, level_size, (NumReactions)); \ DynamicScheduler_ctor(&scheduler, _lf_environment, &event_queue, &system_event_queue, &reaction_queue, (Timeout), \ (KeepAlive)); \ - FederateEnvironment_ctor( \ + FederateEnvironment_ctor( \ &env, (Reactor *)&main_reactor, &scheduler.super, false, (FederatedConnectionBundle **)&main_reactor._bundles, \ (NumBundles), &main_reactor.startup_coordinator.super, (DoClockSync) ? &main_reactor.clock_sync.super : NULL); \ FederateName##_ctor(&main_reactor, NULL, _lf_environment); \ diff --git a/include/reactor-uc/platform.h b/include/reactor-uc/platform.h index 0d03e9a87..7beb013c0 100644 --- a/include/reactor-uc/platform.h +++ b/include/reactor-uc/platform.h @@ -7,6 +7,7 @@ typedef struct Platform Platform; typedef struct Mutex Mutex; +typedef struct Thread Thread; /** * @brief Each supported platform must provide a mutex, this is used by the runtime @@ -20,6 +21,10 @@ struct Mutex { void (*unlock)(Mutex *super); }; +struct Thread { + +}; + /** Construct a Mutex*/ void Mutex_ctor(Mutex *super); @@ -44,7 +49,9 @@ struct Platform { */ lf_ret_t (*wait_until_interruptible)(Platform *super, instant_t wakeup_time); - lf_ret_t (*create_thread)(Platform *super, pthread_t *thread, void* (*thread_func)(void*), void *arguments); + lf_ret_t (*create_thread)(Platform *super, Thread *thread, void* (*thread_func)(void*), void *arguments); + + lf_ret_t (*join_thread)(Platform *super, Thread *thread); /** * @brief Signal the occurrence of an asynchronous event. This should wake diff --git a/include/reactor-uc/platform/posix/posix.h b/include/reactor-uc/platform/posix/posix.h index 4ab99b4f6..a60877205 100644 --- a/include/reactor-uc/platform/posix/posix.h +++ b/include/reactor-uc/platform/posix/posix.h @@ -10,6 +10,11 @@ typedef struct { pthread_mutex_t lock; } MutexPosix; +typedef struct { + Thread super; + pthread_t thread; +} ThreadPosix; + typedef struct { Platform super; pthread_cond_t cond; @@ -19,5 +24,6 @@ typedef struct { #define PLATFORM_T PlatformPosix #define MUTEX_T MutexPosix +#define THREAD_T ThreadPosix #endif diff --git a/include/reactor-uc/port.h b/include/reactor-uc/port.h index 677fbab3d..74998bd6c 100644 --- a/include/reactor-uc/port.h +++ b/include/reactor-uc/port.h @@ -5,9 +5,11 @@ #include "reactor-uc/reaction.h" #include "reactor-uc/reactor.h" #include "reactor-uc/trigger.h" +#include "reactor-uc/platform.h" typedef struct Connection Connection; typedef struct Port Port; +typedef struct EnclaveInputPort EnclaveInputPort; struct Port { Trigger super; @@ -23,6 +25,7 @@ struct Port { size_t conns_out_size; // Number of connections going out of the port. size_t conns_out_registered; // Number of connections that have been registered for cleanup. + interval_t max_wait; // The max-wait for this port. Used by enclaves. void (*set)(Port *self, const void *value); }; @@ -47,6 +50,6 @@ typedef struct { void Port_ctor(Port *self, TriggerType type, Reactor *parent, void *value_ptr, size_t value_size, Reaction **effects, size_t effects_size, Reaction **sources, size_t sources_size, Reaction **observers, - size_t observers_size, Connection **conns_out, size_t conns_out_size); + size_t observers_size, Connection **conns_out, size_t conns_out_size, interval_t max_wait); #endif diff --git a/include/reactor-uc/reactor-uc.h b/include/reactor-uc/reactor-uc.h index d1769dd0f..1f33db5e2 100644 --- a/include/reactor-uc/reactor-uc.h +++ b/include/reactor-uc/reactor-uc.h @@ -23,6 +23,10 @@ #include "reactor-uc/macros_internal.h" #include "reactor-uc/macros_api.h" +#if defined ENCLAVED +#include "reactor-uc/environments/enclave_environment.h" +#endif + #if defined FEDERATED #include "reactor-uc/environments/federated_environment.h" #include "reactor-uc/federated.h" diff --git a/include/reactor-uc/scheduler.h b/include/reactor-uc/scheduler.h index 22016c84c..24c54a4cb 100644 --- a/include/reactor-uc/scheduler.h +++ b/include/reactor-uc/scheduler.h @@ -10,8 +10,7 @@ typedef struct Scheduler Scheduler; typedef struct Environment Environment; struct Scheduler { - bool running; - interval_t start_time; + interval_t start_time; // The logical start time of the program. interval_t duration; // The duration after which the program should stop. bool keep_alive; // Whether the program should keep running even if there are no more events to process. @@ -21,6 +20,10 @@ struct Scheduler { */ lf_ret_t (*schedule_at)(Scheduler *self, Event *event); + /** + * @brief Schedules a system event at a specified tag. This function will + * enter a critcal section if the environment has async events. + */ lf_ret_t (*schedule_system_event_at)(Scheduler *self, SystemEvent *event); /** @@ -28,6 +31,10 @@ struct Scheduler { */ void (*run)(Scheduler *self); + /** + * @brief This function is called if ClockSynchronization steps the clock. + * The scheduler should adjust the tag of system_events to make sure they are not lost. + */ void (*step_clock)(Scheduler *self, interval_t step); /** @@ -35,6 +42,7 @@ struct Scheduler { */ void (*do_shutdown)(Scheduler *self, tag_t stop_tag); + /** Request a shutdown from the scheduler. Shutdown will occur at the next available tag. */ void (*request_shutdown)(Scheduler *self); /** @@ -43,16 +51,26 @@ struct Scheduler { */ void (*register_for_cleanup)(Scheduler *self, Trigger *trigger); + /** Set the start time of the program and schedule startup and timer events. */ void (*set_and_schedule_start_tag)(Scheduler *self, instant_t start_time); - // void (*set_duration)(Scheduler *self, interval_t duration); - + /** Add a reaction to the reaction queue. */ lf_ret_t (*add_to_reaction_queue)(Scheduler *self, Reaction *reaction); + /** Get the current executing tag. */ tag_t (*current_tag)(Scheduler *self); }; Scheduler *Scheduler_new(Environment *env, EventQueue *event_queue, EventQueue *system_event_queue, ReactionQueue *reaction_queue, interval_t duration, bool keep_alive); +#if defined(SCHEDULER_DYNAMIC) +#include "./schedulers/dynamic/scheduler.h" +#elif defined(SCHEDULER_STATIC) +#include "schedulers/static/scheduler.h" +#else +#error "No scheduler specified" +#endif + + #endif diff --git a/include/reactor-uc/schedulers/dynamic/scheduler.h b/include/reactor-uc/schedulers/dynamic/scheduler.h index 0e22e1db4..f60178df6 100644 --- a/include/reactor-uc/schedulers/dynamic/scheduler.h +++ b/include/reactor-uc/schedulers/dynamic/scheduler.h @@ -50,4 +50,6 @@ void DynamicScheduler_ctor(DynamicScheduler *self, Environment *env, EventQueue EventQueue *system_event_queue, ReactionQueue *reaction_queue, interval_t duration, bool keep_alive); + + #endif // SCHEDULER_H diff --git a/include/reactor-uc/trigger.h b/include/reactor-uc/trigger.h index 2795a2ed9..a8710f69a 100644 --- a/include/reactor-uc/trigger.h +++ b/include/reactor-uc/trigger.h @@ -20,6 +20,7 @@ typedef enum { TRIG_OUTPUT, TRIG_CONN, TRIG_CONN_DELAYED, + TRIG_CONN_ENCLAVED, TRIG_CONN_FEDERATED_INPUT, TRIG_CONN_FEDERATED_OUTPUT, TRIG_STARTUP, diff --git a/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext b/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext index ad42aa52a..c67ce49da 100644 --- a/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext +++ b/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext @@ -74,7 +74,7 @@ ImportedReactor: reactorClass=[Reactor] ('as' name=ID)?; * Declaration of a reactor class. */ Reactor: - {Reactor} (attributes+=Attribute)* ((federated?='federated' | main?='main')? & realtime?='realtime'?) 'reactor' (name=ID)? + {Reactor} (attributes+=Attribute)* ((federated?='federated' | main?='main')? & realtime?='realtime'? & enclaved?='enclaved'?) 'reactor' (name=ID)? ('<' typeParms+=TypeParm (',' typeParms+=TypeParm)* '>')? ('(' parameters+=Parameter (',' parameters+=Parameter)* ')')? ('at' host=Host)? diff --git a/lfc/core/src/main/kotlin/org/lflang/ast/AstExtensions.kt b/lfc/core/src/main/kotlin/org/lflang/ast/AstExtensions.kt index 30ac3182c..651a81d0f 100644 --- a/lfc/core/src/main/kotlin/org/lflang/ast/AstExtensions.kt +++ b/lfc/core/src/main/kotlin/org/lflang/ast/AstExtensions.kt @@ -28,6 +28,7 @@ import org.eclipse.emf.ecore.EObject import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtext.nodemodel.util.NodeModelUtils import org.lflang.ast.ASTUtils +import org.lflang.generator.uc.UcReactorGenerator.Companion.isEnclave import org.lflang.lf.* /** @@ -315,6 +316,9 @@ val Variable.isMultiport val Instantiation.reactor get() = this.reactorClass.toDefinition() +val Connection.isEnclaved + get() = (this.eContainer() is Reactor) && (this.eContainer() as Reactor).isEnclaved + /** Check if the receiver is a bank instantiation. */ val Instantiation.isBank: Boolean get() = this.widthSpec != null diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt index bee6bcefb..c54b4fc33 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt @@ -4,6 +4,7 @@ import java.nio.file.Path import kotlin.io.path.name import org.lflang.* import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcReactorGenerator.Companion.containsEnclaves import org.lflang.lf.Instantiation import org.lflang.target.TargetConfig import org.lflang.target.property.* @@ -76,8 +77,14 @@ class UcCmakeGeneratorNonFederated( ) : UcCmakeGenerator(targetConfig, fileConfig) { override val mainTarget = fileConfig.name - override fun generateIncludeCmake(sources: List) = - doGenerateIncludeCmake(sources, emptyList()) + override fun generateIncludeCmake(sources: List): String { + val compileDefs = mutableListOf() + if (mainDef.reactor.containsEnclaves) { + compileDefs.add("ENCLAVED") + } + return doGenerateIncludeCmake(sources, compileDefs) + + } } class UcCmakeGeneratorFederated( diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 6be4d236d..3612ea77e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -127,6 +127,20 @@ class UcConnectionGenerator( destFed, ) + res.add(groupedConnection) + channels.removeAll(grouped.toSet()) + } else if (c.conn.isEnclaved){ + val grouped = + channels.filter { + it.conn.delayString == c.conn.delayString && + it.conn.isPhysical == c.conn.isPhysical && + !it.isFederated && + it.src.varRef == c.src.varRef && + it.src.federate == c.src.federate && + it.dest.varRef == c.dest.varRef + } + + val groupedConnection = UcGroupedConnection(c.src.varRef, grouped, c.conn) res.add(groupedConnection) channels.removeAll(grouped.toSet()) } else { @@ -295,6 +309,12 @@ class UcConnectionGenerator( else "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" + private fun generateEnclavedSelfStruct(conn: UcGroupedConnection) = + if (conn.srcPort.type.isArray) + "LF_DEFINE_ENCLAVED_CONNECTION_STRUCT_ARRAY(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.id}, ${conn.maxNumPendingEvents}, ${conn.srcPort.type.arrayLength});" + else + "LF_DEFINE_ENCLAVED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" + private fun generateFederatedInputSelfStruct(conn: UcFederatedGroupedConnection) = if (conn.srcPort.type.isArray) "LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT_ARRAY(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.id}, ${conn.maxNumPendingEvents}, ${conn.srcPort.type.arrayLength});" @@ -307,6 +327,9 @@ class UcConnectionGenerator( private fun generateDelayedCtor(conn: UcGroupedConnection) = "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.maxNumPendingEvents}, ${conn.isPhysical});" + private fun generateEnclavedCtor(conn: UcGroupedConnection) = + "LF_DEFINE_ENCLAVED_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.maxNumPendingEvents}, ${conn.isPhysical});" + private fun generateFederatedOutputSelfStruct(conn: UcFederatedGroupedConnection) = if (conn.srcPort.type.isArray) "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT_ARRAY(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.id}, ${conn.srcPort.type.arrayLength});" @@ -345,7 +368,9 @@ class UcConnectionGenerator( else generateInitializeFederatedInput(conn) private fun generateReactorCtorCode(conn: UcGroupedConnection) = - if (conn.isLogical) { + if (conn.isEnclaved) { + "LF_INITIALIZE_ENCLAVED_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.delay}, ${conn.bankWidth}, ${conn.portWidth})" + } else if (conn.isLogical) { "LF_INITIALIZE_LOGICAL_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.bankWidth}, ${conn.portWidth})" } else { "LF_INITIALIZE_DELAYED_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.delay}, ${conn.bankWidth}, ${conn.portWidth})" @@ -413,13 +438,17 @@ class UcConnectionGenerator( fun generateCtors() = nonFederatedConnections.joinToString( prefix = "// Connection constructors\n", separator = "\n", postfix = "\n") { - if (it.isDelayed) generateDelayedCtor(it) else generateLogicalCtor(it) + if (it.isEnclaved) generateEnclavedCtor(it) + else if (it.isDelayed) generateDelayedCtor(it) + else generateLogicalCtor(it) } fun generateSelfStructs() = nonFederatedConnections.joinToString( prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { - if (it.isLogical) generateLogicalSelfStruct(it) else generateDelayedSelfStruct(it) + if (it.isEnclaved) generateEnclavedSelfStruct(it) + else if (it.isLogical) generateLogicalSelfStruct(it) + else generateDelayedSelfStruct(it) } private fun generateFederatedConnectionBundleSelfStruct(bundle: UcFederatedConnectionBundle) = @@ -482,7 +511,9 @@ class UcConnectionGenerator( fun generateReactorStructFields() = nonFederatedConnections.joinToString( prefix = "// Connections \n", separator = "\n", postfix = "\n") { - if (it.isLogical) + if (it.isEnclaved) + "LF_ENCLAVED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" + else if (it.isLogical) "LF_LOGICAL_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" else "LF_DELAYED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt index cbc76a8a3..f8c2a3d5b 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt @@ -7,9 +7,12 @@ import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcPortGenerator.Companion.maxWait import org.lflang.generator.uc.UcPortGenerator.Companion.width +import org.lflang.generator.uc.UcReactorGenerator.Companion.isEnclave +import org.lflang.isEnclaved import org.lflang.lf.Connection import org.lflang.lf.Port import org.lflang.lf.VarRef +import org.lflang.reactor /** * A UcConnectionChannel is the fundamental lowest-level representation of a connection in a LF @@ -41,8 +44,6 @@ class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Con * A GroupedConnection is a set of ConnectionChannels that can be grouped together for efficiency. * All ConnectionChannels that start from the same LF port, either because of multiports, banks, or * multiple connections. Are grouped. - * - * TODO: Give a better exaplanation for what a GroupedConnection is. */ open class UcGroupedConnection( val src: VarRef, @@ -52,6 +53,7 @@ open class UcGroupedConnection( val delay = lfConn.delay.orNever().toCCode() val isPhysical = lfConn.isPhysical val isLogical = !lfConn.isPhysical && lfConn.delay == null + val isEnclaved = lfConn.isEnclaved val srcInst = src.container val srcPort = src.variable as Port val isDelayed = lfConn.isPhysical || !isLogical // We define physical connections as delayed. @@ -76,7 +78,7 @@ open class UcGroupedConnection( } /** - * In federated programs, we must group connections differently. For a non federated program. A + * In federated programs, we must group connections differently. For a non-federated program. A * single output port connected to N different input ports could be grouped to a single * GroupedConnection. For a federated program, we would need N GroupedConnections if an output port * goes to N different federates. Moreover, if there are several kinds of NetworkChannels in the @@ -168,8 +170,8 @@ class UcChannel(val varRef: VarRef, val portIdx: Int, val bankIdx: Int, val fede * multiports and banks. * * If this is a federates program. it must be passed a list of federates associated with the LF - * Port. It is a list in case it is a bank. Then each federate of the bank must be passed to the - * constructor. + * Port. A list is used because the port might be on a bank of federates. If it is, then we + * need `federates` to contain all the members of the bank. */ class UcChannelQueue(varRef: VarRef, federates: List) { private val bankWidth = varRef.container?.width ?: 1 @@ -196,9 +198,8 @@ class UcChannelQueue(varRef: VarRef, federates: List) { } } - fun takeRemainingChannels(): List = takeChannels(channels.size) - // Get a number of channels from this port. This has sideeffects and will remove these + // Get a number of channels from this port. This has side-effects and will remove these // channels from the port. fun takeChannels(numChannels: Int): List { assert(numChannels >= channels.size) @@ -209,5 +210,8 @@ class UcChannelQueue(varRef: VarRef, federates: List) { return res } + // Get all the remaining channels. + fun takeRemainingChannels(): List = takeChannels(channels.size) + fun channelsLeft(): Int = channels.size } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcEnclaveGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcEnclaveGenerator.kt new file mode 100644 index 000000000..82327d781 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcEnclaveGenerator.kt @@ -0,0 +1,93 @@ +package org.lflang.generator.uc + +import org.lflang.* +import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcPortGenerator.Companion.width +import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType +import org.lflang.lf.* + +class UcEnclaveGenerator( + private val reactor: Reactor, + private val parameters: UcParameterGenerator, + private val ports: UcPortGenerator, + private val connections: UcConnectionGenerator, + private val reactions: UcReactionGenerator, + private val fileConfig: UcFileConfig, + private val messageReporter: MessageReporter +) { + companion object { + val Instantiation.width + get(): Int = widthSpec?.getWidth() ?: 1 + + val Instantiation.codeWidth + get(): Int = if (this.isAFederate) 1 else width + + val Instantiation.codeTypeFederate + get(): String = "${(eContainer() as Reactor).name}_${name}" + + val Instantiation.isAFederate + get(): Boolean = this.eContainer() is Reactor && (this.eContainer() as Reactor).isFederated + } + + fun generateIncludes(): String = + reactor.allInstantiations + .map { fileConfig.getReactorHeaderPath(it.reactor) } + .distinct() + .joinToString(prefix = "// Include instantiated reactors\n", separator = "\n") { + """#include "${it.toUnixString()}" """ + } + + fun generateReactorStructContainedOutputFields(inst: Instantiation) = + inst.reactor.allOutputs.joinToString(separator = "\n") { + with(PrependOperator) { + """| + |LF_CHILD_OUTPUT_CONNECTIONS(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${connections.getNumConnectionsFromPort(inst, it)}); + |LF_CHILD_OUTPUT_EFFECTS(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionEffectsOfOutput(inst, it).size}); + |LF_CHILD_OUTPUT_OBSERVERS(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionObserversOfOutput(inst, it).size}); + """ + .trimMargin() + } + } + + fun generateReactorStructContainedInputFields(inst: Instantiation) = + inst.reactor.allInputs.joinToString(separator = "\n") { + with(PrependOperator) { + """| + |LF_CHILD_INPUT_SOURCES(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionSourcesOfInput(inst, it).size}); + """ + .trimMargin() + } + } + + fun generateReactorStructField(inst: Instantiation) = + with(PrependOperator) { + """| + |LF_CHILD_REACTOR_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth}); + |${generateReactorStructContainedOutputFields(inst)} + |${generateReactorStructContainedInputFields(inst)} + """ + .trimMargin() + } + + fun generateReactorStructFields() = + reactor.allInstantiations.joinToString( + prefix = "// Child reactor fields\n", separator = "\n", postfix = "\n") { + generateReactorStructField(it) + } + + fun generateReactorCtorCode(inst: Instantiation) = + with(PrependOperator) { + """| + ${" |"..ports.generateDefineContainedOutputArgs(inst)} + ${" |"..ports.generateDefineContainedInputArgs(inst)} + |${ if (parameters.generateReactorCtorDeclArguments(inst).isNotEmpty() || ports.generateReactorCtorDeclArguments(inst).isNotEmpty()) + "LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)});" + else "LF_INITIALIZE_CHILD_REACTOR(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" + } + """ + .trimMargin() + } + + fun generateReactorCtorCodes() = + reactor.allInstantiations.joinToString(separator = "\n") { generateReactorCtorCode(it) } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index 402afdc1a..a59be617e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt @@ -42,7 +42,7 @@ class UcFederateGenerator( """ |typedef struct { | Reactor super; - ${" | "..instances.generateReactorStructField(currentFederate.inst)} + ${" | "..instances.generateReactorStructFields(currentFederate.inst)} ${" | "..connections.generateReactorStructFields()} ${" | "..connections.generateFederateStructFields()} | // Startup and clock sync objects. @@ -61,7 +61,7 @@ class UcFederateGenerator( |${generateCtorDeclaration()} { | LF_FEDERATE_CTOR_PREAMBLE(); | LF_REACTOR_CTOR(${currentFederate.codeType}); - ${" | "..instances.generateReactorCtorCode(currentFederate.inst)} + ${" | "..instances.generateReactorCtorCodes(currentFederate.inst)} ${" | "..connections.generateFederateCtorCodes()} ${" | "..connections.generateReactorCtorCodes()} ${" | "..clockSync.generateFederateCtorCode()} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt index 03cd2896f..61dea0d45 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt @@ -2,6 +2,7 @@ package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth import org.lflang.generator.uc.UcPortGenerator.Companion.width import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType import org.lflang.lf.* @@ -29,6 +30,8 @@ class UcInstanceGenerator( get(): Boolean = this.eContainer() is Reactor && (this.eContainer() as Reactor).isFederated } + private fun withArgs(inst: Instantiation) = (parameters.generateReactorCtorDeclArguments(inst).isNotEmpty() || ports.generateReactorCtorDeclArguments(inst).isNotEmpty()) + fun generateIncludes(): String = reactor.allInstantiations .map { fileConfig.getReactorHeaderPath(it.reactor) } @@ -59,10 +62,14 @@ class UcInstanceGenerator( } } - fun generateReactorStructField(inst: Instantiation) = + fun generateChildReactorField(inst: Instantiation) = + if (reactor.isEnclaved) "LF_ENCLAVE_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" + else "LF_CHILD_REACTOR_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" + + fun generateReactorStructFields(inst: Instantiation) = with(PrependOperator) { """| - |LF_CHILD_REACTOR_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth}); + |${generateChildReactorField(inst)} |${generateReactorStructContainedOutputFields(inst)} |${generateReactorStructContainedInputFields(inst)} """ @@ -72,22 +79,25 @@ class UcInstanceGenerator( fun generateReactorStructFields() = reactor.allInstantiations.joinToString( prefix = "// Child reactor fields\n", separator = "\n", postfix = "\n") { - generateReactorStructField(it) + generateReactorStructFields(it) } - fun generateReactorCtorCode(inst: Instantiation) = + fun generateChildReactorCtor(inst: Instantiation) = + if (reactor.isEnclaved && withArgs(inst)) "LF_INITIALIZE_ENCLAVE_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)})" + else if (reactor.isEnclaved) "LF_INITIALIZE_ENCLAVE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" + else if (withArgs(inst)) "LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)});" + else "LF_INITIALIZE_CHILD_REACTOR(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" + + fun generateReactorCtorCodes(inst: Instantiation) = with(PrependOperator) { """| ${" |"..ports.generateDefineContainedOutputArgs(inst)} ${" |"..ports.generateDefineContainedInputArgs(inst)} - |${ if (parameters.generateReactorCtorDeclArguments(inst).isNotEmpty() || ports.generateReactorCtorDeclArguments(inst).isNotEmpty()) - "LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)});" - else "LF_INITIALIZE_CHILD_REACTOR(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" - } + ${" |"..generateChildReactorCtor(inst)} """ .trimMargin() } fun generateReactorCtorCodes() = - reactor.allInstantiations.joinToString(separator = "\n") { generateReactorCtorCode(it) } + reactor.allInstantiations.joinToString(separator = "\n") { generateReactorCtorCodes(it) } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index 7aea089f5..df700efc3 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -2,6 +2,7 @@ package org.lflang.generator.uc import org.lflang.generator.PrependOperator import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType +import org.lflang.generator.uc.UcReactorGenerator.Companion.containsEnclaves import org.lflang.generator.uc.UcReactorGenerator.Companion.hasPhysicalActions import org.lflang.lf.Reactor import org.lflang.reactor @@ -120,18 +121,16 @@ class UcMainGeneratorNonFederated( |#include "reactor-uc/reactor-uc.h" ${" |"..generateIncludeScheduler()} |#include "${fileConfig.getReactorHeaderPath(main).toUnixString()}" + |LF_DEFINE_ENVIRONMENT_STRUCT(${main.codeType}, ${numEvents}, ${numReactions}) + |LF_DEFINE_ENVIRONMENT_CTOR(${main.codeType}) |static ${main.codeType} main_reactor; - |static Environment lf_environment; - |Environment *_lf_environment = &lf_environment; - ${" |"..generateDefineQueues()} - ${" |"..generateDefineScheduler()} + |static Environment_${main.codeType} environment; + |Environment *_lf_environment = (Environment *) &environment; |void lf_exit(void) { - | Environment_free(&lf_environment); + | Environment_free(_lf_environment); |} |void lf_start(void) { - ${" | "..generateInitializeQueues()} - ${" | "..generateInitializeScheduler()} - | Environment_ctor(&lf_environment, (Reactor *)&main_reactor, scheduler, ${fast()}); + | Environment_${main.codeType}_ctor(&environment, &main_reactor, ${getDuration()}, ${keepAlive()}, ${fast()}); | ${main.codeType}_ctor(&main_reactor, NULL, _lf_environment ${ucParameterGenerator.generateReactorCtorDefaultArguments()}); | _lf_environment->assemble(_lf_environment); | _lf_environment->start(_lf_environment); diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt index adddc28d2..29f47d40e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt @@ -135,7 +135,7 @@ class UcPortGenerator( } private fun generateReactorCtorCode(input: Input) = - "LF_INITIALIZE_INPUT(${reactor.codeType}, ${input.name}, ${input.width}, ${input.external_args});" + "LF_INITIALIZE_INPUT(${reactor.codeType}, ${input.name}, ${input.width}, ${input.external_args}, ${input.maxWait});" private fun generateReactorCtorCode(output: Output) = "LF_INITIALIZE_OUTPUT(${reactor.codeType}, ${output.name}, ${output.width}, ${output.external_args});" diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt index 43324fdcb..d187ddae2 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt @@ -45,6 +45,7 @@ class UcReactorGenerator( } private val numChildren = reactor.allInstantiations.map { it.codeWidth }.sum() + private val enclaves = if (reactor.isEnclaved) reactor.allInstantiations.map{it.reactor}.distinct() else listOf() private val parameters = UcParameterGenerator(reactor) private val connections = UcConnectionGenerator(reactor, null, emptyList()) @@ -71,6 +72,17 @@ class UcReactorGenerator( prefix = "// Private preambles\n", separator = "\n", postfix = "\n") { it.code.toText() } + fun generateEnclaveStructDeclaration() = + enclaves.joinToString( + prefix = "// Enclave structs \n", separator = "\n", postfix = "\n") { + "LF_DEFINE_ENCLAVE_ENVIRONMENT_STRUCT(${it.codeType}, 32, 32);" // FIXME: How to get numEvents and numReactions into here. + } + + fun generateEnclaveCtorDefinition() = + enclaves.joinToString( + prefix = "// Enclave ctors \n", separator = "\n", postfix = "\n") { + "LF_DEFINE_ENCLAVE_ENVIRONMENT_CTOR(${it.codeType});" + } companion object { val Reactor.codeType @@ -99,6 +111,19 @@ class UcReactorGenerator( } .isNotEmpty() + val Reactor.isEnclave + get(): Boolean = (this.eContainer() is Reactor) && (this.eContainer() as Reactor).isEnclaved + + + val Reactor.containsEnclaves + get(): Boolean { + if (this.isEnclaved) return true + for (child in allInstantiations.map{it.reactor}.distinct()) { + if (child.isEnclaved) return true + } + return false + } + fun Reactor.getEffects(v: Variable) = allReactions.filter { it.triggers.filter { it.name == v.name }.isNotEmpty() } @@ -186,6 +211,7 @@ class UcReactorGenerator( ${" |"..actions.generateSelfStructs()} ${" |"..ports.generateSelfStructs()} ${" |"..connections.generateSelfStructs()} + ${" |"..generateEnclaveStructDeclaration()} |//The reactor self struct ${" |"..generateReactorStruct()} | @@ -209,6 +235,7 @@ class UcReactorGenerator( ${" |"..timers.generateCtors()} ${" |"..ports.generateCtors()} ${" |"..connections.generateCtors()} + ${" |"..generateEnclaveCtorDefinition()} ${" |"..generateCtorDefinition()} | """ diff --git a/src/clock_synchronization.c b/src/clock_synchronization.c index 12397de5b..05914958b 100644 --- a/src/clock_synchronization.c +++ b/src/clock_synchronization.c @@ -1,5 +1,5 @@ #include "reactor-uc/clock_synchronization.h" -#include "reactor-uc/environments/federated_environment.h" +#include "reactor-uc/environments/federate_environment.h" #include "reactor-uc/error.h" #include "reactor-uc/logging.h" #include "proto/message.pb.h" diff --git a/src/connection.c b/src/connection.c index 262b115b4..669ab84d7 100644 --- a/src/connection.c +++ b/src/connection.c @@ -165,3 +165,143 @@ void DelayedConnection_ctor(DelayedConnection *self, Reactor *parent, Port **dow Connection_ctor(&self->super, TRIG_CONN_DELAYED, parent, downstreams, num_downstreams, &self->payload_pool, DelayedConnection_prepare, DelayedConnection_cleanup, DelayedConnection_trigger_downstreams); } + +/** + * @brief Prepare function called from the receiving enclave when the event is handeled. + * + * @param trigger + * @param event + */ +void EnclavedConnection_prepare(Trigger *trigger, Event *event) { + LF_DEBUG(CONN, "Preparing Enclaved connection %p for triggering", trigger); + EnclavedConnection *self = (EnclavedConnection *)trigger; + assert(self->staged_payload_ptr == NULL); // Should be reset to NULL at end of last tag. + Scheduler *sched = trigger->parent->env->scheduler; + EventPayloadPool *pool = trigger->payload_pool; + trigger->is_present = true; + sched->register_for_cleanup(sched, trigger); + + LogicalConnection_trigger_downstreams(&self->super, event->super.payload, pool->payload_size); + validate(pool->free(pool, event->super.payload) == LF_OK); +} + +/** + * @brief Cleanup function called from the sending enclave at the end of a tag when it has written to this connection. + * It should schedule the value onto the event queue of the receiving enclave. + * + * @param trigger + */ +void EnclavedConnection_cleanup(Trigger *trigger) { + LF_DEBUG(CONN, "Cleaning up Enclaved connection %p", trigger); + EnclavedConnection *self = (EnclavedConnection *)trigger; + validate(trigger->is_registered_for_cleanup); + + // FIXME: Can we remove this? + if (trigger->is_present) { + LF_DEBUG(CONN, "Enclaved connection %p had a present value this tag. Pop it", trigger); + trigger->is_present = false; + } + + if (self->staged_payload_ptr) { + LF_DEBUG(CONN, "Enclaved connection %p had a staged value. Schedule it", trigger); + Environment *receiving_env = self->super.super.parent->env; + Environment *sending_env = self->super.upstream->super.parent->env; + + Scheduler *receiving_sched = receiving_env->scheduler; + + // FIXME: Handle STP violations. + tag_t base_tag = ZERO_TAG; + if (self->type == PHYSICAL_CONNECTION) { + base_tag.time = receiving_env->get_physical_time(receiving_env); + } else { + base_tag = sending_env->scheduler->current_tag(sending_env->scheduler); + } + tag_t tag = lf_delay_tag(base_tag, self->delay); + Event event = EVENT_INIT(tag, &self->super.super, self->staged_payload_ptr); + + lf_ret_t ret = receiving_sched->schedule_at(receiving_sched, &event); + + // FIXME: There is a race condition here. Actually we need to acquire the lock on the receiving enclave. + // also solved with adding a `schedule_now` function. + if (ret == LF_PAST_TAG) { + // STP-violation + LF_WARN(CONN, "STP violation"); + event.super.tag = lf_delay_tag(receiving_sched->current_tag(receiving_sched), 0); + ret = receiving_sched->schedule_at(receiving_sched, &event); + validate(ret == LF_OK); + } + + for (size_t i = 0; isuper.downstreams_size; i++) { + validate(self->super.downstreams[i]->super.type == TRIG_INPUT_ENCLAVE); + EnclaveInputPort *input = (EnclaveInputPort *) self->super.downstreams[i]; + input->set_last_known_tag(input, event.super.tag); + } + + self->staged_payload_ptr = NULL; + } +} + +// FIXME: How do we deal with a connection going to multiple downstreams? +/** + * @brief This function is called from the context of the sending enclave and + * schedules an event onto the event queue of the receiving enclave. + * + * @param super A pointer to the Connection object. Belongs to the receiving enclave. + * @param value A poiner to the value written over the connection. + * @param value_size The size of the value written over the connection. + */ +void EnclavedConnection_trigger_downstreams(Connection *super, const void *value, size_t value_size) { + assert(value); + assert(value_size > 0); + EnclavedConnection *self = (EnclavedConnection *)super; + lf_ret_t ret; + LF_DEBUG(CONN, "Triggering downstreams on Enclaved connection %p. Stage the value for later scheduling", super); + EventPayloadPool *pool = super->super.payload_pool; + if (self->staged_payload_ptr == NULL) { + ret = pool->allocate(pool, &self->staged_payload_ptr); + if (ret != LF_OK) { + LF_ERR(CONN, "No more space in event buffer for Enclaved connection %p, dropping. Capacity is %d", super, + self->payload_pool.capacity); + return; + } + } + memcpy(self->staged_payload_ptr, value, value_size); + + // Note that we register this trigger for cleanup with the sending scheduler. This trigger does belong to + // the receiving scheduler. But it is within the cleanup function that the value is scheduled. + Scheduler *sending_scheduler = super->upstream->super.parent->env->scheduler; + sending_scheduler->register_for_cleanup(sending_scheduler, &super->super); +} + +tag_t EnclavedConnection_get_last_known_tag(EnclavedConnection *self) { + tag_t res; + MUTEX_LOCK(self->mutex); + res = self->last_known_tag; + MUTEX_UNLOCK(self->mutex); + return res; +} + +void EnclavedConnection_set_last_known_tag(EnclavedConnection*self, tag_t tag) { + MUTEX_LOCK(self->mutex); + self->last_known_tag = tag; + MUTEX_UNLOCK(self->mutex); +} + +void EnclavedConnection_ctor(EnclavedConnection *self, Reactor *parent, Port **downstreams, size_t num_downstreams, + interval_t delay, ConnectionType type, size_t payload_size, void *payload_buf, + bool *payload_used_buf, size_t payload_buf_capacity) { + + self->delay = delay; + self->staged_payload_ptr = NULL; + self->type = type; + + EventPayloadPool_ctor(&self->payload_pool, (char *)payload_buf, payload_used_buf, payload_size, payload_buf_capacity, + 0); + Connection_ctor(&self->super, TRIG_CONN_ENCLAVED, parent, downstreams, num_downstreams, &self->payload_pool, + EnclavedConnection_prepare, EnclavedConnection_cleanup, EnclavedConnection_trigger_downstreams); + + Mutex_ctor(&self->mutex.super); + self->last_known_tag = NEVER_TAG; + self->get_last_known_tag = EnclavedConnection_get_last_known_tag; + self->set_last_known_tag = EnclavedConnection_set_last_known_tag; +} diff --git a/src/enclaved.c b/src/enclaved.c new file mode 100644 index 000000000..e69de29bb diff --git a/src/environment.c b/src/environment.c index b92f0b3ed..e00e1b3d7 100644 --- a/src/environment.c +++ b/src/environment.c @@ -1,6 +1,10 @@ +#include "./environments/base_environment.c" + #if defined(FEDERATED) -#include "./environments/unfederated_environment.c" -#include "./environments/federated_environment.c" -#else -#include "./environments/unfederated_environment.c" +#include "./environments/federate_environment.c" +#endif + +#if defined(ENCLAVED) +#include "./environments/enclave_environment.c" #endif + diff --git a/src/environments/unfederated_environment.c b/src/environments/base_environment.c similarity index 73% rename from src/environments/unfederated_environment.c rename to src/environments/base_environment.c index a778b3f10..35e1b4b28 100644 --- a/src/environments/unfederated_environment.c +++ b/src/environments/base_environment.c @@ -1,4 +1,5 @@ #include "reactor-uc/environment.h" +#include "reactor-uc/environments/enclave_environment.h" #include "reactor-uc/logging.h" #include "reactor-uc/reactor.h" #include "reactor-uc/scheduler.h" @@ -6,12 +7,6 @@ #include #include -static void *enclave_thread(void *environment_pointer) { - Environment *env = (Environment *)environment_pointer; - env->start(env); - return NULL; -} - static void Environment_validate(Environment *self) { Reactor_validate(self->main); } @@ -21,17 +16,40 @@ static void Environment_assemble(Environment *self) { Environment_validate(self); } -static void Environment_start(Environment *self) { - instant_t start_time = self->get_physical_time(self); - LF_INFO(ENV, "Starting program at " PRINTF_TIME " nsec", start_time); - for (size_t i = 0; i < self->num_enclaves; i++) { - self->platform->create_thread(self->platform, &self->enclave_environments[i]->thread, enclave_thread, - (void *)self->enclave_environments[i]); +bool Environment_start_enclave_environments(Reactor * reactor, instant_t start_time) { + bool ret = false; + if (reactor->env->type == ENVIRONMENT_ENCLAVE) { + reactor->env->start_at(reactor->env, start_time); + ret = true; + } + for (size_t i = 0; ichildren_size; i++) { + ret = Environment_start_enclave_environments(reactor->children[i], start_time) || ret; + } + return ret; +} + +static void Environment_join_enclave_environments(Reactor * reactor) { + if (reactor->env->type == ENVIRONMENT_ENCLAVE) { + reactor->env->join(reactor->env); } + for (size_t i = 0; ichildren_size; i++) { + Environment_join_enclave_environments(reactor->children[i]); + } +} + +static void Environment_start_at(Environment *self, instant_t start_time) { + LF_INFO(ENV, "Starting program at " PRINTF_TIME " nsec", start_time); + + Environment_start_enclave_environments(self->main, start_time); self->scheduler->set_and_schedule_start_tag(self->scheduler, start_time); self->scheduler->run(self->scheduler); } +static void Environment_start(Environment *self) { + instant_t start_time = self->get_physical_time(self); + self->start_at(self, start_time); +} + static lf_ret_t Environment_wait_until(Environment *self, instant_t wakeup_time) { if (wakeup_time <= self->get_physical_time(self) || self->fast_mode) { return LF_OK; @@ -77,13 +95,15 @@ static interval_t Environment_get_lag(Environment *self) { return self->get_physical_time(self) - self->get_logical_time(self); } -void Environment_ctor(Environment *self, Reactor *main, Scheduler *scheduler, bool fast_mode) { +void Environment_ctor(Environment *self, EnvironmentType type, Reactor *main, Scheduler *scheduler, bool fast_mode) { self->main = main; + self->type = type; self->scheduler = scheduler; self->platform = Platform_new(); Platform_ctor(self->platform); self->assemble = Environment_assemble; self->start = Environment_start; + self->start_at = Environment_start_at; self->wait_until = Environment_wait_until; self->get_elapsed_logical_time = Environment_get_elapsed_logical_time; self->get_logical_time = Environment_get_logical_time; @@ -91,6 +111,7 @@ void Environment_ctor(Environment *self, Reactor *main, Scheduler *scheduler, bo self->get_elapsed_physical_time = Environment_get_elapsed_physical_time; self->request_shutdown = Environment_request_shutdown; self->wait_for = Environment_wait_for; + self->join = NULL; self->get_lag = Environment_get_lag; self->acquire_tag = NULL; self->poll_network_channels = NULL; @@ -104,4 +125,5 @@ void Environment_ctor(Environment *self, Reactor *main, Scheduler *scheduler, bo void Environment_free(Environment *self) { (void)self; LF_DEBUG(ENV, "Freeing top-level environment."); + Environment_join_enclave_environments(self->main); } diff --git a/src/environments/enclave_environment.c b/src/environments/enclave_environment.c new file mode 100644 index 000000000..a4df5a0fa --- /dev/null +++ b/src/environments/enclave_environment.c @@ -0,0 +1,74 @@ +#include "reactor-uc/environments/enclave_environment.h" +#include "reactor-uc/connection.h" +#include "reactor-uc/port.h" +#include "reactor-uc/logging.h" + +static void *enclave_thread(void *environment_pointer) { + Environment *env = (Environment *)environment_pointer; + env->scheduler->run(env->scheduler); + return NULL; +} + +static void EnclaveEnvironment_start_at(Environment *super, instant_t start_time) { + EnclaveEnvironment *self = (EnclaveEnvironment *)super; + LF_INFO(ENV, "Starting enclave %s " PRINTF_TIME " nsec", super->main->name, start_time); + + self->super.scheduler->set_and_schedule_start_tag(self->super.scheduler, start_time); + lf_ret_t ret = super->platform->create_thread(super->platform, &self->thread.super, enclave_thread, super); + validate(ret == LF_OK); +} + +void EnclaveEnvironment_join(Environment *super) { + EnclaveEnvironment *self = (EnclaveEnvironment *)super; + lf_ret_t ret = super->platform->join_thread(super->platform, &self->thread.super); + validate(ret == LF_OK); +} + +/** + * @brief Acquire a tag by iterating through all network input ports and making + * sure that they are resolved at this tag. If the input port is unresolved we + * must wait for the max_wait time before proceeding. + * + * @param self + * @param next_tag + * @return lf_ret_t + */ +static lf_ret_t EnclaveEnvironment_acquire_tag(Environment *super, tag_t next_tag) { + LF_DEBUG(SCHED, "Acquiring tag " PRINTF_TAG, next_tag); + Reactor *enclave = super->main; + instant_t additional_sleep = 0; + for (size_t i = 0; i < enclave->triggers_size; i++) { + Trigger *trigger = enclave->triggers[i]; + + if (trigger->type == TRIG_INPUT) { + Port* input = (Port *) trigger; + if (!input->conn_in) continue; + EnclavedConnection *conn = (EnclavedConnection *) input->conn_in; + + tag_t last_known_tag = conn->get_last_known_tag(conn); + if (lf_tag_compare(last_known_tag, next_tag) < 0) { + LF_DEBUG(SCHED, "Input %p is unresolved, latest known tag was " PRINTF_TAG, input, input->last_known_tag); + LF_DEBUG(SCHED, "Input %p has maxwait of " PRINTF_TIME, input, input->max_wait); + if (input->max_wait > additional_sleep) { + additional_sleep = input->max_wait; + } + } + } + } + + if (additional_sleep > 0) { + LF_DEBUG(SCHED, "Need to sleep for additional " PRINTF_TIME " ns", additional_sleep); + instant_t sleep_until = lf_time_add(next_tag.time, additional_sleep); + return super->wait_until(super, sleep_until); + } else { + return LF_OK; + } +} + +void EnclaveEnvironment_ctor(EnclaveEnvironment *self, Reactor *main, Scheduler *scheduler, bool fast_mode) { + Environment_ctor(&self->super, ENVIRONMENT_ENCLAVE ,main, scheduler, fast_mode); + self->super.start_at = EnclaveEnvironment_start_at; + self->super.join = EnclaveEnvironment_join; + self->super.has_async_events = true; + self->super.acquire_tag = EnclaveEnvironment_acquire_tag; +} \ No newline at end of file diff --git a/src/environments/federated_environment.c b/src/environments/federate_environment.c similarity index 100% rename from src/environments/federated_environment.c rename to src/environments/federate_environment.c diff --git a/src/federated.c b/src/federated.c index c1c88adc1..21bc79f3a 100644 --- a/src/federated.c +++ b/src/federated.c @@ -1,5 +1,5 @@ #include "reactor-uc/federated.h" -#include "reactor-uc/environments/federated_environment.h" +#include "reactor-uc/environments/federate_environment.h" #include "reactor-uc/logging.h" #include "reactor-uc/platform.h" #include "reactor-uc/serialization.h" diff --git a/src/platform/posix/posix.c b/src/platform/posix/posix.c index 2803c6016..4ff4c19e2 100644 --- a/src/platform/posix/posix.c +++ b/src/platform/posix/posix.c @@ -105,9 +105,10 @@ void PlatformPosix_notify(Platform *super) { LF_DEBUG(PLATFORM, "New async event"); } -lf_ret_t PlatformPosix_create_thread(Platform *super, pthread_t *thread, void* (*thread_func)(void*), void *arguments) { +lf_ret_t PlatformPosix_create_thread(Platform *super, Thread *thread, void *(*thread_func)(void *), void *arguments) { (void)super; - int ret = pthread_create(thread, NULL, thread_func, arguments); + ThreadPosix *thread_posix = (ThreadPosix *)thread; + int ret = pthread_create(&thread_posix->thread, NULL, thread_func, arguments); if (ret == 0) { return LF_OK; } else { @@ -115,12 +116,25 @@ lf_ret_t PlatformPosix_create_thread(Platform *super, pthread_t *thread, void* ( } } +lf_ret_t PlatformPosix_join_thread(Platform *super, Thread *thread) { + (void)super; + void *ret; + ThreadPosix *thread_posix = (ThreadPosix *)thread; + int res = pthread_join(thread_posix->thread, &ret); + if (res == 0) { + return LF_OK; + } else { + validate(false); + } +} + void Platform_ctor(Platform *super) { PlatformPosix *self = (PlatformPosix *)super; super->get_physical_time = PlatformPosix_get_physical_time; super->wait_until = PlatformPosix_wait_until; super->wait_for = PlatformPosix_wait_for; super->create_thread = PlatformPosix_create_thread; + super->join_thread = PlatformPosix_join_thread; super->wait_until_interruptible = PlatformPosix_wait_until_interruptible; super->notify = PlatformPosix_notify; diff --git a/src/port.c b/src/port.c index c04616c56..120ebe368 100644 --- a/src/port.c +++ b/src/port.c @@ -50,7 +50,7 @@ void Port_cleanup(Trigger *_self) { void Port_ctor(Port *self, TriggerType type, Reactor *parent, void *value_ptr, size_t value_size, Reaction **effects, size_t effects_size, Reaction **sources, size_t sources_size, Reaction **observers, - size_t observers_size, Connection **conns_out, size_t conns_out_size) { + size_t observers_size, Connection **conns_out, size_t conns_out_size, interval_t max_wait) { Trigger_ctor(&self->super, type, parent, NULL, Port_prepare, Port_cleanup); self->set = Port_set; self->conn_in = NULL; @@ -68,4 +68,5 @@ void Port_ctor(Port *self, TriggerType type, Reactor *parent, void *value_ptr, s self->observers.num_registered = 0; self->value_ptr = value_ptr; self->value_size = value_size; + self->max_wait = max_wait; } diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index e69298061..1763026f4 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -263,7 +263,6 @@ void Scheduler_set_and_schedule_start_tag(Scheduler *untyped_self, instant_t sta tag_t stop_tag = {.time = lf_time_add(start_time, untyped_self->duration), .microstep = 0}; untyped_self->start_time = start_time; self->stop_tag = stop_tag; - self->super.running = true; MUTEX_UNLOCK(self->mutex); // Schedule the initial events @@ -498,7 +497,6 @@ void DynamicScheduler_ctor(DynamicScheduler *self, Environment *env, EventQueue self->system_event_queue = system_event_queue; self->super.start_time = NEVER; - self->super.running = false; self->super.run = Scheduler_run; self->prepare_timestep = Scheduler_prepare_timestep; self->clean_up_timestep = Scheduler_clean_up_timestep; diff --git a/src/startup_coordinator.c b/src/startup_coordinator.c index 8faea0b17..8c271cff2 100644 --- a/src/startup_coordinator.c +++ b/src/startup_coordinator.c @@ -1,5 +1,5 @@ #include "reactor-uc/startup_coordinator.h" -#include "reactor-uc/environments/federated_environment.h" +#include "reactor-uc/environments/federate_environment.h" #include "reactor-uc/tag.h" #include "reactor-uc/logging.h" #include "proto/message.pb.h" diff --git a/src/util.c b/src/util.c index 34064959d..ca3192f78 100644 --- a/src/util.c +++ b/src/util.c @@ -7,6 +7,11 @@ void lf_connect(Connection *connection, Port *upstream, Port *downstream) { } connection->upstream = upstream; connection->register_downstream(connection, downstream); + + // If a connection is enclaved. We move it into the environment of the receiving federate. + if (connection->super.type == TRIG_CONN_ENCLAVED) { + connection->super.parent = downstream->super.parent; + } } void lf_connect_federated_output(Connection *connection, Port *output) { diff --git a/test/lf/src/EnclavedConnection.lf b/test/lf/src/EnclavedConnection.lf new file mode 100644 index 000000000..08e74ffed --- /dev/null +++ b/test/lf/src/EnclavedConnection.lf @@ -0,0 +1,43 @@ +target uC { + platform: Native, +} + +reactor Src(id: int = 0) { + output out: int + input in: bool + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} + + reaction(in) {= + env->request_shutdown(env); + =} +} + +reactor Dst { + input in: int + output out: bool + state check: bool = false + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) -> out {= + printf("Received %d from Src\n", in->value); + validate(in->value == 42); + self->check = true; + env->request_shutdown(env); + lf_set(out, true); + =} + + reaction(shutdown) {= + validate(self->check); + =} +} + +main enclaved reactor { + r1 = new Src(id=42) + r2 = new Dst() + r1.out -> r2.in + r2.out -> r1.in +} \ No newline at end of file diff --git a/test/lf/src/EnclavedMaxWait.lf b/test/lf/src/EnclavedMaxWait.lf new file mode 100644 index 000000000..597caba80 --- /dev/null +++ b/test/lf/src/EnclavedMaxWait.lf @@ -0,0 +1,39 @@ +target uC { + platform: Native, +} + +reactor Src(id: int = 0) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + env->platform->wait_for(env->platform, SEC(2)); + lf_set(out, 42); + env->request_shutdown(env); + =} +} + +reactor Dst { + input in: int + state check1: bool = false + state check2: bool = false + reaction(startup, in) {= + validate(!self->check2); + printf("Hello from Dst!\n"); + self->check2 = true; + =} maxwait(0) {= + printf("STP violation\n"); + self->check1 = true; + env->request_shutdown(env); + =} + + reaction(shutdown) {= + validate(self->check1); + validate(self->check2); + =} +} + +main enclaved reactor { + r1 = new Src() + r2 = new Dst() + r1.out -> r2.in +} \ No newline at end of file diff --git a/test/lf/src/EnclavedMaxWait2.lf b/test/lf/src/EnclavedMaxWait2.lf new file mode 100644 index 000000000..72c301b3b --- /dev/null +++ b/test/lf/src/EnclavedMaxWait2.lf @@ -0,0 +1,40 @@ +target uC { + platform: Native, + timeout: 5sec +} + +reactor Src(id: int = 0) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + env->platform->wait_for(env->platform, SEC(2)); + lf_set(out, 42); + env->request_shutdown(env); + =} +} + +reactor Dst { + input in: int + state check1: bool = false + state check2: bool = false + reaction(startup, in) {= + validate(!self->check2); + printf("Hello from Dst!\n"); + self->check2 = true; + env->request_shutdown(env); + =} maxwait(forever) {= + printf("STP violation\n"); + validate(false); + env->request_shutdown(env); + =} + + reaction(shutdown) {= + validate(self->check2); + =} +} + +main enclaved reactor { + r1 = new Src() + r2 = new Dst() + r1.out -> r2.in +} \ No newline at end of file diff --git a/test/lf/src/EnclavedSimple.lf b/test/lf/src/EnclavedSimple.lf index 59067e390..dad75abcb 100644 --- a/test/lf/src/EnclavedSimple.lf +++ b/test/lf/src/EnclavedSimple.lf @@ -4,13 +4,19 @@ target uC { reactor R(id: int = 0){ + state test: bool = false reaction(startup) {= printf("Hello From Enclave %d\n", self->id); + self->test = true; + =} + + reaction(shutdown) {= + validate(self->test); =} } -main reactor { +main enclaved reactor { e1 = new R(id=1) e2 = new R(id=2) } \ No newline at end of file From f1eea47ad668eb8c92e89eadeb8dcc8f2273b92a Mon Sep 17 00:00:00 2001 From: erlingrj Date: Tue, 22 Apr 2025 18:21:56 +0200 Subject: [PATCH 05/29] Add support for banked enclaves and multiport enclaves --- include/reactor-uc/connection.h | 5 +- include/reactor-uc/environment.h | 8 +- .../environments/federate_environment.h | 4 +- include/reactor-uc/macros_internal.h | 10 +- include/reactor-uc/platform.h | 6 +- include/reactor-uc/scheduler.h | 7 +- .../reactor-uc/schedulers/dynamic/scheduler.h | 2 - .../kotlin/org/lflang/ast/AstExtensions.kt | 1 - .../lflang/generator/uc/UcCmakeGenerator.kt | 1 - .../generator/uc/UcConnectionGenerator.kt | 106 +++++++++--------- .../lflang/generator/uc/UcConnectionUtils.kt | 38 ++++--- .../org/lflang/generator/uc/UcFederate.kt | 34 ++++-- .../generator/uc/UcInstanceGenerator.kt | 23 ++-- .../lflang/generator/uc/UcMainGenerator.kt | 1 - .../lflang/generator/uc/UcPortGenerator.kt | 2 +- .../lflang/generator/uc/UcReactorGenerator.kt | 37 +++--- src/connection.c | 49 ++++---- src/environment.c | 3 +- src/environments/base_environment.c | 8 +- src/environments/enclave_environment.c | 25 +++-- src/environments/federate_environment.c | 4 +- src/port.c | 2 +- src/schedulers/dynamic/scheduler.c | 2 - test/lf/src/EnclavedArrayConnection.lf | 45 ++++++++ test/lf/src/EnclavedBank.lf | 59 ++++++++++ test/lf/src/EnclavedBankMultiport.lf | 35 ++++++ test/lf/src/EnclavedChain.lf | 50 +++++++++ test/lf/src/EnclavedLoopbackConnection.lf | 34 ++++++ test/lf/src/EnclavedMaxWait3.lf | 61 ++++++++++ test/lf/src/EnclavedMaxWaitNoHandler.lf | 45 ++++++++ test/lf/src/EnclavedMaxWaitNoHandler2.lf | 37 ++++++ test/lf/src/EnclavedMultiConnection.lf | 51 +++++++++ test/lf/src/EnclavedUnconnected2.lf | 41 +++++++ 33 files changed, 670 insertions(+), 166 deletions(-) create mode 100644 test/lf/src/EnclavedArrayConnection.lf create mode 100644 test/lf/src/EnclavedBank.lf create mode 100644 test/lf/src/EnclavedBankMultiport.lf create mode 100644 test/lf/src/EnclavedChain.lf create mode 100644 test/lf/src/EnclavedLoopbackConnection.lf create mode 100644 test/lf/src/EnclavedMaxWait3.lf create mode 100644 test/lf/src/EnclavedMaxWaitNoHandler.lf create mode 100644 test/lf/src/EnclavedMaxWaitNoHandler2.lf create mode 100644 test/lf/src/EnclavedMultiConnection.lf create mode 100644 test/lf/src/EnclavedUnconnected2.lf diff --git a/include/reactor-uc/connection.h b/include/reactor-uc/connection.h index c864c0fd5..77b78a260 100644 --- a/include/reactor-uc/connection.h +++ b/include/reactor-uc/connection.h @@ -52,7 +52,6 @@ void DelayedConnection_ctor(DelayedConnection *self, Reactor *parent, Port **dow interval_t delay, ConnectionType type, size_t payload_size, void *payload_buf, bool *payload_used_buf, size_t payload_buf_capacity); - struct EnclavedConnection { Connection super; interval_t delay; @@ -66,7 +65,7 @@ struct EnclavedConnection { }; void EnclavedConnection_ctor(EnclavedConnection *self, Reactor *parent, Port **downstreams, size_t num_downstreams, - interval_t delay, ConnectionType type, size_t payload_size, void *payload_buf, - bool *payload_used_buf, size_t payload_buf_capacity); + interval_t delay, ConnectionType type, size_t payload_size, void *payload_buf, + bool *payload_used_buf, size_t payload_buf_capacity); #endif diff --git a/include/reactor-uc/environment.h b/include/reactor-uc/environment.h index 4a627dd6c..1c1bae67b 100644 --- a/include/reactor-uc/environment.h +++ b/include/reactor-uc/environment.h @@ -17,7 +17,11 @@ typedef struct Platform Platform; typedef struct Environment Environment; typedef enum { - ENVIRONMENT_BASE, ENVIRONMENT_ENCLAVE, ENVIRONMENT_ENCLAVED, ENVIRONMENT_FEDERATE, ENVIRONMENT_ENCLAVED_FEDERATE + ENVIRONMENT_BASE, + ENVIRONMENT_ENCLAVE, + ENVIRONMENT_ENCLAVED, + ENVIRONMENT_FEDERATE, + ENVIRONMENT_ENCLAVED_FEDERATE } EnvironmentType; extern Environment *_lf_environment; // NOLINT @@ -45,12 +49,10 @@ struct Environment { */ void (*start)(Environment *self); - void (*start_at)(Environment *self, instant_t start_time); void (*join)(Environment *self); - /** * @private * @brief Sleep until the wakeup time. diff --git a/include/reactor-uc/environments/federate_environment.h b/include/reactor-uc/environments/federate_environment.h index 28f6939ac..ffa058be8 100644 --- a/include/reactor-uc/environments/federate_environment.h +++ b/include/reactor-uc/environments/federate_environment.h @@ -23,8 +23,8 @@ struct FederateEnvironment { }; void FederateEnvironment_ctor(FederateEnvironment *self, Reactor *main, Scheduler *scheduler, bool fast_mode, - FederatedConnectionBundle **net_bundles, size_t net_bundles_size, - StartupCoordinator *startup_coordinator, ClockSynchronization *clock_sync); + FederatedConnectionBundle **net_bundles, size_t net_bundles_size, + StartupCoordinator *startup_coordinator, ClockSynchronization *clock_sync); void FederateEnvironment_free(FederateEnvironment *self); #endif diff --git a/include/reactor-uc/macros_internal.h b/include/reactor-uc/macros_internal.h index 7cc192f69..b3c17588c 100644 --- a/include/reactor-uc/macros_internal.h +++ b/include/reactor-uc/macros_internal.h @@ -141,7 +141,7 @@ OutputExternalCtorArgs external) { \ Port_ctor(&self->super, TRIG_OUTPUT, parent, &self->value, sizeof(self->value), external.parent_effects, \ external.parent_effects_size, self->sources, SourceSize, external.parent_observers, \ - external.parent_observers_size, external.conns_out, external.conns_out_size); \ + external.parent_observers_size, external.conns_out, external.conns_out_size, NEVER); \ } #define LF_PORT_INSTANCE(ReactorName, PortName, PortWidth) ReactorName##_##PortName PortName[PortWidth]; @@ -156,10 +156,10 @@ ReactorName##_##PortName##_ctor(&self->PortName[i], &self->super, External[i]); \ } -#define LF_INITIALIZE_INPUT(ReactorName, PortName, PortWidth, External, MaxWait) \ +#define LF_INITIALIZE_INPUT(ReactorName, PortName, PortWidth, External, MaxWait) \ for (int i = 0; i < (PortWidth); i++) { \ self->_triggers[_triggers_idx++] = (Trigger *)&self->PortName[i]; \ - ReactorName##_##PortName##_ctor(&self->PortName[i], &self->super, External[i], MaxWait); \ + ReactorName##_##PortName##_ctor(&self->PortName[i], &self->super, External[i], MaxWait); \ } #define LF_DEFINE_INPUT_STRUCT(ReactorName, PortName, EffectSize, ObserversSize, BufferType, NumConnsOut) \ @@ -183,10 +183,10 @@ #define LF_DEFINE_INPUT_CTOR(ReactorName, PortName, EffectSize, ObserverSize, BufferType, NumConnsOut) \ void ReactorName##_##PortName##_ctor(ReactorName##_##PortName *self, Reactor *parent, \ - InputExternalCtorArgs external, interval_t max_wait) { \ + InputExternalCtorArgs external, interval_t max_wait) { \ Port_ctor(&self->super, TRIG_INPUT, parent, &self->value, sizeof(self->value), self->effects, (EffectSize), \ external.parent_sources, external.parent_sources_size, self->observers, ObserverSize, \ - (Connection **)&self->conns_out, NumConnsOut, max_wait); \ + (Connection **)&self->conns_out, NumConnsOut, max_wait); \ } #define LF_DEFINE_TIMER_STRUCT(ReactorName, TimerName, EffectSize, ObserversSize) \ diff --git a/include/reactor-uc/platform.h b/include/reactor-uc/platform.h index 7beb013c0..293ce4a38 100644 --- a/include/reactor-uc/platform.h +++ b/include/reactor-uc/platform.h @@ -21,9 +21,7 @@ struct Mutex { void (*unlock)(Mutex *super); }; -struct Thread { - -}; +struct Thread {}; /** Construct a Mutex*/ void Mutex_ctor(Mutex *super); @@ -49,7 +47,7 @@ struct Platform { */ lf_ret_t (*wait_until_interruptible)(Platform *super, instant_t wakeup_time); - lf_ret_t (*create_thread)(Platform *super, Thread *thread, void* (*thread_func)(void*), void *arguments); + lf_ret_t (*create_thread)(Platform *super, Thread *thread, void *(*thread_func)(void *), void *arguments); lf_ret_t (*join_thread)(Platform *super, Thread *thread); diff --git a/include/reactor-uc/scheduler.h b/include/reactor-uc/scheduler.h index 24c54a4cb..e4ab4b122 100644 --- a/include/reactor-uc/scheduler.h +++ b/include/reactor-uc/scheduler.h @@ -11,8 +11,8 @@ typedef struct Environment Environment; struct Scheduler { interval_t start_time; // The logical start time of the program. - interval_t duration; // The duration after which the program should stop. - bool keep_alive; // Whether the program should keep running even if there are no more events to process. + interval_t duration; // The duration after which the program should stop. + bool keep_alive; // Whether the program should keep running even if there are no more events to process. /** * @brief Schedules an event on trigger at a specified tag. This function will @@ -33,7 +33,7 @@ struct Scheduler { /** * @brief This function is called if ClockSynchronization steps the clock. - * The scheduler should adjust the tag of system_events to make sure they are not lost. + * The scheduler should adjust the tag of system_events to make sure they are not lost. */ void (*step_clock)(Scheduler *self, interval_t step); @@ -72,5 +72,4 @@ Scheduler *Scheduler_new(Environment *env, EventQueue *event_queue, EventQueue * #error "No scheduler specified" #endif - #endif diff --git a/include/reactor-uc/schedulers/dynamic/scheduler.h b/include/reactor-uc/schedulers/dynamic/scheduler.h index f60178df6..0e22e1db4 100644 --- a/include/reactor-uc/schedulers/dynamic/scheduler.h +++ b/include/reactor-uc/schedulers/dynamic/scheduler.h @@ -50,6 +50,4 @@ void DynamicScheduler_ctor(DynamicScheduler *self, Environment *env, EventQueue EventQueue *system_event_queue, ReactionQueue *reaction_queue, interval_t duration, bool keep_alive); - - #endif // SCHEDULER_H diff --git a/lfc/core/src/main/kotlin/org/lflang/ast/AstExtensions.kt b/lfc/core/src/main/kotlin/org/lflang/ast/AstExtensions.kt index 651a81d0f..c29715e9b 100644 --- a/lfc/core/src/main/kotlin/org/lflang/ast/AstExtensions.kt +++ b/lfc/core/src/main/kotlin/org/lflang/ast/AstExtensions.kt @@ -28,7 +28,6 @@ import org.eclipse.emf.ecore.EObject import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtext.nodemodel.util.NodeModelUtils import org.lflang.ast.ASTUtils -import org.lflang.generator.uc.UcReactorGenerator.Companion.isEnclave import org.lflang.lf.* /** diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt index c54b4fc33..c41071191 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt @@ -83,7 +83,6 @@ class UcCmakeGeneratorNonFederated( compileDefs.add("ENCLAVED") } return doGenerateIncludeCmake(sources, compileDefs) - } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 3612ea77e..40fc5f54b 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -6,6 +6,7 @@ import org.lflang.* import org.lflang.generator.PrependOperator import org.lflang.generator.orNever import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate +import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAnEnclave import org.lflang.generator.uc.UcPortGenerator.Companion.arrayLength import org.lflang.generator.uc.UcPortGenerator.Companion.isArray import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType @@ -21,8 +22,9 @@ class UcConnectionGenerator( private val currentFederate: UcFederate?, // The federate to generate connections for. If set then `reactor` should be // the top-level reactor. - private val allFederates: - List // A list of all the federates in the program. Only used for federated + private val allNodes: + List< + UcSchedulingNode> // A list of all the federates in the program. Only used for federated // code-gen. ) { @@ -44,13 +46,13 @@ class UcConnectionGenerator( */ private fun parseConnectionChannels( conn: Connection, - federates: List + nodes: List ): List { val res = mutableListOf() - val rhsPorts = conn.rightPorts.map { getChannelQueue(it, federates) } + val rhsPorts = conn.rightPorts.map { getChannelQueue(it, nodes) } var rhsPortIndex = 0 - var lhsPorts = conn.leftPorts.map { getChannelQueue(it, federates) } + var lhsPorts = conn.leftPorts.map { getChannelQueue(it, nodes) } var lhsPortIndex = 0 // Keep parsing out connections until we are out of right-hand-side (rhs) ports @@ -86,7 +88,7 @@ class UcConnectionGenerator( // we have been through all rhs channels. if (lhsPortIndex >= lhsPorts.size && rhsPortIndex < rhsPorts.size) { assert(conn.isIterated) - lhsPorts = conn.leftPorts.map { getChannelQueue(it, federates) } + lhsPorts = conn.leftPorts.map { getChannelQueue(it, nodes) } lhsPortIndex = 0 } } @@ -95,7 +97,7 @@ class UcConnectionGenerator( /** * Given a list of ConnectionChannels, group them together. How they are grouepd depends on - * whether we are dealing with federated or non-federated reactors. + * whether we are dealing with federated, enclaved or normal non-federated reactors. */ private fun groupConnections(channels: List): List { val res = mutableListOf() @@ -110,14 +112,17 @@ class UcConnectionGenerator( it.conn.delayString == c.conn.delayString && it.conn.isPhysical == c.conn.isPhysical && it.src.varRef == c.src.varRef && - it.src.federate == c.src.federate && - it.dest.federate == c.dest.federate && + it.src.node == c.src.node && + it.dest.node == c.dest.node && it.getChannelType() == c.getChannelType() } - val srcFed = allFederates.find { it == UcFederate(c.src.varRef.container, c.src.bankIdx) }!! + val srcFed = + allNodes.find { it == UcFederate(c.src.varRef.container, c.src.bankIdx) }!! + as UcFederate val destFed = - allFederates.find { it == UcFederate(c.dest.varRef.container, c.dest.bankIdx) }!! + allNodes.find { it == UcFederate(c.dest.varRef.container, c.dest.bankIdx) }!! + as UcFederate val groupedConnection = UcFederatedGroupedConnection( c.src.varRef, @@ -129,16 +134,17 @@ class UcConnectionGenerator( res.add(groupedConnection) channels.removeAll(grouped.toSet()) - } else if (c.conn.isEnclaved){ + } else if (c.conn.isEnclaved) { val grouped = - channels.filter { - it.conn.delayString == c.conn.delayString && - it.conn.isPhysical == c.conn.isPhysical && - !it.isFederated && - it.src.varRef == c.src.varRef && - it.src.federate == c.src.federate && - it.dest.varRef == c.dest.varRef - } + channels.filter { + it.conn.delayString == c.conn.delayString && + it.conn.isPhysical == c.conn.isPhysical && + !it.isFederated && + it.src.varRef == c.src.varRef && + it.src.node == c.src.node && + it.dest.node == c.dest.node && + it.dest.varRef == c.dest.varRef + } val groupedConnection = UcGroupedConnection(c.src.varRef, grouped, c.conn) res.add(groupedConnection) @@ -150,7 +156,7 @@ class UcConnectionGenerator( it.conn.isPhysical == c.conn.isPhysical && !it.isFederated && it.src.varRef == c.src.varRef && - it.src.federate == c.src.federate + it.src.node == c.src.node } val groupedConnection = UcGroupedConnection(c.src.varRef, grouped, c.conn) @@ -165,10 +171,11 @@ class UcConnectionGenerator( * Given a port VarRef, and the list of federates. Create a channel queue. I.e. create all the * UcChannels and associate them with the correct federates. */ - private fun getChannelQueue(portVarRef: VarRef, federates: List): UcChannelQueue { + private fun getChannelQueue(portVarRef: VarRef, nodes: List): UcChannelQueue { return if (portVarRef.container?.isAFederate ?: false) { - val federates = allFederates.filter { it.inst == portVarRef.container } - UcChannelQueue(portVarRef, federates) + UcChannelQueue(portVarRef, nodes.filter { it.inst == portVarRef.container }) + } else if (portVarRef.container?.isAnEnclave ?: false) { + UcChannelQueue(portVarRef, nodes.filter { it.inst == portVarRef.container }) } else { UcChannelQueue(portVarRef, emptyList()) } @@ -227,7 +234,8 @@ class UcConnectionGenerator( init { // Only pass through all federates and add NetworkInterface objects to them once. if (isFederated && !federateInterfacesInitialized) { - for (fed in allFederates) { + for (node in allNodes) { + val fed = node as UcFederate UcNetworkInterfaceFactory.createInterfaces(fed).forEach { fed.addInterface(it) } } federateInterfacesInitialized = true @@ -235,7 +243,7 @@ class UcConnectionGenerator( // Parse out all GroupedConnections. Note that this is repeated for each federate. val channels = mutableListOf() - reactor.allConnections.forEach { channels.addAll(parseConnectionChannels(it, allFederates)) } + reactor.allConnections.forEach { channels.addAll(parseConnectionChannels(it, allNodes)) } val grouped = groupConnections(channels) nonFederatedConnections = mutableListOf() federatedConnectionBundles = mutableListOf() @@ -256,7 +264,7 @@ class UcConnectionGenerator( grouped .filterNot { it is UcFederatedGroupedConnection } .filter { - it.channels.fold(true) { acc, c -> acc && (c.src.federate == currentFederate) } + it.channels.fold(true) { acc, c -> acc && (c.src.node == currentFederate) } }) } else { // In the non-federated case, all grouped connections are handled togehter. @@ -310,10 +318,10 @@ class UcConnectionGenerator( "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" private fun generateEnclavedSelfStruct(conn: UcGroupedConnection) = - if (conn.srcPort.type.isArray) - "LF_DEFINE_ENCLAVED_CONNECTION_STRUCT_ARRAY(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.id}, ${conn.maxNumPendingEvents}, ${conn.srcPort.type.arrayLength});" - else - "LF_DEFINE_ENCLAVED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" + if (conn.srcPort.type.isArray) + "LF_DEFINE_ENCLAVED_CONNECTION_STRUCT_ARRAY(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.id}, ${conn.maxNumPendingEvents}, ${conn.srcPort.type.arrayLength});" + else + "LF_DEFINE_ENCLAVED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" private fun generateFederatedInputSelfStruct(conn: UcFederatedGroupedConnection) = if (conn.srcPort.type.isArray) @@ -328,7 +336,7 @@ class UcConnectionGenerator( "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.maxNumPendingEvents}, ${conn.isPhysical});" private fun generateEnclavedCtor(conn: UcGroupedConnection) = - "LF_DEFINE_ENCLAVED_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.maxNumPendingEvents}, ${conn.isPhysical});" + "LF_DEFINE_ENCLAVED_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.maxNumPendingEvents}, ${conn.isPhysical});" private fun generateFederatedOutputSelfStruct(conn: UcFederatedGroupedConnection) = if (conn.srcPort.type.isArray) @@ -395,7 +403,7 @@ class UcConnectionGenerator( conn: UcFederatedGroupedConnection ) = conn.channels.joinWithLn { - "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${it.src.federate!!.inst.name}[0].${it.src.varRef.name}[${it.src.portIdx}]);" + "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${it.src.node!!.inst.name}[0].${it.src.varRef.name}[${it.src.portIdx}]);" } private fun generateConnectFederateInputChannel( @@ -403,7 +411,7 @@ class UcConnectionGenerator( conn: UcGroupedConnection ) = conn.channels.joinWithLn { - "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${it.dest.federate!!.inst.name}[0].${it.dest.varRef.name}[${it.dest.portIdx}]);" + "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${it.dest.node!!.inst.name}[0].${it.dest.varRef.name}[${it.dest.portIdx}]);" } private fun generateFederateConnectionStatements(conn: UcFederatedConnectionBundle) = @@ -439,16 +447,14 @@ class UcConnectionGenerator( nonFederatedConnections.joinToString( prefix = "// Connection constructors\n", separator = "\n", postfix = "\n") { if (it.isEnclaved) generateEnclavedCtor(it) - else if (it.isDelayed) generateDelayedCtor(it) - else generateLogicalCtor(it) + else if (it.isDelayed) generateDelayedCtor(it) else generateLogicalCtor(it) } fun generateSelfStructs() = nonFederatedConnections.joinToString( prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { if (it.isEnclaved) generateEnclavedSelfStruct(it) - else if (it.isLogical) generateLogicalSelfStruct(it) - else generateDelayedSelfStruct(it) + else if (it.isLogical) generateLogicalSelfStruct(it) else generateDelayedSelfStruct(it) } private fun generateFederatedConnectionBundleSelfStruct(bundle: UcFederatedConnectionBundle) = @@ -512,7 +518,7 @@ class UcConnectionGenerator( nonFederatedConnections.joinToString( prefix = "// Connections \n", separator = "\n", postfix = "\n") { if (it.isEnclaved) - "LF_ENCLAVED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" + "LF_ENCLAVED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" else if (it.isLogical) "LF_LOGICAL_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" else @@ -549,8 +555,8 @@ class UcConnectionGenerator( // Return the furthest node and its distance from u fun breadthFirstSearch(u: Int, graph: Graph): Pair { - val visited = allFederates.map { false }.toMutableList() - val distance = allFederates.map { -1 }.toMutableList() + val visited = allNodes.map { false }.toMutableList() + val distance = allNodes.map { -1 }.toMutableList() distance[u] = 0 visited[u] = true val queue: Queue = LinkedList() @@ -577,14 +583,14 @@ class UcConnectionGenerator( return Pair(nodeIdx, maxDist) } // Build adjacency matrix - val adjacency = allFederates.map { mutableSetOf() } + val adjacency = allNodes.map { mutableSetOf() } for (bundle in allFederatedConnectionBundles) { - val src = allFederates.indexOf(bundle.src) - val dest = allFederates.indexOf(bundle.dest) + val src = allNodes.indexOf(bundle.src) + val dest = allNodes.indexOf(bundle.dest) adjacency[src].add(dest) adjacency[dest].add(src) } - val graph = Graph(allFederates.size, adjacency) + val graph = Graph(allNodes.size, adjacency) val firstEndPoint = breadthFirstSearch(0, graph) val actualLength = breadthFirstSearch(firstEndPoint.first, graph) return actualLength.second @@ -595,7 +601,7 @@ class UcConnectionGenerator( // Return the furthest node and its distance from u fun breadthFirstSearch(u: Int, graph: Graph): Boolean { - val visited = allFederates.map { false }.toMutableList() + val visited = allNodes.map { false }.toMutableList() visited[u] = true val queue: Queue = LinkedList() queue.add(u) @@ -613,14 +619,14 @@ class UcConnectionGenerator( } // Build adjacency matrix - val adjacency = allFederates.map { mutableSetOf() } + val adjacency = allNodes.map { mutableSetOf() } for (bundle in allFederatedConnectionBundles) { - val src = allFederates.indexOf(bundle.src) - val dest = allFederates.indexOf(bundle.dest) + val src = allNodes.indexOf(bundle.src) + val dest = allNodes.indexOf(bundle.dest) adjacency[src].add(dest) adjacency[dest].add(src) } - val graph = Graph(allFederates.size, adjacency) + val graph = Graph(allNodes.size, adjacency) return breadthFirstSearch(0, graph) } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt index f8c2a3d5b..5e89c37f7 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt @@ -7,34 +7,33 @@ import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcPortGenerator.Companion.maxWait import org.lflang.generator.uc.UcPortGenerator.Companion.width -import org.lflang.generator.uc.UcReactorGenerator.Companion.isEnclave import org.lflang.isEnclaved import org.lflang.lf.Connection import org.lflang.lf.Port import org.lflang.lf.VarRef -import org.lflang.reactor /** * A UcConnectionChannel is the fundamental lowest-level representation of a connection in a LF * program. It connects two UcChannels, one at the source and one at the destination. */ class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Connection) { - val isFederated = - (src.federate != null) && (dest.federate != null) && (src.federate != dest.federate) + val isEnclavedOrFederated = (src.node != null) && (dest.node != null) && (src.node != dest.node) + val isFederated = isEnclavedOrFederated && src.node is UcFederate && dest.node is UcFederate /** * Get the NetworkChannelType of this connection. If we are not in a federated program it is NONE */ fun getChannelType(): NetworkChannelType { val linkAttr = getLinkAttribute(conn) + val fed = src.node as UcFederate return if (linkAttr == null) { - src.federate?.getDefaultInterface()?.type ?: NetworkChannelType.NONE + fed.getDefaultInterface().type } else { val srcIf = linkAttr.getParamString("left") if (srcIf == null) { - src.federate?.getDefaultInterface()?.type ?: NetworkChannelType.NONE + fed.getDefaultInterface().type } else { - src.federate?.getInterface(srcIf)?.type ?: NetworkChannelType.NONE + fed.getInterface(srcIf).type } } } @@ -53,7 +52,8 @@ open class UcGroupedConnection( val delay = lfConn.delay.orNever().toCCode() val isPhysical = lfConn.isPhysical val isLogical = !lfConn.isPhysical && lfConn.delay == null - val isEnclaved = lfConn.isEnclaved + // We do not consider a loopback connection from an enclave as "enclaved" + val isEnclaved = lfConn.isEnclaved && channels.first().src.node != channels.first().dest.node val srcInst = src.container val srcPort = src.variable as Port val isDelayed = lfConn.isPhysical || !isLogical // We define physical connections as delayed. @@ -152,10 +152,15 @@ class UcFederatedConnectionBundle( * An UcChannel represents a single channel of an LF Port. Due to Multiports and Banks, each LF Port * can have multiple channels. */ -class UcChannel(val varRef: VarRef, val portIdx: Int, val bankIdx: Int, val federate: UcFederate?) { +class UcChannel( + val varRef: VarRef, + val portIdx: Int, + val bankIdx: Int, + val node: UcSchedulingNode? +) { fun getCodePortIdx() = portIdx - fun getCodeBankIdx() = if (federate == null) bankIdx else 0 + fun getCodeBankIdx() = if (node != null) bankIdx else 0 private val portOfContainedReactor = varRef.container != null private val reactorInstance = @@ -169,11 +174,11 @@ class UcChannel(val varRef: VarRef, val portIdx: Int, val bankIdx: Int, val fede * This is a convenience-wrapper around a LF Port. It will construct UcChannels corresponding to the * multiports and banks. * - * If this is a federates program. it must be passed a list of federates associated with the LF - * Port. A list is used because the port might be on a bank of federates. If it is, then we - * need `federates` to contain all the members of the bank. + * If we are dealing with an enclaved or federated reactor, it must be passed a list of federates or + * enclaves associated with the LF Port. A list is used because the port might be on a bank of + * federates/enclaves. If it is, then we need `nodes` to contain all the members of the bank. */ -class UcChannelQueue(varRef: VarRef, federates: List) { +class UcChannelQueue(varRef: VarRef, nodes: List) { private val bankWidth = varRef.container?.width ?: 1 private val portWidth = (varRef.variable as Port).width private val isInterleaved = varRef.isInterleaved @@ -186,19 +191,18 @@ class UcChannelQueue(varRef: VarRef, federates: List) { if (isInterleaved) { for (i in 0.. { diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt index 37f59c7cc..536886c1e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt @@ -6,8 +6,31 @@ import org.lflang.isBank import org.lflang.lf.Instantiation import org.lflang.target.property.type.PlatformType -class UcFederate(val inst: Instantiation, val bankIdx: Int) { +enum class NodeType { + ENCLAVE, + FEDERATE +} + +open class UcSchedulingNode(val inst: Instantiation, val bankIdx: Int, val nodeType: NodeType) { + val isBank = inst.isBank + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is UcSchedulingNode) return false + if (this.nodeType != other.nodeType) return false + + val sameInst = inst == other.inst + val sameBank = bankIdx == other.bankIdx + return if (isBank) sameInst && sameBank else sameInst + } +} + +class UcEnclave(inst: Instantiation, bankIdx: Int) : + UcSchedulingNode(inst, bankIdx, NodeType.ENCLAVE) {} + +class UcFederate(inst: Instantiation, bankIdx: Int) : + UcSchedulingNode(inst, bankIdx, NodeType.FEDERATE) { val platform: PlatformType.Platform = AttributeUtils.getFederatePlatform(inst) val interfaces = mutableListOf() val codeType = if (isBank) "${inst.codeTypeFederate}_${bankIdx}" else inst.codeTypeFederate @@ -34,13 +57,4 @@ class UcFederate(val inst: Instantiation, val bankIdx: Int) { fun getCompileDefs(): List = interfaces.distinctBy { it.type }.map { it.compileDefs } + "FEDERATED" - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is UcFederate) return false - - val sameInst = inst == other.inst - val sameBank = bankIdx == other.bankIdx - return if (isBank) sameInst && sameBank else sameInst - } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt index 61dea0d45..864232f4d 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt @@ -28,9 +28,14 @@ class UcInstanceGenerator( val Instantiation.isAFederate get(): Boolean = this.eContainer() is Reactor && (this.eContainer() as Reactor).isFederated + + val Instantiation.isAnEnclave + get(): Boolean = this.eContainer() is Reactor && (this.eContainer() as Reactor).isEnclaved } - private fun withArgs(inst: Instantiation) = (parameters.generateReactorCtorDeclArguments(inst).isNotEmpty() || ports.generateReactorCtorDeclArguments(inst).isNotEmpty()) + private fun withArgs(inst: Instantiation) = + (parameters.generateReactorCtorDeclArguments(inst).isNotEmpty() || + ports.generateReactorCtorDeclArguments(inst).isNotEmpty()) fun generateIncludes(): String = reactor.allInstantiations @@ -63,8 +68,9 @@ class UcInstanceGenerator( } fun generateChildReactorField(inst: Instantiation) = - if (reactor.isEnclaved) "LF_ENCLAVE_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" - else "LF_CHILD_REACTOR_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" + if (reactor.isEnclaved) + "LF_ENCLAVE_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" + else "LF_CHILD_REACTOR_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" fun generateReactorStructFields(inst: Instantiation) = with(PrependOperator) { @@ -83,10 +89,13 @@ class UcInstanceGenerator( } fun generateChildReactorCtor(inst: Instantiation) = - if (reactor.isEnclaved && withArgs(inst)) "LF_INITIALIZE_ENCLAVE_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)})" - else if (reactor.isEnclaved) "LF_INITIALIZE_ENCLAVE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" - else if (withArgs(inst)) "LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)});" - else "LF_INITIALIZE_CHILD_REACTOR(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" + if (reactor.isEnclaved && withArgs(inst)) + "LF_INITIALIZE_ENCLAVE_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)})" + else if (reactor.isEnclaved) + "LF_INITIALIZE_ENCLAVE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" + else if (withArgs(inst)) + "LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)});" + else "LF_INITIALIZE_CHILD_REACTOR(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" fun generateReactorCtorCodes(inst: Instantiation) = with(PrependOperator) { diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index df700efc3..4d1cf7127 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -2,7 +2,6 @@ package org.lflang.generator.uc import org.lflang.generator.PrependOperator import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType -import org.lflang.generator.uc.UcReactorGenerator.Companion.containsEnclaves import org.lflang.generator.uc.UcReactorGenerator.Companion.hasPhysicalActions import org.lflang.lf.Reactor import org.lflang.reactor diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt index 29f47d40e..930e3c8ec 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt @@ -135,7 +135,7 @@ class UcPortGenerator( } private fun generateReactorCtorCode(input: Input) = - "LF_INITIALIZE_INPUT(${reactor.codeType}, ${input.name}, ${input.width}, ${input.external_args}, ${input.maxWait});" + "LF_INITIALIZE_INPUT(${reactor.codeType}, ${input.name}, ${input.width}, ${input.external_args}, ${input.maxWait.toCCode()});" private fun generateReactorCtorCode(output: Output) = "LF_INITIALIZE_OUTPUT(${reactor.codeType}, ${output.name}, ${output.width}, ${output.external_args});" diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt index d187ddae2..04eaa5dc4 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt @@ -4,8 +4,8 @@ import org.lflang.* import org.lflang.generator.PrependOperator import org.lflang.generator.uc.UcActionGenerator.Companion.maxNumPendingEvents import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth +import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcPortGenerator.Companion.width -import org.lflang.generator.uc.UcReactorGenerator.Companion.hasPhysicalActions import org.lflang.lf.* class UcReactorGenerator( @@ -45,10 +45,19 @@ class UcReactorGenerator( } private val numChildren = reactor.allInstantiations.map { it.codeWidth }.sum() - private val enclaves = if (reactor.isEnclaved) reactor.allInstantiations.map{it.reactor}.distinct() else listOf() + private val enclaveReactorDefs = + if (reactor.isEnclaved) reactor.allInstantiations.map { it.reactor }.distinct() + else emptyList() + + private val enclaves = + if (reactor.isEnclaved) + reactor.allInstantiations + .map { inst -> (0 until inst.width).toList().map { idx -> UcEnclave(inst, idx) } } + .flatten() + else emptyList() private val parameters = UcParameterGenerator(reactor) - private val connections = UcConnectionGenerator(reactor, null, emptyList()) + private val connections = UcConnectionGenerator(reactor, null, enclaves) private val state = UcStateGenerator(reactor) private val ports = UcPortGenerator(reactor, connections) private val timers = UcTimerGenerator(reactor) @@ -72,17 +81,20 @@ class UcReactorGenerator( prefix = "// Private preambles\n", separator = "\n", postfix = "\n") { it.code.toText() } + fun generateEnclaveStructDeclaration() = - enclaves.joinToString( - prefix = "// Enclave structs \n", separator = "\n", postfix = "\n") { - "LF_DEFINE_ENCLAVE_ENVIRONMENT_STRUCT(${it.codeType}, 32, 32);" // FIXME: How to get numEvents and numReactions into here. - } + enclaveReactorDefs.joinToString( + prefix = "// Enclave structs \n", separator = "\n", postfix = "\n") { + "LF_DEFINE_ENCLAVE_ENVIRONMENT_STRUCT(${it.codeType}, 32, 32);" // FIXME: How to get + // numEvents and + // numReactions into here. + } fun generateEnclaveCtorDefinition() = - enclaves.joinToString( - prefix = "// Enclave ctors \n", separator = "\n", postfix = "\n") { - "LF_DEFINE_ENCLAVE_ENVIRONMENT_CTOR(${it.codeType});" - } + enclaveReactorDefs.joinToString( + prefix = "// Enclave ctors \n", separator = "\n", postfix = "\n") { + "LF_DEFINE_ENCLAVE_ENVIRONMENT_CTOR(${it.codeType});" + } companion object { val Reactor.codeType @@ -114,11 +126,10 @@ class UcReactorGenerator( val Reactor.isEnclave get(): Boolean = (this.eContainer() is Reactor) && (this.eContainer() as Reactor).isEnclaved - val Reactor.containsEnclaves get(): Boolean { if (this.isEnclaved) return true - for (child in allInstantiations.map{it.reactor}.distinct()) { + for (child in allInstantiations.map { it.reactor }.distinct()) { if (child.isEnclaved) return true } return false diff --git a/src/connection.c b/src/connection.c index 669ab84d7..07c92c820 100644 --- a/src/connection.c +++ b/src/connection.c @@ -168,28 +168,41 @@ void DelayedConnection_ctor(DelayedConnection *self, Reactor *parent, Port **dow /** * @brief Prepare function called from the receiving enclave when the event is handeled. - * - * @param trigger - * @param event + * + * @param trigger + * @param event */ void EnclavedConnection_prepare(Trigger *trigger, Event *event) { - LF_DEBUG(CONN, "Preparing Enclaved connection %p for triggering", trigger); + LF_DEBUG(CONN, "Preparing enclave connection %p for triggering", trigger); EnclavedConnection *self = (EnclavedConnection *)trigger; - assert(self->staged_payload_ptr == NULL); // Should be reset to NULL at end of last tag. - Scheduler *sched = trigger->parent->env->scheduler; + Environment *env = trigger->parent->env; + Scheduler *sched = env->scheduler; EventPayloadPool *pool = trigger->payload_pool; trigger->is_present = true; sched->register_for_cleanup(sched, trigger); - LogicalConnection_trigger_downstreams(&self->super, event->super.payload, pool->payload_size); - validate(pool->free(pool, event->super.payload) == LF_OK); + assert(self->super.downstreams_size == 1); + Port *down = self->super.downstreams[0]; + + if (down->effects.size > 0 || down->observers.size > 0) { + validate(pool->payload_size == down->value_size); + memcpy(down->value_ptr, event->super.payload, pool->payload_size); // NOLINT + down->super.prepare(&down->super, event); + } + + for (size_t i = 0; i < down->conns_out_registered; i++) { + LF_DEBUG(CONN, "Found further downstream connection %p to recurse down", down->conns_out[i]); + down->conns_out[i]->trigger_downstreams(down->conns_out[i], event->super.payload, pool->payload_size); + } + + pool->free(pool, event->super.payload); } /** * @brief Cleanup function called from the sending enclave at the end of a tag when it has written to this connection. * It should schedule the value onto the event queue of the receiving enclave. - * - * @param trigger + * + * @param trigger */ void EnclavedConnection_cleanup(Trigger *trigger) { LF_DEBUG(CONN, "Cleaning up Enclaved connection %p", trigger); @@ -218,7 +231,7 @@ void EnclavedConnection_cleanup(Trigger *trigger) { } tag_t tag = lf_delay_tag(base_tag, self->delay); Event event = EVENT_INIT(tag, &self->super.super, self->staged_payload_ptr); - + lf_ret_t ret = receiving_sched->schedule_at(receiving_sched, &event); // FIXME: There is a race condition here. Actually we need to acquire the lock on the receiving enclave. @@ -231,11 +244,7 @@ void EnclavedConnection_cleanup(Trigger *trigger) { validate(ret == LF_OK); } - for (size_t i = 0; isuper.downstreams_size; i++) { - validate(self->super.downstreams[i]->super.type == TRIG_INPUT_ENCLAVE); - EnclaveInputPort *input = (EnclaveInputPort *) self->super.downstreams[i]; - input->set_last_known_tag(input, event.super.tag); - } + self->set_last_known_tag(self, event.super.tag); self->staged_payload_ptr = NULL; } @@ -245,7 +254,7 @@ void EnclavedConnection_cleanup(Trigger *trigger) { /** * @brief This function is called from the context of the sending enclave and * schedules an event onto the event queue of the receiving enclave. - * + * * @param super A pointer to the Connection object. Belongs to the receiving enclave. * @param value A poiner to the value written over the connection. * @param value_size The size of the value written over the connection. @@ -281,15 +290,15 @@ tag_t EnclavedConnection_get_last_known_tag(EnclavedConnection *self) { return res; } -void EnclavedConnection_set_last_known_tag(EnclavedConnection*self, tag_t tag) { +void EnclavedConnection_set_last_known_tag(EnclavedConnection *self, tag_t tag) { MUTEX_LOCK(self->mutex); self->last_known_tag = tag; MUTEX_UNLOCK(self->mutex); } void EnclavedConnection_ctor(EnclavedConnection *self, Reactor *parent, Port **downstreams, size_t num_downstreams, - interval_t delay, ConnectionType type, size_t payload_size, void *payload_buf, - bool *payload_used_buf, size_t payload_buf_capacity) { + interval_t delay, ConnectionType type, size_t payload_size, void *payload_buf, + bool *payload_used_buf, size_t payload_buf_capacity) { self->delay = delay; self->staged_payload_ptr = NULL; diff --git a/src/environment.c b/src/environment.c index e00e1b3d7..36fd36a93 100644 --- a/src/environment.c +++ b/src/environment.c @@ -2,9 +2,8 @@ #if defined(FEDERATED) #include "./environments/federate_environment.c" -#endif +#endif #if defined(ENCLAVED) #include "./environments/enclave_environment.c" #endif - diff --git a/src/environments/base_environment.c b/src/environments/base_environment.c index 35e1b4b28..66a3ed98d 100644 --- a/src/environments/base_environment.c +++ b/src/environments/base_environment.c @@ -16,23 +16,23 @@ static void Environment_assemble(Environment *self) { Environment_validate(self); } -bool Environment_start_enclave_environments(Reactor * reactor, instant_t start_time) { +bool Environment_start_enclave_environments(Reactor *reactor, instant_t start_time) { bool ret = false; if (reactor->env->type == ENVIRONMENT_ENCLAVE) { reactor->env->start_at(reactor->env, start_time); ret = true; } - for (size_t i = 0; ichildren_size; i++) { + for (size_t i = 0; i < reactor->children_size; i++) { ret = Environment_start_enclave_environments(reactor->children[i], start_time) || ret; } return ret; } -static void Environment_join_enclave_environments(Reactor * reactor) { +static void Environment_join_enclave_environments(Reactor *reactor) { if (reactor->env->type == ENVIRONMENT_ENCLAVE) { reactor->env->join(reactor->env); } - for (size_t i = 0; ichildren_size; i++) { + for (size_t i = 0; i < reactor->children_size; i++) { Environment_join_enclave_environments(reactor->children[i]); } } diff --git a/src/environments/enclave_environment.c b/src/environments/enclave_environment.c index a4df5a0fa..20cf6df46 100644 --- a/src/environments/enclave_environment.c +++ b/src/environments/enclave_environment.c @@ -41,21 +41,24 @@ static lf_ret_t EnclaveEnvironment_acquire_tag(Environment *super, tag_t next_ta Trigger *trigger = enclave->triggers[i]; if (trigger->type == TRIG_INPUT) { - Port* input = (Port *) trigger; - if (!input->conn_in) continue; - EnclavedConnection *conn = (EnclavedConnection *) input->conn_in; + Port *input = (Port *)trigger; + if (!input->conn_in) + continue; + if (input->conn_in->super.type == TRIG_CONN_ENCLAVED) { + EnclavedConnection *conn = (EnclavedConnection *)input->conn_in; - tag_t last_known_tag = conn->get_last_known_tag(conn); - if (lf_tag_compare(last_known_tag, next_tag) < 0) { - LF_DEBUG(SCHED, "Input %p is unresolved, latest known tag was " PRINTF_TAG, input, input->last_known_tag); - LF_DEBUG(SCHED, "Input %p has maxwait of " PRINTF_TIME, input, input->max_wait); - if (input->max_wait > additional_sleep) { - additional_sleep = input->max_wait; + tag_t last_known_tag = conn->get_last_known_tag(conn); + if (lf_tag_compare(last_known_tag, next_tag) < 0) { + LF_DEBUG(SCHED, "Input %p is unresolved, latest known tag was " PRINTF_TAG, input, conn->last_known_tag); + LF_DEBUG(SCHED, "Input %p has maxwait of " PRINTF_TIME, input, input->max_wait); + if (input->max_wait > additional_sleep) { + additional_sleep = input->max_wait; + } } } } } - + if (additional_sleep > 0) { LF_DEBUG(SCHED, "Need to sleep for additional " PRINTF_TIME " ns", additional_sleep); instant_t sleep_until = lf_time_add(next_tag.time, additional_sleep); @@ -66,7 +69,7 @@ static lf_ret_t EnclaveEnvironment_acquire_tag(Environment *super, tag_t next_ta } void EnclaveEnvironment_ctor(EnclaveEnvironment *self, Reactor *main, Scheduler *scheduler, bool fast_mode) { - Environment_ctor(&self->super, ENVIRONMENT_ENCLAVE ,main, scheduler, fast_mode); + Environment_ctor(&self->super, ENVIRONMENT_ENCLAVE, main, scheduler, fast_mode); self->super.start_at = EnclaveEnvironment_start_at; self->super.join = EnclaveEnvironment_join; self->super.has_async_events = true; diff --git a/src/environments/federate_environment.c b/src/environments/federate_environment.c index eb37f2827..a75e4c400 100644 --- a/src/environments/federate_environment.c +++ b/src/environments/federate_environment.c @@ -117,8 +117,8 @@ static lf_ret_t FederateEnvironment_poll_network_channels(Environment *super) { } void FederateEnvironment_ctor(FederateEnvironment *self, Reactor *main, Scheduler *scheduler, bool fast_mode, - FederatedConnectionBundle **net_bundles, size_t net_bundles_size, - StartupCoordinator *startup_coordinator, ClockSynchronization *clock_sync) { + FederatedConnectionBundle **net_bundles, size_t net_bundles_size, + StartupCoordinator *startup_coordinator, ClockSynchronization *clock_sync) { Environment_ctor(&self->super, main, scheduler, fast_mode); self->super.assemble = FederateEnvironment_assemble; self->super.start = FederateEnvironment_start; diff --git a/src/port.c b/src/port.c index 120ebe368..a40b987f9 100644 --- a/src/port.c +++ b/src/port.c @@ -10,7 +10,7 @@ void Port_prepare(Trigger *_self, Event *event) { assert(_self->type == TRIG_INPUT || _self->type == TRIG_OUTPUT); Port *self = (Port *)_self; - // If this is a federated input port, we will get passed an event, if it is a + // If this is a federated or enclaved input port, we will get passed an event, if it is a // normal port there will be no event. if (event != NULL && _self->type == TRIG_INPUT) { self->intended_tag = event->intended_tag; diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index 1763026f4..1840af980 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -150,14 +150,12 @@ static bool _Scheduler_check_and_handle_stp_violations(DynamicScheduler *self, R for (size_t j = 0; j < port->effects.size; j++) { if (port->effects.reactions[j] == reaction) { - LF_WARN(SCHED, "Timeout detected for %s->reaction_%d", reaction->parent->name, reaction->index); reaction->stp_violation_handler(reaction); return true; } } for (size_t j = 0; j < port->observers.size; j++) { if (port->observers.reactions[j] == reaction) { - LF_WARN(SCHED, "Timeout detected for %s->reaction_%d", reaction->parent->name, reaction->index); reaction->stp_violation_handler(reaction); return true; } diff --git a/test/lf/src/EnclavedArrayConnection.lf b/test/lf/src/EnclavedArrayConnection.lf new file mode 100644 index 000000000..32e32ce7b --- /dev/null +++ b/test/lf/src/EnclavedArrayConnection.lf @@ -0,0 +1,45 @@ +target uC { + platform: Native, +} + +reactor Src(id: int = 0) { + output out: int[3] + input in: bool + reaction(startup) -> out{= + printf("Hello from Src!\n"); + int arr[3] = {1,2,3}; + lf_set_array(out, arr); + =} + + reaction(in) {= + env->request_shutdown(env); + =} +} + +reactor Dst { + input in: int[3] + output out: bool + state check: bool = false + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) -> out {= + for (int i = 0; i<3; i++) { + validate(in->value[i] == i+1); + } + self->check = true; + env->request_shutdown(env); + lf_set(out, true); + =} + + reaction(shutdown) {= + validate(self->check); + =} +} + +main enclaved reactor { + r1 = new Src(id=42) + r2 = new Dst() + r1.out -> r2.in + r2.out -> r1.in +} \ No newline at end of file diff --git a/test/lf/src/EnclavedBank.lf b/test/lf/src/EnclavedBank.lf new file mode 100644 index 000000000..5ab000500 --- /dev/null +++ b/test/lf/src/EnclavedBank.lf @@ -0,0 +1,59 @@ +target uC { + platform: Native, + logging: Debug +} + + +reactor Src { + output out: int + + reaction(startup) -> out {= + printf("Hello from Src!\n"); + lf_set(out, 42); + env->request_shutdown(env); + =} + reaction(shutdown) {= + printf("Src is shutting down\n"); + =} +} + +reactor Fed(bank_idx: int = 0) { + output out: int + input in: int + input in2: int + state check1: bool = false + state check2: bool = false + + reaction(in) -> out {= + printf("Received %d from src \n", in->value); + lf_set(out, self->bank_idx); + validate(in->value == 42); + validate(!self->check2); + self->check1 = true; + =} + + reaction(in2) {= + printf("Received %d from myself\n", in2->value); + validate(in2->value == self->bank_idx); + validate(self->check1); + self->check2 = true; + env->request_shutdown(env); + =} + + reaction(shutdown) {= + validate(self->check2); + validate(self->check1); + printf("Federate %d is shutting down\n", self->bank_idx); + =} +} + + +main enclaved reactor { + src = new Src() + dests = new [2] Fed() + + (src.out)+ -> dests.in + + dests.out -> dests.in2 + +} \ No newline at end of file diff --git a/test/lf/src/EnclavedBankMultiport.lf b/test/lf/src/EnclavedBankMultiport.lf new file mode 100644 index 000000000..ff3f97c10 --- /dev/null +++ b/test/lf/src/EnclavedBankMultiport.lf @@ -0,0 +1,35 @@ +target uC { + platform: Native, + timeout: 5 sec, + logging: debug, + build-type: Release +} + +reactor Fed(bank_idx: int = 0) { + output [4] out: int + input [4] in: int + + reaction(startup) -> out {= + printf("Hello from Fed %u\n", self->bank_idx); + for (int i = 0; ibank_idx); + } + =} + + reaction(in) {= + for (int i = 0; ivalue == i); + if (self->bank_idx == 0) { + printf(PRINTF_TIME" Fed %u Received %d from %d \n", env->get_elapsed_logical_time(env), self->bank_idx, in[i]->value, i); + } + } + } + =} +} + + +main enclaved reactor { + feds = new [4] Fed() + feds.out ~> interleaved(feds.in) after 100 msec +} \ No newline at end of file diff --git a/test/lf/src/EnclavedChain.lf b/test/lf/src/EnclavedChain.lf new file mode 100644 index 000000000..cffcb8063 --- /dev/null +++ b/test/lf/src/EnclavedChain.lf @@ -0,0 +1,50 @@ +target uC { + platform: Native, +} + +reactor Src(chain_length:int = 0) { + output out: int + input in: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, 0); + =} + + reaction(in) {= + printf("Received %d from PT\n", in->value); + validate(in->value == self->chain_length); + env->request_shutdown(env); + =} +} + +reactor PT(id: int = 0){ + input in: int + output out: int + state check: bool = false + reaction(in) -> out {= + printf("Received %d from Src\n", in->value); + validate(in->value == self->id); + self->check = true; + lf_set(out, in->value+1); + env->request_shutdown(env); + =} + + reaction(shutdown) {= + validate(self->check); + =} +} + +main enclaved reactor { + start = new Src(chain_length=8) + r0 = new PT(id=0) + r1 = new PT(id=1) + r2 = new PT(id=2) + r3 = new PT(id=3) + r4 = new PT(id=4) + r5 = new PT(id=5) + r6 = new PT(id=6) + r7 = new PT(id=7) + + start.out, r0.out, r1.out, r2.out, r3.out, r4.out, r5.out, r6.out, r7.out -> + r0.in, r1.in, r2.in, r3.in, r4.in, r5.in, r6.in, r7.in, start.in +} \ No newline at end of file diff --git a/test/lf/src/EnclavedLoopbackConnection.lf b/test/lf/src/EnclavedLoopbackConnection.lf new file mode 100644 index 000000000..030a81cba --- /dev/null +++ b/test/lf/src/EnclavedLoopbackConnection.lf @@ -0,0 +1,34 @@ +target uC { + platform: Native, +} + +reactor Src(id: int = 0) { + output out: int + input in: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} + reaction(in) {= + printf("Received %d from myself\n", in->value); + env->request_shutdown(env); + =} +} + +reactor Dst { + input in: int + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) {= + printf("Received %d from Src\n", in->value); + env->request_shutdown(env); + =} +} + +main enclaved reactor { + r1 = new Src(id=42) + r2 = new Dst() + r1.out -> r2.in + r1.out -> r1.in +} \ No newline at end of file diff --git a/test/lf/src/EnclavedMaxWait3.lf b/test/lf/src/EnclavedMaxWait3.lf new file mode 100644 index 000000000..3031c93cf --- /dev/null +++ b/test/lf/src/EnclavedMaxWait3.lf @@ -0,0 +1,61 @@ +target uC { + platform: Native, + timeout: 5sec, +} + +reactor Src(sleep: time = 0 msec) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + env->platform->wait_for(env->platform, self->sleep); + lf_set(out, 42); + env->request_shutdown(env); + =} + reaction(shutdown) {= + printf("Src is shutting down\n"); + =} +} + +reactor Dst { + input in1: int + input in2: int + state check1: bool = false + state check2: bool = false + state check3: bool = false + reaction(in1) {= + printf("Hello from Dst!\n"); + self->check1 = true; + =} maxwait(forever) {= + printf("STP violation on in1\n"); + validate(false); + =} + + reaction(in2) {= + printf("Normal reaction handled at in2\n"); + validate(false); + =} maxwait(100 msec) {= + printf("Hello from STP1!\n"); + self->check2 = true; + =} + + reaction(in2) {= + validate(false); + =} maxwait(50 msec) {= + printf("Hello from STP2!\n"); + self->check3 = true; + =} + + reaction(shutdown) {= + validate(self->check1); + validate(self->check2); + validate(self->check3); + =} +} + +main enclaved reactor { + r1 = new Src(sleep = 0 msec) + r2 = new Src(sleep = 1 sec) + r3 = new Dst() + r1.out -> r3.in1 + r2.out -> r3.in2 +} \ No newline at end of file diff --git a/test/lf/src/EnclavedMaxWaitNoHandler.lf b/test/lf/src/EnclavedMaxWaitNoHandler.lf new file mode 100644 index 000000000..9f1620f9b --- /dev/null +++ b/test/lf/src/EnclavedMaxWaitNoHandler.lf @@ -0,0 +1,45 @@ +target uC { + platform: Native, + timeout: 5sec, +} + +reactor Src(sleep: time = 0 msec) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + env->platform->wait_for(env->platform, self->sleep); + printf("Source finished waiting!\n"); + lf_set(out, 42); + env->request_shutdown(env); + =} + reaction(shutdown) {= + printf("Src is shutting down\n"); + =} +} + +reactor Dst { + input in1: int + state check1: bool = false + state check2: bool = false + reaction(in1) {= + printf("Hello from Dst input!\n"); + self->check1 = true; + =} maxwait(forever) + + reaction(startup) {= + printf("Hello from Dst startup!\n"); + validate(self->check1); + self->check2 = true; + =} + + reaction(shutdown) {= + validate(self->check1); + validate(self->check2); + =} +} + +main enclaved reactor { + r1 = new Src(sleep = 3 sec) + r2 = new Dst() + r1.out -> r2.in1 +} \ No newline at end of file diff --git a/test/lf/src/EnclavedMaxWaitNoHandler2.lf b/test/lf/src/EnclavedMaxWaitNoHandler2.lf new file mode 100644 index 000000000..bd1946a59 --- /dev/null +++ b/test/lf/src/EnclavedMaxWaitNoHandler2.lf @@ -0,0 +1,37 @@ +target uC { + platform: Native, + timeout: 5sec, +} + +reactor Src(sleep: time = 0 msec) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + env->platform->wait_for(env->platform, self->sleep); + lf_set(out, 42); + env->request_shutdown(env); + =} + reaction(shutdown) {= + printf("Src is shutting down\n"); + =} +} + +reactor Dst { + input in1: int + state cnt:int = 0 + reaction(startup, in1) {= + printf("Hello from Dst!\n"); + self->cnt++; + =} maxwait(0) + + reaction(shutdown) {= + printf("Dest is shutting down\n"); + validate(self->cnt == 2); + =} +} + +main enclaved reactor { + r1 = new Src(sleep = 3 sec) + r2 = new Dst() + r1.out -> r2.in1 +} \ No newline at end of file diff --git a/test/lf/src/EnclavedMultiConnection.lf b/test/lf/src/EnclavedMultiConnection.lf new file mode 100644 index 000000000..1eeab7092 --- /dev/null +++ b/test/lf/src/EnclavedMultiConnection.lf @@ -0,0 +1,51 @@ +target uC { + platform: Native, +} + +reactor Src(id: int = 0) { + output out1: int + output out2: float + input in: bool + reaction(startup) -> out1, out2{= + printf("Hello from Src!\n"); + lf_set(out1, self->id); + lf_set(out2, 0.42); + =} + + reaction(in) {= + env->request_shutdown(env); + =} +} + +reactor Dst { + input in1: int + input in2: float + output out: bool + state check: bool = false + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in1,in2) -> out {= + printf("Received %d and %f from Src\n", in1->value, in2->value); + validate(lf_is_present(in1)); + validate(lf_is_present(in2)); + validate(in1->value == 42); + validate(in2->value < 0.5); + validate(in2->value > 0.4); + self->check = true; + env->request_shutdown(env); + lf_set(out, true); + =} maxwait(forever) + + reaction(shutdown) {= + validate(self->check); + =} +} + +main enclaved reactor { + r1 = new Src(id=42) + r2 = new Dst() + r1.out1 -> r2.in1 + r1.out2 -> r2.in2 + r2.out -> r1.in +} \ No newline at end of file diff --git a/test/lf/src/EnclavedUnconnected2.lf b/test/lf/src/EnclavedUnconnected2.lf new file mode 100644 index 000000000..ffa721df9 --- /dev/null +++ b/test/lf/src/EnclavedUnconnected2.lf @@ -0,0 +1,41 @@ +target uC { + platform: Native, +} + +reactor Src(id: int = 0) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + env->request_shutdown(env); + =} +} + +reactor Dst { + input in: int + input in2: int + state check: bool = false + + reaction(in) {= + printf("Received %d from Src\n", in->value); + validate(in->value == 42); + self->check = true; + env->request_shutdown(env); + =} + + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + + + reaction(shutdown) {= + validate(self->check); + =} + +} + +main enclaved reactor { + r1 = new Src(id=42) + r2 = new Dst() + r1.out -> r2.in +} \ No newline at end of file From ebf799ec63a18c501a4336fcdfc3a7a0f0dafc99 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 23 Apr 2025 10:17:59 +0200 Subject: [PATCH 06/29] Bring back federated support --- examples/posix/federated/receiver.c | 2 +- examples/riot/coap_federated/receiver/main.c | 2 +- .../zephyr/basic_federated/common/receiver.h | 2 +- include/reactor-uc/environment.h | 2 +- include/reactor-uc/macros_internal.h | 43 ++++++++++++--- include/reactor-uc/reactor-uc.h | 2 +- .../generator/uc/UcClockSyncGenerator.kt | 46 ++-------------- .../generator/uc/UcFederateGenerator.kt | 11 ---- .../lflang/generator/uc/UcMainGenerator.kt | 54 ++++--------------- .../uc/{UcFederate.kt => UcSchedulingNode.kt} | 0 .../uc/UcStartupCoordinatorGenerator.kt | 39 +------------- src/environments/federate_environment.c | 4 +- .../receiver/main.c | 2 +- test/unit/delayed_conn_test.c | 2 +- test/unit/physical_clock_test.c | 2 +- test/unit/port_test.c | 2 +- test/unit/tcp_channel_test.c | 2 +- 17 files changed, 64 insertions(+), 153 deletions(-) rename lfc/core/src/main/kotlin/org/lflang/generator/uc/{UcFederate.kt => UcSchedulingNode.kt} (100%) diff --git a/examples/posix/federated/receiver.c b/examples/posix/federated/receiver.c index f956e70ec..2887e340d 100644 --- a/examples/posix/federated/receiver.c +++ b/examples/posix/federated/receiver.c @@ -45,7 +45,7 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs *in_ex LF_REACTOR_CTOR_PREAMBLE(); LF_REACTOR_CTOR(Receiver); LF_INITIALIZE_REACTION(Receiver, r, NEVER); - LF_INITIALIZE_INPUT(Receiver, in, 1, in_external); + LF_INITIALIZE_INPUT(Receiver, in, 1, in_external, NEVER); // Register reaction as an effect of in LF_PORT_REGISTER_EFFECT(self->in, self->r, 1); diff --git a/examples/riot/coap_federated/receiver/main.c b/examples/riot/coap_federated/receiver/main.c index 3cd908119..1f1509956 100755 --- a/examples/riot/coap_federated/receiver/main.c +++ b/examples/riot/coap_federated/receiver/main.c @@ -45,7 +45,7 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs *in_ex LF_REACTOR_CTOR_PREAMBLE(); LF_REACTOR_CTOR(Receiver); LF_INITIALIZE_REACTION(Receiver, r, NEVER); - LF_INITIALIZE_INPUT(Receiver, in, 1, in_external); + LF_INITIALIZE_INPUT(Receiver, in, 1, in_external, NEVER); // Register reaction as an effect of in LF_PORT_REGISTER_EFFECT(self->in, self->r, 1); diff --git a/examples/zephyr/basic_federated/common/receiver.h b/examples/zephyr/basic_federated/common/receiver.h index e5413ecf9..14e9de3ca 100644 --- a/examples/zephyr/basic_federated/common/receiver.h +++ b/examples/zephyr/basic_federated/common/receiver.h @@ -59,7 +59,7 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs *in_ex LF_REACTOR_CTOR_PREAMBLE(); LF_REACTOR_CTOR(Receiver); LF_INITIALIZE_REACTION(Receiver, r, NEVER); - LF_INITIALIZE_INPUT(Receiver, in, 1, in_external); + LF_INITIALIZE_INPUT(Receiver, in, 1, in_external, NEVER); // Register reaction as an effect of in LF_PORT_REGISTER_EFFECT(self->in, self->r, 1); diff --git a/include/reactor-uc/environment.h b/include/reactor-uc/environment.h index 1c1bae67b..92994410f 100644 --- a/include/reactor-uc/environment.h +++ b/include/reactor-uc/environment.h @@ -18,7 +18,7 @@ typedef struct Platform Platform; typedef struct Environment Environment; typedef enum { ENVIRONMENT_BASE, - ENVIRONMENT_ENCLAVE, + ENVIRONMENT_ENCLAVE, // FIXME: Unused? ENVIRONMENT_ENCLAVED, ENVIRONMENT_FEDERATE, ENVIRONMENT_ENCLAVED_FEDERATE diff --git a/include/reactor-uc/macros_internal.h b/include/reactor-uc/macros_internal.h index b3c17588c..87204dd32 100644 --- a/include/reactor-uc/macros_internal.h +++ b/include/reactor-uc/macros_internal.h @@ -734,31 +734,60 @@ typedef struct FederatedInputConnection FederatedInputConnection; Environment_ctor(&self->super, ENVIRONMENT_BASE, &main->super, &self->scheduler.super, fast_mode); \ } -#define LF_DEFINE_ENCLAVE_ENVIRONMENT_STRUCT(Name, NumEvents, NumReactions) \ +#define LF_DEFINE_FEDERATE_ENVIRONMENT_STRUCT(Name, NumEvents, NumReactions, NumNeighbors, NumStartupEvents, \ + NumClockSyncEvents) \ typedef struct { \ - EnclaveEnvironment super; \ + FederateEnvironment super; \ struct { \ EventQueue super; \ ArbitraryEvent events[(NumEvents)]; \ } event_queue; \ + struct { \ + EventQueue super; \ + ArbitraryEvent events[(NumClockSyncEvents) + (NumStartupEvents)]; \ + } system_event_queue; \ struct { \ ReactionQueue super; \ Reaction *reactions[(NumReactions)][(NumReactions)]; \ int level_size[(NumReactions)]; \ } reaction_queue; \ + struct { \ + StartupCoordinator super; \ + StartupEvent events[(NumStartupEvents)]; \ + bool used[(NumStartupEvents)]; \ + NeighborState neighbors[NumNeighbors]; \ + } startup; \ + struct { \ + ClockSynchronization super; \ + ClockSyncEvent events[(NumClockSyncEvents)]; \ + NeighborClock neighbor_clocks[(NumNeighbors)]; \ + bool used[(NumClockSyncEvents)]; \ + } clock_sync; \ DynamicScheduler scheduler; \ } Environment_##Name; -#define LF_DEFINE_ENCLAVE_ENVIRONMENT_CTOR(Name) \ +#define LF_DEFINE_FEDERATE_ENVIRONMENT_CTOR(Name, NumNeighbors, LongestPath, DoClockSync, IsGrandmaster, \ + ClockSyncPeriod, ClockSyncMaxAdj, ClockSyncKp, ClockSyncKi) \ void Environment_##Name##_ctor(Environment_##Name *self, Name *main, interval_t duration, bool fast_mode) { \ EventQueue_ctor(&self->event_queue.super, self->event_queue.events, \ sizeof(self->event_queue.events) / sizeof(self->event_queue.events[0])); \ + EventQueue_ctor(&self->system_event_queue.super, self->system_event_queue.events, \ + sizeof(self->system_event_queue.events) / sizeof(self->system_event_queue.events[0])); \ ReactionQueue_ctor(&self->reaction_queue.super, (Reaction **)self->reaction_queue.reactions, \ self->reaction_queue.level_size, \ sizeof(self->reaction_queue.level_size) / sizeof(self->reaction_queue.level_size[0])); \ - DynamicScheduler_ctor(&self->scheduler, &self->super.super, &self->event_queue.super, NULL, \ - &self->reaction_queue.super, duration, true); \ - EnclaveEnvironment_ctor(&self->super, &main->super, &self->scheduler.super, fast_mode); \ + DynamicScheduler_ctor(&self->scheduler, &self->super.super, &self->event_queue.super, \ + &self->system_event_queue.super, &self->reaction_queue.super, duration, true); \ + FederateEnvironment_ctor(&self->super, (Reactor *)main, &self->scheduler.super, fast_mode, \ + (FederatedConnectionBundle **)&main->_bundles, (NumNeighbors), &self->startup.super, \ + (DoClockSync) ? &self->clock_sync.super : NULL); \ + ClockSynchronization_ctor(&self->clock_sync.super, &self->super.super, self->clock_sync.neighbor_clocks, \ + NumNeighbors, IsGrandmaster, sizeof(ClockSyncEvent), (void *)self->clock_sync.events, \ + self->clock_sync.used, sizeof(self->clock_sync.used) / sizeof(self->clock_sync.used[0]), \ + ClockSyncPeriod, ClockSyncMaxAdj, ClockSyncKp, ClockSyncKi); \ + StartupCoordinator_ctor(&self->startup.super, &self->super.super, self->startup.neighbors, NumNeighbors, \ + LongestPath, sizeof(StartupEvent), (void *)self->startup.events, self->startup.used, \ + sizeof(self->clock_sync.used) / sizeof(self->clock_sync.used[0])); \ } #define LF_ENTRY_POINT(MainReactorName, NumEvents, NumReactions, Timeout, KeepAlive, Fast) \ @@ -778,7 +807,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; EventQueue_ctor(&event_queue, events, NumEvents); \ ReactionQueue_ctor(&reaction_queue, (Reaction **)reactions, level_size, NumReactions); \ DynamicScheduler_ctor(&scheduler, _lf_environment, &event_queue, NULL, &reaction_queue, (Timeout), (KeepAlive)); \ - Environment_ctor(&env, (Reactor *)&main_reactor, &scheduler.super, Fast); \ + Environment_ctor(&env, ENVIRONMENT_BASE, (Reactor *)&main_reactor, &scheduler.super, Fast); \ MainReactorName##_ctor(&main_reactor, NULL, &env); \ env.scheduler->duration = Timeout; \ env.scheduler->keep_alive = KeepAlive; \ diff --git a/include/reactor-uc/reactor-uc.h b/include/reactor-uc/reactor-uc.h index 1f33db5e2..08efaa4e0 100644 --- a/include/reactor-uc/reactor-uc.h +++ b/include/reactor-uc/reactor-uc.h @@ -28,7 +28,7 @@ #endif #if defined FEDERATED -#include "reactor-uc/environments/federated_environment.h" +#include "reactor-uc/environments/federate_environment.h" #include "reactor-uc/federated.h" #include "reactor-uc/network_channel.h" #include "reactor-uc/serialization.h" diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt index 96f06b0b9..a24a8b80a 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt @@ -41,46 +41,10 @@ class UcClockSyncGenerator( private val targetConfig: TargetConfig ) { - companion object { - // The number of system events allocated for each neigbor. Used to schedule received messages as - // system events. - val numSystemEventsPerBundle = 3 - - // The number of additional system events allocated. This system event is used for the periodic - // SyncRequest event. The value must match the NUM_RESERVED_EVENTS compile def in - // clock_synchronization. - val numSystemEventsConst = 2 - - // Returns the number of system events needed by the clock sync subsystem, given a number of - // neighbors. - fun getNumSystemEvents(numBundles: Int) = - numSystemEventsPerBundle * numBundles + numSystemEventsConst - - val instName = "clock_sync" - } - private val numNeighbors = connectionGenerator.getNumFederatedConnectionBundles() - private val numSystemEvents = getNumSystemEvents(numNeighbors) - private val typeName = "Federate" - private val clockSync = federate.clockSyncParams - private val disabled = federate.clockSyncParams.disabled - - fun enabled() = - !disabled && - targetConfig.getOrDefault(ClockSyncModeProperty.INSTANCE) != - ClockSyncModeType.ClockSyncMode.OFF - - fun generateSelfStruct() = - if (enabled()) "LF_DEFINE_CLOCK_SYNC_STRUCT(${typeName}, ${numNeighbors}, ${numSystemEvents})" - else "" - - fun generateCtor() = - if (enabled()) - "LF_DEFINE_CLOCK_SYNC_CTOR(Federate, ${numNeighbors}, ${numSystemEvents }, ${clockSync.grandmaster}, ${clockSync.period}, ${clockSync.maxAdj}, ${clockSync.Kp}, ${clockSync.Ki});" - else "" - - fun generateFederateStructField() = - if (enabled()) "${typeName}ClockSynchronization ${instName};" else "" - - fun generateFederateCtorCode() = if (enabled()) "LF_INITIALIZE_CLOCK_SYNC(${typeName});" else "" + val numSystemEvents = numNeighbors*3 + 2 + val enabled = + !federate.clockSyncParams.disabled && + targetConfig.getOrDefault(ClockSyncModeProperty.INSTANCE) != + ClockSyncModeType.ClockSyncMode.OFF } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index a59be617e..65951be6b 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt @@ -22,8 +22,6 @@ class UcFederateGenerator( private val instances = UcInstanceGenerator( container, parameters, ports, connections, reactions, fileConfig, messageReporter) - private val clockSync = UcClockSyncGenerator(currentFederate, connections, targetConfig) - private val startupCooordinator = UcStartupCoordinatorGenerator(currentFederate, connections) private val headerFile = "lf_federate.h" private val includeGuard = "LFC_GEN_FEDERATE_${currentFederate.inst.name.uppercase()}_H" @@ -45,9 +43,6 @@ class UcFederateGenerator( ${" | "..instances.generateReactorStructFields(currentFederate.inst)} ${" | "..connections.generateReactorStructFields()} ${" | "..connections.generateFederateStructFields()} - | // Startup and clock sync objects. - ${" | "..startupCooordinator.generateFederateStructField()} - ${" | "..clockSync.generateFederateStructField()} | LF_FEDERATE_BOOKKEEPING_INSTANCES(${connections.getNumFederatedConnectionBundles()}) |} ${currentFederate.codeType}; | @@ -64,8 +59,6 @@ class UcFederateGenerator( ${" | "..instances.generateReactorCtorCodes(currentFederate.inst)} ${" | "..connections.generateFederateCtorCodes()} ${" | "..connections.generateReactorCtorCodes()} - ${" | "..clockSync.generateFederateCtorCode()} - ${" | "..startupCooordinator.generateFederateCtorCode()} |} | """ @@ -83,8 +76,6 @@ class UcFederateGenerator( |#include "${fileConfig.getReactorHeaderPath(reactor).toUnixString()}" ${" |"..connections.generateNetworkChannelIncludes()} | - ${" |"..startupCooordinator.generateSelfStruct()} - ${" |"..clockSync.generateSelfStruct()} ${" |"..connections.generateFederatedSelfStructs()} ${" |"..connections.generateSelfStructs()} ${" |"..generateFederateStruct()} @@ -99,8 +90,6 @@ class UcFederateGenerator( """ |#include "${headerFile}" | - ${" |"..startupCooordinator.generateCtor()} - ${" |"..clockSync.generateCtor()} ${" |"..connections.generateFederatedCtors()} ${" |"..connections.generateCtors()} ${" |"..generateCtorDefinition()} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index 4d1cf7127..176e440b8 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -26,13 +26,6 @@ abstract class UcMainGenerator( abstract fun keepAlive(): Boolean - fun generateDefineScheduler() = - """ - |static DynamicScheduler _scheduler; - |static Scheduler* scheduler = &_scheduler.super; - """ - .trimMargin() - fun generateIncludeScheduler() = """#include "reactor-uc/schedulers/dynamic/scheduler.h" """ open fun generateInitializeScheduler() = @@ -45,28 +38,6 @@ abstract class UcMainGenerator( fun fast() = if (targetConfig.isSet(FastProperty.INSTANCE)) "true" else "false" - fun generateDefineQueues() = - with(PrependOperator) { - """ - |// Define queues used by scheduler - |LF_DEFINE_EVENT_QUEUE(${eventQueueName}, ${numEvents}) - |LF_DEFINE_EVENT_QUEUE(${systemEventQueueName}, ${getNumSystemEvents()}) - |LF_DEFINE_REACTION_QUEUE(${reactionQueueName}, ${numReactions}) - """ - .trimMargin() - } - - fun generateInitializeQueues() = - with(PrependOperator) { - """ - |// Define queues used by scheduler - |LF_INITIALIZE_EVENT_QUEUE(${eventQueueName}, ${numEvents}) - |LF_INITIALIZE_EVENT_QUEUE(${systemEventQueueName}, ${getNumSystemEvents()}) - |LF_INITIALIZE_REACTION_QUEUE(${reactionQueueName}, ${numReactions}) - """ - .trimMargin() - } - fun generateStartHeader() = with(PrependOperator) { """ @@ -153,13 +124,12 @@ class UcMainGeneratorFederated( private val main = currentFederate.inst.reactor private val ucConnectionGenerator = UcConnectionGenerator(top, currentFederate, otherFederates) private val netBundlesSize = ucConnectionGenerator.getNumFederatedConnectionBundles() - private val clockSyncGenerator = - UcClockSyncGenerator(currentFederate, ucConnectionGenerator, targetConfig) - private val longestPath = 0 + private val clockSync = UcClockSyncGenerator(currentFederate, ucConnectionGenerator, targetConfig) + private val startupCooordinator = UcStartupCoordinatorGenerator(currentFederate, ucConnectionGenerator) override fun getNumSystemEvents(): Int { - val clockSyncSystemEvents = UcClockSyncGenerator.getNumSystemEvents(netBundlesSize) - val startupCoordinatorEvents = UcStartupCoordinatorGenerator.getNumSystemEvents(netBundlesSize) + val clockSyncSystemEvents = clockSync.numSystemEvents + val startupCoordinatorEvents = startupCooordinator.numSystemEvents return clockSyncSystemEvents + startupCoordinatorEvents } @@ -186,20 +156,16 @@ class UcMainGeneratorFederated( |#include "reactor-uc/reactor-uc.h" ${" |"..generateIncludeScheduler()} |#include "lf_federate.h" + |LF_DEFINE_FEDERATE_ENVIRONMENT_STRUCT(${currentFederate.codeType}, ${numEvents}, ${numReactions}, ${netBundlesSize}, ${startupCooordinator.numSystemEvents}, ${clockSync.numSystemEvents}) + |LF_DEFINE_FEDERATE_ENVIRONMENT_CTOR(${currentFederate.codeType}, ${netBundlesSize}, ${ucConnectionGenerator.getLongestFederatePath()}, ${clockSync.enabled}, ${currentFederate.clockSyncParams.grandmaster}, ${currentFederate.clockSyncParams.period}, ${currentFederate.clockSyncParams.maxAdj}, ${currentFederate.clockSyncParams.Kp}, ${currentFederate.clockSyncParams.Ki}) |static ${currentFederate.codeType} main_reactor; - |static FederateEnvironment lf_environment; - |Environment *_lf_environment = &lf_environment.super; - ${" |"..generateDefineQueues()} - ${" |"..generateDefineScheduler()} + |static Environment_${currentFederate.codeType} environment; + |Environment *_lf_environment = (Environment *) &environment; |void lf_exit(void) { - | FederateEnvironment_free(&lf_environment); + | FederateEnvironment_free(&environment.super); |} |void lf_start(void) { - ${" | "..generateInitializeQueues()} - ${" | "..generateInitializeScheduler()} - | FederateEnvironment_ctor(&lf_environment, (Reactor *)&main_reactor, scheduler, ${fast()}, - | (FederatedConnectionBundle **) &main_reactor._bundles, ${netBundlesSize}, &main_reactor.${UcStartupCoordinatorGenerator.instName}.super, - | ${if (clockSyncGenerator.enabled()) "&main_reactor.${UcClockSyncGenerator.instName}.super" else "NULL"}); + | Environment_${currentFederate.codeType}_ctor(&environment, &main_reactor, ${getDuration()}, ${fast()}); | ${currentFederate.codeType}_ctor(&main_reactor, NULL, _lf_environment); | _lf_environment->assemble(_lf_environment); | _lf_environment->start(_lf_environment); diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcSchedulingNode.kt similarity index 100% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt rename to lfc/core/src/main/kotlin/org/lflang/generator/uc/UcSchedulingNode.kt diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStartupCoordinatorGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStartupCoordinatorGenerator.kt index efd0517a6..9e14dc575 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStartupCoordinatorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStartupCoordinatorGenerator.kt @@ -7,43 +7,6 @@ class UcStartupCoordinatorGenerator( private val federate: UcFederate, private val connectionGenerator: UcConnectionGenerator ) { - - companion object { - // The number of system events allocated for each neighbor. Used to schedule received messages - // as - // system events. The worst-case number of events is 3. It happens when you have received: - // HandshakeResponse - // HandshakeRequest - // And then you handle the HandshakeRequest which produces a HandshakeResponse to your peer, - // but before you have completed the handling of this HandshakeRequest you receive the first - // StartTimeProposal from that peer. - val numSystemEventsPerBundle = 3 - - // The number of additional system events allocated. This system event is used for the periodic - // SyncRequest event. The value must match the NUM_RESERVED_EVENTS compile def in - // startup_coordination.c - val numSystemEventsConst = 3 - - // Returns the number of system events needed by the clock sync subsystem, given a number of - // neighbors. - fun getNumSystemEvents(numBundles: Int) = - numSystemEventsPerBundle * numBundles + numSystemEventsConst - - val instName = "startup_coordinator" - } - private val numNeighbors = connectionGenerator.getNumFederatedConnectionBundles() - private val numSystemEvents = getNumSystemEvents(numNeighbors) - private val longestPath = connectionGenerator.getLongestFederatePath() - private val typeName = "Federate" - - fun generateSelfStruct() = - "LF_DEFINE_STARTUP_COORDINATOR_STRUCT(${typeName}, ${numNeighbors}, ${numSystemEvents})" - - fun generateCtor() = - "LF_DEFINE_STARTUP_COORDINATOR_CTOR(Federate, ${numNeighbors}, ${longestPath}, ${numSystemEvents});" - - fun generateFederateStructField() = "${typeName}StartupCoordinator ${instName};" - - fun generateFederateCtorCode() = "LF_INITIALIZE_STARTUP_COORDINATOR(${typeName});" + val numSystemEvents = numNeighbors * 3 + 3 } diff --git a/src/environments/federate_environment.c b/src/environments/federate_environment.c index a75e4c400..5322a186a 100644 --- a/src/environments/federate_environment.c +++ b/src/environments/federate_environment.c @@ -1,4 +1,4 @@ -#include "reactor-uc/environments/federated_environment.h" +#include "reactor-uc/environments/federate_environment.h" #include "reactor-uc/logging.h" #include "reactor-uc/network_channel.h" #include "reactor-uc/federated.h" @@ -119,7 +119,7 @@ static lf_ret_t FederateEnvironment_poll_network_channels(Environment *super) { void FederateEnvironment_ctor(FederateEnvironment *self, Reactor *main, Scheduler *scheduler, bool fast_mode, FederatedConnectionBundle **net_bundles, size_t net_bundles_size, StartupCoordinator *startup_coordinator, ClockSynchronization *clock_sync) { - Environment_ctor(&self->super, main, scheduler, fast_mode); + Environment_ctor(&self->super, ENVIRONMENT_FEDERATE, main, scheduler, fast_mode); self->super.assemble = FederateEnvironment_assemble; self->super.start = FederateEnvironment_start; self->super.wait_until = FederateEnvironment_wait_until; diff --git a/test/platform/riot/coap_channel_federated_test/receiver/main.c b/test/platform/riot/coap_channel_federated_test/receiver/main.c index bc3d42d1c..04a0cca69 100755 --- a/test/platform/riot/coap_channel_federated_test/receiver/main.c +++ b/test/platform/riot/coap_channel_federated_test/receiver/main.c @@ -51,7 +51,7 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs *in_ex LF_REACTOR_CTOR_PREAMBLE(); LF_REACTOR_CTOR(Receiver); LF_INITIALIZE_REACTION(Receiver, r, NEVER); - LF_INITIALIZE_INPUT(Receiver, in, 1, in_external); + LF_INITIALIZE_INPUT(Receiver, in, 1, in_external, NEVER); // Register reaction as an effect of in LF_PORT_REGISTER_EFFECT(self->in, self->r, 1); diff --git a/test/unit/delayed_conn_test.c b/test/unit/delayed_conn_test.c index 1941df7c9..3edab27d5 100644 --- a/test/unit/delayed_conn_test.c +++ b/test/unit/delayed_conn_test.c @@ -65,7 +65,7 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs *sourc LF_REACTOR_CTOR_PREAMBLE(); LF_REACTOR_CTOR(Receiver); LF_INITIALIZE_REACTION(Receiver, r_recv, NEVER); - LF_INITIALIZE_INPUT(Receiver, in, 1, sources_in); + LF_INITIALIZE_INPUT(Receiver, in, 1, sources_in, NEVER); // Register reaction as an effect of in LF_PORT_REGISTER_EFFECT(self->in, self->r_recv, 1); diff --git a/test/unit/physical_clock_test.c b/test/unit/physical_clock_test.c index ba3ed1604..d05eabf06 100644 --- a/test/unit/physical_clock_test.c +++ b/test/unit/physical_clock_test.c @@ -152,7 +152,7 @@ void test_adjust_time(void) { } int main(void) { - Environment_ctor(&env, NULL, NULL, false); + Environment_ctor(&env, ENVIRONMENT_BASE, NULL, NULL, false); env.platform = &p; UNITY_BEGIN(); diff --git a/test/unit/port_test.c b/test/unit/port_test.c index 0286b6c68..0568c7c78 100644 --- a/test/unit/port_test.c +++ b/test/unit/port_test.c @@ -64,7 +64,7 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs *in_ex LF_REACTOR_CTOR(Receiver); LF_REACTOR_CTOR_PREAMBLE(); LF_INITIALIZE_REACTION(Receiver, r_recv, NEVER); - LF_INITIALIZE_INPUT(Receiver, in, 1, in_external); + LF_INITIALIZE_INPUT(Receiver, in, 1, in_external, NEVER); // Register reaction as an effect of in LF_PORT_REGISTER_EFFECT(self->in, self->r_recv, 1); diff --git a/test/unit/tcp_channel_test.c b/test/unit/tcp_channel_test.c index f7c968c57..1a924318a 100644 --- a/test/unit/tcp_channel_test.c +++ b/test/unit/tcp_channel_test.c @@ -1,6 +1,6 @@ #include "reactor-uc/platform/posix/tcp_ip_channel.h" #include "reactor-uc/reactor-uc.h" -#include "reactor-uc/environments/federated_environment.h" +#include "reactor-uc/environments/federate_environment.h" #include "reactor-uc/startup_coordinator.h" #include "unity.h" #include "test_util.h" From b87ffce2e5f3739146094c0e0ca138d578ebafd4 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 23 Apr 2025 18:08:34 +0200 Subject: [PATCH 07/29] More fixes --- include/reactor-uc/environment.h | 9 ++- .../environments/base_environment.h | 22 -------- include/reactor-uc/macros_internal.h | 29 +++++++++- .../generator/uc/UcClockSyncGenerator.kt | 8 +-- .../lflang/generator/uc/UcConnectionUtils.kt | 2 +- .../lflang/generator/uc/UcMainGenerator.kt | 3 +- src/connection.c | 2 + src/environments/base_environment.c | 4 +- src/platform/posix/posix.c | 5 ++ src/schedulers/dynamic/scheduler.c | 12 +++- test/lf/src/EnclavedChain.lf | 1 + test/lf/src/EnclavedConnectionInterleaved.lf | 41 ++++++++++++++ test/lf/src/EnclavedConnectionIterated.lf | 56 +++++++++++++++++++ test/lf/src/EnclavedConnectionIterated2.lf | 52 +++++++++++++++++ test/lf/src/EnclavedDelayedConnection.lf | 36 ++++++++++++ test/lf/src/EnclavedPhysicalConnection2.lf | 36 ++++++++++++ test/lf/src/EnclavedSimple.lf | 1 + test/lf/src/FedTest.lf | 19 ------- 18 files changed, 284 insertions(+), 54 deletions(-) delete mode 100644 include/reactor-uc/environments/base_environment.h create mode 100644 test/lf/src/EnclavedConnectionInterleaved.lf create mode 100644 test/lf/src/EnclavedConnectionIterated.lf create mode 100644 test/lf/src/EnclavedConnectionIterated2.lf create mode 100644 test/lf/src/EnclavedDelayedConnection.lf create mode 100644 test/lf/src/EnclavedPhysicalConnection2.lf delete mode 100644 test/lf/src/FedTest.lf diff --git a/include/reactor-uc/environment.h b/include/reactor-uc/environment.h index 92994410f..a09d8890b 100644 --- a/include/reactor-uc/environment.h +++ b/include/reactor-uc/environment.h @@ -12,6 +12,7 @@ #include "reactor-uc/error.h" #include "reactor-uc/reactor.h" #include "reactor-uc/scheduler.h" +#include "reactor-uc/platform.h" #include "reactor-uc/queues.h" typedef struct Platform Platform; @@ -27,10 +28,12 @@ typedef enum { extern Environment *_lf_environment; // NOLINT struct Environment { + int id; EnvironmentType type; - Reactor *main; // The top-level reactor of the program. - Scheduler *scheduler; // The scheduler in charge of executing the reactions. - Platform *platform; // The platform that provides the physical time and sleep functions. + Reactor *main; // The top-level reactor of the program. + Scheduler *scheduler; // The scheduler in charge of executing the reactions. + Platform *platform; // The platform that provides the physical time and sleep functions. + PLATFORM_T _platform; bool has_async_events; // Whether the program has multiple execution contexts and can receive async events and thus // need critical sections. bool fast_mode; // Whether the program is executing in fast mode where we do not wait for physical time to elapse diff --git a/include/reactor-uc/environments/base_environment.h b/include/reactor-uc/environments/base_environment.h deleted file mode 100644 index 2f919e196..000000000 --- a/include/reactor-uc/environments/base_environment.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef REACTOR_UC_ENVIRONMENT_ENCLAVED_H -#define REACTOR_UC_ENVIRONMENT_ENCLAVED_H - -#include "reactor-uc/environment.h" -#include "reactor-uc/network_channel.h" -#include "reactor-uc/startup_coordinator.h" -#include "reactor-uc/clock_synchronization.h" -#include "reactor-uc/physical_clock.h" - -typedef struct EnclavedEnvironment EnclavedEnvironment; - -struct EnclavedEnvironment { - Environment super; - Environment **enclave_environments; - pthread_t *enclave_threads; - size_t num_enclaves; -}; - -void EnclavedEnvironment_ctor(EnclavedEnvironment *self, Reactor *main, Scheduler *scheduler, bool fast_mode); -void EnclavedEnvironment_free(EnclavedEnvironment *self); - -#endif diff --git a/include/reactor-uc/macros_internal.h b/include/reactor-uc/macros_internal.h index 87204dd32..f7754bf56 100644 --- a/include/reactor-uc/macros_internal.h +++ b/include/reactor-uc/macros_internal.h @@ -734,6 +734,33 @@ typedef struct FederatedInputConnection FederatedInputConnection; Environment_ctor(&self->super, ENVIRONMENT_BASE, &main->super, &self->scheduler.super, fast_mode); \ } +#define LF_DEFINE_ENCLAVE_ENVIRONMENT_STRUCT(Name, NumEvents, NumReactions) \ + typedef struct { \ + EnclaveEnvironment super; \ + struct { \ + EventQueue super; \ + ArbitraryEvent events[(NumEvents)]; \ + } event_queue; \ + struct { \ + ReactionQueue super; \ + Reaction *reactions[(NumReactions)][(NumReactions)]; \ + int level_size[(NumReactions)]; \ + } reaction_queue; \ + DynamicScheduler scheduler; \ + } Environment_##Name; + +#define LF_DEFINE_ENCLAVE_ENVIRONMENT_CTOR(Name) \ + void Environment_##Name##_ctor(Environment_##Name *self, Name *main, interval_t duration, bool fast_mode) { \ + EventQueue_ctor(&self->event_queue.super, self->event_queue.events, \ + sizeof(self->event_queue.events) / sizeof(self->event_queue.events[0])); \ + ReactionQueue_ctor(&self->reaction_queue.super, (Reaction **)self->reaction_queue.reactions, \ + self->reaction_queue.level_size, \ + sizeof(self->reaction_queue.level_size) / sizeof(self->reaction_queue.level_size[0])); \ + DynamicScheduler_ctor(&self->scheduler, &self->super.super, &self->event_queue.super, NULL, \ + &self->reaction_queue.super, duration, true); \ + EnclaveEnvironment_ctor(&self->super, &main->super, &self->scheduler.super, fast_mode); \ + } + #define LF_DEFINE_FEDERATE_ENVIRONMENT_STRUCT(Name, NumEvents, NumReactions, NumNeighbors, NumStartupEvents, \ NumClockSyncEvents) \ typedef struct { \ @@ -779,7 +806,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; DynamicScheduler_ctor(&self->scheduler, &self->super.super, &self->event_queue.super, \ &self->system_event_queue.super, &self->reaction_queue.super, duration, true); \ FederateEnvironment_ctor(&self->super, (Reactor *)main, &self->scheduler.super, fast_mode, \ - (FederatedConnectionBundle **)&main->_bundles, (NumNeighbors), &self->startup.super, \ + (FederatedConnectionBundle **)&main->_bundles, (NumNeighbors), &self->startup.super, \ (DoClockSync) ? &self->clock_sync.super : NULL); \ ClockSynchronization_ctor(&self->clock_sync.super, &self->super.super, self->clock_sync.neighbor_clocks, \ NumNeighbors, IsGrandmaster, sizeof(ClockSyncEvent), (void *)self->clock_sync.events, \ diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt index a24a8b80a..b01c726d8 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt @@ -42,9 +42,9 @@ class UcClockSyncGenerator( ) { private val numNeighbors = connectionGenerator.getNumFederatedConnectionBundles() - val numSystemEvents = numNeighbors*3 + 2 + val numSystemEvents = numNeighbors * 3 + 2 val enabled = - !federate.clockSyncParams.disabled && - targetConfig.getOrDefault(ClockSyncModeProperty.INSTANCE) != - ClockSyncModeType.ClockSyncMode.OFF + !federate.clockSyncParams.disabled && + targetConfig.getOrDefault(ClockSyncModeProperty.INSTANCE) != + ClockSyncModeType.ClockSyncMode.OFF } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt index 5e89c37f7..ee2bd4657 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt @@ -160,7 +160,7 @@ class UcChannel( ) { fun getCodePortIdx() = portIdx - fun getCodeBankIdx() = if (node != null) bankIdx else 0 + fun getCodeBankIdx() = if (node == null || node.nodeType == NodeType.ENCLAVE) bankIdx else 0 private val portOfContainedReactor = varRef.container != null private val reactorInstance = diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index 176e440b8..0537534c4 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -125,7 +125,8 @@ class UcMainGeneratorFederated( private val ucConnectionGenerator = UcConnectionGenerator(top, currentFederate, otherFederates) private val netBundlesSize = ucConnectionGenerator.getNumFederatedConnectionBundles() private val clockSync = UcClockSyncGenerator(currentFederate, ucConnectionGenerator, targetConfig) - private val startupCooordinator = UcStartupCoordinatorGenerator(currentFederate, ucConnectionGenerator) + private val startupCooordinator = + UcStartupCoordinatorGenerator(currentFederate, ucConnectionGenerator) override fun getNumSystemEvents(): Int { val clockSyncSystemEvents = clockSync.numSystemEvents diff --git a/src/connection.c b/src/connection.c index 07c92c820..0df840e80 100644 --- a/src/connection.c +++ b/src/connection.c @@ -232,6 +232,8 @@ void EnclavedConnection_cleanup(Trigger *trigger) { tag_t tag = lf_delay_tag(base_tag, self->delay); Event event = EVENT_INIT(tag, &self->super.super, self->staged_payload_ptr); + printf("%i schedule event to %i at " PRINTF_TIME ".\n", sending_env->id, receiving_env->id, event.super.tag.time); + lf_ret_t ret = receiving_sched->schedule_at(receiving_sched, &event); // FIXME: There is a race condition here. Actually we need to acquire the lock on the receiving enclave. diff --git a/src/environments/base_environment.c b/src/environments/base_environment.c index 66a3ed98d..fefaa3743 100644 --- a/src/environments/base_environment.c +++ b/src/environments/base_environment.c @@ -17,8 +17,10 @@ static void Environment_assemble(Environment *self) { } bool Environment_start_enclave_environments(Reactor *reactor, instant_t start_time) { + static int idx = 0; bool ret = false; if (reactor->env->type == ENVIRONMENT_ENCLAVE) { + reactor->env->id = idx++; reactor->env->start_at(reactor->env, start_time); ret = true; } @@ -99,7 +101,7 @@ void Environment_ctor(Environment *self, EnvironmentType type, Reactor *main, Sc self->main = main; self->type = type; self->scheduler = scheduler; - self->platform = Platform_new(); + self->platform = (Platform *)&self->_platform; Platform_ctor(self->platform); self->assemble = Environment_assemble; self->start = Environment_start; diff --git a/src/platform/posix/posix.c b/src/platform/posix/posix.c index 4ff4c19e2..9351b657b 100644 --- a/src/platform/posix/posix.c +++ b/src/platform/posix/posix.c @@ -48,20 +48,25 @@ lf_ret_t PlatformPosix_wait_until_interruptible(Platform *super, instant_t wakeu LF_DEBUG(PLATFORM, "Interruptable wait until " PRINTF_TIME, wakeup_time); lf_ret_t ret; PlatformPosix *self = (PlatformPosix *)super; + // printf("%lu Wait until " PRINTF_TIME " \n", pthread_self(), wakeup_time); MUTEX_LOCK(self->mutex); if (self->new_async_event) { self->new_async_event = false; MUTEX_UNLOCK(self->mutex); + // printf("Already an event here\n"); return LF_SLEEP_INTERRUPTED; } const struct timespec tspec = convert_ns_to_timespec(wakeup_time); + // printf("%lu pthread_cond_timedwait\n", pthread_self()); int res = pthread_cond_timedwait(&self->cond, &self->mutex.lock, &tspec); if (res == 0) { + // printf("pthread_cond_timedwait interrupted\n"); LF_DEBUG(PLATFORM, "Wait until interrupted"); ret = LF_SLEEP_INTERRUPTED; } else if (res == ETIMEDOUT) { + // printf("pthread_cond_timedwait completed\n"); LF_DEBUG(PLATFORM, "Wait until completed"); ret = LF_OK; } else { diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index 1840af980..bb8c97ad1 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -235,6 +235,7 @@ void Scheduler_schedule_startups(Scheduler *self, tag_t start_tag) { void Scheduler_schedule_timers(Scheduler *self, Reactor *reactor, tag_t start_tag) { lf_ret_t ret; + // FIXME: Does this work with enclaves? for (size_t i = 0; i < reactor->triggers_size; i++) { Trigger *trigger = reactor->triggers[i]; if (trigger->type == TRIG_TIMER) { @@ -246,7 +247,10 @@ void Scheduler_schedule_timers(Scheduler *self, Reactor *reactor, tag_t start_ta } } for (size_t i = 0; i < reactor->children_size; i++) { - Scheduler_schedule_timers(self, reactor->children[i], start_tag); + Reactor *child = reactor->children[i]; + if (child->env == reactor->env) { + Scheduler_schedule_timers(self, reactor->children[i], start_tag); + } } } @@ -322,14 +326,17 @@ void Scheduler_run(Scheduler *untyped_self) { } // We have found the next tag we want to handle. Wait until physical time reaches this tag. + printf("%i wait until " PRINTF_TIME "\n", env->id, next_tag.time); res = self->env->wait_until(self->env, next_tag.time); if (res == LF_SLEEP_INTERRUPTED) { + printf("%i interrupted. Rerun loop.\n", env->id); LF_DEBUG(SCHED, "Sleep interrupted before completion"); continue; } else if (res != LF_OK) { throw("Sleep failed"); } + printf("%i completed. Handle tag " PRINTF_TIME ".\n", env->id, next_tag.time); if (next_event_is_system_event) { Scheduler_pop_system_events_and_handle(untyped_self, next_tag); @@ -413,6 +420,7 @@ lf_ret_t Scheduler_schedule_at(Scheduler *super, Event *event) { ret = self->event_queue->insert(self->event_queue, (AbstractEvent *)event); validate(ret == LF_OK); + printf("%i notified\n", self->env->id); self->env->platform->notify(self->env->platform); unlock_and_return: @@ -445,7 +453,7 @@ void Scheduler_request_shutdown(Scheduler *untyped_self) { // Thus we enter a critical section before setting the stop tag. MUTEX_LOCK(self->mutex); self->stop_tag = lf_delay_tag(self->current_tag, 0); - LF_INFO(SCHED, "Shutdown requested, will stop at tag" PRINTF_TAG, self->stop_tag); + LF_INFO(SCHED, "%i Shutdown requested, will stop at tag" PRINTF_TAG, env->id, self->stop_tag); env->platform->notify(env->platform); MUTEX_UNLOCK(self->mutex); } diff --git a/test/lf/src/EnclavedChain.lf b/test/lf/src/EnclavedChain.lf index cffcb8063..186db12f0 100644 --- a/test/lf/src/EnclavedChain.lf +++ b/test/lf/src/EnclavedChain.lf @@ -1,5 +1,6 @@ target uC { platform: Native, + logging: Debug } reactor Src(chain_length:int = 0) { diff --git a/test/lf/src/EnclavedConnectionInterleaved.lf b/test/lf/src/EnclavedConnectionInterleaved.lf new file mode 100644 index 000000000..a177429f8 --- /dev/null +++ b/test/lf/src/EnclavedConnectionInterleaved.lf @@ -0,0 +1,41 @@ +target uC { + platform: Native +} + + +reactor Test(bank_idx:int = 0){ + input [3] in: int + output [3] out: int + + state check: bool[3]; + + reaction(startup) -> out {= + for (int i = 0; i < 3; i++) { + self->check[i] = false; + lf_set(out[i], i); + } + =} + + reaction(in) {= + for (int i = 0; i < 3; i++) { + if (lf_is_present(in[i])) { + printf("%d Got % From %d\n", self->bank_idx, in[i]->value, i); + validate(!self->check[i]); + validate(in[i]->value == self->bank_idx); + self->check[i] = true; + } + } + for (int i = 0; i < 3; i++) { + if (!self->check[i]) { + return; + } + } + env->request_shutdown(env); + =} +} + + +main enclaved reactor { + test = new [3] Test(); + test.out -> interleaved(test.in) +} diff --git a/test/lf/src/EnclavedConnectionIterated.lf b/test/lf/src/EnclavedConnectionIterated.lf new file mode 100644 index 000000000..7afcfae07 --- /dev/null +++ b/test/lf/src/EnclavedConnectionIterated.lf @@ -0,0 +1,56 @@ +target uC { + platform: Native +} + +reactor Src { + output [4] out:int + + reaction(startup) -> out {= + for (int i = 0; i < out_width; i++) { + lf_set(out[i], i); + } + env->request_shutdown(env); + =} +} + +reactor Dest(bank_idx:int=0) { + input in:int + reaction(in) {= + printf("Received %d on %d\n", self->bank_idx, in->value); + if (self->bank_idx < 4) { + validate(in->value == self->bank_idx); + } else { + validate(in->value == 0); + } + + env->request_shutdown(env); + =} +} + +reactor Dest2 { + input[2] in:int + reaction(in) {= + validate(in[0]->value == 1); + validate(in[1]->value == 2); + + env->request_shutdown(env); + =} +} + +reactor Dest3 { + input in:int + reaction(in) {= + validate(in->value == 3); + + env->request_shutdown(env); + =} + +} + +main enclaved reactor { + src = new Src() + dest = new[5] Dest() + dest2 = new Dest2() + dest3 = new Dest3() + (src.out)+ -> dest.in, dest2.in, dest3.in +} \ No newline at end of file diff --git a/test/lf/src/EnclavedConnectionIterated2.lf b/test/lf/src/EnclavedConnectionIterated2.lf new file mode 100644 index 000000000..c4dda77d0 --- /dev/null +++ b/test/lf/src/EnclavedConnectionIterated2.lf @@ -0,0 +1,52 @@ +target uC { + platform: Native +} + +reactor Src { + output [2] out:int + + reaction(startup) -> out {= + for (int i = 0; i < out_width; i++) { + lf_set(out[i], i); + } + env->request_shutdown(env); + =} +} + +reactor Dest(bank_idx:int=0) { + input in:int + reaction(in) {= + printf("Bank %d received %d\n", self->bank_idx, in->value); + validate(in->value == self->bank_idx % 2); + env->request_shutdown(env); + =} +} + +reactor Dest2 { + input[2] in:int + reaction(in) {= + printf("Received %d and %d\n", in[0]->value, in[1]->value); + validate(in[0]->value == 0); + validate(in[1]->value == 1); + env->request_shutdown(env); + =} +} + +reactor Dest3(expected:int=2) { + input in:int + reaction(in) {= + printf("received %d\n", in->value); + validate(in->value == self->expected); + env->request_shutdown(env); + =} +} + +main enclaved reactor { + src = new Src() + src2 = new Src() + dest = new[4] Dest() + dest2 = new Dest2() + dest3_1 = new Dest3(expected=0) + dest3_2 = new Dest3(expected=1) + (src.out, src2.out)+ -> dest.in, dest2.in, dest3_1.in, dest3_2.in +} \ No newline at end of file diff --git a/test/lf/src/EnclavedDelayedConnection.lf b/test/lf/src/EnclavedDelayedConnection.lf new file mode 100644 index 000000000..45b76282a --- /dev/null +++ b/test/lf/src/EnclavedDelayedConnection.lf @@ -0,0 +1,36 @@ +target uC { + platform: Native +} + +reactor Sender { + output out: int + timer t(10 msec) + reaction(t) -> out {= + lf_set(out, 42); + env->request_shutdown(env); + =} +} + +reactor Recv { + input in: int + state success:bool = false + reaction(in) {= + LF_INFO(ENV, "Triggered;"); + interval_t now_l = env->get_elapsed_logical_time(env); + interval_t now_p = env->get_elapsed_physical_time(env); + validate(now_l == MSEC(10) + MSEC(500)); + validate(in->value == 42); + self->success = true; + env->request_shutdown(env); + =} + reaction(shutdown) {= + validate(self->success); + =} +} + +main enclaved reactor { + s = new Sender() + r = new Recv() + + s.out -> r.in after 500 msec +} \ No newline at end of file diff --git a/test/lf/src/EnclavedPhysicalConnection2.lf b/test/lf/src/EnclavedPhysicalConnection2.lf new file mode 100644 index 000000000..f504c1b7b --- /dev/null +++ b/test/lf/src/EnclavedPhysicalConnection2.lf @@ -0,0 +1,36 @@ +target uC { + platform: Native +} + +reactor Sender { + output out: int + timer t(10 msec) + reaction(t) -> out {= + lf_set(out, 42); + env->request_shutdown(env); + =} +} + +reactor Recv { + input in: int + state success:bool = false + reaction(in) {= + LF_INFO(ENV, "Triggered;"); + interval_t now_l = env->get_elapsed_logical_time(env); + interval_t now_p = env->get_elapsed_physical_time(env); + validate(now_l > MSEC(10) + MSEC(500)); + validate(in->value == 42); + self->success = true; + env->request_shutdown(env); + =} + reaction(shutdown) {= + validate(self->success); + =} +} + +main enclaved reactor { + s = new Sender() + r = new Recv() + + s.out ~> r.in after 500 msec +} \ No newline at end of file diff --git a/test/lf/src/EnclavedSimple.lf b/test/lf/src/EnclavedSimple.lf index dad75abcb..08b2c675c 100644 --- a/test/lf/src/EnclavedSimple.lf +++ b/test/lf/src/EnclavedSimple.lf @@ -8,6 +8,7 @@ reactor R(id: int = 0){ reaction(startup) {= printf("Hello From Enclave %d\n", self->id); self->test = true; + env->request_shutdown(env); =} reaction(shutdown) {= diff --git a/test/lf/src/FedTest.lf b/test/lf/src/FedTest.lf deleted file mode 100644 index 4691fef06..000000000 --- a/test/lf/src/FedTest.lf +++ /dev/null @@ -1,19 +0,0 @@ -target uC { - platform: Native -} - - -reactor R(id: int = 0){ - output out: int - input in: int - reaction(startup) {= - printf("Hello From Enclave %d\n", self->id); - =} -} - - -federated reactor { - e1 = new R(id=1) - e2 = new R(id=2) - e1.out -> e2.in -} \ No newline at end of file From 63de23fa1576ce97854c8cde4591d7b6f3b7ce7f Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 23 Apr 2025 19:22:55 +0200 Subject: [PATCH 08/29] Updates --- src/platform/posix/posix.c | 5 ----- src/schedulers/dynamic/scheduler.c | 4 ---- test/lf/src/EnclavedConnectionIterated2.lf | 21 ++++++++++++++++++--- test/lf/src/EnclavedMaxWait.lf | 5 +++-- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/platform/posix/posix.c b/src/platform/posix/posix.c index 9351b657b..4ff4c19e2 100644 --- a/src/platform/posix/posix.c +++ b/src/platform/posix/posix.c @@ -48,25 +48,20 @@ lf_ret_t PlatformPosix_wait_until_interruptible(Platform *super, instant_t wakeu LF_DEBUG(PLATFORM, "Interruptable wait until " PRINTF_TIME, wakeup_time); lf_ret_t ret; PlatformPosix *self = (PlatformPosix *)super; - // printf("%lu Wait until " PRINTF_TIME " \n", pthread_self(), wakeup_time); MUTEX_LOCK(self->mutex); if (self->new_async_event) { self->new_async_event = false; MUTEX_UNLOCK(self->mutex); - // printf("Already an event here\n"); return LF_SLEEP_INTERRUPTED; } const struct timespec tspec = convert_ns_to_timespec(wakeup_time); - // printf("%lu pthread_cond_timedwait\n", pthread_self()); int res = pthread_cond_timedwait(&self->cond, &self->mutex.lock, &tspec); if (res == 0) { - // printf("pthread_cond_timedwait interrupted\n"); LF_DEBUG(PLATFORM, "Wait until interrupted"); ret = LF_SLEEP_INTERRUPTED; } else if (res == ETIMEDOUT) { - // printf("pthread_cond_timedwait completed\n"); LF_DEBUG(PLATFORM, "Wait until completed"); ret = LF_OK; } else { diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index bb8c97ad1..23d5d1621 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -326,17 +326,14 @@ void Scheduler_run(Scheduler *untyped_self) { } // We have found the next tag we want to handle. Wait until physical time reaches this tag. - printf("%i wait until " PRINTF_TIME "\n", env->id, next_tag.time); res = self->env->wait_until(self->env, next_tag.time); if (res == LF_SLEEP_INTERRUPTED) { - printf("%i interrupted. Rerun loop.\n", env->id); LF_DEBUG(SCHED, "Sleep interrupted before completion"); continue; } else if (res != LF_OK) { throw("Sleep failed"); } - printf("%i completed. Handle tag " PRINTF_TIME ".\n", env->id, next_tag.time); if (next_event_is_system_event) { Scheduler_pop_system_events_and_handle(untyped_self, next_tag); @@ -420,7 +417,6 @@ lf_ret_t Scheduler_schedule_at(Scheduler *super, Event *event) { ret = self->event_queue->insert(self->event_queue, (AbstractEvent *)event); validate(ret == LF_OK); - printf("%i notified\n", self->env->id); self->env->platform->notify(self->env->platform); unlock_and_return: diff --git a/test/lf/src/EnclavedConnectionIterated2.lf b/test/lf/src/EnclavedConnectionIterated2.lf index c4dda77d0..d8993ed7f 100644 --- a/test/lf/src/EnclavedConnectionIterated2.lf +++ b/test/lf/src/EnclavedConnectionIterated2.lf @@ -24,11 +24,26 @@ reactor Dest(bank_idx:int=0) { reactor Dest2 { input[2] in:int + state check:bool[2] + reaction(startup) {= + self->check[0] = false; + self->check[1] = false; + =} reaction(in) {= printf("Received %d and %d\n", in[0]->value, in[1]->value); - validate(in[0]->value == 0); - validate(in[1]->value == 1); - env->request_shutdown(env); + if (lf_is_present(in[0])) { + validate(in[0]->value == 0); + validate(!self->check[0]); + self->check[0] = true; + } + if (lf_is_present(in[1])) { + validate(in[1]->value == 1); + validate(!self->check[1]); + self->check[1] = true; + } + if (self->check[0] && self->check[1]) { + env->request_shutdown(env); + } =} } diff --git a/test/lf/src/EnclavedMaxWait.lf b/test/lf/src/EnclavedMaxWait.lf index 597caba80..75dd90568 100644 --- a/test/lf/src/EnclavedMaxWait.lf +++ b/test/lf/src/EnclavedMaxWait.lf @@ -32,8 +32,9 @@ reactor Dst { =} } -main enclaved reactor { +main reactor { r1 = new Src() r2 = new Dst() - r1.out -> r2.in + r1.out -> r2.in + } \ No newline at end of file From ef0cb457a2610964a5d961c8d53e6ffda14fe7ec Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 23 Apr 2025 19:28:19 +0200 Subject: [PATCH 09/29] Small platform updates --- include/reactor-uc/platform/aducm355/aducm355.h | 1 + include/reactor-uc/platform/flexpret/flexpret.h | 1 + include/reactor-uc/platform/patmos/patmos.h | 1 + include/reactor-uc/platform/pico/pico.h | 1 + include/reactor-uc/platform/riot/riot.h | 1 + include/reactor-uc/platform/zephyr/zephyr.h | 1 + src/platform/aducm355/aducm355.c | 2 ++ src/platform/flexpret/flexpret.c | 2 ++ src/platform/patmos/patmos.c | 2 ++ src/platform/pico/pico.c | 2 ++ src/platform/riot/riot.c | 2 ++ src/platform/zephyr/zephyr.c | 2 ++ 12 files changed, 18 insertions(+) diff --git a/include/reactor-uc/platform/aducm355/aducm355.h b/include/reactor-uc/platform/aducm355/aducm355.h index 2634a83c2..15a97fc5f 100644 --- a/include/reactor-uc/platform/aducm355/aducm355.h +++ b/include/reactor-uc/platform/aducm355/aducm355.h @@ -20,5 +20,6 @@ typedef struct { #define PLATFORM_T PlatformAducm355 #define MUTEX_T MutexAducm355 +#define THREAD_T void #endif diff --git a/include/reactor-uc/platform/flexpret/flexpret.h b/include/reactor-uc/platform/flexpret/flexpret.h index 3b4d775b1..7a083f1ee 100644 --- a/include/reactor-uc/platform/flexpret/flexpret.h +++ b/include/reactor-uc/platform/flexpret/flexpret.h @@ -20,5 +20,6 @@ void PlatformFlexpret_ctor(Platform *super); #define PLATFORM_T PlatformFlexpret #define MUTEX_T MutexFlexpret +#define THREAD_T void #endif diff --git a/include/reactor-uc/platform/patmos/patmos.h b/include/reactor-uc/platform/patmos/patmos.h index 0bb925cd3..58980febf 100644 --- a/include/reactor-uc/platform/patmos/patmos.h +++ b/include/reactor-uc/platform/patmos/patmos.h @@ -18,4 +18,5 @@ typedef struct { #define PLATFORM_T PlatformPatmos #define MUTEX_T MutexPatmos +#define THREAD_T void #endif diff --git a/include/reactor-uc/platform/pico/pico.h b/include/reactor-uc/platform/pico/pico.h index 7b48f37fa..e2c3d886b 100644 --- a/include/reactor-uc/platform/pico/pico.h +++ b/include/reactor-uc/platform/pico/pico.h @@ -19,5 +19,6 @@ typedef struct { #define PLATFORM_T PlatformPico #define MUTEX_T MutexPico +#define THREAD_T void #endif diff --git a/include/reactor-uc/platform/riot/riot.h b/include/reactor-uc/platform/riot/riot.h index d50eac1c0..2fc08ca84 100644 --- a/include/reactor-uc/platform/riot/riot.h +++ b/include/reactor-uc/platform/riot/riot.h @@ -16,5 +16,6 @@ typedef struct { #define PLATFORM_T PlatformRiot #define MUTEX_T MutexRiot +#define THREAD_T void #endif diff --git a/include/reactor-uc/platform/zephyr/zephyr.h b/include/reactor-uc/platform/zephyr/zephyr.h index 2d18d449f..f6a62cd0d 100644 --- a/include/reactor-uc/platform/zephyr/zephyr.h +++ b/include/reactor-uc/platform/zephyr/zephyr.h @@ -16,6 +16,7 @@ typedef struct { } MutexZephyr; #define PLATFORM_T PlatformZephyr +#define THREAD_T void #define MUTEX_T MutexZephyr #endif diff --git a/src/platform/aducm355/aducm355.c b/src/platform/aducm355/aducm355.c index b079032ec..bcbc8d250 100644 --- a/src/platform/aducm355/aducm355.c +++ b/src/platform/aducm355/aducm355.c @@ -199,6 +199,8 @@ void Platform_ctor(Platform *super) { super->wait_for = PlatformAducm355_wait_for; super->wait_until_interruptible = PlatformAducm355_wait_until_interruptible; super->notify = PlatformAducm355_notify; + super->create_thread = NULL; + super->join_thread = NULL; self->ticks_last = 0; self->epoch = 0; self->new_async_event = false; diff --git a/src/platform/flexpret/flexpret.c b/src/platform/flexpret/flexpret.c index c0452c4b6..a466a37f0 100644 --- a/src/platform/flexpret/flexpret.c +++ b/src/platform/flexpret/flexpret.c @@ -59,6 +59,8 @@ void Platform_ctor(Platform *super) { super->wait_for = PlatformFlexpret_wait_for; super->wait_until_interruptible = PlatformFlexpret_wait_until_interruptible; super->notify = PlatformFlexpret_notify; + super->create_thread = NULL; + super->join_thread = NULL; self->num_nested_critical_sections = 0; self->async_event_occurred = false; self->mutex = (fp_lock_t)FP_LOCK_INITIALIZER; diff --git a/src/platform/patmos/patmos.c b/src/platform/patmos/patmos.c index 0ae35c732..d9fa8923a 100644 --- a/src/platform/patmos/patmos.c +++ b/src/platform/patmos/patmos.c @@ -99,6 +99,8 @@ void Platform_ctor(Platform *super) { super->wait_for = PlatformPatmos_wait_for; super->wait_until_interruptible_locked = PlatformPatmos_wait_until_interruptible; super->notify = PlatformPatmos_notify; + super->create_thread = NULL; + super->join_thread = NULL; self->num_nested_critical_sections = 0; } diff --git a/src/platform/pico/pico.c b/src/platform/pico/pico.c index 49013f3b1..8e0400e53 100644 --- a/src/platform/pico/pico.c +++ b/src/platform/pico/pico.c @@ -73,6 +73,8 @@ void Platform_ctor(Platform *super) { super->wait_for = PlatformPico_wait_for; super->wait_until_interruptible = PlatformPico_wait_until_interruptible; super->notify = PlatformPico_notify; + super->create_thread = NULL; + super->join_thread = NULL; self->num_nested_critical_sections = 0; stdio_init_all(); diff --git a/src/platform/riot/riot.c b/src/platform/riot/riot.c index 2d1fccf35..f6cef0560 100644 --- a/src/platform/riot/riot.c +++ b/src/platform/riot/riot.c @@ -72,6 +72,8 @@ void Platform_ctor(Platform *super) { super->wait_for = PlatformRiot_wait_for; super->wait_until_interruptible = PlatformRiot_wait_until_interruptible; super->notify = PlatformRiot_notify; + super->create_thread = NULL; + super->join_thread = NULL; mutex_init(&self->lock); mutex_lock(&self->lock); diff --git a/src/platform/zephyr/zephyr.c b/src/platform/zephyr/zephyr.c index ee6f86488..423fcbefd 100644 --- a/src/platform/zephyr/zephyr.c +++ b/src/platform/zephyr/zephyr.c @@ -87,6 +87,8 @@ void Platform_ctor(Platform *super) { super->wait_for = PlatformZephyr_wait_for; super->wait_until_interruptible = PlatformZephyr_wait_until_interruptible; super->notify = PlatformZephyr_notify; + super->create_thread = NULL; + super->join_thread = NULL; // Initialize semaphore with initial count 0 and limit 1. int ret = k_sem_init(&self->sem, 0, 1); From b2c571426b661372726a51c730b0d23631a8eb8c Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 23 Apr 2025 19:30:25 +0200 Subject: [PATCH 10/29] Increase delay in MaxWait test --- test/lf/src/EnclavedMaxWait.lf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lf/src/EnclavedMaxWait.lf b/test/lf/src/EnclavedMaxWait.lf index 75dd90568..a778baed9 100644 --- a/test/lf/src/EnclavedMaxWait.lf +++ b/test/lf/src/EnclavedMaxWait.lf @@ -6,7 +6,7 @@ reactor Src(id: int = 0) { output out: int reaction(startup) -> out{= printf("Hello from Src!\n"); - env->platform->wait_for(env->platform, SEC(2)); + env->platform->wait_for(env->platform, SEC(10)); lf_set(out, 42); env->request_shutdown(env); =} From 379e282ec930ddde3232926a6a6c0f7e72cda503 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 23 Apr 2025 19:31:00 +0200 Subject: [PATCH 11/29] Fix --- src/connection.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/connection.c b/src/connection.c index 0df840e80..07c92c820 100644 --- a/src/connection.c +++ b/src/connection.c @@ -232,8 +232,6 @@ void EnclavedConnection_cleanup(Trigger *trigger) { tag_t tag = lf_delay_tag(base_tag, self->delay); Event event = EVENT_INIT(tag, &self->super.super, self->staged_payload_ptr); - printf("%i schedule event to %i at " PRINTF_TIME ".\n", sending_env->id, receiving_env->id, event.super.tag.time); - lf_ret_t ret = receiving_sched->schedule_at(receiving_sched, &event); // FIXME: There is a race condition here. Actually we need to acquire the lock on the receiving enclave. From 1d34eaf31d8af97479b0b6efbf0011e45c0c7436 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 23 Apr 2025 19:35:39 +0200 Subject: [PATCH 12/29] Updates --- include/reactor-uc/platform/aducm355/aducm355.h | 2 +- include/reactor-uc/platform/flexpret/flexpret.h | 2 +- include/reactor-uc/platform/patmos/patmos.h | 2 +- include/reactor-uc/platform/pico/pico.h | 2 +- include/reactor-uc/platform/riot/riot.h | 2 +- include/reactor-uc/platform/zephyr/zephyr.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/reactor-uc/platform/aducm355/aducm355.h b/include/reactor-uc/platform/aducm355/aducm355.h index 15a97fc5f..1484ff90a 100644 --- a/include/reactor-uc/platform/aducm355/aducm355.h +++ b/include/reactor-uc/platform/aducm355/aducm355.h @@ -20,6 +20,6 @@ typedef struct { #define PLATFORM_T PlatformAducm355 #define MUTEX_T MutexAducm355 -#define THREAD_T void +#define THREAD_T void* #endif diff --git a/include/reactor-uc/platform/flexpret/flexpret.h b/include/reactor-uc/platform/flexpret/flexpret.h index 7a083f1ee..c5f155595 100644 --- a/include/reactor-uc/platform/flexpret/flexpret.h +++ b/include/reactor-uc/platform/flexpret/flexpret.h @@ -20,6 +20,6 @@ void PlatformFlexpret_ctor(Platform *super); #define PLATFORM_T PlatformFlexpret #define MUTEX_T MutexFlexpret -#define THREAD_T void +#define THREAD_T void* #endif diff --git a/include/reactor-uc/platform/patmos/patmos.h b/include/reactor-uc/platform/patmos/patmos.h index 58980febf..d4f315c51 100644 --- a/include/reactor-uc/platform/patmos/patmos.h +++ b/include/reactor-uc/platform/patmos/patmos.h @@ -18,5 +18,5 @@ typedef struct { #define PLATFORM_T PlatformPatmos #define MUTEX_T MutexPatmos -#define THREAD_T void +#define THREAD_T void* #endif diff --git a/include/reactor-uc/platform/pico/pico.h b/include/reactor-uc/platform/pico/pico.h index e2c3d886b..7b70d000c 100644 --- a/include/reactor-uc/platform/pico/pico.h +++ b/include/reactor-uc/platform/pico/pico.h @@ -19,6 +19,6 @@ typedef struct { #define PLATFORM_T PlatformPico #define MUTEX_T MutexPico -#define THREAD_T void +#define THREAD_T void* #endif diff --git a/include/reactor-uc/platform/riot/riot.h b/include/reactor-uc/platform/riot/riot.h index 2fc08ca84..94bc4c6d2 100644 --- a/include/reactor-uc/platform/riot/riot.h +++ b/include/reactor-uc/platform/riot/riot.h @@ -16,6 +16,6 @@ typedef struct { #define PLATFORM_T PlatformRiot #define MUTEX_T MutexRiot -#define THREAD_T void +#define THREAD_T void* #endif diff --git a/include/reactor-uc/platform/zephyr/zephyr.h b/include/reactor-uc/platform/zephyr/zephyr.h index f6a62cd0d..f66734dfb 100644 --- a/include/reactor-uc/platform/zephyr/zephyr.h +++ b/include/reactor-uc/platform/zephyr/zephyr.h @@ -16,7 +16,7 @@ typedef struct { } MutexZephyr; #define PLATFORM_T PlatformZephyr -#define THREAD_T void +#define THREAD_T void* #define MUTEX_T MutexZephyr #endif From 72ea428bb884c69a90419752e6d5301b3949cb45 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 24 Apr 2025 08:57:23 +0200 Subject: [PATCH 13/29] Minor fixes --- include/reactor-uc/platform/aducm355/aducm355.h | 2 +- include/reactor-uc/platform/flexpret/flexpret.h | 2 +- include/reactor-uc/platform/patmos/patmos.h | 2 +- include/reactor-uc/platform/pico/pico.h | 2 +- include/reactor-uc/platform/riot/riot.h | 2 +- include/reactor-uc/platform/zephyr/zephyr.h | 2 +- src/platform/riot/coap_udp_ip_channel.c | 2 +- test/lf/src/EnclavedMaxWait.lf | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/reactor-uc/platform/aducm355/aducm355.h b/include/reactor-uc/platform/aducm355/aducm355.h index 1484ff90a..0e19dccfd 100644 --- a/include/reactor-uc/platform/aducm355/aducm355.h +++ b/include/reactor-uc/platform/aducm355/aducm355.h @@ -20,6 +20,6 @@ typedef struct { #define PLATFORM_T PlatformAducm355 #define MUTEX_T MutexAducm355 -#define THREAD_T void* +#define THREAD_T void * #endif diff --git a/include/reactor-uc/platform/flexpret/flexpret.h b/include/reactor-uc/platform/flexpret/flexpret.h index c5f155595..84b2233dd 100644 --- a/include/reactor-uc/platform/flexpret/flexpret.h +++ b/include/reactor-uc/platform/flexpret/flexpret.h @@ -20,6 +20,6 @@ void PlatformFlexpret_ctor(Platform *super); #define PLATFORM_T PlatformFlexpret #define MUTEX_T MutexFlexpret -#define THREAD_T void* +#define THREAD_T void * #endif diff --git a/include/reactor-uc/platform/patmos/patmos.h b/include/reactor-uc/platform/patmos/patmos.h index d4f315c51..88a47771b 100644 --- a/include/reactor-uc/platform/patmos/patmos.h +++ b/include/reactor-uc/platform/patmos/patmos.h @@ -18,5 +18,5 @@ typedef struct { #define PLATFORM_T PlatformPatmos #define MUTEX_T MutexPatmos -#define THREAD_T void* +#define THREAD_T void * #endif diff --git a/include/reactor-uc/platform/pico/pico.h b/include/reactor-uc/platform/pico/pico.h index 7b70d000c..a652febb2 100644 --- a/include/reactor-uc/platform/pico/pico.h +++ b/include/reactor-uc/platform/pico/pico.h @@ -19,6 +19,6 @@ typedef struct { #define PLATFORM_T PlatformPico #define MUTEX_T MutexPico -#define THREAD_T void* +#define THREAD_T void * #endif diff --git a/include/reactor-uc/platform/riot/riot.h b/include/reactor-uc/platform/riot/riot.h index 94bc4c6d2..b32c1612a 100644 --- a/include/reactor-uc/platform/riot/riot.h +++ b/include/reactor-uc/platform/riot/riot.h @@ -16,6 +16,6 @@ typedef struct { #define PLATFORM_T PlatformRiot #define MUTEX_T MutexRiot -#define THREAD_T void* +#define THREAD_T void * #endif diff --git a/include/reactor-uc/platform/zephyr/zephyr.h b/include/reactor-uc/platform/zephyr/zephyr.h index f66734dfb..ac17f660a 100644 --- a/include/reactor-uc/platform/zephyr/zephyr.h +++ b/include/reactor-uc/platform/zephyr/zephyr.h @@ -16,7 +16,7 @@ typedef struct { } MutexZephyr; #define PLATFORM_T PlatformZephyr -#define THREAD_T void* +#define THREAD_T void * #define MUTEX_T MutexZephyr #endif diff --git a/src/platform/riot/coap_udp_ip_channel.c b/src/platform/riot/coap_udp_ip_channel.c index a1e24997f..3c66d6a80 100644 --- a/src/platform/riot/coap_udp_ip_channel.c +++ b/src/platform/riot/coap_udp_ip_channel.c @@ -1,6 +1,6 @@ #include "reactor-uc/platform/riot/coap_udp_ip_channel.h" #include "reactor-uc/logging.h" -#include "reactor-uc/environments/federated_environment.h" +#include "reactor-uc/environments/federate_environment.h" #include "reactor-uc/serialization.h" #include "net/gcoap.h" diff --git a/test/lf/src/EnclavedMaxWait.lf b/test/lf/src/EnclavedMaxWait.lf index a778baed9..e85923400 100644 --- a/test/lf/src/EnclavedMaxWait.lf +++ b/test/lf/src/EnclavedMaxWait.lf @@ -6,7 +6,7 @@ reactor Src(id: int = 0) { output out: int reaction(startup) -> out{= printf("Hello from Src!\n"); - env->platform->wait_for(env->platform, SEC(10)); + env->platform->wait_for(env->platform, SEC(2)); lf_set(out, 42); env->request_shutdown(env); =} @@ -32,7 +32,7 @@ reactor Dst { =} } -main reactor { +main enclaved reactor { r1 = new Src() r2 = new Dst() r1.out -> r2.in From f70cc1b4177ac6e7e60470a600a2d0cead7b2f6d Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 24 Apr 2025 09:14:45 +0200 Subject: [PATCH 14/29] Fix maxwait with physical connections and shutdown --- src/environments/enclave_environment.c | 26 +++++++++++++++ .../src/EnclavedMaxWaitPhysicalConnection.lf | 32 ++++++++++++++++++ test/lf/src/EnclavedMaxWaitShutdown.lf | 33 +++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 test/lf/src/EnclavedMaxWaitPhysicalConnection.lf create mode 100644 test/lf/src/EnclavedMaxWaitShutdown.lf diff --git a/src/environments/enclave_environment.c b/src/environments/enclave_environment.c index 20cf6df46..7c2470253 100644 --- a/src/environments/enclave_environment.c +++ b/src/environments/enclave_environment.c @@ -3,12 +3,35 @@ #include "reactor-uc/port.h" #include "reactor-uc/logging.h" +static void EnclaveEnvironment_shutdown(Environment *env); + static void *enclave_thread(void *environment_pointer) { Environment *env = (Environment *)environment_pointer; env->scheduler->run(env->scheduler); + + // Unblock any downstream enclaves + EnclaveEnvironment_shutdown(env); + return NULL; } +static void EnclaveEnvironment_shutdown(Environment *super) { + Reactor *main = super->main; + for (size_t i = 0; i < main->triggers_size; i++) { + Trigger *trigger = main->triggers[i]; + if (trigger->type == TRIG_OUTPUT) { + Port *output = (Port *)trigger; + for (size_t j = 0; j < output->conns_out_registered; j++) { + Connection *conn = output->conns_out[j]; + if (conn->super.type == TRIG_CONN_ENCLAVED) { + EnclavedConnection *enclaved_conn = (EnclavedConnection *)conn; + enclaved_conn->set_last_known_tag(enclaved_conn, FOREVER_TAG); + } + } + } + } +} + static void EnclaveEnvironment_start_at(Environment *super, instant_t start_time) { EnclaveEnvironment *self = (EnclaveEnvironment *)super; LF_INFO(ENV, "Starting enclave %s " PRINTF_TIME " nsec", super->main->name, start_time); @@ -47,6 +70,9 @@ static lf_ret_t EnclaveEnvironment_acquire_tag(Environment *super, tag_t next_ta if (input->conn_in->super.type == TRIG_CONN_ENCLAVED) { EnclavedConnection *conn = (EnclavedConnection *)input->conn_in; + if (conn->type == PHYSICAL_CONNECTION) + continue; + tag_t last_known_tag = conn->get_last_known_tag(conn); if (lf_tag_compare(last_known_tag, next_tag) < 0) { LF_DEBUG(SCHED, "Input %p is unresolved, latest known tag was " PRINTF_TAG, input, conn->last_known_tag); diff --git a/test/lf/src/EnclavedMaxWaitPhysicalConnection.lf b/test/lf/src/EnclavedMaxWaitPhysicalConnection.lf new file mode 100644 index 000000000..f3815e38f --- /dev/null +++ b/test/lf/src/EnclavedMaxWaitPhysicalConnection.lf @@ -0,0 +1,32 @@ +target uC { + platform: Native, +} + +reactor Src(id: int = 0) { + output out: int + input in: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + =} + reaction(in) {= + env->request_shutdown(env); + =} +} + +reactor Dst { + input in: int + output out: int + reaction(startup, in) -> out {= + printf("Hello from Dst!\n"); + lf_set(out, 42); + env->request_shutdown(env); + =} maxwait(forever) +} + +main enclaved reactor { + r1 = new Src() + r2 = new Dst() + r1.out ~> r2.in + r2.out -> r1.in + +} \ No newline at end of file diff --git a/test/lf/src/EnclavedMaxWaitShutdown.lf b/test/lf/src/EnclavedMaxWaitShutdown.lf new file mode 100644 index 000000000..5ebc3d5c4 --- /dev/null +++ b/test/lf/src/EnclavedMaxWaitShutdown.lf @@ -0,0 +1,33 @@ +target uC { + platform: Native, +} + +reactor Src(id: int = 0) { + output out: int + input in: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, 42); + env->request_shutdown(env); + =} +} + +reactor Dst { + input in: int + timer t (1 msec) + reaction(startup, in) {= + printf("Hello from Dst!\n"); + =} maxwait(forever) + + reaction(t) {= + printf("Hello from t\n"); + env->request_shutdown(env); + + =} +} + +main enclaved reactor { + r1 = new Src() + r2 = new Dst() + r1.out -> r2.in +} \ No newline at end of file From 1ed0bfec5324647e0a219b6c6c885cbc73e87f74 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 24 Apr 2025 09:17:02 +0200 Subject: [PATCH 15/29] Fix Federated maxwait with physical connections --- src/environments/federate_environment.c | 2 ++ .../src/FederatedMaxWaitPhysicalConnection.lf | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 test/lf/src/FederatedMaxWaitPhysicalConnection.lf diff --git a/src/environments/federate_environment.c b/src/environments/federate_environment.c index 5322a186a..78515ab14 100644 --- a/src/environments/federate_environment.c +++ b/src/environments/federate_environment.c @@ -83,6 +83,8 @@ static lf_ret_t FederateEnvironment_acquire_tag(Environment *super, tag_t next_t for (size_t j = 0; j < bundle->inputs_size; j++) { FederatedInputConnection *input = bundle->inputs[j]; + if (input->type == PHYSICAL_CONNECTION) + continue; // Before reading the last_known_tag of an FederatedInputConnection, we must acquire its mutex. MUTEX_LOCK(input->mutex); if (lf_tag_compare(input->last_known_tag, next_tag) < 0) { diff --git a/test/lf/src/FederatedMaxWaitPhysicalConnection.lf b/test/lf/src/FederatedMaxWaitPhysicalConnection.lf new file mode 100644 index 000000000..97316f94c --- /dev/null +++ b/test/lf/src/FederatedMaxWaitPhysicalConnection.lf @@ -0,0 +1,32 @@ +target uC { + platform: Native, +} + +reactor Src(id: int = 0) { + output out: int + input in: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + =} + reaction(in) {= + env->request_shutdown(env); + =} +} + +reactor Dst { + input in: int + output out: int + reaction(startup, in) -> out {= + printf("Hello from Dst!\n"); + lf_set(out, 42); + env->request_shutdown(env); + =} maxwait(forever) +} + +federated reactor { + r1 = new Src() + r2 = new Dst() + r1.out ~> r2.in + r2.out -> r1.in + +} \ No newline at end of file From c8cae50f21a72450c6f03eee07db575af095b78e Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 24 Apr 2025 10:35:05 +0200 Subject: [PATCH 16/29] WIP --- .../org/lflang/generator/uc/UcConnectionGenerator.kt | 7 ++++++- .../org/lflang/generator/uc/UcFederateGenerator.kt | 2 +- .../kotlin/org/lflang/generator/uc/UcGenerator.kt | 11 +++++++---- .../org/lflang/generator/uc/UcGeneratorFederated.kt | 2 +- .../generator/uc/UcPlatformGeneratorNonFederated.kt | 2 +- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 40fc5f54b..eed879514 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -528,7 +528,7 @@ class UcConnectionGenerator( fun getMaxNumPendingEvents(): Int { var res = 0 for (conn in nonFederatedConnections) { - if (!conn.isLogical) { + if (!conn.isLogical && ) { res += conn.maxNumPendingEvents } } @@ -542,6 +542,11 @@ class UcConnectionGenerator( return res } + fun getNumEvents(node: UcSchedulingNode): Int { + + } + + fun generateNetworkChannelIncludes(): String = federatedConnectionBundles .distinctBy { it.networkChannel.type } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index 65951be6b..63faaff8b 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt @@ -27,7 +27,7 @@ class UcFederateGenerator( init { if (!connections.areFederatesFullyConnected()) { - messageReporter.nowhere().error("Federates are not fully connected!") + messageReporter.nowhere().error("The federation must make up a fully connected graph!") } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index ce9bb30f9..12afa09b9 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -8,6 +8,7 @@ import org.eclipse.xtext.xbase.lib.IteratorExtensions import org.lflang.allInstantiations import org.lflang.allReactions import org.lflang.generator.* +import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAnEnclave import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcReactorGenerator.Companion.hasStartup import org.lflang.lf.Instantiation @@ -51,15 +52,16 @@ abstract class UcGenerator( // Compute the total number of events and reactions within an instance (and its children) // Also returns whether there is any startup event within the instance. - private fun totalNumEventsAndReactions(inst: Instantiation): Triple { + private fun totalNumEventsReactionsAndStartup(inst: Instantiation): Triple { var numEvents = 0 var numReactions = 0 var hasStartup = false + if (!inst.isAnEnclave) { val remaining = mutableListOf() remaining.addAll(inst.reactor.allInstantiations) while (remaining.isNotEmpty()) { val child = remaining.removeFirst() - val childRes = totalNumEventsAndReactions(child) + val childRes = totalNumEventsReactionsAndStartup(child) numEvents += childRes.first * child.width numReactions += childRes.second * child.width @@ -68,15 +70,16 @@ abstract class UcGenerator( numEvents += maxNumPendingEvents[inst.reactor]!! numReactions += inst.reactor.allReactions.size hasStartup = hasStartup or inst.reactor.hasStartup + } return Triple(numEvents, numReactions, hasStartup) } // Compute the total number of events and reactions for a top-level reactor. - fun totalNumEventsAndReactions(main: Reactor): Pair { + fun totalNumEventsReactionsAndStartup(main: Reactor): Pair { val res = MutablePair(maxNumPendingEvents[main]!!, main.allReactions.size) var hasStartup = main.hasStartup for (inst in main.allInstantiations) { - val childRes = totalNumEventsAndReactions(inst) + val childRes = totalNumEventsReactionsAndStartup(inst) res.left += childRes.first * inst.width res.right += childRes.second * inst.width hasStartup = hasStartup or childRes.third diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt index 3948a2b40..0f6b5c5c9 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt @@ -29,7 +29,7 @@ class UcGeneratorFederated(context: LFGeneratorContext, scopeProvider: LFGlobalS fun totalNumEventsAndReactionsFederated(federate: UcFederate): Pair { val eventsFromFederatedConnections = maxNumPendingEvents[mainDef.reactor]!! val eventsAndReactionsInFederate = - nonFederatedGenerator.totalNumEventsAndReactions(federate.inst.reactor) + nonFederatedGenerator.totalNumEventsReactionsAndStartup(federate.inst.reactor) return Pair( eventsFromFederatedConnections + eventsAndReactionsInFederate.first, eventsAndReactionsInFederate.second) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt index d09c0bfa3..fc6618785 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt @@ -10,7 +10,7 @@ class UcPlatformGeneratorNonFederated(generator: UcGenerator, override val srcGe override val targetName = fileConfig.name override fun generatePlatformFiles() { - val numEventsAndReactions = generator.totalNumEventsAndReactions(generator.mainDef.reactor) + val numEventsAndReactions = generator.totalNumEventsReactionsAndStartup(generator.mainDef.reactor) val mainGenerator = UcMainGeneratorNonFederated( mainReactor, From 0d435f4f2f1daa8b0b7be274ce4eaec5df9bf275 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 24 Apr 2025 16:52:39 +0200 Subject: [PATCH 17/29] Fix bug with enqueuing reaction multiple times --- include/reactor-uc/reaction.h | 5 ++ .../generator/uc/UcConnectionGenerator.kt | 29 ++++++--- .../generator/uc/UcFederateGenerator.kt | 2 +- .../org/lflang/generator/uc/UcGenerator.kt | 24 +++---- .../uc/UcPlatformGeneratorNonFederated.kt | 3 +- .../lflang/generator/uc/UcReactorGenerator.kt | 63 ++++++++++++++++++- src/connection.c | 4 -- src/environments/base_environment.c | 17 ++--- src/queues.c | 6 ++ src/reaction.c | 17 ++--- src/schedulers/dynamic/scheduler.c | 4 ++ test/lf/src/EnclavedConnectionIterated.lf | 3 +- test/lf/src/EnclavedSimple.lf | 2 - 13 files changed, 132 insertions(+), 47 deletions(-) diff --git a/include/reactor-uc/reaction.h b/include/reactor-uc/reaction.h index f9ea4e451..921c9e573 100644 --- a/include/reactor-uc/reaction.h +++ b/include/reactor-uc/reaction.h @@ -8,8 +8,13 @@ typedef struct Reaction Reaction; typedef struct Reactor Reactor; typedef struct Trigger Trigger; +typedef enum { + REACTION_IDLE, REACTION_ENQUEUED, REACTION_EXECUTING +} ReactionState; + struct Reaction { Reactor *parent; + ReactionState state; void (*body)(Reaction *self); void (*deadline_violation_handler)(Reaction *self); void (*stp_violation_handler)(Reaction *self); diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index eed879514..05862d39e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -525,28 +525,37 @@ class UcConnectionGenerator( "LF_DELAYED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" } - fun getMaxNumPendingEvents(): Int { + fun getNumEvents(): Int { var res = 0 for (conn in nonFederatedConnections) { - if (!conn.isLogical && ) { + if (!conn.isLogical && !conn.isEnclaved) { res += conn.maxNumPendingEvents } } - for (bundle in federatedConnectionBundles) { - for (conn in bundle.groupedConnections) { - if (conn.destFed == currentFederate) { - res += conn.maxNumPendingEvents - } - } - } return res } fun getNumEvents(node: UcSchedulingNode): Int { + var res = 0 + if (node.nodeType == NodeType.FEDERATE) { + for (bundle in federatedConnectionBundles) { + for (conn in bundle.groupedConnections) { + if (conn.destFed == currentFederate) { + res += conn.maxNumPendingEvents + } + } + } + } else if (node.nodeType == NodeType.ENCLAVE) { + for (conn in nonFederatedConnections) { + if (conn.isEnclaved) { + res += conn.maxNumPendingEvents + } + } + } + return res } - fun generateNetworkChannelIncludes(): String = federatedConnectionBundles .distinctBy { it.networkChannel.type } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index 63faaff8b..bf503b79a 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt @@ -32,7 +32,7 @@ class UcFederateGenerator( } fun getMaxNumPendingEvents(): Int { - return connections.getMaxNumPendingEvents() + return connections.getNumEvents(currentFederate) } private fun generateFederateStruct() = diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index 12afa09b9..e9b80e25f 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -57,19 +57,19 @@ abstract class UcGenerator( var numReactions = 0 var hasStartup = false if (!inst.isAnEnclave) { - val remaining = mutableListOf() - remaining.addAll(inst.reactor.allInstantiations) - while (remaining.isNotEmpty()) { - val child = remaining.removeFirst() - val childRes = totalNumEventsReactionsAndStartup(child) + val remaining = mutableListOf() + remaining.addAll(inst.reactor.allInstantiations) + while (remaining.isNotEmpty()) { + val child = remaining.removeFirst() + val childRes = totalNumEventsReactionsAndStartup(child) - numEvents += childRes.first * child.width - numReactions += childRes.second * child.width - hasStartup = hasStartup or childRes.third - } - numEvents += maxNumPendingEvents[inst.reactor]!! - numReactions += inst.reactor.allReactions.size - hasStartup = hasStartup or inst.reactor.hasStartup + numEvents += childRes.first * child.width + numReactions += childRes.second * child.width + hasStartup = hasStartup or childRes.third + } + numEvents += maxNumPendingEvents[inst.reactor]!! + numReactions += inst.reactor.allReactions.size + hasStartup = hasStartup or inst.reactor.hasStartup } return Triple(numEvents, numReactions, hasStartup) } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt index fc6618785..f37f86076 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt @@ -10,7 +10,8 @@ class UcPlatformGeneratorNonFederated(generator: UcGenerator, override val srcGe override val targetName = fileConfig.name override fun generatePlatformFiles() { - val numEventsAndReactions = generator.totalNumEventsReactionsAndStartup(generator.mainDef.reactor) + val numEventsAndReactions = + generator.totalNumEventsReactionsAndStartup(generator.mainDef.reactor) val mainGenerator = UcMainGeneratorNonFederated( mainReactor, diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt index 04eaa5dc4..8660c4a86 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt @@ -82,10 +82,69 @@ class UcReactorGenerator( it.code.toText() } + // Given the reactor definition of an enclave. Find the number of events within it. + private fun getNumEventsInEnclave(enclave: Reactor): Int { + var ret = 0 + var hasStartup = enclave.hasStartup + fun getNumEventsInner(r: Reactor): Pair { + var ret = 0 + var hasStartup = r.hasStartup + for (inst in r.allInstantiations) { + val res = getNumEventsInner(inst.reactor) + ret += res.first + hasStartup = hasStartup || res.second + } + ret += r.allTimers.size + ret += r.allActions.map { it.maxNumPendingEvents }.sum() + val connections = UcConnectionGenerator(r, null, enclaves) + ret += connections.getNumEvents() + return Pair(ret, hasStartup) + } + // Get number of events in all children and childrens children and so on. + for (inst in enclave.allInstantiations) { + val res = getNumEventsInner(inst.reactor) + ret += res.first + hasStartup = res.second || hasStartup + } + + if (hasStartup) ret += 1 + + // Get worst-case number of events due to enclaved connections. Need to check all enclave + // instantiations + val enclaveInsts = mutableListOf() + for (inst in reactor.allInstantiations) { + if (inst.reactor == enclave) { + for (i in 0 until inst.width) { + enclaveInsts.add(UcEnclave(inst, i)) + } + } + } + ret += enclaveInsts.map { connections.getNumEvents(it) }.maxOrNull() ?: 0 + return ret + } + + // Get the numer of reactions + private fun getNumReactionsInEnclave(enclave: Reactor): Int { + var ret = 0 + fun getNumReactionsInner(r: Reactor): Int { + var ret = 0 + for (inst in r.allInstantiations) { + ret += getNumReactionsInner(inst.reactor) + } + ret += r.allReactions.size + return ret + } + for (inst in enclave.allInstantiations) { + ret += getNumReactionsInner(inst.reactor) + } + ret += enclave.allReactions.size + return ret + } + fun generateEnclaveStructDeclaration() = enclaveReactorDefs.joinToString( prefix = "// Enclave structs \n", separator = "\n", postfix = "\n") { - "LF_DEFINE_ENCLAVE_ENVIRONMENT_STRUCT(${it.codeType}, 32, 32);" // FIXME: How to get + "LF_DEFINE_ENCLAVE_ENVIRONMENT_STRUCT(${it.codeType}, ${getNumEventsInEnclave(it)}, ${getNumReactionsInEnclave(it)});" // FIXME: How to get // numEvents and // numReactions into here. } @@ -163,7 +222,7 @@ class UcReactorGenerator( for (action in reactor.allActions) { numEvents += action.maxNumPendingEvents } - numEvents += connections.getMaxNumPendingEvents() + numEvents += connections.getNumEvents() return numEvents } diff --git a/src/connection.c b/src/connection.c index 07c92c820..9877a2627 100644 --- a/src/connection.c +++ b/src/connection.c @@ -175,11 +175,7 @@ void DelayedConnection_ctor(DelayedConnection *self, Reactor *parent, Port **dow void EnclavedConnection_prepare(Trigger *trigger, Event *event) { LF_DEBUG(CONN, "Preparing enclave connection %p for triggering", trigger); EnclavedConnection *self = (EnclavedConnection *)trigger; - Environment *env = trigger->parent->env; - Scheduler *sched = env->scheduler; EventPayloadPool *pool = trigger->payload_pool; - trigger->is_present = true; - sched->register_for_cleanup(sched, trigger); assert(self->super.downstreams_size == 1); Port *down = self->super.downstreams[0]; diff --git a/src/environments/base_environment.c b/src/environments/base_environment.c index fefaa3743..267e7f975 100644 --- a/src/environments/base_environment.c +++ b/src/environments/base_environment.c @@ -16,18 +16,13 @@ static void Environment_assemble(Environment *self) { Environment_validate(self); } -bool Environment_start_enclave_environments(Reactor *reactor, instant_t start_time) { - static int idx = 0; - bool ret = false; +void Environment_start_enclave_environments(Reactor *reactor, instant_t start_time) { if (reactor->env->type == ENVIRONMENT_ENCLAVE) { - reactor->env->id = idx++; reactor->env->start_at(reactor->env, start_time); - ret = true; } for (size_t i = 0; i < reactor->children_size; i++) { - ret = Environment_start_enclave_environments(reactor->children[i], start_time) || ret; + Environment_start_enclave_environments(reactor->children[i], start_time); } - return ret; } static void Environment_join_enclave_environments(Reactor *reactor) { @@ -42,6 +37,14 @@ static void Environment_join_enclave_environments(Reactor *reactor) { static void Environment_start_at(Environment *self, instant_t start_time) { LF_INFO(ENV, "Starting program at " PRINTF_TIME " nsec", start_time); + int idx = 1; + for (size_t i = 0; i < self->main->children_size; i++) { + Reactor *reactor = self->main->children[i]; + if (reactor->env->type == ENVIRONMENT_ENCLAVE) { + reactor->env->id = idx++; + } + } + Environment_start_enclave_environments(self->main, start_time); self->scheduler->set_and_schedule_start_tag(self->scheduler, start_time); self->scheduler->run(self->scheduler); diff --git a/src/queues.c b/src/queues.c index 6a00e734a..b1f15c7a4 100644 --- a/src/queues.c +++ b/src/queues.c @@ -128,6 +128,11 @@ void EventQueue_ctor(EventQueue *self, ArbitraryEvent *array, size_t capacity) { } static lf_ret_t ReactionQueue_insert(ReactionQueue *self, Reaction *reaction) { + + if (reaction->state == REACTION_ENQUEUED) { + return LF_OK; + } + validate(reaction); validate(reaction->level < (int)self->capacity); validate(reaction->level >= 0); @@ -144,6 +149,7 @@ static lf_ret_t ReactionQueue_insert(ReactionQueue *self, Reaction *reaction) { if (reaction->level > self->max_active_level) { self->max_active_level = reaction->level; } + reaction->state = REACTION_ENQUEUED; return LF_OK; } diff --git a/src/reaction.c b/src/reaction.c index 088be5131..36ae18931 100644 --- a/src/reaction.c +++ b/src/reaction.c @@ -13,13 +13,15 @@ size_t Reaction_get_level(Reaction *self) { int calculate_port_level(Port *port) { int current = -1; if (port->conn_in) { - Port *final_upstream_port = port->conn_in->get_final_upstream(port->conn_in); - if (final_upstream_port) { - for (size_t k = 0; k < final_upstream_port->sources.size; k++) { - Reaction *upstream = final_upstream_port->sources.reactions[k]; - int upstream_level = upstream->get_level(upstream); - if (upstream_level > current) { - current = upstream_level; + if (port->conn_in->super.type == TRIG_CONN) { + Port *final_upstream_port = port->conn_in->get_final_upstream(port->conn_in); + if (final_upstream_port) { + for (size_t k = 0; k < final_upstream_port->sources.size; k++) { + Reaction *upstream = final_upstream_port->sources.reactions[k]; + int upstream_level = upstream->get_level(upstream); + if (upstream_level > current) { + current = upstream_level; + } } } } @@ -114,4 +116,5 @@ void Reaction_ctor(Reaction *self, Reactor *parent, void (*body)(Reaction *self) self->deadline_violation_handler = deadline_violation_handler; self->deadline = deadline; self->stp_violation_handler = stp_violation_handler; + self->state = REACTION_IDLE; } diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index 23d5d1621..100d6ed1f 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -201,7 +201,9 @@ void Scheduler_run_timestep(Scheduler *untyped_self) { } LF_DEBUG(SCHED, "Executing %s->reaction_%d", reaction->parent->name, reaction->index); + reaction->state = REACTION_EXECUTING; reaction->body(reaction); + reaction->state = REACTION_IDLE; } } @@ -414,6 +416,7 @@ lf_ret_t Scheduler_schedule_at(Scheduler *super, Event *event) { } } + printf("%d insert event queue\n", self->env->id); ret = self->event_queue->insert(self->event_queue, (AbstractEvent *)event); validate(ret == LF_OK); @@ -476,6 +479,7 @@ static void Scheduler_step_clock(Scheduler *_self, interval_t step) { lf_ret_t Scheduler_add_to_reaction_queue(Scheduler *untyped_self, Reaction *reaction) { DynamicScheduler *self = (DynamicScheduler *)untyped_self; + printf("%d insert reaction\n", self->env->id); return self->reaction_queue->insert(self->reaction_queue, reaction); } diff --git a/test/lf/src/EnclavedConnectionIterated.lf b/test/lf/src/EnclavedConnectionIterated.lf index 7afcfae07..598bc62f3 100644 --- a/test/lf/src/EnclavedConnectionIterated.lf +++ b/test/lf/src/EnclavedConnectionIterated.lf @@ -30,11 +30,12 @@ reactor Dest(bank_idx:int=0) { reactor Dest2 { input[2] in:int reaction(in) {= + printf("Dest2 triggered\n"); validate(in[0]->value == 1); validate(in[1]->value == 2); env->request_shutdown(env); - =} + =} maxwait(forever) } reactor Dest3 { diff --git a/test/lf/src/EnclavedSimple.lf b/test/lf/src/EnclavedSimple.lf index 08b2c675c..e03279946 100644 --- a/test/lf/src/EnclavedSimple.lf +++ b/test/lf/src/EnclavedSimple.lf @@ -2,7 +2,6 @@ target uC { platform: Native } - reactor R(id: int = 0){ state test: bool = false reaction(startup) {= @@ -16,7 +15,6 @@ reactor R(id: int = 0){ =} } - main enclaved reactor { e1 = new R(id=1) e2 = new R(id=2) From 2c26b1aeea679758d7d24335d49f8c1b406fd264 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 24 Apr 2025 16:57:20 +0200 Subject: [PATCH 18/29] Formatting --- include/reactor-uc/reaction.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/reactor-uc/reaction.h b/include/reactor-uc/reaction.h index 921c9e573..8150a181a 100644 --- a/include/reactor-uc/reaction.h +++ b/include/reactor-uc/reaction.h @@ -8,9 +8,7 @@ typedef struct Reaction Reaction; typedef struct Reactor Reactor; typedef struct Trigger Trigger; -typedef enum { - REACTION_IDLE, REACTION_ENQUEUED, REACTION_EXECUTING -} ReactionState; +typedef enum { REACTION_IDLE, REACTION_ENQUEUED, REACTION_EXECUTING } ReactionState; struct Reaction { Reactor *parent; From 05b98fe5614f72f86af01f9ffd4ef34ccbe23cdd Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 24 Apr 2025 18:48:03 +0200 Subject: [PATCH 19/29] Fix reaction_queue_test --- include/reactor-uc/reaction.h | 6 ++---- src/queues.c | 11 ++++++----- src/reaction.c | 2 +- test/unit/reaction_queue_test.c | 2 ++ 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/include/reactor-uc/reaction.h b/include/reactor-uc/reaction.h index 8150a181a..d0e2dd8a5 100644 --- a/include/reactor-uc/reaction.h +++ b/include/reactor-uc/reaction.h @@ -8,16 +8,14 @@ typedef struct Reaction Reaction; typedef struct Reactor Reactor; typedef struct Trigger Trigger; -typedef enum { REACTION_IDLE, REACTION_ENQUEUED, REACTION_EXECUTING } ReactionState; - struct Reaction { Reactor *parent; - ReactionState state; void (*body)(Reaction *self); void (*deadline_violation_handler)(Reaction *self); void (*stp_violation_handler)(Reaction *self); interval_t deadline; - int level; // Negative level means it is invalid. + int level; // Negative level means it is invalid. + bool enqueued; // Whether the reaction currently is enqueued. size_t index; Trigger **effects; size_t effects_size; diff --git a/src/queues.c b/src/queues.c index b1f15c7a4..bad27f2b0 100644 --- a/src/queues.c +++ b/src/queues.c @@ -129,11 +129,9 @@ void EventQueue_ctor(EventQueue *self, ArbitraryEvent *array, size_t capacity) { static lf_ret_t ReactionQueue_insert(ReactionQueue *self, Reaction *reaction) { - if (reaction->state == REACTION_ENQUEUED) { - return LF_OK; - } - validate(reaction); + if (reaction->enqueued) + return LF_OK; validate(reaction->level < (int)self->capacity); validate(reaction->level >= 0); validate(self->level_size[reaction->level] < (int)self->capacity); @@ -149,7 +147,7 @@ static lf_ret_t ReactionQueue_insert(ReactionQueue *self, Reaction *reaction) { if (reaction->level > self->max_active_level) { self->max_active_level = reaction->level; } - reaction->state = REACTION_ENQUEUED; + reaction->enqueued = true; return LF_OK; } @@ -166,6 +164,9 @@ static Reaction *ReactionQueue_pop(ReactionQueue *self) { } else { ret = NULL; } + + if (ret) + ret->enqueued = false; return ret; } diff --git a/src/reaction.c b/src/reaction.c index 36ae18931..db1dafda8 100644 --- a/src/reaction.c +++ b/src/reaction.c @@ -116,5 +116,5 @@ void Reaction_ctor(Reaction *self, Reactor *parent, void (*body)(Reaction *self) self->deadline_violation_handler = deadline_violation_handler; self->deadline = deadline; self->stp_violation_handler = stp_violation_handler; - self->state = REACTION_IDLE; + self->enqueued = true; } diff --git a/test/unit/reaction_queue_test.c b/test/unit/reaction_queue_test.c index 00123d471..874f6387e 100644 --- a/test/unit/reaction_queue_test.c +++ b/test/unit/reaction_queue_test.c @@ -10,6 +10,7 @@ void test_insert(void) { ReactionQueue_ctor(&q, (Reaction **)array, level_size, REACTION_QUEUE_SIZE); for (size_t i = 0; i < REACTION_QUEUE_SIZE; i++) { + rs[i].enqueued = false; for (size_t j = 0; j < REACTION_QUEUE_SIZE; j++) { TEST_ASSERT_NULL(array[i][j]); } @@ -37,6 +38,7 @@ void test_levels_with_gaps(void) { Reaction rs[REACTION_QUEUE_SIZE]; ReactionQueue_ctor(&q, (Reaction **)array, level_size, REACTION_QUEUE_SIZE); for (int i = 0; i < REACTION_QUEUE_SIZE; i++) { + rs[i].enqueued = false; if (i < REACTION_QUEUE_SIZE / 2) { rs[i].level = 1; } else { From aec8edf11c22e101a2b2af3b50a99405c842d2c1 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 24 Apr 2025 20:16:52 +0200 Subject: [PATCH 20/29] Minor fix --- src/schedulers/dynamic/scheduler.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index 100d6ed1f..c344e3e2e 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -201,9 +201,7 @@ void Scheduler_run_timestep(Scheduler *untyped_self) { } LF_DEBUG(SCHED, "Executing %s->reaction_%d", reaction->parent->name, reaction->index); - reaction->state = REACTION_EXECUTING; reaction->body(reaction); - reaction->state = REACTION_IDLE; } } From 1fc7427c8abd8cd7508f3c9694158cbd98f14adf Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 24 Apr 2025 20:30:29 +0200 Subject: [PATCH 21/29] Fix typo in reaction ctor --- src/queues.c | 9 +++++++-- src/reaction.c | 2 +- src/schedulers/dynamic/scheduler.c | 2 -- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/queues.c b/src/queues.c index bad27f2b0..e9e13481b 100644 --- a/src/queues.c +++ b/src/queues.c @@ -130,8 +130,11 @@ void EventQueue_ctor(EventQueue *self, ArbitraryEvent *array, size_t capacity) { static lf_ret_t ReactionQueue_insert(ReactionQueue *self, Reaction *reaction) { validate(reaction); - if (reaction->enqueued) + + if (reaction->enqueued) { return LF_OK; + } + validate(reaction->level < (int)self->capacity); validate(reaction->level >= 0); validate(self->level_size[reaction->level] < (int)self->capacity); @@ -165,8 +168,10 @@ static Reaction *ReactionQueue_pop(ReactionQueue *self) { ret = NULL; } - if (ret) + if (ret) { ret->enqueued = false; + } + return ret; } diff --git a/src/reaction.c b/src/reaction.c index db1dafda8..518b328aa 100644 --- a/src/reaction.c +++ b/src/reaction.c @@ -116,5 +116,5 @@ void Reaction_ctor(Reaction *self, Reactor *parent, void (*body)(Reaction *self) self->deadline_violation_handler = deadline_violation_handler; self->deadline = deadline; self->stp_violation_handler = stp_violation_handler; - self->enqueued = true; + self->enqueued = false; } diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index c344e3e2e..23d5d1621 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -414,7 +414,6 @@ lf_ret_t Scheduler_schedule_at(Scheduler *super, Event *event) { } } - printf("%d insert event queue\n", self->env->id); ret = self->event_queue->insert(self->event_queue, (AbstractEvent *)event); validate(ret == LF_OK); @@ -477,7 +476,6 @@ static void Scheduler_step_clock(Scheduler *_self, interval_t step) { lf_ret_t Scheduler_add_to_reaction_queue(Scheduler *untyped_self, Reaction *reaction) { DynamicScheduler *self = (DynamicScheduler *)untyped_self; - printf("%d insert reaction\n", self->env->id); return self->reaction_queue->insert(self->reaction_queue, reaction); } From 1927feb89bce4e82bf7c0c2aa2210f790b154f4e Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 24 Apr 2025 21:10:12 +0200 Subject: [PATCH 22/29] Start on docs --- doc/markdown/5_federated.md | 17 +++++++ doc/markdown/6_enclaved.md | 44 +++++++++++++++++++ examples/fed-template/buildAll.sh | 2 +- examples/zephyr/hello_lf/run/build.sh | 2 +- .../generator/uc/UcConnectionGenerator.kt | 4 +- .../lflang/generator/uc/UcReactorGenerator.kt | 25 ++++++----- test/lf/Makefile | 8 ++-- 7 files changed, 82 insertions(+), 20 deletions(-) create mode 100644 doc/markdown/5_federated.md create mode 100644 doc/markdown/6_enclaved.md diff --git a/doc/markdown/5_federated.md b/doc/markdown/5_federated.md new file mode 100644 index 000000000..877ea50ca --- /dev/null +++ b/doc/markdown/5_federated.md @@ -0,0 +1,17 @@ +\page federated Federated Execution + +reactor-uc supports federated (distributed) execution with decentralized, peer-to-peer coordination based on the PTIDES +principle. This opens up a vast design space within distributed real-time systems. + +To make a program distributed, the main reactor is marked as `federated`. This will turn each of the top-level reactors +into a federate. + +``` +reactor Src { + +} +``` + + + + diff --git a/doc/markdown/6_enclaved.md b/doc/markdown/6_enclaved.md new file mode 100644 index 000000000..fbf15c067 --- /dev/null +++ b/doc/markdown/6_enclaved.md @@ -0,0 +1,44 @@ +\page enclaved Enclaved Execution + +An enclave is a reactor that has its own scheduler and event queue and thus executes independently from other +reactors in the program. This is equivalent to doing federated execution, but having all the federates +co-exist within the same process and communicating directly through shared memory. + +Enclaves are made by denoting a reactor definition as `enclaved`. This has the effect that all reactors within it +turns into enclaves. This is similar to how federates are created by denoting the main reactor as `federated`. +An enclaved reactor is thus a reactor where all the child reactors are enclaves. An enclaved reactor should not +have any triggers or reactions, only reactor instantiations. + +Consider the following program that uses enclaves. +``` +reactor Slow { + timer t(0, 1 sec) + reaction(t) {= + env->wait_for(MSEC(500)); + =} +} + +reactor Fast { + timer t(0, 100 msec) + reaction(t) {= + printf("Hello from Fast!\n"); + =} deadline (100 msec) {= + printf("ERROR: Deadline miss"); + =} +} + +main enclaved reactor { + slow = new Slow() + fast = new Fast() +} + +``` + +The main reactor is marked as enclaved, meaning that the two child reactors, `slow` and `fast` are enclaves. This allows them +to execute completely independently. If they were not enclaves, but instead executing under the same scheduler, `fast` +would experience deadline misses every time `slow` is triggered and blocks execution for 500 msec. + + + + + diff --git a/examples/fed-template/buildAll.sh b/examples/fed-template/buildAll.sh index bfe78ea93..acfbcd7b8 100755 --- a/examples/fed-template/buildAll.sh +++ b/examples/fed-template/buildAll.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -e -${REACTOR_UC_PATH}/lfc/bin/lfc-dev --gen-fed-templates src/MyFed.lf +${REACTOR_UC_PATH}/lfc/bin/lfc-dev --runtime-symlink --gen-fed-templates src/MyFed.lf pushd MyFed/src diff --git a/examples/zephyr/hello_lf/run/build.sh b/examples/zephyr/hello_lf/run/build.sh index 6dd50c87b..1cb8d6ac4 100755 --- a/examples/zephyr/hello_lf/run/build.sh +++ b/examples/zephyr/hello_lf/run/build.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash -${REACTOR_UC_PATH}/lfc/bin/lfc-dev src/HelloLF.lf +${REACTOR_UC_PATH}/lfc/bin/lfc-dev -c --runtime-symlink src/HelloLF.lf west build -b qemu_cortex_m3 -p always \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 05862d39e..763e8554a 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -548,8 +548,8 @@ class UcConnectionGenerator( } } else if (node.nodeType == NodeType.ENCLAVE) { for (conn in nonFederatedConnections) { - if (conn.isEnclaved) { - res += conn.maxNumPendingEvents + if (conn.isEnclaved && conn.channels.first().dest.node == node) { + res += conn.maxNumPendingEvents * conn.channels.size } } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt index 8660c4a86..f05077eff 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt @@ -84,8 +84,7 @@ class UcReactorGenerator( // Given the reactor definition of an enclave. Find the number of events within it. private fun getNumEventsInEnclave(enclave: Reactor): Int { - var ret = 0 - var hasStartup = enclave.hasStartup + var numEvents = 0 fun getNumEventsInner(r: Reactor): Pair { var ret = 0 var hasStartup = r.hasStartup @@ -100,14 +99,9 @@ class UcReactorGenerator( ret += connections.getNumEvents() return Pair(ret, hasStartup) } - // Get number of events in all children and childrens children and so on. - for (inst in enclave.allInstantiations) { - val res = getNumEventsInner(inst.reactor) - ret += res.first - hasStartup = res.second || hasStartup - } - - if (hasStartup) ret += 1 + val ret = getNumEventsInner(enclave) + numEvents += ret.first + if (ret.second) numEvents += 1 // Get worst-case number of events due to enclaved connections. Need to check all enclave // instantiations @@ -119,8 +113,15 @@ class UcReactorGenerator( } } } - ret += enclaveInsts.map { connections.getNumEvents(it) }.maxOrNull() ?: 0 - return ret + var maxConnEvents = 0 + for (enclave in enclaveInsts) { + val connEvent = connections.getNumEvents(enclave) + if (connEvent > maxConnEvents) { + maxConnEvents = connEvent + } + } + numEvents += maxConnEvents + return numEvents } // Get the numer of reactions diff --git a/test/lf/Makefile b/test/lf/Makefile index 5772cf5c8..08c6292ce 100644 --- a/test/lf/Makefile +++ b/test/lf/Makefile @@ -10,7 +10,7 @@ SRCS_LEGACY = $(wildcard src/legacy/*.lf) BINS_LEGACY = $(patsubst src/legacy/%.lf, bin/%, $(SRCS_LEGACY)) LFC_PATH=../../lfc -LFC = ${LFC_PATH}/build/install/lf-cli/bin/lfc +LFC = ${LFC_PATH}/build/install/lf-cli/bin/lfc -c --runtime-symlink .PHONY: all clean legacy all: build_lfc ${BINS} legacy @@ -19,14 +19,14 @@ build_lfc: ${LFC_PATH}/bin/lfc-dev --version bin/%: src/%.lf - ${LFC} $^ -c + ${LFC} $^ timeout ${TIMEOUT_S}s ./$@ bin/%: src/only_build/%.lf - ${LFC} $^ -c + ${LFC} $^ bin/%: src/legacy/%.lf - ${LFC} $^ -c + ${LFC} $^ timeout ${TIMEOUT_S}s ./$@ legacy: ${BINS_LEGACY} From 00ac63a8fc06368fd7e5a63165ce37e6b11f016b Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 25 Apr 2025 09:17:24 +0200 Subject: [PATCH 23/29] Improve docs --- doc/markdown/3_philosophy.md | 26 ++-- doc/markdown/5_federated.md | 247 +++++++++++++++++++++++++++++++++++ 2 files changed, 255 insertions(+), 18 deletions(-) diff --git a/doc/markdown/3_philosophy.md b/doc/markdown/3_philosophy.md index 6c8e7ef68..9fef44ddb 100644 --- a/doc/markdown/3_philosophy.md +++ b/doc/markdown/3_philosophy.md @@ -28,25 +28,15 @@ build-system. For common platforms like [Zephyr](https://zephyrproject.org/), [pico-sdk](https://www.raspberrypi.com/documentation/pico-sdk/) we provide build-templates. +If the target platform is set to native using the following target property: -## Federation Design -We call the network abstraction in reactor-uc `NetworkChannels`, which are bidirectional -message pipes between two federates. Depending on which platform you are working on, -reactor-uc supports different NetworkChannels, such as: `TcpIpChannel`, `UARTChannel` or -`CoapUdpChannel`. You can find a complete table with all network channels that are -supported on the individual platforms [here](TODO). - -```lf -federated reactor { - @interface_tcp(name="if1", address="127.0.0.1") - src = new Src() - - @interface_tcp(name="if1", address="127.0.0.1") - dst = new Dst() - - @link(left="if1", right="if1", server_side="right", server_port=1042) - src.out -> dst.in +``` +target uC { + platform: Native } ``` - +Then, lfc will invoke the CMake build tool on the host system to produce an executable +for the program. If the platform is speified to any other platform, or just left +unspecified, `lfc` will only do code-generation and leave the final compilation of the +program to the user. \ No newline at end of file diff --git a/doc/markdown/5_federated.md b/doc/markdown/5_federated.md index 877ea50ca..ae94c57cb 100644 --- a/doc/markdown/5_federated.md +++ b/doc/markdown/5_federated.md @@ -6,12 +6,259 @@ principle. This opens up a vast design space within distributed real-time system To make a program distributed, the main reactor is marked as `federated`. This will turn each of the top-level reactors into a federate. +## Coordination +Consider the following, simple, federated program. + ``` reactor Src { + output out: int + + timer t(0, 100 msec) + state cnt: int = 0 + + reaction(t) -> out {= + lf_set(out, self->cnt++); + =} +} + +reactor Sink { + input in: int + reaction(in) { + printf("Got %d\n", in->value); + } +} + +federated reactor { + src = new Src() + sink = new Sink() +} +``` + +By changing `main reactor` into `federated reactor`, we create a federated program. Each reactor in the top-level +is a federate. There can not be any reactions or triggers in the top-level, only reactors and connections. Currently, +the federates only support decentralized coordination using PTIDES. Each federate will handle its own local events at +its own speed and exchange events with its neighbors. This creates the risk of out-of-order processing of events. + +E.g. consider the following change to `Sink` + +``` +reactor Sink { + input in: int + + timer t(0, 100 msec) + reaction(in) { + printf("Got %d\n", in->value); + } + reaction(t) {= + printf("Timer!\n"); + =} +} +``` + +This reactor specifies that if `t` and `in` are present at the same tag, the reaction to +`in` should be handled first. With any modifications, this program will likely lead to +STP violations, because the reaction to `t` will likely be handled immediately, and then later +the event on `in` will arrive. + +There are two strategies to avoid STP violations. + +### LET-based coordination +First, we can use the LET principle and introduce a logical delay +on the connection between `src` and `sink`. This will modify the tag of the events sent between the federates and +thus the requirement to ordering. This is done with the `after` keywords as follows: + +``` +src.out -> sink.in after 100 msec +``` + +If the delay on the connection exceeds the total latency from the `t` trigger at `src` +to the `in` port of `sink`, then an STP violation cannot occur. However, it is important +to note that logical delays change the semantic of the program. + +### maxwait-based coordination +The other strategy is to associate a `maxwait` with the reaction to the input port `in` as follows: + +``` +reaction(in) { + printf("Got %d\n", in->value); +} maxwait(100 msec) {= + printf("STP violation"); +=} +``` + +Here the `maxwait` of a 100 msec specifies that the scheduler should wait up to a 100 +msec before assuming that any associated federated input port is absent at a given tag. +If the state of an input port is known, because a message has arrived with the same or +later tag, the scheduler does not have to wait. This waiting happens before the +scheduler starts handling a particular tag. + +It is important to note that the scheduler will wait for the `maxwait` duration +when handling any tag from any trigger, except the shutdown trigger. So in `Sink`, +every time a trigger from `t` is handled, the scheduler first waits for `maxwait`. +This works well in our current program because `t` in `in` are always logically +simultaneous. + +You can also set the `maxwait` to `forever`, in which case a tag will never be +handled until all input ports are known at the tag or later. The default value of +`maxwait` is 0, meaning that the federate does not wait at all. + +You can pass an optional STP handler after specifying the `maxwait`. This will be +invoked if the reaction is triggered at the wrong tag, due to an STP violation. The +intended tag can be inspected by looking at `in->intended_tag`. If no STP handler is +provided, the reaction body will be executed. + +### Federate cycles +reactor-uc does not yet support zero-delay cycles between federates. Such programs will +always yield STP violations. Once a tag has been committed to, any message later +received with this tag, will be handled later, and lead to an STP violation. + +`maxwait`s should be used with care in cycles. Consider the following program which +will deadlock. + +``` +reactor Producer { + output out: int + input in: int + + timer t(0, 100 msec) + state cnt: int = 0 + + reaction(t) -> out {= + lf_set(out, self->cnt++); + =} + reaction(in) {= + printf("Got %d back\n", in->value); + =} maxwait(forever) +} + +reactor Passthrough{ + input in: int + output out: int + reaction(in) -> out { + lf_set(out, in->value);s + } +} + +federated reactor { + prod = new Producer() + pt = new Passthrough() + prod.out -> pt.in + pt.out -> prod.in } ``` +Here, we immediately get a deadlock because `Producer` can not handle the `t` trigger +until it has resolved its input port `in`. However, `in` will not be resolved until we +have handled `t` and produced an output. Introducing a delay on the connection does not +help. Using a physical connection would solve the problem as it means that you always +know the state of an input port at the current physical time. +## Network channels +reactor-uc supports inter-federate communication over various protocols, known as network channels. Federates can be annotated with a set of network interfaces that they have, and + +Which network +channel to use is configured using attributes. By default, a TCP connection is used and all federates are assumed to +run on the same host. Through annotations, we can add several network channel interfaces to each federate. Consider +the following program where both federates has a TCP and a COAP interface, +but we use the TCP interface for the connection. + +``` +federated reactor { + @interface_tcp(name="if1", address="127.0.0.1") + @interface_coap(name="if2", address="127.0.0.1") + src = new Src() + + @interface_tcp(name="if1", address="127.0.0.1") + @interface_coap(name="if2", address="127.0.0.1") + sink = new Sink() + + @link(left="if1", right="if1", server_side="right", server_port=1042) + r1.out -> r2.in +} + +``` + +It is also possible to provide your own, custom network channel implementation as follows: + +``` +federated reactor { + @interface_custom(name="MyInterface", include="my_interface.h", args="1") + source = new Src() + + @interface_custom(name="MyInterface", include="my_interface.h", args="2") + sink = new Sink() + + r1.out -> r2.in +} +``` + +Here you must provide a file `my_interface.h` which is on the include path for the +compiler, also you must add sources files that defines a `MyInterface` struct which +should inherit from `NetworkChannel` and also has a function `void + +MyInterface_ctor(MyInterface *self, int arg)`. The additional arguments must match what +is passed as a string to the `args` annotation. You must provide a complete +implementation of the `NetworkChannel` interface defined in `network_channel.h`. + + +## Platform and Network Channel Support Matrix + +| **Platform** | **TCP** | **UART** | **CoAP** | **Custom** | +|---------------------|---------|---------|----------|----------| +| **Native (POSIX)** | ✅ | ❌ | ❌ | ✅ | +| **Zephyr** | ✅ | ❌ | ❌ | ✅ | +| **RIOT** | ✅ | ❌ | ✅ | ✅ | +| **PICO** | ❌ | ✅ | ❌ | ✅ | + + + +## Clock synchronization +reactor-uc includes clock-synchronization that is enabled by default. Unless specified otherwise, the first federate assumes the role as the clock-sync grandmaster. +We can disable clock-sync by setting the target property +``` +target uC { + clock-sync: off +} +``` + +Clock-sync can be configured using federate annotations as follows: + +``` +federated reactor { + @clock_sync(grandmaster=true, disabled=false) + src = new Src() + @clock_sync(grandmaster=false, disabled=false, period=1000000000, max_adj=512000, kp=0.5, ki=0.1) + sink = new Sink() +} +``` +All the arguments are optional, and `disabled=false` is redundant but shown for completeness. +The `period`, `max_adj`, `kp`, and `ki` arguments are only used by clock sync slaves and denote +the period between clock sync rounds, the maximum parts-per-billion adjustment applied to the clock frequency, as well as the PI-controller constant. The clock-sync algorithm is inspired by PTP. + +## Coordination of the startup tag +The first thing that happens when a federate is started is that it establishes a connection to its neighboring federates. Once it has connected to all its federates, the startup tag negotiation starts. Only clock-sync grandmasters are allowed to propose a start tag, and this start tag is distributed using gossip. If there are multiple grandmasters, the maximum of their proposed start tag is used. + +In order for this to work in all scenarios, we require all network channels to be +bidirectional, meaning that messages can be sent in either direction. Further we require +that the federation is fully-connected, which means that there exists a path between any +two federates by following the bidirectional connections. + +## Heterogeneous Federations +reactor-uc works Heterogeneous Federations which is federations consisting of different platforms. E.g. one federate running Linux and another running Zephyr. This is achieved by annotating the target +platform for each federate. + +``` +federated reactor { + @platform_native + src = new Src() + + @platform_zephyr + dest = new Sink() + + src.out -> dest.in +} +``` +To compile the federates we compile this program as follows: `lfc --gen-fed-templates src/MyFederation.lf`. Assuming the program is called `MyFederation.lf` and is located in a `src` directory relative the current working directory. This will produce a template project for each of the federates under `MyFederation/src` and `MyFederation/sink`. A `run_lfc.sh` script is produced into each template. This script runs code-generation using `lfc` and puts the sources into `src-gen`. The generated sources can be compiled using `west build` for Zephyr and `cmake -BBuild . && cmake --build build` for the native execution. From 371d52104df127050a1a2a2bc681165f511d7e33 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 25 Apr 2025 09:17:35 +0200 Subject: [PATCH 24/29] Fix mistake in UcCustomChannel --- .../main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt index ec6db3c73..bd1172b27 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt @@ -372,9 +372,9 @@ class UcCustomChannel( private val srcArgs = if (srcIface.args != null) ", ${srcIface.args}" else "" private val destArgs = if (destIface.args != null) ", ${destIface.args}" else "" - override fun generateChannelCtorSrc() = "${srcIface.name}_ctor(&self->channel, ${srcArgs});" + override fun generateChannelCtorSrc() = "${srcIface.name}_ctor(&self->channel ${srcArgs});" - override fun generateChannelCtorDest() = "${destIface.name}_ctor(&self->channel, ${destArgs});" + override fun generateChannelCtorDest() = "${destIface.name}_ctor(&self->channel ${destArgs});" override val codeType: String get() = srcIface.name From 5992f376e6817a41e930888355d68faf92e6327a Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 25 Apr 2025 12:27:26 +0200 Subject: [PATCH 25/29] Some refactoring --- include/reactor-uc/connection.h | 4 +- include/reactor-uc/environment.h | 15 ++++-- include/reactor-uc/macros_internal.h | 36 +++++++------ include/reactor-uc/platform.h | 6 +++ include/reactor-uc/scheduler.h | 5 ++ .../generator/uc/UcInstanceGenerator.kt | 13 ++--- .../lflang/generator/uc/UcReactorGenerator.kt | 49 +++++++---------- src/connection.c | 24 ++++----- src/environments/base_environment.c | 8 --- src/environments/enclave_environment.c | 2 +- src/federated.c | 4 +- src/schedulers/dynamic/scheduler.c | 39 +++++++++----- test/lf/src/EnclavedNested.lf | 52 +++++++++++++++++++ 13 files changed, 159 insertions(+), 98 deletions(-) create mode 100644 test/lf/src/EnclavedNested.lf diff --git a/include/reactor-uc/connection.h b/include/reactor-uc/connection.h index 77b78a260..3a3f80a45 100644 --- a/include/reactor-uc/connection.h +++ b/include/reactor-uc/connection.h @@ -58,8 +58,8 @@ struct EnclavedConnection { ConnectionType type; EventPayloadPool payload_pool; void *staged_payload_ptr; - MUTEX_T mutex; - tag_t last_known_tag; + MUTEX_T mutex; // Used to protect the _last_known_tag variable. + tag_t _last_known_tag; void (*set_last_known_tag)(EnclavedConnection *self, tag_t tag); tag_t (*get_last_known_tag)(EnclavedConnection *self); }; diff --git a/include/reactor-uc/environment.h b/include/reactor-uc/environment.h index a09d8890b..791291c66 100644 --- a/include/reactor-uc/environment.h +++ b/include/reactor-uc/environment.h @@ -17,18 +17,17 @@ typedef struct Platform Platform; typedef struct Environment Environment; + +// The different types of environments, typedef enum { ENVIRONMENT_BASE, - ENVIRONMENT_ENCLAVE, // FIXME: Unused? - ENVIRONMENT_ENCLAVED, + ENVIRONMENT_ENCLAVE, ENVIRONMENT_FEDERATE, - ENVIRONMENT_ENCLAVED_FEDERATE } EnvironmentType; extern Environment *_lf_environment; // NOLINT struct Environment { - int id; EnvironmentType type; Reactor *main; // The top-level reactor of the program. Scheduler *scheduler; // The scheduler in charge of executing the reactions. @@ -52,8 +51,16 @@ struct Environment { */ void (*start)(Environment *self); + /** + * @private + * @brief Start the program at a particular tag. Used to start off enclaves within the program. + */ void (*start_at)(Environment *self, instant_t start_time); + /** + * @private + * @brief Join on the environment. Means block until it has terminated. Used to wait for enclaves. + */ void (*join)(Environment *self); /** diff --git a/include/reactor-uc/macros_internal.h b/include/reactor-uc/macros_internal.h index f7754bf56..3262a10a8 100644 --- a/include/reactor-uc/macros_internal.h +++ b/include/reactor-uc/macros_internal.h @@ -662,24 +662,24 @@ typedef struct FederatedInputConnection FederatedInputConnection; self->_children[_child_idx++] = &self->instanceName[i].super; \ } -#define LF_ENCLAVE_INSTANCE(ReactorName, instanceName, BankWidth) \ - Environment_##ReactorName instanceName##_env[BankWidth]; \ - ReactorName instanceName[BankWidth]; +#define LF_ENCLAVE_INSTANCE(ReactorName, InstanceName, ParentName, BankWidth) \ + Environment_##ParentName##_##ReactorName##_##InstanceName InstanceName##_env[BankWidth]; \ + ReactorName InstanceName[BankWidth]; -#define LF_INITIALIZE_ENCLAVE(ReactorName, instanceName, BankWidth) \ +#define LF_INITIALIZE_ENCLAVE(ReactorName, InstanceName, ParentName, BankWidth) \ for (int i = 0; i < BankWidth; i++) { \ - Environment_##ReactorName##_ctor(&self->instanceName##_env[i], &self->instanceName[i], env->scheduler->duration, \ - env->fast_mode); \ - ReactorName##_ctor(&self->instanceName[i], &self->super, &self->instanceName##_env[i].super.super); \ - self->_children[_child_idx++] = &self->instanceName[i].super; \ + Environment_##ParentName##_##ReactorName##_##InstanceName##_ctor( \ + &self->InstanceName##_env[i], &self->InstanceName[i], env->scheduler->duration, env->fast_mode); \ + ReactorName##_ctor(&self->InstanceName[i], &self->super, &self->InstanceName##_env[i].super.super); \ + self->_children[_child_idx++] = &self->InstanceName[i].super; \ } -#define LF_INITIALIZE_ENCLAVE_WITH_PARAMETERS(ReactorName, instanceName, BankWidth, ...) \ +#define LF_INITIALIZE_ENCLAVE_WITH_PARAMETERS(ReactorName, InstanceName, ParentName, BankWidth, ...) \ for (int i = 0; i < (BankWidth); i++) { \ - Environment_##ReactorName##_ctor(&self->instanceName##_env[i], &self->instanceName[i], env->scheduler->duration, \ - env->fast_mode); \ - ReactorName##_ctor(&self->instanceName[i], &self->super, &self->instanceName##_env[i].super.super, __VA_ARGS__); \ - self->_children[_child_idx++] = &self->instanceName[i].super; \ + Environment_##ParentName##_##ReactorName##_##InstanceName##_ctor( \ + &self->InstanceName##_env[i], &self->InstanceName[i], env->scheduler->duration, env->fast_mode); \ + ReactorName##_ctor(&self->InstanceName[i], &self->super, &self->InstanceName##_env[i].super.super, __VA_ARGS__); \ + self->_children[_child_idx++] = &self->InstanceName[i].super; \ } #define LF_DEFINE_EVENT_QUEUE(Name, NumEvents) \ @@ -734,7 +734,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; Environment_ctor(&self->super, ENVIRONMENT_BASE, &main->super, &self->scheduler.super, fast_mode); \ } -#define LF_DEFINE_ENCLAVE_ENVIRONMENT_STRUCT(Name, NumEvents, NumReactions) \ +#define LF_DEFINE_ENCLAVE_ENVIRONMENT_STRUCT(ReactorName, InstanceName, ParentName, NumEvents, NumReactions) \ typedef struct { \ EnclaveEnvironment super; \ struct { \ @@ -747,10 +747,12 @@ typedef struct FederatedInputConnection FederatedInputConnection; int level_size[(NumReactions)]; \ } reaction_queue; \ DynamicScheduler scheduler; \ - } Environment_##Name; + } Environment_##ParentName##_##ReactorName##_##InstanceName; -#define LF_DEFINE_ENCLAVE_ENVIRONMENT_CTOR(Name) \ - void Environment_##Name##_ctor(Environment_##Name *self, Name *main, interval_t duration, bool fast_mode) { \ +#define LF_DEFINE_ENCLAVE_ENVIRONMENT_CTOR(ReactorName, InstanceName, ParentName) \ + void Environment_##ParentName##_##ReactorName##_##InstanceName##_ctor( \ + Environment_##ParentName##_##ReactorName##_##InstanceName *self, ReactorName *main, interval_t duration, \ + bool fast_mode) { \ EventQueue_ctor(&self->event_queue.super, self->event_queue.events, \ sizeof(self->event_queue.events) / sizeof(self->event_queue.events[0])); \ ReactionQueue_ctor(&self->reaction_queue.super, (Reaction **)self->reaction_queue.reactions, \ diff --git a/include/reactor-uc/platform.h b/include/reactor-uc/platform.h index 293ce4a38..ab34e46ae 100644 --- a/include/reactor-uc/platform.h +++ b/include/reactor-uc/platform.h @@ -47,8 +47,14 @@ struct Platform { */ lf_ret_t (*wait_until_interruptible)(Platform *super, instant_t wakeup_time); + /** + * @brief Create a a new thread. + */ lf_ret_t (*create_thread)(Platform *super, Thread *thread, void *(*thread_func)(void *), void *arguments); + /** + * @brief Join a thread. Blocks until the thread has terminated. + */ lf_ret_t (*join_thread)(Platform *super, Thread *thread); /** diff --git a/include/reactor-uc/scheduler.h b/include/reactor-uc/scheduler.h index e4ab4b122..d57fbd85d 100644 --- a/include/reactor-uc/scheduler.h +++ b/include/reactor-uc/scheduler.h @@ -20,6 +20,11 @@ struct Scheduler { */ lf_ret_t (*schedule_at)(Scheduler *self, Event *event); + /** @brief Schedule the event at the earliest possible tag. This function will + * modify the tag of the provided event to the earliest possible. + */ + lf_ret_t (*schedule_at_earilest_possible_tag)(Scheduler *self, Event *event); + /** * @brief Schedules a system event at a specified tag. This function will * enter a critcal section if the environment has async events. diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt index 864232f4d..8bf3641c4 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt @@ -68,8 +68,8 @@ class UcInstanceGenerator( } fun generateChildReactorField(inst: Instantiation) = - if (reactor.isEnclaved) - "LF_ENCLAVE_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" + if (reactor.isEnclaved && !inst.reactor.isEnclaved) + "LF_ENCLAVE_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${reactor.name}, ${inst.codeWidth});" else "LF_CHILD_REACTOR_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" fun generateReactorStructFields(inst: Instantiation) = @@ -89,10 +89,11 @@ class UcInstanceGenerator( } fun generateChildReactorCtor(inst: Instantiation) = - if (reactor.isEnclaved && withArgs(inst)) - "LF_INITIALIZE_ENCLAVE_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)})" - else if (reactor.isEnclaved) - "LF_INITIALIZE_ENCLAVE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" + if (reactor.isEnclaved && !inst.reactor.isEnclaved) + if (withArgs(inst)) + "LF_INITIALIZE_ENCLAVE_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${reactor.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)})" + else + "LF_INITIALIZE_ENCLAVE(${inst.reactor.codeType}, ${inst.name}, ${reactor.name}, ${inst.codeWidth});" else if (withArgs(inst)) "LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)});" else "LF_INITIALIZE_CHILD_REACTOR(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt index f05077eff..15b2c1dc3 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt @@ -45,16 +45,17 @@ class UcReactorGenerator( } private val numChildren = reactor.allInstantiations.map { it.codeWidth }.sum() - private val enclaveReactorDefs = - if (reactor.isEnclaved) reactor.allInstantiations.map { it.reactor }.distinct() + + private val enclaveInsts = + if (reactor.isEnclaved) reactor.allInstantiations.filterNot { it.reactor.isEnclaved } else emptyList() + private val enclaveReactorDefs = enclaveInsts.map { it.reactor }.distinct() + private val enclaves = - if (reactor.isEnclaved) - reactor.allInstantiations - .map { inst -> (0 until inst.width).toList().map { idx -> UcEnclave(inst, idx) } } - .flatten() - else emptyList() + enclaveInsts + .map { inst -> (0 until inst.width).toList().map { idx -> UcEnclave(inst, idx) } } + .flatten() private val parameters = UcParameterGenerator(reactor) private val connections = UcConnectionGenerator(reactor, null, enclaves) @@ -83,7 +84,7 @@ class UcReactorGenerator( } // Given the reactor definition of an enclave. Find the number of events within it. - private fun getNumEventsInEnclave(enclave: Reactor): Int { + private fun getNumEventsInEnclave(enclave: Instantiation): Int { var numEvents = 0 fun getNumEventsInner(r: Reactor): Pair { var ret = 0 @@ -99,23 +100,17 @@ class UcReactorGenerator( ret += connections.getNumEvents() return Pair(ret, hasStartup) } - val ret = getNumEventsInner(enclave) + val ret = getNumEventsInner(enclave.reactor) numEvents += ret.first if (ret.second) numEvents += 1 // Get worst-case number of events due to enclaved connections. Need to check all enclave // instantiations - val enclaveInsts = mutableListOf() - for (inst in reactor.allInstantiations) { - if (inst.reactor == enclave) { - for (i in 0 until inst.width) { - enclaveInsts.add(UcEnclave(inst, i)) - } - } - } + val enclaveNodes = enclaves.filter { it.inst == enclave } var maxConnEvents = 0 - for (enclave in enclaveInsts) { - val connEvent = connections.getNumEvents(enclave) + + for (enclaveNode in enclaveNodes) { + val connEvent = connections.getNumEvents(enclaveNode) if (connEvent > maxConnEvents) { maxConnEvents = connEvent } @@ -143,18 +138,15 @@ class UcReactorGenerator( } fun generateEnclaveStructDeclaration() = - enclaveReactorDefs.joinToString( + enclaveInsts.joinToString( prefix = "// Enclave structs \n", separator = "\n", postfix = "\n") { - "LF_DEFINE_ENCLAVE_ENVIRONMENT_STRUCT(${it.codeType}, ${getNumEventsInEnclave(it)}, ${getNumReactionsInEnclave(it)});" // FIXME: How to get - // numEvents and - // numReactions into here. + "LF_DEFINE_ENCLAVE_ENVIRONMENT_STRUCT(${it.reactor.codeType}, ${it.name},${reactor.name}, ${getNumEventsInEnclave(it)}, ${getNumReactionsInEnclave(it.reactor)});" // FIXME: How to get } fun generateEnclaveCtorDefinition() = - enclaveReactorDefs.joinToString( - prefix = "// Enclave ctors \n", separator = "\n", postfix = "\n") { - "LF_DEFINE_ENCLAVE_ENVIRONMENT_CTOR(${it.codeType});" - } + enclaveInsts.joinToString(prefix = "// Enclave ctors \n", separator = "\n", postfix = "\n") { + "LF_DEFINE_ENCLAVE_ENVIRONMENT_CTOR(${it.reactor.codeType}, ${it.name}, ${reactor.name});" + } companion object { val Reactor.codeType @@ -183,9 +175,6 @@ class UcReactorGenerator( } .isNotEmpty() - val Reactor.isEnclave - get(): Boolean = (this.eContainer() is Reactor) && (this.eContainer() as Reactor).isEnclaved - val Reactor.containsEnclaves get(): Boolean { if (this.isEnclaved) return true diff --git a/src/connection.c b/src/connection.c index 9877a2627..03e3971d7 100644 --- a/src/connection.c +++ b/src/connection.c @@ -205,12 +205,6 @@ void EnclavedConnection_cleanup(Trigger *trigger) { EnclavedConnection *self = (EnclavedConnection *)trigger; validate(trigger->is_registered_for_cleanup); - // FIXME: Can we remove this? - if (trigger->is_present) { - LF_DEBUG(CONN, "Enclaved connection %p had a present value this tag. Pop it", trigger); - trigger->is_present = false; - } - if (self->staged_payload_ptr) { LF_DEBUG(CONN, "Enclaved connection %p had a staged value. Schedule it", trigger); Environment *receiving_env = self->super.super.parent->env; @@ -218,11 +212,13 @@ void EnclavedConnection_cleanup(Trigger *trigger) { Scheduler *receiving_sched = receiving_env->scheduler; - // FIXME: Handle STP violations. tag_t base_tag = ZERO_TAG; if (self->type == PHYSICAL_CONNECTION) { base_tag.time = receiving_env->get_physical_time(receiving_env); } else { + // FIXME: When federated support is added, we must check whether this enclaved connection + // is connected to a federated input port. If this is the case, we should use the `intended_tag` + // of the federated input port. Not the current tag of the scheduler. base_tag = sending_env->scheduler->current_tag(sending_env->scheduler); } tag_t tag = lf_delay_tag(base_tag, self->delay); @@ -230,13 +226,10 @@ void EnclavedConnection_cleanup(Trigger *trigger) { lf_ret_t ret = receiving_sched->schedule_at(receiving_sched, &event); - // FIXME: There is a race condition here. Actually we need to acquire the lock on the receiving enclave. - // also solved with adding a `schedule_now` function. if (ret == LF_PAST_TAG) { // STP-violation LF_WARN(CONN, "STP violation"); - event.super.tag = lf_delay_tag(receiving_sched->current_tag(receiving_sched), 0); - ret = receiving_sched->schedule_at(receiving_sched, &event); + ret = receiving_sched->schedule_at_earilest_possible_tag(receiving_sched, &event); validate(ret == LF_OK); } @@ -246,7 +239,6 @@ void EnclavedConnection_cleanup(Trigger *trigger) { } } -// FIXME: How do we deal with a connection going to multiple downstreams? /** * @brief This function is called from the context of the sending enclave and * schedules an event onto the event queue of the receiving enclave. @@ -278,17 +270,19 @@ void EnclavedConnection_trigger_downstreams(Connection *super, const void *value sending_scheduler->register_for_cleanup(sending_scheduler, &super->super); } +/** Returns the latest known tag of the connection. Used if we have specified a maxwait. */ tag_t EnclavedConnection_get_last_known_tag(EnclavedConnection *self) { tag_t res; MUTEX_LOCK(self->mutex); - res = self->last_known_tag; + res = self->_last_known_tag; MUTEX_UNLOCK(self->mutex); return res; } +/** Sets the latest known tag of this connection. Used if we have specified a maxwait. */ void EnclavedConnection_set_last_known_tag(EnclavedConnection *self, tag_t tag) { MUTEX_LOCK(self->mutex); - self->last_known_tag = tag; + self->_last_known_tag = tag; MUTEX_UNLOCK(self->mutex); } @@ -306,7 +300,7 @@ void EnclavedConnection_ctor(EnclavedConnection *self, Reactor *parent, Port **d EnclavedConnection_prepare, EnclavedConnection_cleanup, EnclavedConnection_trigger_downstreams); Mutex_ctor(&self->mutex.super); - self->last_known_tag = NEVER_TAG; + self->_last_known_tag = NEVER_TAG; self->get_last_known_tag = EnclavedConnection_get_last_known_tag; self->set_last_known_tag = EnclavedConnection_set_last_known_tag; } diff --git a/src/environments/base_environment.c b/src/environments/base_environment.c index 267e7f975..f6e288246 100644 --- a/src/environments/base_environment.c +++ b/src/environments/base_environment.c @@ -37,14 +37,6 @@ static void Environment_join_enclave_environments(Reactor *reactor) { static void Environment_start_at(Environment *self, instant_t start_time) { LF_INFO(ENV, "Starting program at " PRINTF_TIME " nsec", start_time); - int idx = 1; - for (size_t i = 0; i < self->main->children_size; i++) { - Reactor *reactor = self->main->children[i]; - if (reactor->env->type == ENVIRONMENT_ENCLAVE) { - reactor->env->id = idx++; - } - } - Environment_start_enclave_environments(self->main, start_time); self->scheduler->set_and_schedule_start_tag(self->scheduler, start_time); self->scheduler->run(self->scheduler); diff --git a/src/environments/enclave_environment.c b/src/environments/enclave_environment.c index 7c2470253..b1fa0618b 100644 --- a/src/environments/enclave_environment.c +++ b/src/environments/enclave_environment.c @@ -75,7 +75,7 @@ static lf_ret_t EnclaveEnvironment_acquire_tag(Environment *super, tag_t next_ta tag_t last_known_tag = conn->get_last_known_tag(conn); if (lf_tag_compare(last_known_tag, next_tag) < 0) { - LF_DEBUG(SCHED, "Input %p is unresolved, latest known tag was " PRINTF_TAG, input, conn->last_known_tag); + LF_DEBUG(SCHED, "Input %p is unresolved, latest known tag was " PRINTF_TAG, input, last_known_tag); LF_DEBUG(SCHED, "Input %p has maxwait of " PRINTF_TIME, input, input->max_wait); if (input->max_wait > additional_sleep) { additional_sleep = input->max_wait; diff --git a/src/federated.c b/src/federated.c index 21bc79f3a..8bb39e02f 100644 --- a/src/federated.c +++ b/src/federated.c @@ -185,9 +185,7 @@ void FederatedConnectionBundle_handle_tagged_msg(FederatedConnectionBundle *self break; case LF_PAST_TAG: LF_INFO(FED, "Safe-to-process violation! Tried scheduling event to a past tag. Handling now instead!"); - event.super.tag = sched->current_tag(sched); - event.super.tag.microstep++; - status = sched->schedule_at(sched, &event); + status = sched->schedule_at_earilest_possible_tag(sched, &event); if (status != LF_OK) { LF_ERR(FED, "Failed to schedule event at current tag also. Dropping"); } diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index 23d5d1621..f06f1c21e 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -381,27 +381,22 @@ void Scheduler_run(Scheduler *untyped_self) { self->super.do_shutdown(untyped_self, shutdown_tag); } -lf_ret_t Scheduler_schedule_at(Scheduler *super, Event *event) { +lf_ret_t Scheduler_schedule_at_locked(Scheduler *super, Event *event) { DynamicScheduler *self = (DynamicScheduler *)super; lf_ret_t ret; - // This can be called from the async context and the channel context. It reads stop_tag, current_tag, start_time - // and more and we lock the scheduler mutex before doing anything. - MUTEX_LOCK(self->mutex); // Check if we are trying to schedule past stop tag if (lf_tag_compare(event->super.tag, self->stop_tag) > 0) { LF_WARN(SCHED, "Trying to schedule event at tag " PRINTF_TAG " past stop tag " PRINTF_TAG, event->super.tag, self->stop_tag); - ret = LF_AFTER_STOP_TAG; - goto unlock_and_return; + return LF_AFTER_STOP_TAG; } // Check if we are tring to schedule into the past if (lf_tag_compare(event->super.tag, self->current_tag) <= 0) { LF_WARN(SCHED, "Trying to schedule event at tag " PRINTF_TAG " which is before current tag " PRINTF_TAG, event->super.tag, self->current_tag); - ret = LF_PAST_TAG; - goto unlock_and_return; + return LF_PAST_TAG; } // Check if we are trying to schedule before the start tag @@ -409,8 +404,7 @@ lf_ret_t Scheduler_schedule_at(Scheduler *super, Event *event) { tag_t start_tag = {.time = self->super.start_time, .microstep = 0}; if (lf_tag_compare(event->super.tag, start_tag) < 0 || self->super.start_time == NEVER) { LF_WARN(SCHED, "Trying to schedule event at tag " PRINTF_TAG " which is before start tag", event->super.tag); - ret = LF_INVALID_TAG; - goto unlock_and_return; + return LF_INVALID_TAG; } } @@ -419,7 +413,27 @@ lf_ret_t Scheduler_schedule_at(Scheduler *super, Event *event) { self->env->platform->notify(self->env->platform); -unlock_and_return: + return ret; +} + +lf_ret_t Scheduler_schedule_at(Scheduler *super, Event *event) { + DynamicScheduler *self = (DynamicScheduler *)super; + lf_ret_t ret; + MUTEX_LOCK(self->mutex); + ret = Scheduler_schedule_at_locked(super, event); + MUTEX_UNLOCK(self->mutex); + return ret; +} + +lf_ret_t Scheduler_schedule_at_earliest_possible_tag(Scheduler *super, Event *event) { + DynamicScheduler *self = (DynamicScheduler *)super; + lf_ret_t ret; + MUTEX_LOCK(self->mutex); + + event->super.tag = lf_delay_tag(self->current_tag, 0); + ret = Scheduler_schedule_at_locked(super, event); + validate(ret == LF_OK); + MUTEX_UNLOCK(self->mutex); return ret; } @@ -449,7 +463,7 @@ void Scheduler_request_shutdown(Scheduler *untyped_self) { // Thus we enter a critical section before setting the stop tag. MUTEX_LOCK(self->mutex); self->stop_tag = lf_delay_tag(self->current_tag, 0); - LF_INFO(SCHED, "%i Shutdown requested, will stop at tag" PRINTF_TAG, env->id, self->stop_tag); + LF_INFO(SCHED, "Shutdown requested, will stop at tag" PRINTF_TAG, self->stop_tag); env->platform->notify(env->platform); MUTEX_UNLOCK(self->mutex); } @@ -505,6 +519,7 @@ void DynamicScheduler_ctor(DynamicScheduler *self, Environment *env, EventQueue self->run_timestep = Scheduler_run_timestep; self->super.do_shutdown = Scheduler_do_shutdown; self->super.schedule_at = Scheduler_schedule_at; + self->super.schedule_at_earilest_possible_tag = Scheduler_schedule_at_earliest_possible_tag; self->super.schedule_system_event_at = Scheduler_schedule_system_event_at; self->super.register_for_cleanup = Scheduler_register_for_cleanup; self->super.request_shutdown = Scheduler_request_shutdown; diff --git a/test/lf/src/EnclavedNested.lf b/test/lf/src/EnclavedNested.lf new file mode 100644 index 000000000..7b073f725 --- /dev/null +++ b/test/lf/src/EnclavedNested.lf @@ -0,0 +1,52 @@ +target uC { + platform: Native, +} + +reactor Src(id: int = 0) { + output out: int + input in: bool + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} + + reaction(in) {= + env->request_shutdown(env); + =} +} + +reactor Dst(exp: int = 0) { + input in: int + output out: bool + state check: bool = false + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) -> out {= + printf("Received %d from Src\n", in->value); + validate(in->value == self->exp); + self->check = true; + env->request_shutdown(env); + lf_set(out, true); + =} + + reaction(shutdown) {= + validate(self->check); + =} +} + + +enclaved reactor Nested { + r1 = new Src(id=43) + r2 = new Dst(exp=43) + r1.out -> r2.in + r2.out -> r1.in +} + +main enclaved reactor { + r1 = new Src(id=42) + r2 = new Dst(exp=42) + r1.out -> r2.in + r2.out -> r1.in + nested = new Nested() +} \ No newline at end of file From f76c94940fd83c1f97c405121ac167c27e550d9b Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 30 Apr 2025 09:05:10 +0200 Subject: [PATCH 26/29] Refactorings --- include/reactor-uc/clock_synchronization.h | 6 - include/reactor-uc/connection.h | 18 --- include/reactor-uc/enclaved.h | 26 ++++ include/reactor-uc/environment.h | 7 +- .../environments/enclave_environment.h | 2 - include/reactor-uc/reactor-uc.h | 1 + include/reactor-uc/scheduler.h | 8 +- .../generator/uc/UcClockSyncGenerator.kt | 7 + .../lflang/generator/uc/UcCmakeGenerator.kt | 7 +- .../generator/uc/UcConnectionGenerator.kt | 108 ++++++++----- .../lflang/generator/uc/UcConnectionUtils.kt | 28 ++-- .../lflang/generator/uc/UcEnclaveGenerator.kt | 93 ----------- .../org/lflang/generator/uc/UcGenerator.kt | 22 +-- .../generator/uc/UcGeneratorFederated.kt | 2 +- .../generator/uc/UcInstanceGenerator.kt | 20 ++- .../lflang/generator/uc/UcMainGenerator.kt | 16 +- .../uc/UcPlatformGeneratorNonFederated.kt | 3 +- .../lflang/generator/uc/UcSchedulingNode.kt | 5 +- src/clock_synchronization.c | 1 + src/connection.c | 139 ----------------- src/enclaved.c | 145 ++++++++++++++++++ src/environments/base_environment.c | 16 +- src/environments/enclave_environment.c | 57 ++++++- src/schedulers/dynamic/scheduler.c | 1 - 24 files changed, 374 insertions(+), 364 deletions(-) create mode 100644 include/reactor-uc/enclaved.h delete mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcEnclaveGenerator.kt diff --git a/include/reactor-uc/clock_synchronization.h b/include/reactor-uc/clock_synchronization.h index 605705acb..dcfdb69f1 100644 --- a/include/reactor-uc/clock_synchronization.h +++ b/include/reactor-uc/clock_synchronization.h @@ -6,12 +6,6 @@ #include "reactor-uc/event.h" #include "proto/message.pb.h" -#define CLOCK_SYNC_DEFAULT_PERIOD SEC(1) -#define CLOCK_SYNC_DEFAULT_KP 0.7 // Default value from linuxptp -#define CLOCK_SYNC_DEFAULT_KI 0.3 // Default value from linuxptp -#define CLOCK_SYNC_DEFAULT_MAX_ADJ 200000000 // This is the default max-ppb value for linuxptp -#define CLOCK_SYNC_INITAL_STEP_THRESHOLD MSEC(100) - typedef struct ClockSynchronization ClockSynchronization; typedef struct Environment Environment; diff --git a/include/reactor-uc/connection.h b/include/reactor-uc/connection.h index 3a3f80a45..be6de2b66 100644 --- a/include/reactor-uc/connection.h +++ b/include/reactor-uc/connection.h @@ -7,13 +7,11 @@ #include "reactor-uc/reaction.h" #include "reactor-uc/reactor.h" #include "reactor-uc/trigger.h" -#include "reactor-uc/platform.h" typedef struct Connection Connection; typedef struct LogicalConnection LogicalConnection; typedef struct PhysicalConnection PhysicalConnection; typedef struct DelayedConnection DelayedConnection; -typedef struct EnclavedConnection EnclavedConnection; typedef struct Port Port; typedef struct Output Output; @@ -52,20 +50,4 @@ void DelayedConnection_ctor(DelayedConnection *self, Reactor *parent, Port **dow interval_t delay, ConnectionType type, size_t payload_size, void *payload_buf, bool *payload_used_buf, size_t payload_buf_capacity); -struct EnclavedConnection { - Connection super; - interval_t delay; - ConnectionType type; - EventPayloadPool payload_pool; - void *staged_payload_ptr; - MUTEX_T mutex; // Used to protect the _last_known_tag variable. - tag_t _last_known_tag; - void (*set_last_known_tag)(EnclavedConnection *self, tag_t tag); - tag_t (*get_last_known_tag)(EnclavedConnection *self); -}; - -void EnclavedConnection_ctor(EnclavedConnection *self, Reactor *parent, Port **downstreams, size_t num_downstreams, - interval_t delay, ConnectionType type, size_t payload_size, void *payload_buf, - bool *payload_used_buf, size_t payload_buf_capacity); - #endif diff --git a/include/reactor-uc/enclaved.h b/include/reactor-uc/enclaved.h new file mode 100644 index 000000000..64c54f941 --- /dev/null +++ b/include/reactor-uc/enclaved.h @@ -0,0 +1,26 @@ +#ifndef REACTOR_UC_ENCLAVED_H +#define REACTOR_UC_ENCLAVED_H + +#include "reactor-uc/platform.h" +#include "reactor-uc/tag.h" +#include "reactor-uc/connection.h" + +typedef struct EnclavedConnection EnclavedConnection; + +struct EnclavedConnection { + Connection super; + interval_t delay; + ConnectionType type; + EventPayloadPool payload_pool; + void *staged_payload_ptr; + MUTEX_T mutex; // Used to protect the _last_known_tag variable. + tag_t _last_known_tag; + void (*set_last_known_tag)(EnclavedConnection *self, tag_t tag); + tag_t (*get_last_known_tag)(EnclavedConnection *self); +}; + +void EnclavedConnection_ctor(EnclavedConnection *self, Reactor *parent, Port **downstreams, size_t num_downstreams, + interval_t delay, ConnectionType type, size_t payload_size, void *payload_buf, + bool *payload_used_buf, size_t payload_buf_capacity); + +#endif \ No newline at end of file diff --git a/include/reactor-uc/environment.h b/include/reactor-uc/environment.h index 791291c66..d9071e4c0 100644 --- a/include/reactor-uc/environment.h +++ b/include/reactor-uc/environment.h @@ -20,11 +20,12 @@ typedef struct Environment Environment; // The different types of environments, typedef enum { - ENVIRONMENT_BASE, - ENVIRONMENT_ENCLAVE, - ENVIRONMENT_FEDERATE, + ENVIRONMENT_BASE, // Base environment, used in non-federated and non-enclaved programs. + ENVIRONMENT_ENCLAVE, // Enclave environment, all reactors within an enclave shares this environment. + ENVIRONMENT_FEDERATE, // Federate environment, all reactors within a federate shares this environment. } EnvironmentType; +// A pointer to the top-level environment is exposed as a global variable. extern Environment *_lf_environment; // NOLINT struct Environment { diff --git a/include/reactor-uc/environments/enclave_environment.h b/include/reactor-uc/environments/enclave_environment.h index 44574113b..d2a1c49c4 100644 --- a/include/reactor-uc/environments/enclave_environment.h +++ b/include/reactor-uc/environments/enclave_environment.h @@ -3,8 +3,6 @@ #include "reactor-uc/environment.h" -#include - typedef struct EnclaveEnvironment EnclaveEnvironment; struct EnclaveEnvironment { diff --git a/include/reactor-uc/reactor-uc.h b/include/reactor-uc/reactor-uc.h index 08efaa4e0..a5a7d99fc 100644 --- a/include/reactor-uc/reactor-uc.h +++ b/include/reactor-uc/reactor-uc.h @@ -25,6 +25,7 @@ #if defined ENCLAVED #include "reactor-uc/environments/enclave_environment.h" +#include "reactor-uc/enclaved.h" #endif #if defined FEDERATED diff --git a/include/reactor-uc/scheduler.h b/include/reactor-uc/scheduler.h index d57fbd85d..33b316155 100644 --- a/include/reactor-uc/scheduler.h +++ b/include/reactor-uc/scheduler.h @@ -37,8 +37,10 @@ struct Scheduler { void (*run)(Scheduler *self); /** - * @brief This function is called if ClockSynchronization steps the clock. - * The scheduler should adjust the tag of system_events to make sure they are not lost. + * @brief This function is called if ClockSynchronization steps the physical clock. + * This should not occur while the program is running, but can occur during startup. + * If a large step backwards is done, any scheduled system event will be delayed alot. + * To avoid this, the scheduler should adjust all the tags of the system events. */ void (*step_clock)(Scheduler *self, interval_t step); @@ -59,7 +61,7 @@ struct Scheduler { /** Set the start time of the program and schedule startup and timer events. */ void (*set_and_schedule_start_tag)(Scheduler *self, instant_t start_time); - /** Add a reaction to the reaction queue. */ + /** Schedule a reaction to be executed at the current tag. */ lf_ret_t (*add_to_reaction_queue)(Scheduler *self, Reaction *reaction); /** Get the current executing tag. */ diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt index b01c726d8..e059b4e51 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt @@ -15,6 +15,7 @@ data class UcClockSyncParameters( val Ki: Double = UcClockSyncParameters.DEFAULT_KI, ) { companion object { + // Default values for clock sync params. Taken from linuxptp. const val DEFAULT_DISABLED = false const val DEFAULT_GRANDMASTER = false const val DEFAULT_PERIOD = 1000000000L @@ -41,8 +42,14 @@ class UcClockSyncGenerator( private val targetConfig: TargetConfig ) { + // Number of neighbors is the same as the number of federated connection bundles. private val numNeighbors = connectionGenerator.getNumFederatedConnectionBundles() + // We allocate three event per neighbor and an additional two events for the bookkeeping timer. val numSystemEvents = numNeighbors * 3 + 2 + + // Clock sync is enabled by default. But can be disabled by a global target property (clock-sync: + // off) or by + // federate-specific annotation. val enabled = !federate.clockSyncParams.disabled && targetConfig.getOrDefault(ClockSyncModeProperty.INSTANCE) != diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt index c41071191..05875b553 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt @@ -15,7 +15,7 @@ abstract class UcCmakeGenerator( ) { protected val S = '$' // a little trick to escape the dollar sign with $S private val minCmakeVersion = "3.10" - protected val includeFiles = + private val includeFiles = targetConfig.get(CmakeIncludeProperty.INSTANCE)?.map { fileConfig.srcPath.resolve(it).toUnixString() } @@ -78,11 +78,10 @@ class UcCmakeGeneratorNonFederated( override val mainTarget = fileConfig.name override fun generateIncludeCmake(sources: List): String { - val compileDefs = mutableListOf() if (mainDef.reactor.containsEnclaves) { - compileDefs.add("ENCLAVED") + return doGenerateIncludeCmake(sources, compileDefs = listOf("ENCLAVED")) } - return doGenerateIncludeCmake(sources, compileDefs) + return doGenerateIncludeCmake(sources, compileDefs = emptyList()) } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 763e8554a..0a80fe3c5 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -16,16 +16,17 @@ import org.lflang.lf.* * This generator creates code for configuring the connections between reactors. This is perhaps the * most complicated part of the code-generator. This generator handles both federated and * non-federated programs + * + * @param reactor The reactor declaration that we are generating connections for. + * @param currentFederate The current federate. This is only used when generating for a federate. In + * which case the reactor is set to the top-level `federated reactor` and currentFederate is the + * federate instance. + * @param allNodes A list of all the federates/enclaves */ class UcConnectionGenerator( - private val reactor: Reactor, // The reactor to generator connections for - private val currentFederate: - UcFederate?, // The federate to generate connections for. If set then `reactor` should be - // the top-level reactor. - private val allNodes: - List< - UcSchedulingNode> // A list of all the federates in the program. Only used for federated - // code-gen. + private val reactor: Reactor, + private val currentFederate: UcFederate?, + private val allNodes: List ) { /** A list containing all non-federated gruoped connections within this reactor. */ @@ -40,9 +41,9 @@ class UcConnectionGenerator( private val isFederated = currentFederate != null /** - * Given a LF connection and possibly the list of federates of the program. Create all the - * ConnectionChannels found within the LF Connection. This must handle multiports, banks, iterated - * connections and federated connections. + * Given a LF connection and possibly the list of federates/enclaves of the program. Create all + * the ConnectionChannels found within the LF Connection. This must handle multiports, banks, + * iterated connections, federated connections and enclaved connections. */ private fun parseConnectionChannels( conn: Connection, @@ -96,70 +97,87 @@ class UcConnectionGenerator( } /** - * Given a list of ConnectionChannels, group them together. How they are grouepd depends on - * whether we are dealing with federated, enclaved or normal non-federated reactors. + * Given a list of ConnectionChannels, group them together. How they are grouped depends on + * whether we are dealing with federated, enclaved or normal reactors. For normal reactors, all + * connections from the same port are grouped together into a single Connection object. For + * federated or enclaved, only those that are from the same port and destined to the same remote + * enclave/federate. + * + * @param channels The list of all the connection channels within this reactor declaration + * @return A list of those connection channels grouped into Grouped Connections. */ private fun groupConnections(channels: List): List { val res = mutableListOf() val channels = HashSet(channels) while (channels.isNotEmpty()) { - val c = channels.first()!! - if (c.isFederated) { + // Select a channel + val nextChannel = channels.first()!! + + if (nextChannel.isFederated) { + // Find all other channels that can be grouped with this one. val grouped = channels.filter { - it.conn.delayString == c.conn.delayString && - it.conn.isPhysical == c.conn.isPhysical && - it.src.varRef == c.src.varRef && - it.src.node == c.src.node && - it.dest.node == c.dest.node && - it.getChannelType() == c.getChannelType() + it.conn.delayString == nextChannel.conn.delayString && + it.conn.isPhysical == nextChannel.conn.isPhysical && + it.src.varRef == nextChannel.src.varRef && + it.src.node == nextChannel.src.node && + it.dest.node == nextChannel.dest.node && + it.getChannelType() == nextChannel.getChannelType() } - + // Find the associated UcFederate val srcFed = - allNodes.find { it == UcFederate(c.src.varRef.container, c.src.bankIdx) }!! + allNodes.find { + it == UcFederate(nextChannel.src.varRef.container, nextChannel.src.bankIdx) + }!! as UcFederate val destFed = - allNodes.find { it == UcFederate(c.dest.varRef.container, c.dest.bankIdx) }!! + allNodes.find { + it == UcFederate(nextChannel.dest.varRef.container, nextChannel.dest.bankIdx) + }!! as UcFederate + + // Create the grouped connectino val groupedConnection = UcFederatedGroupedConnection( - c.src.varRef, + nextChannel.src.varRef, grouped, - c.conn, + nextChannel.conn, srcFed, destFed, ) res.add(groupedConnection) channels.removeAll(grouped.toSet()) - } else if (c.conn.isEnclaved) { + } else if (nextChannel.conn.isEnclaved) { val grouped = channels.filter { - it.conn.delayString == c.conn.delayString && - it.conn.isPhysical == c.conn.isPhysical && + it.conn.delayString == nextChannel.conn.delayString && + it.conn.isPhysical == nextChannel.conn.isPhysical && !it.isFederated && - it.src.varRef == c.src.varRef && - it.src.node == c.src.node && - it.dest.node == c.dest.node && - it.dest.varRef == c.dest.varRef + it.src.varRef == nextChannel.src.varRef && + it.src.node == nextChannel.src.node && + it.dest.node == nextChannel.dest.node && + it.dest.varRef == nextChannel.dest.varRef } - val groupedConnection = UcGroupedConnection(c.src.varRef, grouped, c.conn) + val groupedConnection = + UcGroupedConnection(nextChannel.src.varRef, grouped, nextChannel.conn) res.add(groupedConnection) channels.removeAll(grouped.toSet()) } else { val grouped = channels.filter { - it.conn.delayString == c.conn.delayString && - it.conn.isPhysical == c.conn.isPhysical && + it.conn.delayString == nextChannel.conn.delayString && + it.conn.isPhysical == nextChannel.conn.isPhysical && !it.isFederated && - it.src.varRef == c.src.varRef && - it.src.node == c.src.node + it.src.varRef == nextChannel.src.varRef && + it.src.node == nextChannel.src.node } - val groupedConnection = UcGroupedConnection(c.src.varRef, grouped, c.conn) + val groupedConnection = + UcGroupedConnection(nextChannel.src.varRef, grouped, nextChannel.conn) res.add(groupedConnection) channels.removeAll(grouped.toSet()) } @@ -525,6 +543,7 @@ class UcConnectionGenerator( "LF_DELAYED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" } + /** Returns the number of events needed by the connections within this reactor declaration. */ fun getNumEvents(): Int { var res = 0 for (conn in nonFederatedConnections) { @@ -535,6 +554,10 @@ class UcConnectionGenerator( return res } + /** + * Returns the number of events needed by the enclave/federate [node] which is inside this reactor + * declaration. This will only include events needed for any input connections to [node] + */ fun getNumEvents(node: UcSchedulingNode): Int { var res = 0 @@ -556,6 +579,9 @@ class UcConnectionGenerator( return res } + /** + * Generate include statements for the NetworkChannels that are used in this reactor declaration. + */ fun generateNetworkChannelIncludes(): String = federatedConnectionBundles .distinctBy { it.networkChannel.type } @@ -610,6 +636,10 @@ class UcConnectionGenerator( return actualLength.second } + /** + * If this reactor declaration is federated. Then we return whether all the federates inside it + * are fully connected, which is a requirement for e.g. startup tag coordination to work. + */ fun areFederatesFullyConnected(): Boolean { data class Graph(val nodes: Int, val adj: List>) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt index ee2bd4657..bb952ba87 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt @@ -14,10 +14,11 @@ import org.lflang.lf.VarRef /** * A UcConnectionChannel is the fundamental lowest-level representation of a connection in a LF - * program. It connects two UcChannels, one at the source and one at the destination. + * program. It connects [src] and [dest] and is also associtaed with a LF connection [conn] */ class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Connection) { - val isEnclavedOrFederated = (src.node != null) && (dest.node != null) && (src.node != dest.node) + private val isEnclavedOrFederated = + (src.node != null) && (dest.node != null) && (src.node != dest.node) val isFederated = isEnclavedOrFederated && src.node is UcFederate && dest.node is UcFederate /** @@ -42,13 +43,18 @@ class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Con /** * A GroupedConnection is a set of ConnectionChannels that can be grouped together for efficiency. * All ConnectionChannels that start from the same LF port, either because of multiports, banks, or - * multiple connections. Are grouped. + * multiple connections are grouped. A grouped connetion is associated with a [src] VarRef which + * refers to the source port, and a list of connection [channels], all of which originate from + * [src]. Finally, we associate a single LF connection. [lfConn] with the grouped connection. If + * there were multiple different any will do. */ open class UcGroupedConnection( val src: VarRef, val channels: List, val lfConn: Connection, ) { + + // The logical delay of this connection, (NEVER means no delay, 0 means microstep) val delay = lfConn.delay.orNever().toCCode() val isPhysical = lfConn.isPhysical val isLogical = !lfConn.isPhysical && lfConn.delay == null @@ -58,8 +64,6 @@ open class UcGroupedConnection( val srcPort = src.variable as Port val isDelayed = lfConn.isPhysical || !isLogical // We define physical connections as delayed. - private var uid: Int = -1 - val bankWidth = srcInst?.codeWidth ?: 1 val portWidth = srcPort.width val numDownstreams = { @@ -70,6 +74,9 @@ open class UcGroupedConnection( val maxNumPendingEvents = if (getConnectionBufferSize(lfConn) > 0) getConnectionBufferSize(lfConn) else 1 + // Each grouped connection needs a unique ID to avoid the possibility of naming collision. + private var uid: Int = -1 + fun assignUid(id: Int) { uid = id } @@ -107,7 +114,7 @@ class UcFederatedGroupedConnection( this.bundle = bundle } - // THe connection index of this FederatedGroupedConnection is the index + // The connection index of this FederatedGroupedConnection is the index // which it will appear in the destination UcFederatedConnectionBundle. fun getDestinationConnectionId(): Int { require(bundle != null) @@ -119,7 +126,7 @@ class UcFederatedGroupedConnection( /** * A FederatedConnectionBundle will contain all GroupedConnections going between two federates, in - * either direction. It also contains a NetworkChannel connecting and NetworkEndpoint in each + * either direction. It also contains a NetworkChannel connecting an NetworkEndpoint in each * federate. */ class UcFederatedConnectionBundle( @@ -203,10 +210,11 @@ class UcChannelQueue(varRef: VarRef, nodes: List) { } } - // Get a number of channels from this port. This has side-effects and will remove these - // channels from the port. + // Take a number of channels from this port. If it is a multiport or in a bank we can get multiple + // channels out + // They are taken out as we establish their connection. fun takeChannels(numChannels: Int): List { - assert(numChannels >= channels.size) + require(channels.size >= numChannels) val res = mutableListOf() for (i in 1..numChannels) { res.add(channels.removeFirst()) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcEnclaveGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcEnclaveGenerator.kt deleted file mode 100644 index 82327d781..000000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcEnclaveGenerator.kt +++ /dev/null @@ -1,93 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.* -import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcPortGenerator.Companion.width -import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType -import org.lflang.lf.* - -class UcEnclaveGenerator( - private val reactor: Reactor, - private val parameters: UcParameterGenerator, - private val ports: UcPortGenerator, - private val connections: UcConnectionGenerator, - private val reactions: UcReactionGenerator, - private val fileConfig: UcFileConfig, - private val messageReporter: MessageReporter -) { - companion object { - val Instantiation.width - get(): Int = widthSpec?.getWidth() ?: 1 - - val Instantiation.codeWidth - get(): Int = if (this.isAFederate) 1 else width - - val Instantiation.codeTypeFederate - get(): String = "${(eContainer() as Reactor).name}_${name}" - - val Instantiation.isAFederate - get(): Boolean = this.eContainer() is Reactor && (this.eContainer() as Reactor).isFederated - } - - fun generateIncludes(): String = - reactor.allInstantiations - .map { fileConfig.getReactorHeaderPath(it.reactor) } - .distinct() - .joinToString(prefix = "// Include instantiated reactors\n", separator = "\n") { - """#include "${it.toUnixString()}" """ - } - - fun generateReactorStructContainedOutputFields(inst: Instantiation) = - inst.reactor.allOutputs.joinToString(separator = "\n") { - with(PrependOperator) { - """| - |LF_CHILD_OUTPUT_CONNECTIONS(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${connections.getNumConnectionsFromPort(inst, it)}); - |LF_CHILD_OUTPUT_EFFECTS(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionEffectsOfOutput(inst, it).size}); - |LF_CHILD_OUTPUT_OBSERVERS(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionObserversOfOutput(inst, it).size}); - """ - .trimMargin() - } - } - - fun generateReactorStructContainedInputFields(inst: Instantiation) = - inst.reactor.allInputs.joinToString(separator = "\n") { - with(PrependOperator) { - """| - |LF_CHILD_INPUT_SOURCES(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionSourcesOfInput(inst, it).size}); - """ - .trimMargin() - } - } - - fun generateReactorStructField(inst: Instantiation) = - with(PrependOperator) { - """| - |LF_CHILD_REACTOR_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth}); - |${generateReactorStructContainedOutputFields(inst)} - |${generateReactorStructContainedInputFields(inst)} - """ - .trimMargin() - } - - fun generateReactorStructFields() = - reactor.allInstantiations.joinToString( - prefix = "// Child reactor fields\n", separator = "\n", postfix = "\n") { - generateReactorStructField(it) - } - - fun generateReactorCtorCode(inst: Instantiation) = - with(PrependOperator) { - """| - ${" |"..ports.generateDefineContainedOutputArgs(inst)} - ${" |"..ports.generateDefineContainedInputArgs(inst)} - |${ if (parameters.generateReactorCtorDeclArguments(inst).isNotEmpty() || ports.generateReactorCtorDeclArguments(inst).isNotEmpty()) - "LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)});" - else "LF_INITIALIZE_CHILD_REACTOR(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" - } - """ - .trimMargin() - } - - fun generateReactorCtorCodes() = - reactor.allInstantiations.joinToString(separator = "\n") { generateReactorCtorCode(it) } -} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index e9b80e25f..46ac141e2 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -53,10 +53,15 @@ abstract class UcGenerator( // Compute the total number of events and reactions within an instance (and its children) // Also returns whether there is any startup event within the instance. private fun totalNumEventsReactionsAndStartup(inst: Instantiation): Triple { - var numEvents = 0 - var numReactions = 0 - var hasStartup = false - if (!inst.isAnEnclave) { + // If the instance is an enclave, then we dont count its events and reactions since they go + // on a different event and reaction queue. + if (inst.isAnEnclave) { + return Triple(0, 0, false) + } else { + var numEvents = 0 + var numReactions = 0 + var hasStartup = false + val remaining = mutableListOf() remaining.addAll(inst.reactor.allInstantiations) while (remaining.isNotEmpty()) { @@ -70,12 +75,12 @@ abstract class UcGenerator( numEvents += maxNumPendingEvents[inst.reactor]!! numReactions += inst.reactor.allReactions.size hasStartup = hasStartup or inst.reactor.hasStartup + return Triple(numEvents, numReactions, hasStartup) } - return Triple(numEvents, numReactions, hasStartup) } // Compute the total number of events and reactions for a top-level reactor. - fun totalNumEventsReactionsAndStartup(main: Reactor): Pair { + fun totalNumEventsReactions(main: Reactor): Pair { val res = MutablePair(maxNumPendingEvents[main]!!, main.allReactions.size) var hasStartup = main.hasStartup for (inst in main.allInstantiations) { @@ -88,11 +93,6 @@ abstract class UcGenerator( return res.toPair() } - companion object { - const val libDir = "/lib/c" - const val MINIMUM_CMAKE_VERSION = "3.5" - } - // Returns a possibly empty list of the federates in the current program. protected fun getAllFederates(): List { val res = mutableListOf() diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt index 0f6b5c5c9..e2db1a7e2 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt @@ -29,7 +29,7 @@ class UcGeneratorFederated(context: LFGeneratorContext, scopeProvider: LFGlobalS fun totalNumEventsAndReactionsFederated(federate: UcFederate): Pair { val eventsFromFederatedConnections = maxNumPendingEvents[mainDef.reactor]!! val eventsAndReactionsInFederate = - nonFederatedGenerator.totalNumEventsReactionsAndStartup(federate.inst.reactor) + nonFederatedGenerator.totalNumEventsReactions(federate.inst.reactor) return Pair( eventsFromFederatedConnections + eventsAndReactionsInFederate.first, eventsAndReactionsInFederate.second) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt index 8bf3641c4..7429bdd96 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt @@ -2,7 +2,6 @@ package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth import org.lflang.generator.uc.UcPortGenerator.Companion.width import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType import org.lflang.lf.* @@ -30,7 +29,10 @@ class UcInstanceGenerator( get(): Boolean = this.eContainer() is Reactor && (this.eContainer() as Reactor).isFederated val Instantiation.isAnEnclave - get(): Boolean = this.eContainer() is Reactor && (this.eContainer() as Reactor).isEnclaved + get(): Boolean = + this.eContainer() is Reactor && + (this.eContainer() as Reactor).isEnclaved && + !this.reactor.isEnclaved } private fun withArgs(inst: Instantiation) = @@ -45,7 +47,7 @@ class UcInstanceGenerator( """#include "${it.toUnixString()}" """ } - fun generateReactorStructContainedOutputFields(inst: Instantiation) = + private fun generateReactorStructContainedOutputFields(inst: Instantiation) = inst.reactor.allOutputs.joinToString(separator = "\n") { with(PrependOperator) { """| @@ -57,7 +59,7 @@ class UcInstanceGenerator( } } - fun generateReactorStructContainedInputFields(inst: Instantiation) = + private fun generateReactorStructContainedInputFields(inst: Instantiation) = inst.reactor.allInputs.joinToString(separator = "\n") { with(PrependOperator) { """| @@ -67,8 +69,8 @@ class UcInstanceGenerator( } } - fun generateChildReactorField(inst: Instantiation) = - if (reactor.isEnclaved && !inst.reactor.isEnclaved) + private fun generateChildReactorField(inst: Instantiation) = + if (inst.isAnEnclave) "LF_ENCLAVE_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${reactor.name}, ${inst.codeWidth});" else "LF_CHILD_REACTOR_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" @@ -88,8 +90,10 @@ class UcInstanceGenerator( generateReactorStructFields(it) } - fun generateChildReactorCtor(inst: Instantiation) = - if (reactor.isEnclaved && !inst.reactor.isEnclaved) + private fun generateChildReactorCtor(inst: Instantiation) = + // If the parent reactor is enclaved, but also inst is enclaved, then we dont consider it an + // enclave. + if (inst.isAnEnclave) if (withArgs(inst)) "LF_INITIALIZE_ENCLAVE_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${reactor.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)})" else diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index 0537534c4..088342b0a 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -125,25 +125,25 @@ class UcMainGeneratorFederated( private val ucConnectionGenerator = UcConnectionGenerator(top, currentFederate, otherFederates) private val netBundlesSize = ucConnectionGenerator.getNumFederatedConnectionBundles() private val clockSync = UcClockSyncGenerator(currentFederate, ucConnectionGenerator, targetConfig) - private val startupCooordinator = + private val startupCoordinator = UcStartupCoordinatorGenerator(currentFederate, ucConnectionGenerator) override fun getNumSystemEvents(): Int { val clockSyncSystemEvents = clockSync.numSystemEvents - val startupCoordinatorEvents = startupCooordinator.numSystemEvents + val startupCoordinatorEvents = startupCoordinator.numSystemEvents return clockSyncSystemEvents + startupCoordinatorEvents } override fun keepAlive(): Boolean { - if (targetConfig.isSet(KeepaliveProperty.INSTANCE)) { - return targetConfig.get(KeepaliveProperty.INSTANCE) + return if (targetConfig.isSet(KeepaliveProperty.INSTANCE)) { + targetConfig.get(KeepaliveProperty.INSTANCE) } else { if (main.inputs.isNotEmpty()) { - return true + true } else if (top.hasPhysicalActions()) { - return true + true } else { - return false + false } } } @@ -157,7 +157,7 @@ class UcMainGeneratorFederated( |#include "reactor-uc/reactor-uc.h" ${" |"..generateIncludeScheduler()} |#include "lf_federate.h" - |LF_DEFINE_FEDERATE_ENVIRONMENT_STRUCT(${currentFederate.codeType}, ${numEvents}, ${numReactions}, ${netBundlesSize}, ${startupCooordinator.numSystemEvents}, ${clockSync.numSystemEvents}) + |LF_DEFINE_FEDERATE_ENVIRONMENT_STRUCT(${currentFederate.codeType}, ${numEvents}, ${numReactions}, ${netBundlesSize}, ${startupCoordinator.numSystemEvents}, ${clockSync.numSystemEvents}) |LF_DEFINE_FEDERATE_ENVIRONMENT_CTOR(${currentFederate.codeType}, ${netBundlesSize}, ${ucConnectionGenerator.getLongestFederatePath()}, ${clockSync.enabled}, ${currentFederate.clockSyncParams.grandmaster}, ${currentFederate.clockSyncParams.period}, ${currentFederate.clockSyncParams.maxAdj}, ${currentFederate.clockSyncParams.Kp}, ${currentFederate.clockSyncParams.Ki}) |static ${currentFederate.codeType} main_reactor; |static Environment_${currentFederate.codeType} environment; diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt index f37f86076..043d74c39 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt @@ -10,8 +10,7 @@ class UcPlatformGeneratorNonFederated(generator: UcGenerator, override val srcGe override val targetName = fileConfig.name override fun generatePlatformFiles() { - val numEventsAndReactions = - generator.totalNumEventsReactionsAndStartup(generator.mainDef.reactor) + val numEventsAndReactions = generator.totalNumEventsReactions(generator.mainDef.reactor) val mainGenerator = UcMainGeneratorNonFederated( mainReactor, diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcSchedulingNode.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcSchedulingNode.kt index 536886c1e..99bf726de 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcSchedulingNode.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcSchedulingNode.kt @@ -11,8 +11,11 @@ enum class NodeType { FEDERATE } +/** + * A SchedulingNode is either a federate or an enclave. We use SchedulingNodes for disentangling + * connections because federated and enclaved connections are handled similarly. + */ open class UcSchedulingNode(val inst: Instantiation, val bankIdx: Int, val nodeType: NodeType) { - val isBank = inst.isBank override fun equals(other: Any?): Boolean { diff --git a/src/clock_synchronization.c b/src/clock_synchronization.c index 05914958b..ecf8ceb6e 100644 --- a/src/clock_synchronization.c +++ b/src/clock_synchronization.c @@ -9,6 +9,7 @@ #define NEIGHBOR_INDEX_SELF -1 #define NEIGHBOR_INDEX_UNKNOWN -2 #define NUM_RESERVED_EVENTS 2 // There is 1 periodic event, but it is rescheduled before it is freed so we need 2. +#define CLOCK_SYNC_INITAL_STEP_THRESHOLD MSEC(100) static void ClockSynchronization_correct_clock(ClockSynchronization *self, ClockSyncTimestamps *timestamps) { LF_DEBUG(CLOCK_SYNC, "Correcting clock. T1=" PRINTF_TIME " T2=" PRINTF_TIME " T3=" PRINTF_TIME " T4=" PRINTF_TIME, diff --git a/src/connection.c b/src/connection.c index 03e3971d7..262b115b4 100644 --- a/src/connection.c +++ b/src/connection.c @@ -165,142 +165,3 @@ void DelayedConnection_ctor(DelayedConnection *self, Reactor *parent, Port **dow Connection_ctor(&self->super, TRIG_CONN_DELAYED, parent, downstreams, num_downstreams, &self->payload_pool, DelayedConnection_prepare, DelayedConnection_cleanup, DelayedConnection_trigger_downstreams); } - -/** - * @brief Prepare function called from the receiving enclave when the event is handeled. - * - * @param trigger - * @param event - */ -void EnclavedConnection_prepare(Trigger *trigger, Event *event) { - LF_DEBUG(CONN, "Preparing enclave connection %p for triggering", trigger); - EnclavedConnection *self = (EnclavedConnection *)trigger; - EventPayloadPool *pool = trigger->payload_pool; - - assert(self->super.downstreams_size == 1); - Port *down = self->super.downstreams[0]; - - if (down->effects.size > 0 || down->observers.size > 0) { - validate(pool->payload_size == down->value_size); - memcpy(down->value_ptr, event->super.payload, pool->payload_size); // NOLINT - down->super.prepare(&down->super, event); - } - - for (size_t i = 0; i < down->conns_out_registered; i++) { - LF_DEBUG(CONN, "Found further downstream connection %p to recurse down", down->conns_out[i]); - down->conns_out[i]->trigger_downstreams(down->conns_out[i], event->super.payload, pool->payload_size); - } - - pool->free(pool, event->super.payload); -} - -/** - * @brief Cleanup function called from the sending enclave at the end of a tag when it has written to this connection. - * It should schedule the value onto the event queue of the receiving enclave. - * - * @param trigger - */ -void EnclavedConnection_cleanup(Trigger *trigger) { - LF_DEBUG(CONN, "Cleaning up Enclaved connection %p", trigger); - EnclavedConnection *self = (EnclavedConnection *)trigger; - validate(trigger->is_registered_for_cleanup); - - if (self->staged_payload_ptr) { - LF_DEBUG(CONN, "Enclaved connection %p had a staged value. Schedule it", trigger); - Environment *receiving_env = self->super.super.parent->env; - Environment *sending_env = self->super.upstream->super.parent->env; - - Scheduler *receiving_sched = receiving_env->scheduler; - - tag_t base_tag = ZERO_TAG; - if (self->type == PHYSICAL_CONNECTION) { - base_tag.time = receiving_env->get_physical_time(receiving_env); - } else { - // FIXME: When federated support is added, we must check whether this enclaved connection - // is connected to a federated input port. If this is the case, we should use the `intended_tag` - // of the federated input port. Not the current tag of the scheduler. - base_tag = sending_env->scheduler->current_tag(sending_env->scheduler); - } - tag_t tag = lf_delay_tag(base_tag, self->delay); - Event event = EVENT_INIT(tag, &self->super.super, self->staged_payload_ptr); - - lf_ret_t ret = receiving_sched->schedule_at(receiving_sched, &event); - - if (ret == LF_PAST_TAG) { - // STP-violation - LF_WARN(CONN, "STP violation"); - ret = receiving_sched->schedule_at_earilest_possible_tag(receiving_sched, &event); - validate(ret == LF_OK); - } - - self->set_last_known_tag(self, event.super.tag); - - self->staged_payload_ptr = NULL; - } -} - -/** - * @brief This function is called from the context of the sending enclave and - * schedules an event onto the event queue of the receiving enclave. - * - * @param super A pointer to the Connection object. Belongs to the receiving enclave. - * @param value A poiner to the value written over the connection. - * @param value_size The size of the value written over the connection. - */ -void EnclavedConnection_trigger_downstreams(Connection *super, const void *value, size_t value_size) { - assert(value); - assert(value_size > 0); - EnclavedConnection *self = (EnclavedConnection *)super; - lf_ret_t ret; - LF_DEBUG(CONN, "Triggering downstreams on Enclaved connection %p. Stage the value for later scheduling", super); - EventPayloadPool *pool = super->super.payload_pool; - if (self->staged_payload_ptr == NULL) { - ret = pool->allocate(pool, &self->staged_payload_ptr); - if (ret != LF_OK) { - LF_ERR(CONN, "No more space in event buffer for Enclaved connection %p, dropping. Capacity is %d", super, - self->payload_pool.capacity); - return; - } - } - memcpy(self->staged_payload_ptr, value, value_size); - - // Note that we register this trigger for cleanup with the sending scheduler. This trigger does belong to - // the receiving scheduler. But it is within the cleanup function that the value is scheduled. - Scheduler *sending_scheduler = super->upstream->super.parent->env->scheduler; - sending_scheduler->register_for_cleanup(sending_scheduler, &super->super); -} - -/** Returns the latest known tag of the connection. Used if we have specified a maxwait. */ -tag_t EnclavedConnection_get_last_known_tag(EnclavedConnection *self) { - tag_t res; - MUTEX_LOCK(self->mutex); - res = self->_last_known_tag; - MUTEX_UNLOCK(self->mutex); - return res; -} - -/** Sets the latest known tag of this connection. Used if we have specified a maxwait. */ -void EnclavedConnection_set_last_known_tag(EnclavedConnection *self, tag_t tag) { - MUTEX_LOCK(self->mutex); - self->_last_known_tag = tag; - MUTEX_UNLOCK(self->mutex); -} - -void EnclavedConnection_ctor(EnclavedConnection *self, Reactor *parent, Port **downstreams, size_t num_downstreams, - interval_t delay, ConnectionType type, size_t payload_size, void *payload_buf, - bool *payload_used_buf, size_t payload_buf_capacity) { - - self->delay = delay; - self->staged_payload_ptr = NULL; - self->type = type; - - EventPayloadPool_ctor(&self->payload_pool, (char *)payload_buf, payload_used_buf, payload_size, payload_buf_capacity, - 0); - Connection_ctor(&self->super, TRIG_CONN_ENCLAVED, parent, downstreams, num_downstreams, &self->payload_pool, - EnclavedConnection_prepare, EnclavedConnection_cleanup, EnclavedConnection_trigger_downstreams); - - Mutex_ctor(&self->mutex.super); - self->_last_known_tag = NEVER_TAG; - self->get_last_known_tag = EnclavedConnection_get_last_known_tag; - self->set_last_known_tag = EnclavedConnection_set_last_known_tag; -} diff --git a/src/enclaved.c b/src/enclaved.c index e69de29bb..ca485d413 100644 --- a/src/enclaved.c +++ b/src/enclaved.c @@ -0,0 +1,145 @@ +#include "reactor-uc/enclaved.h" +#include "reactor-uc/logging.h" +#include "reactor-uc/scheduler.h" +#include "reactor-uc/environment.h" + +#include + +/** + * @brief Prepare function called from the receiving enclave when the event is handeled. + * + * @param trigger + * @param event + */ +void EnclavedConnection_prepare(Trigger *trigger, Event *event) { + LF_DEBUG(CONN, "Preparing enclave connection %p for triggering", trigger); + EnclavedConnection *self = (EnclavedConnection *)trigger; + EventPayloadPool *pool = trigger->payload_pool; + + assert(self->super.downstreams_size == 1); + Port *down = self->super.downstreams[0]; + + if (down->effects.size > 0 || down->observers.size > 0) { + validate(pool->payload_size == down->value_size); + memcpy(down->value_ptr, event->super.payload, pool->payload_size); // NOLINT + down->super.prepare(&down->super, event); + } + + for (size_t i = 0; i < down->conns_out_registered; i++) { + LF_DEBUG(CONN, "Found further downstream connection %p to recurse down", down->conns_out[i]); + down->conns_out[i]->trigger_downstreams(down->conns_out[i], event->super.payload, pool->payload_size); + } + + pool->free(pool, event->super.payload); +} + +/** + * @brief Cleanup function called from the sending enclave at the end of a tag when it has written to this connection. + * It should schedule the value onto the event queue of the receiving enclave. + * + * @param trigger + */ +void EnclavedConnection_cleanup(Trigger *trigger) { + LF_DEBUG(CONN, "Cleaning up Enclaved connection %p", trigger); + EnclavedConnection *self = (EnclavedConnection *)trigger; + validate(trigger->is_registered_for_cleanup); + + if (self->staged_payload_ptr) { + LF_DEBUG(CONN, "Enclaved connection %p had a staged value. Schedule it", trigger); + Environment *receiving_env = self->super.super.parent->env; + Environment *sending_env = self->super.upstream->super.parent->env; + + Scheduler *receiving_sched = receiving_env->scheduler; + + tag_t base_tag = ZERO_TAG; + if (self->type == PHYSICAL_CONNECTION) { + base_tag.time = receiving_env->get_physical_time(receiving_env); + } else { + // FIXME: When federated support is added, we must check whether this enclaved connection + // is connected to a federated input port. If this is the case, we should use the `intended_tag` + // of the federated input port. Not the current tag of the scheduler. + base_tag = sending_env->scheduler->current_tag(sending_env->scheduler); + } + tag_t tag = lf_delay_tag(base_tag, self->delay); + Event event = EVENT_INIT(tag, &self->super.super, self->staged_payload_ptr); + + lf_ret_t ret = receiving_sched->schedule_at(receiving_sched, &event); + + if (ret == LF_PAST_TAG) { + // STP-violation + LF_WARN(CONN, "STP violation detected in enclaved connection."); + ret = receiving_sched->schedule_at_earilest_possible_tag(receiving_sched, &event); + validate(ret == LF_OK); + } + + self->set_last_known_tag(self, event.super.tag); + + self->staged_payload_ptr = NULL; + } +} + +/** + * @brief This function is called from the context of the sending enclave and + * schedules an event onto the event queue of the receiving enclave. + * + * @param super A pointer to the Connection object. Belongs to the receiving enclave. + * @param value A poiner to the value written over the connection. + * @param value_size The size of the value written over the connection. + */ +void EnclavedConnection_trigger_downstreams(Connection *super, const void *value, size_t value_size) { + assert(value); + assert(value_size > 0); + EnclavedConnection *self = (EnclavedConnection *)super; + lf_ret_t ret; + LF_DEBUG(CONN, "Triggering downstreams on Enclaved connection %p. Stage the value for later scheduling", super); + EventPayloadPool *pool = super->super.payload_pool; + if (self->staged_payload_ptr == NULL) { + ret = pool->allocate(pool, &self->staged_payload_ptr); + if (ret != LF_OK) { + LF_ERR(CONN, "No more space in event buffer for Enclaved connection %p, dropping. Capacity is %d", super, + self->payload_pool.capacity); + return; + } + } + memcpy(self->staged_payload_ptr, value, value_size); + + // Note that we register this trigger for cleanup with the sending scheduler. This trigger does belong to + // the receiving scheduler. But it is within the cleanup function that the value is scheduled. + Scheduler *sending_scheduler = super->upstream->super.parent->env->scheduler; + sending_scheduler->register_for_cleanup(sending_scheduler, &super->super); +} + +/** Returns the latest known tag of the connection. Used if we have specified a maxwait. */ +tag_t EnclavedConnection_get_last_known_tag(EnclavedConnection *self) { + tag_t res; + MUTEX_LOCK(self->mutex); + res = self->_last_known_tag; + MUTEX_UNLOCK(self->mutex); + return res; +} + +/** Sets the latest known tag of this connection. Used if we have specified a maxwait. */ +void EnclavedConnection_set_last_known_tag(EnclavedConnection *self, tag_t tag) { + MUTEX_LOCK(self->mutex); + self->_last_known_tag = tag; + MUTEX_UNLOCK(self->mutex); +} + +void EnclavedConnection_ctor(EnclavedConnection *self, Reactor *parent, Port **downstreams, size_t num_downstreams, + interval_t delay, ConnectionType type, size_t payload_size, void *payload_buf, + bool *payload_used_buf, size_t payload_buf_capacity) { + + self->delay = delay; + self->staged_payload_ptr = NULL; + self->type = type; + + EventPayloadPool_ctor(&self->payload_pool, (char *)payload_buf, payload_used_buf, payload_size, payload_buf_capacity, + 0); + Connection_ctor(&self->super, TRIG_CONN_ENCLAVED, parent, downstreams, num_downstreams, &self->payload_pool, + EnclavedConnection_prepare, EnclavedConnection_cleanup, EnclavedConnection_trigger_downstreams); + + Mutex_ctor(&self->mutex.super); + self->_last_known_tag = NEVER_TAG; + self->get_last_known_tag = EnclavedConnection_get_last_known_tag; + self->set_last_known_tag = EnclavedConnection_set_last_known_tag; +} \ No newline at end of file diff --git a/src/environments/base_environment.c b/src/environments/base_environment.c index f6e288246..92624679d 100644 --- a/src/environments/base_environment.c +++ b/src/environments/base_environment.c @@ -16,21 +16,23 @@ static void Environment_assemble(Environment *self) { Environment_validate(self); } -void Environment_start_enclave_environments(Reactor *reactor, instant_t start_time) { +static void Environment_start_enclave_environments(Reactor *reactor, instant_t start_time) { if (reactor->env->type == ENVIRONMENT_ENCLAVE) { reactor->env->start_at(reactor->env, start_time); - } - for (size_t i = 0; i < reactor->children_size; i++) { - Environment_start_enclave_environments(reactor->children[i], start_time); + } else { + for (size_t i = 0; i < reactor->children_size; i++) { + Environment_start_enclave_environments(reactor->children[i], start_time); + } } } static void Environment_join_enclave_environments(Reactor *reactor) { if (reactor->env->type == ENVIRONMENT_ENCLAVE) { reactor->env->join(reactor->env); - } - for (size_t i = 0; i < reactor->children_size; i++) { - Environment_join_enclave_environments(reactor->children[i]); + } else { + for (size_t i = 0; i < reactor->children_size; i++) { + Environment_join_enclave_environments(reactor->children[i]); + } } } diff --git a/src/environments/enclave_environment.c b/src/environments/enclave_environment.c index b1fa0618b..4e7f02712 100644 --- a/src/environments/enclave_environment.c +++ b/src/environments/enclave_environment.c @@ -1,5 +1,5 @@ #include "reactor-uc/environments/enclave_environment.h" -#include "reactor-uc/connection.h" +#include "reactor-uc/enclaved.h" #include "reactor-uc/port.h" #include "reactor-uc/logging.h" @@ -32,32 +32,73 @@ static void EnclaveEnvironment_shutdown(Environment *super) { } } +/** + * @brief Search recursively down the hierarchy for more enclaves and start them. + * + * @param current_env The environment of the current enclave. + * @param reactor The reactor to inspect and possibly search further down from. + * @param start_time The start time of the program. + */ +static void EnclaveEnvironment_start_at_nested(Environment *current_env, Reactor *reactor, instant_t start_time) { + if (reactor->env != current_env && reactor->env->type == ENVIRONMENT_ENCLAVE) { + reactor->env->start_at(reactor->env, start_time); + } else { + for (size_t i = 0; i < reactor->children_size; i++) { + EnclaveEnvironment_start_at_nested(current_env, reactor->children[i], start_time); + } + } +} + static void EnclaveEnvironment_start_at(Environment *super, instant_t start_time) { EnclaveEnvironment *self = (EnclaveEnvironment *)super; - LF_INFO(ENV, "Starting enclave %s " PRINTF_TIME " nsec", super->main->name, start_time); + + // Before starting this enclave, we search down and see if there are contained enclaves + // that we start first. + for (size_t i = 0; i < super->main->children_size; i++) { + EnclaveEnvironment_start_at_nested(super, super->main->children[i], start_time); + } + + LF_INFO(ENV, "Starting enclave %s at " PRINTF_TIME " nsec", super->main->name, start_time); self->super.scheduler->set_and_schedule_start_tag(self->super.scheduler, start_time); lf_ret_t ret = super->platform->create_thread(super->platform, &self->thread.super, enclave_thread, super); validate(ret == LF_OK); } -void EnclaveEnvironment_join(Environment *super) { +/** + * @brief Recursively find nested enclaves and join on them + */ +static void EnclaveEnvironment_join_nested(Environment *current_env, Reactor *reactor) { + if (reactor->env != current_env && reactor->env->type == ENVIRONMENT_ENCLAVE) { + reactor->env->join(reactor->env); + } else { + for (size_t i = 0; i < reactor->children_size; i++) { + EnclaveEnvironment_join_nested(current_env, reactor->children[i]); + } + } +} + +static void EnclaveEnvironment_join(Environment *super) { EnclaveEnvironment *self = (EnclaveEnvironment *)super; + // Before joining on this thread, check for any contained enclave and join them first. + for (size_t i = 0; i < super->main->children_size; i++) { + EnclaveEnvironment_join_nested(super, super->main->children[i]); + } lf_ret_t ret = super->platform->join_thread(super->platform, &self->thread.super); validate(ret == LF_OK); } /** - * @brief Acquire a tag by iterating through all network input ports and making - * sure that they are resolved at this tag. If the input port is unresolved we - * must wait for the max_wait time before proceeding. + * @brief Acquire a tag for an enclave by looking at all the input port of the top-level reactor + * in the enclave. If they are connected to an EnclavedConnection, then we check the last known + * tag of that connection and the maxwait of the input port. * * @param self * @param next_tag - * @return lf_ret_t + * @return lf_ret_t LF_OK if tag is acquired, LF_SLEEP_INTERRUPTED if we were interrupted before acquiring the tag. */ static lf_ret_t EnclaveEnvironment_acquire_tag(Environment *super, tag_t next_tag) { - LF_DEBUG(SCHED, "Acquiring tag " PRINTF_TAG, next_tag); + LF_DEBUG(SCHED, "Enclave %s acquiring tag " PRINTF_TAG, super->main->name, next_tag); Reactor *enclave = super->main; instant_t additional_sleep = 0; for (size_t i = 0; i < enclave->triggers_size; i++) { diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index f06f1c21e..efd200e85 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -235,7 +235,6 @@ void Scheduler_schedule_startups(Scheduler *self, tag_t start_tag) { void Scheduler_schedule_timers(Scheduler *self, Reactor *reactor, tag_t start_tag) { lf_ret_t ret; - // FIXME: Does this work with enclaves? for (size_t i = 0; i < reactor->triggers_size; i++) { Trigger *trigger = reactor->triggers[i]; if (trigger->type == TRIG_TIMER) { From d3789bfbe96502478c00e9a22b894ad9327e30ec Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 30 Apr 2025 09:33:24 +0200 Subject: [PATCH 27/29] Minor fixes --- examples/posix/federated/sender.c | 2 +- include/reactor-uc/macros_internal.h | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/posix/federated/sender.c b/examples/posix/federated/sender.c index bb3ebddee..8f1fdbab3 100644 --- a/examples/posix/federated/sender.c +++ b/examples/posix/federated/sender.c @@ -104,7 +104,7 @@ LF_REACTOR_CTOR_SIGNATURE(MainSender) { LF_DEFINE_CHILD_OUTPUT_ARGS(sender, out, 1, 1); LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, 1, _sender_out_args[i]); LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Sender, Receiver); - lf_connect_federated_output(&self->Sender_Receiver_bundle.outputs[0]->super, &self->sender->out[0].super); + lf_connect_federated_output((Connection *)&self->Sender_Receiver_bundle.outputs[0]->super, &self->sender->out[0].super); LF_INITIALIZE_STARTUP_COORDINATOR(Federate); LF_INITIALIZE_CLOCK_SYNC(Federate); } diff --git a/include/reactor-uc/macros_internal.h b/include/reactor-uc/macros_internal.h index 3262a10a8..643bafdcd 100644 --- a/include/reactor-uc/macros_internal.h +++ b/include/reactor-uc/macros_internal.h @@ -621,9 +621,8 @@ typedef struct FederatedInputConnection FederatedInputConnection; #define LF_DEFINE_CLOCK_SYNC_DEFAULTS_CTOR(ReactorName, NumNeighbors, NumEvents, IsGrandmaster) \ void ReactorName##ClockSynchronization_ctor(ReactorName##ClockSynchronization *self, Environment *env) { \ ClockSynchronization_ctor(&self->super, env, self->neighbor_clocks, NumNeighbors, IsGrandmaster, \ - sizeof(ClockSyncEvent), (void *)self->events, self->used, (NumEvents), \ - CLOCK_SYNC_DEFAULT_PERIOD, CLOCK_SYNC_DEFAULT_MAX_ADJ, CLOCK_SYNC_DEFAULT_KP, \ - CLOCK_SYNC_DEFAULT_KI); \ + sizeof(ClockSyncEvent), (void *)self->events, self->used, (NumEvents), SEC(1), \ + 200000000, 0.7, 0.3); \ } #define LF_DEFINE_CLOCK_SYNC(ReactorName) ReactorName##ClockSynchronization clock_sync; From b7a1c51c8a4e7397211a0974db80b510f7f9b8d4 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 30 Apr 2025 09:49:05 +0200 Subject: [PATCH 28/29] Fix warning --- include/reactor-uc/platform.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/reactor-uc/platform.h b/include/reactor-uc/platform.h index ab34e46ae..23b153c22 100644 --- a/include/reactor-uc/platform.h +++ b/include/reactor-uc/platform.h @@ -68,7 +68,7 @@ struct Platform { void Platform_ctor(Platform *super); // Returns a pointer to the platform.P -Platform *Platform_new(); +Platform *Platform_new(void); // Allow each platform to provide its own implementation for printing. void Platform_vprintf(const char *fmt, va_list args); From 5a28ee213482ac2f4192d0f1b3c231b56539f410 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 30 Apr 2025 11:12:19 +0200 Subject: [PATCH 29/29] Optmizing a tiny bit --- src/schedulers/dynamic/scheduler.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index efd200e85..b888837ef 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -304,16 +304,15 @@ void Scheduler_run(Scheduler *untyped_self) { // If we have system events, we need to check if the next event is a system event. if (self->system_event_queue) { next_system_tag = self->system_event_queue->next_tag(self->system_event_queue); - } - - // Handle the one with lower tag, if they are equal, prioritize normal events. - if (lf_tag_compare(next_tag, next_system_tag) > 0) { - next_tag = next_system_tag; - next_event_is_system_event = true; - LF_DEBUG(SCHED, "Next event is a system_event at " PRINTF_TAG, next_tag); - } else { - next_event_is_system_event = false; - LF_DEBUG(SCHED, "Next event is at " PRINTF_TAG, next_tag); + // Handle the one with lower tag, if they are equal, prioritize normal events. + if (lf_tag_compare(next_tag, next_system_tag) > 0) { + next_tag = next_system_tag; + next_event_is_system_event = true; + LF_DEBUG(SCHED, "Next event is a system_event at " PRINTF_TAG, next_tag); + } else { + next_event_is_system_event = false; + LF_DEBUG(SCHED, "Next event is at " PRINTF_TAG, next_tag); + } } // Detect if event is past the stop tag, in which case we go to shutdown instead.