diff --git a/core/lf_token.c b/core/lf_token.c index 48913f85b..b97a3b366 100644 --- a/core/lf_token.c +++ b/core/lf_token.c @@ -26,7 +26,20 @@ int _lf_count_token_allocations; #include "platform.h" // Enter/exit critical sections #include "port.h" // Defines lf_port_base_t. -lf_token_t* _lf_tokens_allocated_in_reactions = NULL; +/** + * @brief List of tokens created within reactions that must be freed. + * + * Tokens created by lf_writable_copy, which is automatically invoked + * when an input is mutable, must have their reference count decremented + * at the end of a tag (or the beginning of the next tag). + * Otherwise, their memory could leak. If they are passed on to + * an output or to a call to lf_schedule during the reaction, then + * those will also result in incremented reference counts, enabling + * the token to live on until used. For example, a new token created + * by lf_writable_copy could become the new template token for an output + * via a call to lf_set. + */ +static lf_token_t* _lf_tokens_allocated_in_reactions = NULL; //////////////////////////////////////////////////////////////////// //// Global variables not visible outside this file. @@ -197,6 +210,8 @@ lf_token_t* _lf_new_token(token_type_t* type, void* value, size_t length) { if (hashset_iterator_next(iterator) >= 0) { result = hashset_iterator_value(iterator); hashset_remove(_lf_token_recycling_bin, result); + // Make sure there isn't a previous value. + result->value = NULL; LF_PRINT_DEBUG("_lf_new_token: Retrieved token from the recycling bin: %p", (void*)result); } free(iterator); @@ -222,22 +237,20 @@ lf_token_t* _lf_new_token(token_type_t* type, void* value, size_t length) { } lf_token_t* _lf_get_token(token_template_t* tmplt) { - if (tmplt->token != NULL) { - if (tmplt->token->ref_count == 1) { - LF_PRINT_DEBUG("_lf_get_token: Reusing template token: %p with ref_count %zu", (void*)tmplt->token, - tmplt->token->ref_count); - // Free any previous value in the token. - _lf_free_token_value(tmplt->token); - return tmplt->token; - } else { - // Liberate the token. - _lf_done_using(tmplt->token); - } + LF_CRITICAL_SECTION_ENTER(GLOBAL_ENVIRONMENT); + if (tmplt->token != NULL && tmplt->token->ref_count == 1) { + LF_PRINT_DEBUG("_lf_get_token: Reusing template token: %p with ref_count %zu", (void*)tmplt->token, + tmplt->token->ref_count); + // Free any previous value in the token. + _lf_free_token_value(tmplt->token); + LF_CRITICAL_SECTION_EXIT(GLOBAL_ENVIRONMENT); + return tmplt->token; } + LF_CRITICAL_SECTION_EXIT(GLOBAL_ENVIRONMENT); // If we get here, we need a new token. - tmplt->token = _lf_new_token((token_type_t*)tmplt, NULL, 0); - tmplt->token->ref_count = 1; - return tmplt->token; + lf_token_t* result = _lf_new_token((token_type_t*)tmplt, NULL, 0); + result->ref_count = 1; + return result; } void _lf_initialize_template(token_template_t* tmplt, size_t element_size) { @@ -352,8 +365,7 @@ token_freed _lf_done_using(lf_token_t* token) { void _lf_free_token_copies() { while (_lf_tokens_allocated_in_reactions != NULL) { - lf_token_t* next = _lf_tokens_allocated_in_reactions->next; _lf_done_using(_lf_tokens_allocated_in_reactions); - _lf_tokens_allocated_in_reactions = next; + _lf_tokens_allocated_in_reactions = _lf_tokens_allocated_in_reactions->next; } } diff --git a/core/reactor_common.c b/core/reactor_common.c index 455b3a065..358480eb8 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -386,12 +386,16 @@ void _lf_initialize_timer(environment_t* env, trigger_t* timer) { // Get an event_t struct to put on the event queue. // Recycle event_t structs, if possible. - event_t* e = lf_get_new_event(env); - e->trigger = timer; - e->base.tag = (tag_t){.time = lf_time_logical(env) + delay, .microstep = 0}; - // NOTE: No lock is being held. Assuming this only happens at startup. - pqueue_tag_insert(env->event_q, (pqueue_tag_element_t*)e); - tracepoint_schedule(env, timer, delay); // Trace even though schedule is not called. + tag_t next_tag = (tag_t){.time = lf_time_logical(env) + delay, .microstep = 0}; + // Do not schedule the next event if it is after the timeout. + if (!lf_is_tag_after_stop_tag(env, next_tag)) { + event_t* e = lf_get_new_event(env); + e->trigger = timer; + e->base.tag = next_tag; + // NOTE: No lock is being held. Assuming this only happens at startup. + pqueue_tag_insert(env->event_q, (pqueue_tag_element_t*)e); + tracepoint_schedule(env, timer, delay); // Trace even though schedule is not called. + } } void _lf_initialize_timers(environment_t* env) { diff --git a/include/api/reaction_macros.h b/include/api/reaction_macros.h index 37d90258d..52c39e125 100644 --- a/include/api/reaction_macros.h +++ b/include/api/reaction_macros.h @@ -77,6 +77,7 @@ /* The cast "*((void**) &out->value)" is a hack to make the code */ \ /* compile with non-token types where value is not a pointer. */ \ lf_token_t* token = _lf_initialize_token_with_value((token_template_t*)out, *((void**)&out->value), 1); \ + out->token = token; \ } \ } while (0) @@ -97,6 +98,7 @@ do { \ lf_set_present(out); \ lf_token_t* token = _lf_initialize_token_with_value((token_template_t*)out, val, len); \ + out->token = token; \ out->value = token->value; \ out->length = len; \ } while (0) @@ -105,6 +107,7 @@ do { \ lf_set_present(out); \ lf_token_t* token = _lf_initialize_token_with_value((token_template_t*)out, val, len); \ + out->token = token; \ out->value = static_castvalue)>(token->value); \ out->length = len; \ } while (0) diff --git a/include/core/lf_token.h b/include/core/lf_token.h index 38e80310a..49069fa95 100644 --- a/include/core/lf_token.h +++ b/include/core/lf_token.h @@ -151,20 +151,6 @@ typedef struct lf_port_base_t { ////////////////////////////////////////////////////////// //// Global variables -/** - * @brief List of tokens created within reactions that must be freed. - * Tokens created by lf_writable_copy, which is automatically invoked - * when an input is mutable, must have their reference count decremented - * at the end of a tag (or the beginning of the next tag). - * Otherwise, their memory could leak. If they are passed on to - * an output or to a call to lf_schedule during the reaction, then - * those will also result in incremented reference counts, enabling - * the token to live on until used. For example, a new token created - * by lf_writable_copy could become the new template token for an output - * via a call to lf_set. - */ -extern lf_token_t* _lf_tokens_allocated_in_reactions; - /** * Counter used to issue a warning if memory is * allocated for tokens and never freed. Note that diff --git a/lingua-franca-ref.txt b/lingua-franca-ref.txt index 52199a147..030a049b9 100644 --- a/lingua-franca-ref.txt +++ b/lingua-franca-ref.txt @@ -1 +1 @@ -transient-fed +transient-fed \ No newline at end of file diff --git a/python/lib/pythontarget.c b/python/lib/pythontarget.c index ae43959d3..8e41f40f9 100644 --- a/python/lib/pythontarget.c +++ b/python/lib/pythontarget.c @@ -542,7 +542,8 @@ PyObject* convert_C_action_to_py(void* action) { } // Actions in Python always use token type - ((generic_action_capsule_struct*)cap)->value = trigger->tmplt.token->value; + if (((generic_action_instance_struct*)action)->token != NULL) + ((generic_action_capsule_struct*)cap)->value = ((generic_action_instance_struct*)action)->token->value; return cap; }