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/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 new file mode 100644 index 000000000..ae94c57cb --- /dev/null +++ b/doc/markdown/5_federated.md @@ -0,0 +1,264 @@ +\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. + +## 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. + 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/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/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/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/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/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/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 17ef1432a..d9071e4c0 100644 --- a/include/reactor-uc/environment.h +++ b/include/reactor-uc/environment.h @@ -12,16 +12,28 @@ #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; typedef struct Environment Environment; + +// The different types of environments, +typedef enum { + 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 { + EnvironmentType type; Reactor *main; // The top-level reactor of the program. Scheduler *scheduler; // The scheduler in charge of executing the reactions. - Platform *platform; + 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 @@ -40,6 +52,18 @@ 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); + /** * @private * @brief Sleep until the wakeup time. @@ -147,7 +171,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/enclave_environment.h b/include/reactor-uc/environments/enclave_environment.h new file mode 100644 index 000000000..d2a1c49c4 --- /dev/null +++ b/include/reactor-uc/environments/enclave_environment.h @@ -0,0 +1,16 @@ +#ifndef REACTOR_UC_ENCLAVE_ENVIRONMENT_H +#define REACTOR_UC_ENCLAVE_ENVIRONMENT_H + +#include "reactor-uc/environment.h" + +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 70% rename from include/reactor-uc/environments/federated_environment.h rename to include/reactor-uc/environments/federate_environment.h index b945ee71c..ffa058be8 100644 --- a/include/reactor-uc/environments/federated_environment.h +++ b/include/reactor-uc/environments/federate_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, - FederatedConnectionBundle **net_bundles, size_t net_bundles_size, - StartupCoordinator *startup_coordinator, ClockSynchronization *clock_sync); -void FederatedEnvironment_free(FederatedEnvironment *self); +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 FederateEnvironment_free(FederateEnvironment *self); #endif diff --git a/include/reactor-uc/macros_internal.h b/include/reactor-uc/macros_internal.h index 1c3a648fa..643bafdcd 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) \ +#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 { \ @@ -588,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; @@ -629,25 +661,162 @@ typedef struct FederatedInputConnection FederatedInputConnection; self->_children[_child_idx++] = &self->instanceName[i].super; \ } +#define LF_ENCLAVE_INSTANCE(ReactorName, InstanceName, ParentName, BankWidth) \ + Environment_##ParentName##_##ReactorName##_##InstanceName InstanceName##_env[BankWidth]; \ + ReactorName InstanceName[BankWidth]; + +#define LF_INITIALIZE_ENCLAVE(ReactorName, InstanceName, ParentName, BankWidth) \ + for (int i = 0; i < BankWidth; i++) { \ + 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, ParentName, BankWidth, ...) \ + for (int i = 0; i < (BankWidth); i++) { \ + 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) \ typedef struct { \ EventQueue super; \ ArbitraryEvent events[(NumEvents)]; \ - } Name##_t; \ - static Name##_t Name; + } Name##_t; + +#define LF_EVENT_QUEUE_INSTANCE(Name) Name##_t Name; #define LF_DEFINE_REACTION_QUEUE(Name, NumReactions) \ typedef struct { \ ReactionQueue super; \ Reaction *reactions[(NumReactions)][(NumReactions)]; \ int level_size[(NumReactions)]; \ - } Name##_t; \ - static Name##_t Name; + } Name##_t; -#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_REACTION_QUEUE(Name, NumReactions) \ - ReactionQueue_ctor(&Name.super, (Reaction **)Name.reactions, Name.level_size, NumReactions); +#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) \ + 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(ReactorName, InstanceName, ParentName, 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_##ParentName##_##ReactorName##_##InstanceName; + +#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, \ + 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 { \ + 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_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, \ + &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) \ static MainReactorName main_reactor; \ @@ -666,7 +835,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; \ @@ -679,7 +848,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)]; \ @@ -690,7 +859,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)); \ @@ -698,7 +867,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/include/reactor-uc/platform.h b/include/reactor-uc/platform.h index ad87c5892..23b153c22 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,8 @@ struct Mutex { void (*unlock)(Mutex *super); }; +struct Thread {}; + /** Construct a Mutex*/ void Mutex_ctor(Mutex *super); @@ -44,6 +47,16 @@ 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); + /** * @brief Signal the occurrence of an asynchronous event. This should wake * up the platform if it is sleeping on `wait_until_interruptible_locked`. @@ -55,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); diff --git a/include/reactor-uc/platform/aducm355/aducm355.h b/include/reactor-uc/platform/aducm355/aducm355.h index 2634a83c2..0e19dccfd 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..84b2233dd 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..88a47771b 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..a652febb2 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/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/platform/riot/riot.h b/include/reactor-uc/platform/riot/riot.h index d50eac1c0..b32c1612a 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..ac17f660a 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/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/reaction.h b/include/reactor-uc/reaction.h index f9ea4e451..d0e2dd8a5 100644 --- a/include/reactor-uc/reaction.h +++ b/include/reactor-uc/reaction.h @@ -14,7 +14,8 @@ struct Reaction { 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/include/reactor-uc/reactor-uc.h b/include/reactor-uc/reactor-uc.h index d1769dd0f..a5a7d99fc 100644 --- a/include/reactor-uc/reactor-uc.h +++ b/include/reactor-uc/reactor-uc.h @@ -23,8 +23,13 @@ #include "reactor-uc/macros_internal.h" #include "reactor-uc/macros_api.h" +#if defined ENCLAVED +#include "reactor-uc/environments/enclave_environment.h" +#include "reactor-uc/enclaved.h" +#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/include/reactor-uc/scheduler.h b/include/reactor-uc/scheduler.h index 22016c84c..33b316155 100644 --- a/include/reactor-uc/scheduler.h +++ b/include/reactor-uc/scheduler.h @@ -10,10 +10,9 @@ typedef struct Scheduler Scheduler; typedef struct Environment Environment; struct Scheduler { - bool running; - interval_t start_time; - 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 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. /** * @brief Schedules an event on trigger at a specified tag. This function will @@ -21,6 +20,15 @@ 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. + */ lf_ret_t (*schedule_system_event_at)(Scheduler *self, SystemEvent *event); /** @@ -28,6 +36,12 @@ struct Scheduler { */ void (*run)(Scheduler *self); + /** + * @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); /** @@ -35,6 +49,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 +58,25 @@ 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); - + /** 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. */ 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/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..c29715e9b 100644 --- a/lfc/core/src/main/kotlin/org/lflang/ast/AstExtensions.kt +++ b/lfc/core/src/main/kotlin/org/lflang/ast/AstExtensions.kt @@ -315,6 +315,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/UcClockSyncGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcClockSyncGenerator.kt index 96f06b0b9..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,46 +42,16 @@ 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" - } - + // Number of neighbors is the same as the number of federated connection bundles. 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 && + // 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) != 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 "" } 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..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 @@ -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.* @@ -14,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() } @@ -76,8 +77,12 @@ class UcCmakeGeneratorNonFederated( ) : UcCmakeGenerator(targetConfig, fileConfig) { override val mainTarget = fileConfig.name - override fun generateIncludeCmake(sources: List) = - doGenerateIncludeCmake(sources, emptyList()) + override fun generateIncludeCmake(sources: List): String { + if (mainDef.reactor.containsEnclaves) { + return doGenerateIncludeCmake(sources, compileDefs = listOf("ENCLAVED")) + } + return doGenerateIncludeCmake(sources, compileDefs = emptyList()) + } } 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..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 @@ -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 @@ -15,15 +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 allFederates: - List // 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. */ @@ -38,19 +41,19 @@ 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, - 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 +89,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 } } @@ -94,52 +97,87 @@ 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. + * 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.federate == c.src.federate && - it.dest.federate == c.dest.federate && - 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() } - - val srcFed = allFederates.find { it == UcFederate(c.src.varRef.container, c.src.bankIdx) }!! + // Find the associated UcFederate + val srcFed = + allNodes.find { + it == UcFederate(nextChannel.src.varRef.container, nextChannel.src.bankIdx) + }!! + as UcFederate val destFed = - allFederates.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 (nextChannel.conn.isEnclaved) { + val grouped = + channels.filter { + it.conn.delayString == nextChannel.conn.delayString && + it.conn.isPhysical == nextChannel.conn.isPhysical && + !it.isFederated && + 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(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.federate == c.src.federate + 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()) } @@ -151,10 +189,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()) } @@ -213,7 +252,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 @@ -221,7 +261,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() @@ -242,7 +282,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. @@ -295,6 +335,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 +353,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 +394,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})" @@ -370,7 +421,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( @@ -378,7 +429,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) = @@ -413,13 +464,15 @@ 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,29 +535,53 @@ 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});" } - fun getMaxNumPendingEvents(): Int { + /** Returns the number of events needed by the connections within this reactor declaration. */ + 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 + } + + /** + * 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 + + 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 && conn.channels.first().dest.node == node) { + res += conn.maxNumPendingEvents * conn.channels.size } } } return res } + /** + * Generate include statements for the NetworkChannels that are used in this reactor declaration. + */ fun generateNetworkChannelIncludes(): String = federatedConnectionBundles .distinctBy { it.networkChannel.type } @@ -518,8 +595,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() @@ -546,25 +623,29 @@ 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 } + /** + * 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>) // 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) @@ -582,14 +663,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 cbc76a8a3..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 @@ -7,31 +7,34 @@ 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.isEnclaved import org.lflang.lf.Connection import org.lflang.lf.Port 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 isFederated = - (src.federate != null) && (dest.federate != null) && (src.federate != dest.federate) + private 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 } } } @@ -40,24 +43,27 @@ 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. + * 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 + // 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. - private var uid: Int = -1 - val bankWidth = srcInst?.codeWidth ?: 1 val portWidth = srcPort.width val numDownstreams = { @@ -68,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 } @@ -76,7 +85,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 @@ -105,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) @@ -117,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( @@ -150,10 +159,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 || node.nodeType == NodeType.ENCLAVE) bankIdx else 0 private val portOfContainedReactor = varRef.container != null private val reactorInstance = @@ -167,11 +181,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. It is a list in case it is a bank. Then each federate of the bank must be passed to the - * constructor. + * 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 @@ -184,24 +198,23 @@ class UcChannelQueue(varRef: VarRef, federates: List) { if (isInterleaved) { for (i in 0.. = takeChannels(channels.size) - - // Get a number of channels from this port. This has sideeffects 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()) @@ -209,5 +222,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/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index 402afdc1a..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 @@ -22,19 +22,17 @@ 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" init { if (!connections.areFederatesFullyConnected()) { - messageReporter.nowhere().error("Federates are not fully connected!") + messageReporter.nowhere().error("The federation must make up a fully connected graph!") } } fun getMaxNumPendingEvents(): Int { - return connections.getMaxNumPendingEvents() + return connections.getNumEvents(currentFederate) } private fun generateFederateStruct() = @@ -42,12 +40,9 @@ class UcFederateGenerator( """ |typedef struct { | Reactor super; - ${" | "..instances.generateReactorStructField(currentFederate.inst)} + ${" | "..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}; | @@ -61,11 +56,9 @@ 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()} - ${" | "..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/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index ce9bb30f9..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 @@ -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,32 +52,39 @@ 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 { - var numEvents = 0 - var numReactions = 0 - var hasStartup = false - val remaining = mutableListOf() - remaining.addAll(inst.reactor.allInstantiations) - while (remaining.isNotEmpty()) { - val child = remaining.removeFirst() - val childRes = totalNumEventsAndReactions(child) + private fun totalNumEventsReactionsAndStartup(inst: Instantiation): Triple { + // 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 - numEvents += childRes.first * child.width - numReactions += childRes.second * child.width - hasStartup = hasStartup or childRes.third + 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 + return Triple(numEvents, numReactions, hasStartup) } - 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 totalNumEventsReactions(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 @@ -85,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 3948a2b40..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.totalNumEventsAndReactions(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 03cd2896f..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 @@ -27,8 +27,18 @@ 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 && + !this.reactor.isEnclaved } + private fun withArgs(inst: Instantiation) = + (parameters.generateReactorCtorDeclArguments(inst).isNotEmpty() || + ports.generateReactorCtorDeclArguments(inst).isNotEmpty()) + fun generateIncludes(): String = reactor.allInstantiations .map { fileConfig.getReactorHeaderPath(it.reactor) } @@ -37,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) { """| @@ -49,7 +59,7 @@ class UcInstanceGenerator( } } - fun generateReactorStructContainedInputFields(inst: Instantiation) = + private fun generateReactorStructContainedInputFields(inst: Instantiation) = inst.reactor.allInputs.joinToString(separator = "\n") { with(PrependOperator) { """| @@ -59,10 +69,15 @@ class UcInstanceGenerator( } } - fun generateReactorStructField(inst: Instantiation) = + 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});" + + 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 +87,31 @@ class UcInstanceGenerator( fun generateReactorStructFields() = reactor.allInstantiations.joinToString( prefix = "// Child reactor fields\n", separator = "\n", postfix = "\n") { - generateReactorStructField(it) + generateReactorStructFields(it) } - fun generateReactorCtorCode(inst: Instantiation) = + 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 + "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});" + + 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 75a190389..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 @@ -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) { """ @@ -120,18 +91,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); @@ -155,26 +124,26 @@ 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 startupCoordinator = + UcStartupCoordinatorGenerator(currentFederate, ucConnectionGenerator) override fun getNumSystemEvents(): Int { - val clockSyncSystemEvents = UcClockSyncGenerator.getNumSystemEvents(netBundlesSize) - val startupCoordinatorEvents = UcStartupCoordinatorGenerator.getNumSystemEvents(netBundlesSize) + val clockSyncSystemEvents = clockSync.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 } } } @@ -188,20 +157,16 @@ class UcMainGeneratorFederated( |#include "reactor-uc/reactor-uc.h" ${" |"..generateIncludeScheduler()} |#include "lf_federate.h" + |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 FederatedEnvironment lf_environment; - |Environment *_lf_environment = &lf_environment.super; - ${" |"..generateDefineQueues()} - ${" |"..generateDefineScheduler()} + |static Environment_${currentFederate.codeType} environment; + |Environment *_lf_environment = (Environment *) &environment; |void lf_exit(void) { - | FederatedEnvironment_free(&lf_environment); + | FederateEnvironment_free(&environment.super); |} |void lf_start(void) { - ${" | "..generateInitializeQueues()} - ${" | "..generateInitializeScheduler()} - | FederatedEnvironment_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/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 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..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,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.totalNumEventsReactions(generator.mainDef.reactor) val mainGenerator = UcMainGeneratorNonFederated( mainReactor, 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..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});" + "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 43324fdcb..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 @@ -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( @@ -46,8 +46,19 @@ class UcReactorGenerator( private val numChildren = reactor.allInstantiations.map { it.codeWidth }.sum() + 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 = + 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, 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,6 +83,71 @@ class UcReactorGenerator( it.code.toText() } + // Given the reactor definition of an enclave. Find the number of events within it. + private fun getNumEventsInEnclave(enclave: Instantiation): Int { + var numEvents = 0 + 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) + } + 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 enclaveNodes = enclaves.filter { it.inst == enclave } + var maxConnEvents = 0 + + for (enclaveNode in enclaveNodes) { + val connEvent = connections.getNumEvents(enclaveNode) + if (connEvent > maxConnEvents) { + maxConnEvents = connEvent + } + } + numEvents += maxConnEvents + return numEvents + } + + // 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() = + enclaveInsts.joinToString( + prefix = "// Enclave structs \n", separator = "\n", postfix = "\n") { + "LF_DEFINE_ENCLAVE_ENVIRONMENT_STRUCT(${it.reactor.codeType}, ${it.name},${reactor.name}, ${getNumEventsInEnclave(it)}, ${getNumReactionsInEnclave(it.reactor)});" // FIXME: How to get + } + + fun generateEnclaveCtorDefinition() = + 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 get(): String = "Reactor_$name" @@ -99,6 +175,15 @@ class UcReactorGenerator( } .isNotEmpty() + 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() } @@ -127,7 +212,7 @@ class UcReactorGenerator( for (action in reactor.allActions) { numEvents += action.maxNumPendingEvents } - numEvents += connections.getMaxNumPendingEvents() + numEvents += connections.getNumEvents() return numEvents } @@ -186,6 +271,7 @@ class UcReactorGenerator( ${" |"..actions.generateSelfStructs()} ${" |"..ports.generateSelfStructs()} ${" |"..connections.generateSelfStructs()} + ${" |"..generateEnclaveStructDeclaration()} |//The reactor self struct ${" |"..generateReactorStruct()} | @@ -209,6 +295,7 @@ class UcReactorGenerator( ${" |"..timers.generateCtors()} ${" |"..ports.generateCtors()} ${" |"..connections.generateCtors()} + ${" |"..generateEnclaveCtorDefinition()} ${" |"..generateCtorDefinition()} | """ 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 69% 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 index 37f59c7cc..99bf726de 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcSchedulingNode.kt @@ -6,8 +6,34 @@ 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 +} + +/** + * 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 { + 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 +60,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/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/clock_synchronization.c b/src/clock_synchronization.c index e666816c3..ecf8ceb6e 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" @@ -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, @@ -16,7 +17,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 +62,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 +158,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 +208,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 +230,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 +272,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/enclaved.c b/src/enclaved.c new file mode 100644 index 000000000..ca485d413 --- /dev/null +++ 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/environment.c b/src/environment.c index b92f0b3ed..36fd36a93 100644 --- a/src/environment.c +++ b/src/environment.c @@ -1,6 +1,9 @@ +#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 72% rename from src/environments/unfederated_environment.c rename to src/environments/base_environment.c index 7905bc2a0..92624679d 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" @@ -15,13 +16,39 @@ static void Environment_assemble(Environment *self) { Environment_validate(self); } -static void Environment_start(Environment *self) { - instant_t start_time = self->get_physical_time(self); +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); + } 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); + } else { + for (size_t i = 0; i < reactor->children_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; @@ -67,13 +94,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(); + self->platform = (Platform *)&self->_platform; 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; @@ -81,6 +110,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; @@ -94,4 +124,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..4e7f02712 --- /dev/null +++ b/src/environments/enclave_environment.c @@ -0,0 +1,144 @@ +#include "reactor-uc/environments/enclave_environment.h" +#include "reactor-uc/enclaved.h" +#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); + } + } + } + } +} + +/** + * @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; + + // 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); +} + +/** + * @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 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 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, "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++) { + Trigger *trigger = enclave->triggers[i]; + + if (trigger->type == TRIG_INPUT) { + Port *input = (Port *)trigger; + if (!input->conn_in) + continue; + 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, 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 70% rename from src/environments/federated_environment.c rename to src/environments/federate_environment.c index 760f932a0..78515ab14 100644 --- a/src/environments/federated_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" @@ -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]; @@ -83,6 +83,8 @@ static lf_ret_t FederatedEnvironment_acquire_tag(Environment *super, tag_t next_ 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) { @@ -105,8 +107,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 +118,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, - 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; +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, ENVIRONMENT_FEDERATE, main, scheduler, fast_mode); + 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 +142,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..8bb39e02f 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" @@ -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"); } @@ -219,7 +217,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/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/posix/posix.c b/src/platform/posix/posix.c index daf76e260..4ff4c19e2 100644 --- a/src/platform/posix/posix.c +++ b/src/platform/posix/posix.c @@ -105,11 +105,36 @@ void PlatformPosix_notify(Platform *super) { LF_DEBUG(PLATFORM, "New async event"); } +lf_ret_t PlatformPosix_create_thread(Platform *super, Thread *thread, void *(*thread_func)(void *), void *arguments) { + (void)super; + ThreadPosix *thread_posix = (ThreadPosix *)thread; + int ret = pthread_create(&thread_posix->thread, NULL, thread_func, arguments); + if (ret == 0) { + return LF_OK; + } else { + validate(false); + } +} + +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/platform/riot/coap_udp_ip_channel.c b/src/platform/riot/coap_udp_ip_channel.c index db38ec224..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" @@ -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/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); diff --git a/src/port.c b/src/port.c index c04616c56..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; @@ -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/queues.c b/src/queues.c index 6a00e734a..e9e13481b 100644 --- a/src/queues.c +++ b/src/queues.c @@ -128,7 +128,13 @@ 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) { + return LF_OK; + } + validate(reaction->level < (int)self->capacity); validate(reaction->level >= 0); validate(self->level_size[reaction->level] < (int)self->capacity); @@ -144,6 +150,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->enqueued = true; return LF_OK; } @@ -160,6 +167,11 @@ 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 088be5131..518b328aa 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->enqueued = false; } diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index e69298061..b888837ef 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; } @@ -248,7 +246,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); + } } } @@ -263,7 +264,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 @@ -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. @@ -380,27 +379,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 @@ -408,8 +402,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; } } @@ -418,7 +411,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; } @@ -498,13 +511,13 @@ 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; 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/src/startup_coordinator.c b/src/startup_coordinator.c index 6b07b07c1..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" @@ -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/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/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} 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/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..186db12f0 --- /dev/null +++ b/test/lf/src/EnclavedChain.lf @@ -0,0 +1,51 @@ +target uC { + platform: Native, + logging: Debug +} + +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/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/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..598bc62f3 --- /dev/null +++ b/test/lf/src/EnclavedConnectionIterated.lf @@ -0,0 +1,57 @@ +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) {= + printf("Dest2 triggered\n"); + validate(in[0]->value == 1); + validate(in[1]->value == 2); + + env->request_shutdown(env); + =} maxwait(forever) +} + +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..d8993ed7f --- /dev/null +++ b/test/lf/src/EnclavedConnectionIterated2.lf @@ -0,0 +1,67 @@ +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 + 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); + 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); + } + =} +} + +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/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/EnclavedMaxWait.lf b/test/lf/src/EnclavedMaxWait.lf new file mode 100644 index 000000000..e85923400 --- /dev/null +++ b/test/lf/src/EnclavedMaxWait.lf @@ -0,0 +1,40 @@ +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/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/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 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/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 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 new file mode 100644 index 000000000..e03279946 --- /dev/null +++ b/test/lf/src/EnclavedSimple.lf @@ -0,0 +1,21 @@ +target uC { + platform: Native +} + +reactor R(id: int = 0){ + state test: bool = false + reaction(startup) {= + printf("Hello From Enclave %d\n", self->id); + self->test = true; + env->request_shutdown(env); + =} + + reaction(shutdown) {= + validate(self->test); + =} +} + +main enclaved reactor { + e1 = new R(id=1) + e2 = new R(id=2) +} \ 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 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 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/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/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/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 { diff --git a/test/unit/tcp_channel_test.c b/test/unit/tcp_channel_test.c index 17e0c7bef..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" @@ -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);