From f6306c13a56356db4454126b02530ae9fa592a58 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 20 Oct 2021 15:48:06 +0200 Subject: [PATCH 01/13] modes: Migrated changes for modal models --- core/reactor.c | 12 +++ core/reactor.h | 56 +++++++++++ core/reactor_common.c | 227 ++++++++++++++++++++++++++++++++++++++++++ include/ctarget.h | 14 +++ 4 files changed, 309 insertions(+) diff --git a/core/reactor.c b/core/reactor.c index 2bef98a18..aa77586f9 100644 --- a/core/reactor.c +++ b/core/reactor.c @@ -132,6 +132,13 @@ void print_snapshot() { * @param reaction The reaction. */ void _lf_enqueue_reaction(reaction_t* reaction) { +#ifdef MODAL_REACTORS + // Check if reaction is disabled by mode inactivity + if (!_lf_mode_is_active(reaction->mode)) { + DEBUG_PRINT("Suppressing downstream reaction %s due inactivity of mode %s.", reaction->name, reaction->mode->name); + return; // Suppress reaction by preventing entering reaction queue + } +#endif // Do not enqueue this reaction twice. if (pqueue_find_equal_same_priority(reaction_q, reaction) == NULL) { DEBUG_PRINT("Enqueing downstream reaction %s.", reaction->name); @@ -199,6 +206,11 @@ int _lf_do_step() { } } +#ifdef MODAL_REACTORS + // At the end of the step, perform mode transitions + _lf_handle_mode_changes(); +#endif + // No more reactions should be blocked at this point. //assert(pqueue_size(blocked_q) == 0); diff --git a/core/reactor.h b/core/reactor.h index b31499d50..f27776ff9 100644 --- a/core/reactor.h +++ b/core/reactor.h @@ -247,6 +247,21 @@ do { \ #endif +/** + * Sets the next mode of a modal reactor. Same as SET for outputs, only + * the last value will have effect if invoked multiple times. + * Works only in reactions with the target mode declared as effect. + * + * @param mode The target mode to set for activation. + */ +#ifdef MODAL_REACTORS +#define _LF_SET_MODE(mode) \ +do { \ + self->_lf__mode_state.next_mode = mode; \ + self->_lf__mode_state.mode_change = _lf_##mode##_change_type; \ +} while(0) +#endif + /** * Macro for extracting the deadline from the index of a reaction. * The reaction queue is sorted according to this index, and the @@ -428,6 +443,36 @@ typedef struct token_present_t { bool reset_is_present; // True to set is_present to false after calling done_using(). } token_present_t; + +#ifdef MODAL_REACTORS +/** Typedef for reactor_mode_t struct, used for representing a mode. */ +typedef struct reactor_mode_t reactor_mode_t; +/** Typedef for reactor_mode_state_t struct, used for storing modal state of reactor and/or its relation to enclosing modes. */ +typedef struct reactor_mode_state_t reactor_mode_state_t; + +/** A struct to represent a single mode instace in a reactor instance. */ +struct reactor_mode_t { + reactor_mode_state_t* state; // Pointer to a struct with the reactor's mode state. INSTANCE. + string name; // Name of this mode. + instant_t deactivation_time; // Time when the mode was left. +}; +/** A struct to store state of the modes in a reactor instance and/or its relation to enclosing modes. */ +struct reactor_mode_state_t { + reactor_mode_t* parent_mode; // Pointer to the next enclosing mode (if exsits). + reactor_mode_t* initial_mode; // Pointer to the initial mode. + reactor_mode_t* active_mode; // Pointer to the currently active mode. + reactor_mode_t* next_mode; // Pointer to the next mode to activate at the end of this step (if set). + char mode_change; // A mode change type flag (0: no change, 1: reset, 2: history). +}; +#else +/* + * Reactions and triggers must have a mode pointer to set up connection to enclosing modes, + * also when they are precompiled without modal reactors in order to later work in modal reactors. + * Hence define mode type as void in the absence of modes to treat mode pointer as void pointers for that time being. + */ +typedef void reactor_mode_t; +#endif + /** * Reaction activation record to push onto the reaction queue. * Some of the information in this struct is common among all instances @@ -471,6 +516,8 @@ struct reaction_t { char* name; // If logging is set to LOG or higher, then this will // point to the full name of the reactor followed by // the reaction number. + reactor_mode_t* mode; // The enclosing mode of this reaction (if exists). + // If enclosed in multiple, this will point to the innermost mode. }; /** Typedef for event_t struct, used for storing activation records. */ @@ -521,6 +568,8 @@ struct trigger_t { // coordination. // - Finally, if status is 'present', then this is an error since multiple // downstream messages have been produced for the same port for the same logical time. + reactor_mode_t* mode; // The enclosing mode of this reaction (if exists). + // If enclosed in multiple, this will point to the innermost mode. #ifdef FEDERATED tag_t last_known_status_tag; // Last known status of the port, either via a timed message, a port absent, or a // TAG from the RTI. @@ -632,6 +681,13 @@ void terminate_execution(); */ bool _lf_trigger_shutdown_reactions(); +/** + * Function (to be code generated) to handle mode changes. + */ +#ifdef MODAL_REACTORS +void _lf_handle_mode_changes(); +#endif + /** * Create a new token and initialize it. * The value pointer will be NULL and the length will be 0. diff --git a/core/reactor_common.c b/core/reactor_common.c index 913888f73..7a866a9f1 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -132,6 +132,12 @@ void enqueue_network_control_reactions(pqueue_t* reaction_q); port_status_t determine_port_status_if_possible(int portID); #endif +// Forward declaration for mode related functions +#ifdef MODAL_REACTORS +bool _lf_mode_is_active(reactor_mode_t* mode); +void _lf_suspend_event(event_t* event); +#endif + /** * Set the stop tag. * @@ -591,6 +597,14 @@ void _lf_pop_events() { continue; } +#ifdef MODAL_REACTORS + // If this event is associated with an incative it should haven been suspended and no longer on the event queue. + // FIXME This should not be possible + if (!_lf_mode_is_active(event->trigger->mode)) { + warning_print("Assumption violated. There is an event on the event queue that is associated to an inactive mode."); + } +#endif + lf_token_t *token = event->token; // Put the corresponding reactions onto the reaction queue. @@ -620,6 +634,15 @@ void _lf_pop_events() { } } #endif + +#ifdef MODAL_REACTORS + // Check if reaction is disabled by mode inactivity + if (!_lf_mode_is_active(reaction->mode)) { + DEBUG_PRINT("Suppressing reaction %s due inactive mode.", reaction->name); + continue; // Suppress reaction by preventing entering reaction queue + } +#endif + DEBUG_PRINT("Enqueing reaction %s.", reaction->name); pqueue_insert(reaction_q, reaction); } else { @@ -693,6 +716,17 @@ void _lf_pop_events() { */ void _lf_initialize_timer(trigger_t* timer) { interval_t delay = 0; + +#ifdef MODAL_REACTORS + // Suspend all timer events that start in inactive mode + if (!_lf_mode_is_active(timer->mode) && (timer->offset != 0 || timer->period != 0)) { + event_t* e = _lf_get_new_event(); + e->trigger = timer; + e->time = get_logical_time() + timer->offset; + _lf_suspend_event(e); + return; + } +#endif if (timer->offset == 0) { for (int i = 0; i < timer->number_of_reactions; i++) { _lf_enqueue_reaction(timer->reactions[i]); @@ -1286,6 +1320,14 @@ trigger_handle_t _lf_insert_reactions_for_trigger(trigger_t* trigger, lf_token_t return 0; } +#ifdef MODAL_REACTORS + // If this trigger is associated with an inactive mode, it should not trigger any reaction. + if (!_lf_mode_is_active(trigger->mode)) { + DEBUG_PRINT("Suppressing reactions of trigger due inactivity of mode %s.", trigger->mode->name); + return 1; + } +#endif + // Increment the reference count of the token. if (token != NULL) { token->ref_count++; @@ -1332,6 +1374,15 @@ trigger_handle_t _lf_insert_reactions_for_trigger(trigger_t* trigger, lf_token_t // onto the reaction queue. for (int i = 0; i < trigger->number_of_reactions; i++) { reaction_t* reaction = trigger->reactions[i]; + +#ifdef MODAL_REACTORS + // Check if reaction is disabled by mode inactivity + if (!_lf_mode_is_active(reaction->mode)) { + DEBUG_PRINT("Suppressing reaction %s due inactivity of mode %s.", reaction->name, reaction->mode->name); + continue; // Suppress reaction by preventing entering reaction queue + } +#endif + // Do not enqueue this reaction twice. if (pqueue_find_equal_same_priority(reaction_q, reaction) == NULL) { reaction->is_STP_violated = is_STP_violated; @@ -1881,3 +1932,179 @@ void termination() { } } } + +// Functions for handling modal reactors. +#ifdef MODAL_REACTORS + +/** + * Checks if the given mode is currently considered active. + * This includes checking all enclosing modes. + * @param mode The mode instace to check. + */ +bool _lf_mode_is_active(reactor_mode_t* mode) { + if (mode != NULL) { + //DEBUG_PRINT("Checking mode state of %s", mode->name); + reactor_mode_state_t* state = mode->state; + while (state != NULL) { + // If this or any parent mode is inactive, return inactive + if (state->active_mode != mode) { + //DEBUG_PRINT(" => Mode is inactive"); + return false; + } + mode = state->parent_mode; + if (mode != NULL) { + state = mode->state; + } else { + state = NULL; + } + } + //DEBUG_PRINT(" => Mode is active"); + } + return true; +} + +// Collection for suspending events in inactive modes +event_t** _suspended_events = NULL; +int _suspended_event_capacity = 0; +int _suspended_event_size = 0; + +/** + * Save the given event as suspended. + */ +void _lf_suspend_event(event_t* event) { + if (_suspended_event_size + 1 >= _suspended_event_capacity) { + _suspended_event_capacity += 10; + _suspended_events = (event_t**) realloc (_suspended_events, sizeof(event_t*) * _suspended_event_capacity); + } + _suspended_events[_suspended_event_size++] = event; +} + +/** + * Removes all null pointers in the collection of suspended events and updates its size. + */ +void _lf_defrag_suspended_events() { + int gap_head = -1; + for (int i = 0; i < _suspended_event_size; i++) { + if (_suspended_events[i] == NULL && gap_head == -1) { // new gap + gap_head = i; + } else if (gap_head != -1) { // end of gap -> move tail forward + memmove(_suspended_events + gap_head, _suspended_events + i, (_suspended_event_size - i) * sizeof(event_t*)); + _suspended_event_size -= i - gap_head; + i = gap_head; // continue on new position of this element + gap_head = -1; + } + } + if (gap_head != -1) { // gap end at the end of the list + _suspended_event_size -= _suspended_event_size - gap_head; // simply reduce size + } +} + +/** + * Performs transitions in all modal reactors. + * @param state An array of mode state of modal reactor instance Must be ordered hierarchically. + * Enclosing mode must come before inner. + * @param num_states The number of mode state. + */ +void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states) { + bool transition = false; // any mode change in this step + + // Detect mode changes (top down for hierarchical reset) + for (int i = 0; i < num_states; i++) { + reactor_mode_state_t* state = states[i]; + if (state != NULL) { + // Hierarchical reset: if this mode has parent that is entered in this step with a reset this reactor has to enter its initial mode + if (state->parent_mode != NULL && + state->parent_mode->state != NULL && + state->parent_mode->state->next_mode == state->parent_mode && + state->parent_mode->state->mode_change == 1) { + // Reset to initial state + state->next_mode = state->initial_mode; + state->mode_change = 1; // Enter with reset, to cascade it further down + DEBUG_PRINT("Hierarchical mode reset to %s when entering %s.", state->initial_mode->name, state->parent_mode->name); + } + + // Handle effect of entering next mode + if (state->next_mode != NULL) { + DEBUG_PRINT("Mode transition to %s.", state->next_mode->name); + transition = true; + + // TODO Introduce reset reaction trigger (for resetting state variables) and schedule here for next microstep. + + // Reset/Reactivate previously suspended events of next state + for (int i = 0; i < _suspended_event_size; i++) { + event_t* event = _suspended_events[i]; + + if (event != NULL && event->trigger != NULL && event->trigger->mode == state->next_mode) { + if (state->mode_change == 1) { // Reset transition + if (event->trigger->is_timer) { // Only reset timers + trigger_t* timer = event->trigger; + + DEBUG_PRINT("Re-enqueuing reset timer."); + // Reschedule the timer with no additional delay. + // This will take care of super dense time when offset is 0. + _lf_schedule(timer, 0, NULL); + } + // Drop all events upon reset (timer event was recreated by schedule and original can be removed here) + } else if (state->next_mode != state->active_mode && event->trigger != NULL) { // History transition to a different mode + // Remaining time that the event would have been waiting before mode was left + instant_t local_remaining_delay = event->time - (state->next_mode->deactivation_time != 0 ? state->next_mode->deactivation_time : get_start_time()); + // Reschedule event with original delay (schedule will add the standard offset, hence remove it) + DEBUG_PRINT("Re-enqueuing event with a suspended delay of %d (TTH: %d, Mode left at: %d).", local_remaining_delay, event->time, state->next_mode->deactivation_time); + // FIXME this does not correctly take microsteps into account + _lf_schedule(event->trigger, local_remaining_delay - event->trigger->offset, event->token); + } + // Clear out field for now; collection will be defragmented later + _suspended_events[i] = NULL; + + // A fresh event was created by schedule, hence, recycle old one + _lf_recycle_event(event); + } + } + } + } + } + + // Handle leaving active mode in all states + if (transition) { + // Defragment suspended events and update size (before suspending new ones) + _lf_defrag_suspended_events(); + + // Set new active mode and clear mode change flags + for (int i = 0; i < num_states; i++) { + reactor_mode_state_t* state = states[i]; + if (state != NULL && state->next_mode != NULL) { + // Save time when mode was left to handle suspended events in the future + state->active_mode->deactivation_time = get_logical_time(); + + // Apply transition + state->active_mode = state->next_mode; + state->next_mode = NULL; + state->mode_change = 0; + } + } + + // Retract all events from the event queue that are associate with now inactive modes + if (event_q != NULL) { + int q_size = pqueue_size(event_q); + event_t* delayed_removal[q_size]; + int delayed_removal_count = 0; + + // Find events + for (int i = 0; i < q_size; i++) { + event_t* event = (event_t*)event_q->d[i + 1]; // internal queue data structure omits index 0 + if (event != NULL && event->trigger != NULL && !_lf_mode_is_active(event->trigger->mode)) { + delayed_removal[delayed_removal_count++] = event; + _lf_suspend_event(event); + } + } + + // Events are removed delayed in order to allow linear iteration over the queue + DEBUG_PRINT("Pulling %d events from the event queue to suspend them. %d events are now suspended.", delayed_removal_count, _suspended_event_size); + for (int i = 0; i < delayed_removal_count; i++) { + // FIXME This will break next event references that models super dense time!!! + pqueue_remove(event_q, delayed_removal[i]); + } + } + } +} +#endif diff --git a/include/ctarget.h b/include/ctarget.h index 8427c35b2..5c28c3b0e 100644 --- a/include/ctarget.h +++ b/include/ctarget.h @@ -136,6 +136,20 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define SET_TOKEN(out, newtoken) _LF_SET_TOKEN(out, newtoken) +////////////////////////////////////////////////////////////// +///////////// SET_MODE Function (to switch a mode) + +/** + * Sets the next mode of a modal reactor. Same as SET for outputs, only + * the last value will have effect if invoked multiple times. + * Works only in reactions with the target mode declared as effect. + * + * @param mode The target mode to set for activation. + */ +#ifdef MODAL_REACTORS +#define SET_MODE(mode) _LF_SET_MODE(mode) +#endif + ////////////////////////////////////////////////////////////// ///////////// Schedule Functions From 07181695d61c88c855a7eb538b9531f5669f1b27 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 20 Oct 2021 18:50:56 +0200 Subject: [PATCH 02/13] modes: Added support for resetting state variables --- core/reactor.h | 9 ++ core/reactor_common.c | 279 ++++++++++++++++++++++-------------------- 2 files changed, 154 insertions(+), 134 deletions(-) diff --git a/core/reactor.h b/core/reactor.h index f27776ff9..3015819e7 100644 --- a/core/reactor.h +++ b/core/reactor.h @@ -449,6 +449,8 @@ typedef struct token_present_t { typedef struct reactor_mode_t reactor_mode_t; /** Typedef for reactor_mode_state_t struct, used for storing modal state of reactor and/or its relation to enclosing modes. */ typedef struct reactor_mode_state_t reactor_mode_state_t; +/** Typedef for mode_state_variable_reset_data_t struct, used for storing data for resetting state variables nested in modes. */ +typedef struct mode_state_variable_reset_data_t mode_state_variable_reset_data_t; /** A struct to represent a single mode instace in a reactor instance. */ struct reactor_mode_t { @@ -464,6 +466,13 @@ struct reactor_mode_state_t { reactor_mode_t* next_mode; // Pointer to the next mode to activate at the end of this step (if set). char mode_change; // A mode change type flag (0: no change, 1: reset, 2: history). }; +/** A struct to store data for resetting state variables nested in modes. */ +struct mode_state_variable_reset_data_t { + reactor_mode_t* mode; // Pointer to the enclosing mode. + void* target; // Pointer to the target variable. + void* source; // Pointer to the data source. + size_t size; // The size of the variable. +}; #else /* * Reactions and triggers must have a mode pointer to set up connection to enclosing modes, diff --git a/core/reactor_common.c b/core/reactor_common.c index 7a866a9f1..519ecbc0d 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -1942,25 +1942,25 @@ void termination() { * @param mode The mode instace to check. */ bool _lf_mode_is_active(reactor_mode_t* mode) { - if (mode != NULL) { - //DEBUG_PRINT("Checking mode state of %s", mode->name); - reactor_mode_state_t* state = mode->state; - while (state != NULL) { - // If this or any parent mode is inactive, return inactive - if (state->active_mode != mode) { - //DEBUG_PRINT(" => Mode is inactive"); - return false; - } - mode = state->parent_mode; - if (mode != NULL) { - state = mode->state; - } else { - state = NULL; - } - } - //DEBUG_PRINT(" => Mode is active"); - } - return true; + if (mode != NULL) { + //DEBUG_PRINT("Checking mode state of %s", mode->name); + reactor_mode_state_t* state = mode->state; + while (state != NULL) { + // If this or any parent mode is inactive, return inactive + if (state->active_mode != mode) { + //DEBUG_PRINT(" => Mode is inactive"); + return false; + } + mode = state->parent_mode; + if (mode != NULL) { + state = mode->state; + } else { + state = NULL; + } + } + //DEBUG_PRINT(" => Mode is active"); + } + return true; } // Collection for suspending events in inactive modes @@ -1972,31 +1972,31 @@ int _suspended_event_size = 0; * Save the given event as suspended. */ void _lf_suspend_event(event_t* event) { - if (_suspended_event_size + 1 >= _suspended_event_capacity) { - _suspended_event_capacity += 10; - _suspended_events = (event_t**) realloc (_suspended_events, sizeof(event_t*) * _suspended_event_capacity); - } - _suspended_events[_suspended_event_size++] = event; + if (_suspended_event_size + 1 >= _suspended_event_capacity) { + _suspended_event_capacity += 10; + _suspended_events = (event_t**) realloc (_suspended_events, sizeof(event_t*) * _suspended_event_capacity); + } + _suspended_events[_suspended_event_size++] = event; } /** * Removes all null pointers in the collection of suspended events and updates its size. */ void _lf_defrag_suspended_events() { - int gap_head = -1; - for (int i = 0; i < _suspended_event_size; i++) { - if (_suspended_events[i] == NULL && gap_head == -1) { // new gap - gap_head = i; - } else if (gap_head != -1) { // end of gap -> move tail forward - memmove(_suspended_events + gap_head, _suspended_events + i, (_suspended_event_size - i) * sizeof(event_t*)); - _suspended_event_size -= i - gap_head; - i = gap_head; // continue on new position of this element - gap_head = -1; - } - } - if (gap_head != -1) { // gap end at the end of the list - _suspended_event_size -= _suspended_event_size - gap_head; // simply reduce size - } + int gap_head = -1; + for (int i = 0; i < _suspended_event_size; i++) { + if (_suspended_events[i] == NULL && gap_head == -1) { // new gap + gap_head = i; + } else if (gap_head != -1) { // end of gap -> move tail forward + memmove(_suspended_events + gap_head, _suspended_events + i, (_suspended_event_size - i) * sizeof(event_t*)); + _suspended_event_size -= i - gap_head; + i = gap_head; // continue on new position of this element + gap_head = -1; + } + } + if (gap_head != -1) { // gap end at the end of the list + _suspended_event_size -= _suspended_event_size - gap_head; // simply reduce size + } } /** @@ -2005,106 +2005,117 @@ void _lf_defrag_suspended_events() { * Enclosing mode must come before inner. * @param num_states The number of mode state. */ -void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states) { - bool transition = false; // any mode change in this step - - // Detect mode changes (top down for hierarchical reset) - for (int i = 0; i < num_states; i++) { - reactor_mode_state_t* state = states[i]; - if (state != NULL) { - // Hierarchical reset: if this mode has parent that is entered in this step with a reset this reactor has to enter its initial mode - if (state->parent_mode != NULL && - state->parent_mode->state != NULL && - state->parent_mode->state->next_mode == state->parent_mode && - state->parent_mode->state->mode_change == 1) { - // Reset to initial state - state->next_mode = state->initial_mode; - state->mode_change = 1; // Enter with reset, to cascade it further down - DEBUG_PRINT("Hierarchical mode reset to %s when entering %s.", state->initial_mode->name, state->parent_mode->name); - } - - // Handle effect of entering next mode - if (state->next_mode != NULL) { - DEBUG_PRINT("Mode transition to %s.", state->next_mode->name); - transition = true; - - // TODO Introduce reset reaction trigger (for resetting state variables) and schedule here for next microstep. - - // Reset/Reactivate previously suspended events of next state - for (int i = 0; i < _suspended_event_size; i++) { - event_t* event = _suspended_events[i]; - - if (event != NULL && event->trigger != NULL && event->trigger->mode == state->next_mode) { - if (state->mode_change == 1) { // Reset transition - if (event->trigger->is_timer) { // Only reset timers - trigger_t* timer = event->trigger; - - DEBUG_PRINT("Re-enqueuing reset timer."); - // Reschedule the timer with no additional delay. - // This will take care of super dense time when offset is 0. - _lf_schedule(timer, 0, NULL); - } - // Drop all events upon reset (timer event was recreated by schedule and original can be removed here) - } else if (state->next_mode != state->active_mode && event->trigger != NULL) { // History transition to a different mode - // Remaining time that the event would have been waiting before mode was left - instant_t local_remaining_delay = event->time - (state->next_mode->deactivation_time != 0 ? state->next_mode->deactivation_time : get_start_time()); - // Reschedule event with original delay (schedule will add the standard offset, hence remove it) - DEBUG_PRINT("Re-enqueuing event with a suspended delay of %d (TTH: %d, Mode left at: %d).", local_remaining_delay, event->time, state->next_mode->deactivation_time); - // FIXME this does not correctly take microsteps into account - _lf_schedule(event->trigger, local_remaining_delay - event->trigger->offset, event->token); - } - // Clear out field for now; collection will be defragmented later - _suspended_events[i] = NULL; - - // A fresh event was created by schedule, hence, recycle old one - _lf_recycle_event(event); - } - } - } +void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mode_state_variable_reset_data_t reset_data[], int reset_data_size) { + bool transition = false; // any mode change in this step + + // Detect mode changes (top down for hierarchical reset) + for (int i = 0; i < num_states; i++) { + reactor_mode_state_t* state = states[i]; + if (state != NULL) { + // Hierarchical reset: if this mode has parent that is entered in this step with a reset this reactor has to enter its initial mode + if (state->parent_mode != NULL && + state->parent_mode->state != NULL && + state->parent_mode->state->next_mode == state->parent_mode && + state->parent_mode->state->mode_change == 1) { + // Reset to initial state + state->next_mode = state->initial_mode; + state->mode_change = 1; // Enter with reset, to cascade it further down + DEBUG_PRINT("Modes: Hierarchical mode reset to %s when entering %s.", state->initial_mode->name, state->parent_mode->name); + } + + // Handle effect of entering next mode + if (state->next_mode != NULL) { + DEBUG_PRINT("Modes: Transition to %s.", state->next_mode->name); + transition = true; + + if (state->mode_change == 1) { + // Reset state variables + for (int i = 0; i < reset_data_size; i++) { + mode_state_variable_reset_data_t data = reset_data[i]; + if (data.mode == state->next_mode) { + DEBUG_PRINT("Modes: Reseting state variable."); + memcpy(data.target, data.source, data.size); + } + } + } + + // TODO Introduce reset reaction trigger and schedule here for next microstep. + + // Reset/Reactivate previously suspended events of next state + for (int i = 0; i < _suspended_event_size; i++) { + event_t* event = _suspended_events[i]; + + if (event != NULL && event->trigger != NULL && event->trigger->mode == state->next_mode) { + if (state->mode_change == 1) { // Reset transition + if (event->trigger->is_timer) { // Only reset timers + trigger_t* timer = event->trigger; + + DEBUG_PRINT("Modes: Re-enqueuing reset timer."); + // Reschedule the timer with no additional delay. + // This will take care of super dense time when offset is 0. + _lf_schedule(timer, 0, NULL); + } + // Drop all events upon reset (timer event was recreated by schedule and original can be removed here) + } else if (state->next_mode != state->active_mode && event->trigger != NULL) { // History transition to a different mode + // Remaining time that the event would have been waiting before mode was left + instant_t local_remaining_delay = event->time - (state->next_mode->deactivation_time != 0 ? state->next_mode->deactivation_time : get_start_time()); + // Reschedule event with original delay (schedule will add the standard offset, hence remove it) + DEBUG_PRINT("Modes: Re-enqueuing event with a suspended delay of %d (TTH: %d, Mode left at: %d).", local_remaining_delay, event->time, state->next_mode->deactivation_time); + // FIXME this does not correctly take microsteps into account + _lf_schedule(event->trigger, local_remaining_delay - event->trigger->offset, event->token); + } + // Clear out field for now; collection will be defragmented later + _suspended_events[i] = NULL; + + // A fresh event was created by schedule, hence, recycle old one + _lf_recycle_event(event); + } + } + } } } - // Handle leaving active mode in all states + // Handle leaving active mode in all states if (transition) { - // Defragment suspended events and update size (before suspending new ones) - _lf_defrag_suspended_events(); - - // Set new active mode and clear mode change flags - for (int i = 0; i < num_states; i++) { - reactor_mode_state_t* state = states[i]; - if (state != NULL && state->next_mode != NULL) { - // Save time when mode was left to handle suspended events in the future - state->active_mode->deactivation_time = get_logical_time(); - - // Apply transition - state->active_mode = state->next_mode; - state->next_mode = NULL; - state->mode_change = 0; - } - } - - // Retract all events from the event queue that are associate with now inactive modes - if (event_q != NULL) { - int q_size = pqueue_size(event_q); - event_t* delayed_removal[q_size]; - int delayed_removal_count = 0; - - // Find events - for (int i = 0; i < q_size; i++) { - event_t* event = (event_t*)event_q->d[i + 1]; // internal queue data structure omits index 0 - if (event != NULL && event->trigger != NULL && !_lf_mode_is_active(event->trigger->mode)) { - delayed_removal[delayed_removal_count++] = event; - _lf_suspend_event(event); - } - } - - // Events are removed delayed in order to allow linear iteration over the queue - DEBUG_PRINT("Pulling %d events from the event queue to suspend them. %d events are now suspended.", delayed_removal_count, _suspended_event_size); - for (int i = 0; i < delayed_removal_count; i++) { - // FIXME This will break next event references that models super dense time!!! - pqueue_remove(event_q, delayed_removal[i]); - } - } + // Defragment suspended events and update size (before suspending new ones) + _lf_defrag_suspended_events(); + + // Set new active mode and clear mode change flags + for (int i = 0; i < num_states; i++) { + reactor_mode_state_t* state = states[i]; + if (state != NULL && state->next_mode != NULL) { + // Save time when mode was left to handle suspended events in the future + state->active_mode->deactivation_time = get_logical_time(); + + // Apply transition + state->active_mode = state->next_mode; + state->next_mode = NULL; + state->mode_change = 0; + } + } + + // Retract all events from the event queue that are associate with now inactive modes + if (event_q != NULL) { + int q_size = pqueue_size(event_q); + event_t* delayed_removal[q_size]; + int delayed_removal_count = 0; + + // Find events + for (int i = 0; i < q_size; i++) { + event_t* event = (event_t*)event_q->d[i + 1]; // internal queue data structure omits index 0 + if (event != NULL && event->trigger != NULL && !_lf_mode_is_active(event->trigger->mode)) { + delayed_removal[delayed_removal_count++] = event; + _lf_suspend_event(event); + } + } + + // Events are removed delayed in order to allow linear iteration over the queue + DEBUG_PRINT("Modes: Pulling %d events from the event queue to suspend them. %d events are now suspended.", delayed_removal_count, _suspended_event_size); + for (int i = 0; i < delayed_removal_count; i++) { + // FIXME This will break next event references that models super dense time!!! + pqueue_remove(event_q, delayed_removal[i]); + } + } } } #endif From 2416c663b11508e7f5c952570cd548dbed68d8c3 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Fri, 29 Oct 2021 18:49:51 +0200 Subject: [PATCH 03/13] Added handling for suspended events with micro steps --- core/reactor_common.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/core/reactor_common.c b/core/reactor_common.c index 519ecbc0d..2a9a38289 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -2039,8 +2039,6 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo } } - // TODO Introduce reset reaction trigger and schedule here for next microstep. - // Reset/Reactivate previously suspended events of next state for (int i = 0; i < _suspended_event_size; i++) { event_t* event = _suspended_events[i]; @@ -2060,9 +2058,17 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo // Remaining time that the event would have been waiting before mode was left instant_t local_remaining_delay = event->time - (state->next_mode->deactivation_time != 0 ? state->next_mode->deactivation_time : get_start_time()); // Reschedule event with original delay (schedule will add the standard offset, hence remove it) - DEBUG_PRINT("Modes: Re-enqueuing event with a suspended delay of %d (TTH: %d, Mode left at: %d).", local_remaining_delay, event->time, state->next_mode->deactivation_time); - // FIXME this does not correctly take microsteps into account + DEBUG_PRINT("Modes: Re-enqueuing event with a suspended delay of %d (previous TTH: %d, Mode left at: %d).", local_remaining_delay, event->time, state->next_mode->deactivation_time); _lf_schedule(event->trigger, local_remaining_delay - event->trigger->offset, event->token); + + if (event->next != NULL) { + // The event has more events stacked up in super dense time, attach them to the newly created event. + if (event->trigger->last->next == NULL) { + event->trigger->last->next = event->next; + } else { + error_print("Modes: Cannot attach events stacked up in super dense to the just unsuspended root event."); + } + } } // Clear out field for now; collection will be defragmented later _suspended_events[i] = NULL; @@ -2105,6 +2111,7 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo event_t* event = (event_t*)event_q->d[i + 1]; // internal queue data structure omits index 0 if (event != NULL && event->trigger != NULL && !_lf_mode_is_active(event->trigger->mode)) { delayed_removal[delayed_removal_count++] = event; + // This will store the event including possibly those chained up next in super dense time _lf_suspend_event(event); } } @@ -2112,7 +2119,6 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo // Events are removed delayed in order to allow linear iteration over the queue DEBUG_PRINT("Modes: Pulling %d events from the event queue to suspend them. %d events are now suspended.", delayed_removal_count, _suspended_event_size); for (int i = 0; i < delayed_removal_count; i++) { - // FIXME This will break next event references that models super dense time!!! pqueue_remove(event_q, delayed_removal[i]); } } From 4f5ead51b1fd2ab1c808606ee77c9ffea595ce41 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Thu, 4 Nov 2021 14:53:06 +0100 Subject: [PATCH 04/13] modes: Memory of suspended evens will be freed at termination --- core/reactor_common.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/core/reactor_common.c b/core/reactor_common.c index 2a9a38289..48227d518 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -136,6 +136,7 @@ port_status_t determine_port_status_if_possible(int portID); #ifdef MODAL_REACTORS bool _lf_mode_is_active(reactor_mode_t* mode); void _lf_suspend_event(event_t* event); +void _lf_terminate_modal_reactors(); #endif /** @@ -1900,6 +1901,11 @@ void termination() { // In order to free tokens, we perform the same actions we would have for a new time step. _lf_start_time_step(); +#ifdef MODAL_REACTORS + // Free events and tokens suspended by modal reactors. + _lf_terminate_modal_reactors(); +#endif + // If the event queue still has events on it, report that. if (event_q != NULL && pqueue_size(event_q) > 0) { warning_print("---- There are %zu unprocessed future events on the event queue.", pqueue_size(event_q)); @@ -2124,4 +2130,17 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo } } } + +/** + * Releases internal data structures for modes. + * - Frees all suspended events. + */ +void _lf_terminate_modal_reactors() { + for (int i = 0; i < _suspended_event_size; i++) { + _lf_recycle_event(_suspended_events[i]); + } + free(_suspended_events); + _suspended_event_size = -999; + _suspended_event_capacity = -999; +} #endif From 79c7af7cd9dc1b26ea04d023013eef4f0d2a5eb7 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Fri, 3 Dec 2021 13:41:16 +0100 Subject: [PATCH 05/13] modes: Switched to using _lf_schedule_at_tag when for suspended events _lf_schedule applied minimal spacing and base delays --- core/reactor_common.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/core/reactor_common.c b/core/reactor_common.c index 48227d518..badb5fb6d 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -1945,9 +1945,14 @@ void termination() { /** * Checks if the given mode is currently considered active. * This includes checking all enclosing modes. + * * @param mode The mode instace to check. */ bool _lf_mode_is_active(reactor_mode_t* mode) { + /* + * This code could be optimized by introducing a cached activity indicator + * in all mode states. But for now: no premature optimization. + */ if (mode != NULL) { //DEBUG_PRINT("Checking mode state of %s", mode->name); reactor_mode_state_t* state = mode->state; @@ -1969,7 +1974,7 @@ bool _lf_mode_is_active(reactor_mode_t* mode) { return true; } -// Collection for suspending events in inactive modes +// Collections for suspended events in inactive modes event_t** _suspended_events = NULL; int _suspended_event_capacity = 0; int _suspended_event_size = 0; @@ -2048,7 +2053,6 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo // Reset/Reactivate previously suspended events of next state for (int i = 0; i < _suspended_event_size; i++) { event_t* event = _suspended_events[i]; - if (event != NULL && event->trigger != NULL && event->trigger->mode == state->next_mode) { if (state->mode_change == 1) { // Reset transition if (event->trigger->is_timer) { // Only reset timers @@ -2056,16 +2060,19 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo DEBUG_PRINT("Modes: Re-enqueuing reset timer."); // Reschedule the timer with no additional delay. - // This will take care of super dense time when offset is 0. + // This will take care of super dense time when offset is 0 and will apply the initial offset. _lf_schedule(timer, 0, NULL); } - // Drop all events upon reset (timer event was recreated by schedule and original can be removed here) + // No further processing; drops all events upon reset (timer event was recreated by schedule and original can be removed here) } else if (state->next_mode != state->active_mode && event->trigger != NULL) { // History transition to a different mode // Remaining time that the event would have been waiting before mode was left instant_t local_remaining_delay = event->time - (state->next_mode->deactivation_time != 0 ? state->next_mode->deactivation_time : get_start_time()); - // Reschedule event with original delay (schedule will add the standard offset, hence remove it) - DEBUG_PRINT("Modes: Re-enqueuing event with a suspended delay of %d (previous TTH: %d, Mode left at: %d).", local_remaining_delay, event->time, state->next_mode->deactivation_time); - _lf_schedule(event->trigger, local_remaining_delay - event->trigger->offset, event->token); + tag_t current_logical_tag = get_current_tag(); + + // Reschedule event with original local delay + DEBUG_PRINT("Modes: Re-enqueuing event with a suspended delay of %d (previous TTH: %d, Mode suspended at: %d).", local_remaining_delay, event->time, state->next_mode->deactivation_time); + tag_t schedule_tag = {.time = current_logical_tag.time + local_remaining_delay, .microstep = (local_remaining_delay == 0 ? current_logical_tag.microstep + 1 : 0)}; + _lf_schedule_at_tag(event->trigger, schedule_tag, event->token); if (event->next != NULL) { // The event has more events stacked up in super dense time, attach them to the newly created event. @@ -2089,7 +2096,7 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo // Handle leaving active mode in all states if (transition) { - // Defragment suspended events and update size (before suspending new ones) + // Defragment suspended events collection and update size (before suspending new ones) _lf_defrag_suspended_events(); // Set new active mode and clear mode change flags @@ -2106,7 +2113,7 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo } } - // Retract all events from the event queue that are associate with now inactive modes + // Retract all events from the event queue that are associated with now inactive modes if (event_q != NULL) { int q_size = pqueue_size(event_q); event_t* delayed_removal[q_size]; @@ -2117,7 +2124,7 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo event_t* event = (event_t*)event_q->d[i + 1]; // internal queue data structure omits index 0 if (event != NULL && event->trigger != NULL && !_lf_mode_is_active(event->trigger->mode)) { delayed_removal[delayed_removal_count++] = event; - // This will store the event including possibly those chained up next in super dense time + // This will store the event including possibly those chained up in super dense time _lf_suspend_event(event); } } @@ -2140,7 +2147,7 @@ void _lf_terminate_modal_reactors() { _lf_recycle_event(_suspended_events[i]); } free(_suspended_events); - _suspended_event_size = -999; - _suspended_event_capacity = -999; + _suspended_event_size = -666; + _suspended_event_capacity = -666; } #endif From 99519062de8f467f281e6c4ce5e2c1bc2c84f134 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 8 Dec 2021 18:17:25 +0100 Subject: [PATCH 06/13] modes: Fix bug in defragmentation of suspended events list that caused seg fault --- core/reactor_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/reactor_common.c b/core/reactor_common.c index 7215c5e51..13d1fdb5d 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -1983,7 +1983,7 @@ void _lf_defrag_suspended_events() { for (int i = 0; i < _suspended_event_size; i++) { if (_suspended_events[i] == NULL && gap_head == -1) { // new gap gap_head = i; - } else if (gap_head != -1) { // end of gap -> move tail forward + } else if (_suspended_events[i] != NULL && gap_head != -1) { // end of gap -> move tail forward memmove(_suspended_events + gap_head, _suspended_events + i, (_suspended_event_size - i) * sizeof(event_t*)); _suspended_event_size -= i - gap_head; i = gap_head; // continue on new position of this element From 52ad31a6278686433e50a3311e55e818bb83e486 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Fri, 10 Dec 2021 15:09:29 +0100 Subject: [PATCH 07/13] modes: Switched to a linked list structure to store suspended events --- core/reactor_common.c | 116 +++++++++++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 42 deletions(-) diff --git a/core/reactor_common.c b/core/reactor_common.c index 13d1fdb5d..bf26a5ddf 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -147,7 +147,7 @@ port_status_t determine_port_status_if_possible(int portID); // Forward declaration for mode related functions #ifdef MODAL_REACTORS bool _lf_mode_is_active(reactor_mode_t* mode); -void _lf_suspend_event(event_t* event); +void _lf_add_suspended_event(event_t* event); void _lf_terminate_modal_reactors(); #endif @@ -708,7 +708,7 @@ void _lf_initialize_timer(trigger_t* timer) { event_t* e = _lf_get_new_event(); e->trigger = timer; e->time = get_logical_time() + timer->offset; - _lf_suspend_event(e); + _lf_add_suspended_event(e); return; } #endif @@ -1931,7 +1931,7 @@ void termination() { * Checks if the given mode is currently considered active. * This includes checking all enclosing modes. * - * @param mode The mode instace to check. + * @param mode The mode instance to check. */ bool _lf_mode_is_active(reactor_mode_t* mode) { /* @@ -1959,40 +1959,60 @@ bool _lf_mode_is_active(reactor_mode_t* mode) { return true; } -// Collections for suspended events in inactive modes -event_t** _suspended_events = NULL; -int _suspended_event_capacity = 0; -int _suspended_event_size = 0; +// Linked list element for suspended events in inactive modes +typedef struct _lf_suspended_event { + struct _lf_suspended_event* next; + event_t* event; +} _lf_suspended_event_t; +_lf_suspended_event_t* _lf_suspended_events_head = NULL; // Start of linked collection of suspended events (managed automatically!) +int _lf_suspended_events_num = 0; // Number of suspended events (managed automatically!) +_lf_suspended_event_t* _lf_unsused_suspended_events_head = NULL; // Internal collection of reusable list elements (managed automatically!) /** * Save the given event as suspended. */ -void _lf_suspend_event(event_t* event) { - if (_suspended_event_size + 1 >= _suspended_event_capacity) { - _suspended_event_capacity += 10; - _suspended_events = (event_t**) realloc (_suspended_events, sizeof(event_t*) * _suspended_event_capacity); +void _lf_add_suspended_event(event_t* event) { + _lf_suspended_event_t* new_suspended_event; + if (_lf_unsused_suspended_events_head != NULL) { + new_suspended_event = _lf_unsused_suspended_events_head; + _lf_unsused_suspended_events_head = _lf_unsused_suspended_events_head->next; + } else { + new_suspended_event = malloc(sizeof(_lf_suspended_event_t)); } - _suspended_events[_suspended_event_size++] = event; + + new_suspended_event->event = event; + new_suspended_event->next = _lf_suspended_events_head; // prepend + _lf_suspended_events_num++; + + _lf_suspended_events_head = new_suspended_event; } /** - * Removes all null pointers in the collection of suspended events and updates its size. + * Removes the given node from the list of suspended events. + * Returns the next element in the list. */ -void _lf_defrag_suspended_events() { - int gap_head = -1; - for (int i = 0; i < _suspended_event_size; i++) { - if (_suspended_events[i] == NULL && gap_head == -1) { // new gap - gap_head = i; - } else if (_suspended_events[i] != NULL && gap_head != -1) { // end of gap -> move tail forward - memmove(_suspended_events + gap_head, _suspended_events + i, (_suspended_event_size - i) * sizeof(event_t*)); - _suspended_event_size -= i - gap_head; - i = gap_head; // continue on new position of this element - gap_head = -1; - } +_lf_suspended_event_t* _lf_remove_suspended_event(_lf_suspended_event_t* event) { + _lf_suspended_event_t* next = event->next; + + // Clear content + event->event = NULL; + event->next = NULL; + _lf_suspended_events_num--; + + // Store for recycling + if (_lf_unsused_suspended_events_head == NULL) { + _lf_unsused_suspended_events_head = event; + } else { + event->next = _lf_unsused_suspended_events_head; + _lf_unsused_suspended_events_head = event; } - if (gap_head != -1) { // gap end at the end of the list - _suspended_event_size -= _suspended_event_size - gap_head; // simply reduce size + + // Adjust head + if (_lf_suspended_events_head == event) { + _lf_suspended_events_head = next; } + + return next; } /** @@ -2036,8 +2056,9 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo } // Reset/Reactivate previously suspended events of next state - for (int i = 0; i < _suspended_event_size; i++) { - event_t* event = _suspended_events[i]; + _lf_suspended_event_t* suspended_event = _lf_suspended_events_head; + while(suspended_event != NULL) { + event_t* event = suspended_event->event; if (event != NULL && event->trigger != NULL && event->trigger->mode == state->next_mode) { if (state->mode_change == 1) { // Reset transition if (event->trigger->is_timer) { // Only reset timers @@ -2055,7 +2076,7 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo tag_t current_logical_tag = get_current_tag(); // Reschedule event with original local delay - DEBUG_PRINT("Modes: Re-enqueuing event with a suspended delay of %d (previous TTH: %d, Mode suspended at: %d).", local_remaining_delay, event->time, state->next_mode->deactivation_time); + DEBUG_PRINT("Modes: Re-enqueuing event with a suspended delay of %d (previous TTH: %u, Mode suspended at: %u).", local_remaining_delay, event->time, state->next_mode->deactivation_time); tag_t schedule_tag = {.time = current_logical_tag.time + local_remaining_delay, .microstep = (local_remaining_delay == 0 ? current_logical_tag.microstep + 1 : 0)}; _lf_schedule_at_tag(event->trigger, schedule_tag, event->token); @@ -2068,11 +2089,13 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo } } } - // Clear out field for now; collection will be defragmented later - _suspended_events[i] = NULL; - // A fresh event was created by schedule, hence, recycle old one _lf_recycle_event(event); + + // Remove suspended event and continue + suspended_event = _lf_remove_suspended_event(suspended_event); + } else { + suspended_event = suspended_event->next; } } } @@ -2081,9 +2104,6 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo // Handle leaving active mode in all states if (transition) { - // Defragment suspended events collection and update size (before suspending new ones) - _lf_defrag_suspended_events(); - // Set new active mode and clear mode change flags for (int i = 0; i < num_states; i++) { reactor_mode_state_t* state = states[i]; @@ -2110,12 +2130,12 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo if (event != NULL && event->trigger != NULL && !_lf_mode_is_active(event->trigger->mode)) { delayed_removal[delayed_removal_count++] = event; // This will store the event including possibly those chained up in super dense time - _lf_suspend_event(event); + _lf_add_suspended_event(event); } } // Events are removed delayed in order to allow linear iteration over the queue - DEBUG_PRINT("Modes: Pulling %d events from the event queue to suspend them. %d events are now suspended.", delayed_removal_count, _suspended_event_size); + DEBUG_PRINT("Modes: Pulling %d events from the event queue to suspend them. %d events are now suspended.", delayed_removal_count, _lf_suspended_events_num); for (int i = 0; i < delayed_removal_count; i++) { pqueue_remove(event_q, delayed_removal[i]); } @@ -2128,11 +2148,23 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo * - Frees all suspended events. */ void _lf_terminate_modal_reactors() { - for (int i = 0; i < _suspended_event_size; i++) { - _lf_recycle_event(_suspended_events[i]); + _lf_suspended_event_t* suspended_event = _lf_suspended_events_head; + while(suspended_event != NULL) { + _lf_recycle_event(suspended_event->event); + _lf_suspended_event_t* next = suspended_event->next; + free(suspended_event); + suspended_event = next; + } + _lf_suspended_events_head = NULL; + _lf_suspended_events_num = 0; + + // Also free suspended_event elements stored for recycling + suspended_event = _lf_unsused_suspended_events_head; + while(suspended_event != NULL) { + _lf_suspended_event_t* next = suspended_event->next; + free(suspended_event); + suspended_event = next; } - free(_suspended_events); - _suspended_event_size = -666; - _suspended_event_capacity = -666; + _lf_unsused_suspended_events_head = NULL; } #endif From 9f133cafae61ad150d80ba31355e8d217cd7b7e8 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Wed, 15 Dec 2021 15:36:17 +0100 Subject: [PATCH 08/13] modes: Added experimental support for threaded execution for modes --- core/reactor_threaded.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/reactor_threaded.c b/core/reactor_threaded.c index 28bb89306..c7c013f9f 100644 --- a/core/reactor_threaded.c +++ b/core/reactor_threaded.c @@ -903,6 +903,12 @@ void _lf_enqueue_reaction(reaction_t* reaction) { // Acquire the mutex lock. lf_mutex_lock(&mutex); if (reaction != NULL && reaction->status == inactive) { +#ifdef MODAL_REACTORS + // Check if reaction is disabled by mode inactivity + if (!_lf_mode_is_active(reaction->mode)) { + DEBUG_PRINT("Suppressing downstream reaction %s due inactivity of mode %s.", reaction->name, reaction->mode->name); + } else { // Suppress reaction by preventing entering reaction queue +#endif DEBUG_PRINT("Enqueing downstream reaction %s, which has level %lld.", reaction->name, reaction->index & 0xffffLL); reaction->status = queued; @@ -912,6 +918,9 @@ void _lf_enqueue_reaction(reaction_t* reaction) { // It is now handled by schedule_output_reactions() in reactor_common, // which calls the _lf_notify_workers() function defined below. // lf_cond_signal(&reaction_q_changed); +#ifdef MODAL_REACTORS + } +#endif } lf_mutex_unlock(&mutex); } @@ -1094,6 +1103,11 @@ bool _lf_worker_advance_tag_locked(int worker_number) { _lf_logical_tag_completed = true; +#ifdef MODAL_REACTORS + // Perform mode transitions + _lf_handle_mode_changes(); +#endif + // Advance time. // _lf_next_locked() may block waiting for real time to pass or events to appear. // to appear on the event queue. Note that we already From 19a9f5973990a6f7ae861305f504ad3dfee48976 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Thu, 16 Dec 2021 15:15:31 +0100 Subject: [PATCH 09/13] modes: Improved allocation of array. Resolved compiler errors C2057 and C2466. --- core/reactor_common.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/core/reactor_common.c b/core/reactor_common.c index bf26a5ddf..c97332bb3 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -2120,24 +2120,28 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo // Retract all events from the event queue that are associated with now inactive modes if (event_q != NULL) { - int q_size = pqueue_size(event_q); - event_t* delayed_removal[q_size]; - int delayed_removal_count = 0; - - // Find events - for (int i = 0; i < q_size; i++) { - event_t* event = (event_t*)event_q->d[i + 1]; // internal queue data structure omits index 0 - if (event != NULL && event->trigger != NULL && !_lf_mode_is_active(event->trigger->mode)) { - delayed_removal[delayed_removal_count++] = event; - // This will store the event including possibly those chained up in super dense time - _lf_add_suspended_event(event); + size_t q_size = pqueue_size(event_q); + if (q_size > 0) { + event_t** delayed_removal = (event_t**) calloc(q_size, sizeof(event_t*)); + size_t delayed_removal_count = 0; + + // Find events + for (int i = 0; i < q_size; i++) { + event_t* event = (event_t*)event_q->d[i + 1]; // internal queue data structure omits index 0 + if (event != NULL && event->trigger != NULL && !_lf_mode_is_active(event->trigger->mode)) { + delayed_removal[delayed_removal_count++] = event; + // This will store the event including possibly those chained up in super dense time + _lf_add_suspended_event(event); + } + } + + // Events are removed delayed in order to allow linear iteration over the queue + DEBUG_PRINT("Modes: Pulling %d events from the event queue to suspend them. %d events are now suspended.", delayed_removal_count, _lf_suspended_events_num); + for (int i = 0; i < delayed_removal_count; i++) { + pqueue_remove(event_q, delayed_removal[i]); } - } - // Events are removed delayed in order to allow linear iteration over the queue - DEBUG_PRINT("Modes: Pulling %d events from the event queue to suspend them. %d events are now suspended.", delayed_removal_count, _lf_suspended_events_num); - for (int i = 0; i < delayed_removal_count; i++) { - pqueue_remove(event_q, delayed_removal[i]); + free(delayed_removal); } } } From e1d381e62aa240763a4a9ad77f691dac7e4838ca Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Thu, 16 Dec 2021 16:09:55 +0100 Subject: [PATCH 10/13] modes: Added explicit cast after malloc --- core/reactor_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/reactor_common.c b/core/reactor_common.c index c97332bb3..b72299db5 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -1977,7 +1977,7 @@ void _lf_add_suspended_event(event_t* event) { new_suspended_event = _lf_unsused_suspended_events_head; _lf_unsused_suspended_events_head = _lf_unsused_suspended_events_head->next; } else { - new_suspended_event = malloc(sizeof(_lf_suspended_event_t)); + new_suspended_event = (_lf_suspended_event_t*) malloc(sizeof(_lf_suspended_event_t)); } new_suspended_event->event = event; From f027918e2ff2524bed302f7f274c283cfe09c9a1 Mon Sep 17 00:00:00 2001 From: Alexander Schulz-Rosengarten Date: Fri, 4 Feb 2022 16:13:47 +0100 Subject: [PATCH 11/13] modes: Fixed bug in suspended events list and timer offset --- core/reactor_common.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/core/reactor_common.c b/core/reactor_common.c index d8cd8c7f4..a878673cb 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -2148,9 +2148,16 @@ _lf_suspended_event_t* _lf_remove_suspended_event(_lf_suspended_event_t* event) _lf_unsused_suspended_events_head = event; } - // Adjust head if (_lf_suspended_events_head == event) { - _lf_suspended_events_head = next; + _lf_suspended_events_head = next; // Adjust head + } else { + _lf_suspended_event_t* predecessor = _lf_suspended_events_head; + while(predecessor->next != event && predecessor != NULL) { + predecessor = predecessor->next; + } + if (predecessor != NULL) { + predecessor->next = next; // Remove from linked list + } } return next; @@ -2207,8 +2214,8 @@ void _lf_process_mode_changes(reactor_mode_state_t* states[], int num_states, mo DEBUG_PRINT("Modes: Re-enqueuing reset timer."); // Reschedule the timer with no additional delay. - // This will take care of super dense time when offset is 0 and will apply the initial offset. - _lf_schedule(timer, 0, NULL); + // This will take care of super dense time when offset is 0. + _lf_schedule(timer, event->trigger->offset, NULL); } // No further processing; drops all events upon reset (timer event was recreated by schedule and original can be removed here) } else if (state->next_mode != state->active_mode && event->trigger != NULL) { // History transition to a different mode From cc0f07c6e09ab75ce1c8529309824b42142470f4 Mon Sep 17 00:00:00 2001 From: eal Date: Fri, 25 Feb 2022 14:37:25 -0800 Subject: [PATCH 12/13] Small stylistic changes to lf_sched_trigger_reaction implementations in schedulers --- core/threaded/scheduler_GEDF_NP.c | 12 ++++------ core/threaded/scheduler_GEDF_NP_CI.c | 35 +++++++++------------------- core/threaded/scheduler_NP.c | 16 ++++++------- core/threaded/scheduler_PEDF_NP.c | 21 +++++++---------- 4 files changed, 32 insertions(+), 52 deletions(-) diff --git a/core/threaded/scheduler_GEDF_NP.c b/core/threaded/scheduler_GEDF_NP.c index e491f56cf..ead86566f 100644 --- a/core/threaded/scheduler_GEDF_NP.c +++ b/core/threaded/scheduler_GEDF_NP.c @@ -350,12 +350,10 @@ void lf_sched_done_with_reaction(size_t worker_number, * worker number does not make sense (e.g., the caller is not a worker thread). */ void lf_sched_trigger_reaction(reaction_t* reaction, int worker_number) { - // Protect against putting a reaction twice on the reaction queue by - // checking its status. - if (reaction != NULL && - lf_bool_compare_and_swap(&reaction->status, inactive, queued)) { - DEBUG_PRINT("Scheduler: Enqueing reaction %s, which has level %lld.", - reaction->name, LEVEL(reaction->index)); - _lf_sched_insert_reaction(reaction); + if (reaction == NULL || !lf_bool_compare_and_swap(&reaction->status, inactive, queued)) { + return; } + DEBUG_PRINT("Scheduler: Enqueing reaction %s, which has level %lld.", + reaction->name, LEVEL(reaction->index)); + _lf_sched_insert_reaction(reaction); } \ No newline at end of file diff --git a/core/threaded/scheduler_GEDF_NP_CI.c b/core/threaded/scheduler_GEDF_NP_CI.c index 5279e8957..a7364f05a 100644 --- a/core/threaded/scheduler_GEDF_NP_CI.c +++ b/core/threaded/scheduler_GEDF_NP_CI.c @@ -512,32 +512,19 @@ void lf_sched_done_with_reaction(size_t worker_number, * worker number does not make sense (e.g., the caller is not a worker thread). */ void lf_sched_trigger_reaction(reaction_t* reaction, int worker_number) { - if (worker_number == -1) { - // The scheduler should handle this immediately - // Protect against putting a reaction twice on the reaction queue by - // checking its status. - if (reaction != NULL && - lf_bool_compare_and_swap(&reaction->status, inactive, queued)) { - lf_mutex_lock(&mutex); - DEBUG_PRINT( - "Scheduler: Enqueing reaction %s, which has level %lld.", - reaction->name, LEVEL(reaction->index)); - // Immediately put 'reaction' on the reaction queue. - pqueue_insert( - (pqueue_t*)_lf_sched_instance->_lf_sched_triggered_reactions, - (void*)reaction); - lf_mutex_unlock(&mutex); - } + if (reaction == NULL || !lf_bool_compare_and_swap(&reaction->status, inactive, queued)) { return; } - // Protect against putting a reaction twice on the reaction queue by - // checking its status. - if (reaction != NULL && - lf_bool_compare_and_swap(&reaction->status, inactive, queued)) { - DEBUG_PRINT( - "Scheduler: Worker %d: Enqueuing reaction %s, which has level " - "%lld.", - worker_number, reaction->name, LEVEL(reaction->index)); + DEBUG_PRINT("Scheduler: Enqueing reaction %s, which has level %lld.", + reaction->name, LEVEL(reaction->index)); + if (worker_number == -1) { + lf_mutex_lock(&mutex); + // Immediately put 'reaction' on the reaction queue. + pqueue_insert( + (pqueue_t*)_lf_sched_instance->_lf_sched_triggered_reactions, + (void*)reaction); + lf_mutex_unlock(&mutex); + } else { reaction->worker_affinity = worker_number; // Note: The scheduler has already checked that we are not enqueueing // this reaction twice. diff --git a/core/threaded/scheduler_NP.c b/core/threaded/scheduler_NP.c index 5b1b61319..e74d96cfb 100644 --- a/core/threaded/scheduler_NP.c +++ b/core/threaded/scheduler_NP.c @@ -416,6 +416,8 @@ void lf_sched_done_with_reaction(size_t worker_number, * * If a worker number is not available (e.g., this function is not called by a * worker thread), -1 should be passed as the 'worker_number'. + * + * This scheduler ignores the worker number. * * The scheduler will ensure that the same reaction is not triggered twice in * the same tag. @@ -428,12 +430,10 @@ void lf_sched_done_with_reaction(size_t worker_number, * */ void lf_sched_trigger_reaction(reaction_t* reaction, int worker_number) { - // Protect against putting a reaction twice in the reaction vectors by - // checking its status. - if (reaction != NULL && - lf_bool_compare_and_swap(&reaction->status, inactive, queued)) { - DEBUG_PRINT("Scheduler: Enqueing reaction %s, which has level %lld.", - reaction->name, LEVEL(reaction->index)); - _lf_sched_insert_reaction(reaction); + if (reaction == NULL || !lf_bool_compare_and_swap(&reaction->status, inactive, queued)) { + return; } -} \ No newline at end of file + DEBUG_PRINT("Scheduler: Enqueing reaction %s, which has level %lld.", + reaction->name, LEVEL(reaction->index)); + _lf_sched_insert_reaction(reaction); +} diff --git a/core/threaded/scheduler_PEDF_NP.c b/core/threaded/scheduler_PEDF_NP.c index b204f7118..89c2199a9 100644 --- a/core/threaded/scheduler_PEDF_NP.c +++ b/core/threaded/scheduler_PEDF_NP.c @@ -656,22 +656,17 @@ void lf_sched_done_with_reaction(size_t worker_number, reaction_t* done_reaction * */ void lf_sched_trigger_reaction(reaction_t* reaction, int worker_number) { + if (reaction == NULL || !lf_bool_compare_and_swap(&reaction->status, inactive, queued)) { + return; + } + DEBUG_PRINT("Scheduler: Enqueing reaction %s, which has level %lld.", + reaction->name, LEVEL(reaction->index)); if (worker_number == -1) { - // The scheduler should handle this immediately lf_mutex_lock(&mutex); - // Do not enqueue this reaction twice. - if (reaction != NULL && lf_bool_compare_and_swap(&reaction->status, inactive, queued)) { - DEBUG_PRINT("Enqueing downstream reaction %s, which has level %lld.", - reaction->name, reaction->index & 0xffffLL); - // Immediately put 'reaction' on the reaction queue. - pqueue_insert((pqueue_t*)_lf_sched_instance->_lf_sched_triggered_reactions, reaction); - } + // Immediately put 'reaction' on the reaction queue. + pqueue_insert((pqueue_t*)_lf_sched_instance->_lf_sched_triggered_reactions, reaction); lf_mutex_unlock(&mutex); - return; - } - if (reaction != NULL && lf_bool_compare_and_swap(&reaction->status, inactive, queued)) { - DEBUG_PRINT("Worker %d: Enqueuing downstream reaction %s, which has level %lld.", - worker_number, reaction->name, reaction->index & 0xffffLL); + } else { reaction->worker_affinity = worker_number; // Note: The scheduler will check that we don't enqueue this reaction // twice when it is actually pushing it to the global reaction queue. From 76a055fe52b1b2ea335b57f79885d64d9a895b1c Mon Sep 17 00:00:00 2001 From: eal Date: Sat, 26 Feb 2022 16:48:27 -0800 Subject: [PATCH 13/13] Specify to use master branch of lingua-franca when running CI tests --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25c832772..871dc1b9f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: lf-gedf-np: needs: fetch-lf - uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@c-new-scheduler + uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@master with: runtime-ref: ${{ github.ref }} compiler-ref: ${{ needs.fetch-lf.outputs.ref }} @@ -41,7 +41,7 @@ jobs: lf-gedf-np-ci: needs: fetch-lf - uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@c-new-scheduler + uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@master with: runtime-ref: ${{ github.ref }} compiler-ref: ${{ needs.fetch-lf.outputs.ref }}