diff --git a/core/environment.c b/core/environment.c index eb012589b..d24de4bfc 100644 --- a/core/environment.c +++ b/core/environment.c @@ -210,10 +210,16 @@ int environment_init(environment_t* env, const char* name, int id, int num_worke const char* trace_file_name) { (void)trace_file_name; // Will be used with future enclave support. - env->name = malloc(strlen(name) + 1); // +1 for the null terminator - LF_ASSERT_NON_NULL(env->name); - strcpy(env->name, name); - + // Space for the name string with the null terminator. + if (name != NULL) { + size_t name_size = strlen(name) + 1; // +1 for the null terminator + env->name = (char*)malloc(name_size); + LF_ASSERT_NON_NULL(env->name); + // Use strncpy rather than strcpy to avoid compiler warnings. + strncpy(env->name, name, name_size); + } else { + env->name = NULL; + } env->id = id; env->stop_tag = FOREVER_TAG; diff --git a/core/threaded/reactor_threaded.c b/core/threaded/reactor_threaded.c index df0f271b1..82dd2b648 100644 --- a/core/threaded/reactor_threaded.c +++ b/core/threaded/reactor_threaded.c @@ -1028,8 +1028,12 @@ int lf_reactor_c_main(int argc, const char* argv[]) { lf_print("---- Start execution ----"); #else struct timespec physical_time_timespec = {start_time / BILLION, start_time % BILLION}; - lf_print("---- Start execution at time %s---- plus %ld nanoseconds", ctime(&physical_time_timespec.tv_sec), - physical_time_timespec.tv_nsec); + struct tm* time_info = localtime(&physical_time_timespec.tv_sec); + char buffer[80]; // Long enough to hold the formatted time string. + // Use strftime rather than ctime because as of C23, ctime is deprecated. + strftime(buffer, sizeof(buffer), "%a %b %d %H:%M:%S %Y", time_info); + + lf_print("---- Start execution on %s ---- plus %ld nanoseconds", buffer, physical_time_timespec.tv_nsec); #endif // MINIMAL_STDLIB // Create and initialize the environments for each enclave @@ -1114,6 +1118,8 @@ int lf_reactor_c_main(int argc, const char* argv[]) { } else { int failure = lf_thread_join(env->thread_ids[j], &worker_thread_exit_status); if (failure) { + // Windows warns that strerror is deprecated but doesn't define strerror_r. + // There seems to be no portable replacement. lf_print_error("Failed to join thread listening for incoming messages: %s", strerror(failure)); } } diff --git a/core/utils/lf_semaphore.c b/core/utils/lf_semaphore.c index 2d0255ecb..3d79f9e4c 100644 --- a/core/utils/lf_semaphore.c +++ b/core/utils/lf_semaphore.c @@ -41,7 +41,7 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @param count The count to start with. * @return lf_semaphore_t* Can be NULL on error. */ -lf_semaphore_t* lf_semaphore_new(int count) { +lf_semaphore_t* lf_semaphore_new(size_t count) { lf_semaphore_t* semaphore = (lf_semaphore_t*)malloc(sizeof(lf_semaphore_t)); LF_MUTEX_INIT(&semaphore->mutex); LF_COND_INIT(&semaphore->cond, &semaphore->mutex); @@ -55,7 +55,7 @@ lf_semaphore_t* lf_semaphore_new(int count) { * @param semaphore Instance of a semaphore * @param i The count to add. */ -void lf_semaphore_release(lf_semaphore_t* semaphore, int i) { +void lf_semaphore_release(lf_semaphore_t* semaphore, size_t i) { assert(semaphore != NULL); LF_MUTEX_LOCK(&semaphore->mutex); semaphore->count += i; diff --git a/core/utils/util.c b/core/utils/util.c index 62de9fd27..4fb956b3a 100644 --- a/core/utils/util.c +++ b/core/utils/util.c @@ -206,6 +206,8 @@ void lf_print_error_system_failure(const char* format, ...) { va_start(args, format); lf_vprint_error(format, args); va_end(args); + // Windows warns that strerror is deprecated but doesn't define strerror_r. + // There seems to be no portable replacement. lf_print_error_and_exit("Error %d: %s", errno, strerror(errno)); exit(EXIT_FAILURE); } diff --git a/include/core/lf_token.h b/include/core/lf_token.h index 219538dd3..38e80310a 100644 --- a/include/core/lf_token.h +++ b/include/core/lf_token.h @@ -108,9 +108,9 @@ typedef struct lf_token_t { * A record of the subset of channels of a multiport that have present inputs. */ typedef struct lf_sparse_io_record_t { - int size; // -1 if overflowed. 0 if empty. - size_t capacity; // Max number of writes to be considered sparse. - size_t present_channels[]; // Array of channel indices that are present. + int size; // -1 if overflowed. 0 if empty. + size_t capacity; // Max number of writes to be considered sparse. + size_t* present_channels; // Array of channel indices that are present. } lf_sparse_io_record_t; /** diff --git a/include/core/utils/lf_semaphore.h b/include/core/utils/lf_semaphore.h index 73d3e4eb4..341c43cc4 100644 --- a/include/core/utils/lf_semaphore.h +++ b/include/core/utils/lf_semaphore.h @@ -41,7 +41,7 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include typedef struct { - int count; + size_t count; lf_mutex_t mutex; lf_cond_t cond; } lf_semaphore_t; @@ -52,7 +52,7 @@ typedef struct { * @param count The count to start with. * @return lf_semaphore_t* Can be NULL on error. */ -lf_semaphore_t* lf_semaphore_new(int count); +lf_semaphore_t* lf_semaphore_new(size_t count); /** * @brief Release the 'semaphore' and add 'i' to its count. @@ -60,7 +60,7 @@ lf_semaphore_t* lf_semaphore_new(int count); * @param semaphore Instance of a semaphore * @param i The count to add. */ -void lf_semaphore_release(lf_semaphore_t* semaphore, int i); +void lf_semaphore_release(lf_semaphore_t* semaphore, size_t i); /** * @brief Acquire the 'semaphore'. Will block if count is 0. diff --git a/lingua-franca-ref.txt b/lingua-franca-ref.txt index 5d6695559..1f7391f92 100644 --- a/lingua-franca-ref.txt +++ b/lingua-franca-ref.txt @@ -1 +1 @@ -add-reaction +master diff --git a/logging/api/logging_macros.h b/logging/api/logging_macros.h index 6f7ea1eba..3e22950b5 100644 --- a/logging/api/logging_macros.h +++ b/logging/api/logging_macros.h @@ -1,3 +1,5 @@ +#ifndef LOGGING_MACROS_H +#define LOGGING_MACROS_H #include "logging.h" /** @@ -12,6 +14,11 @@ #define LOG_LEVEL LOG_LEVEL_INFO #endif +// To prevent warnings "conditional expression is constant", we define static booleans +// here instead of directly testing LOG_LEVEL in the if statements in the macros below. +static const bool _lf_log_level_is_log = LOG_LEVEL >= LOG_LEVEL_LOG; +static const bool _lf_log_level_is_debug = LOG_LEVEL >= LOG_LEVEL_DEBUG; + /** * A macro used to print useful logging information. It can be enabled * by setting the target property 'logging' to 'LOG' or @@ -31,7 +38,7 @@ */ #define LF_PRINT_LOG(format, ...) \ do { \ - if (LOG_LEVEL >= LOG_LEVEL_LOG) { \ + if (_lf_log_level_is_log) { \ lf_print_log(format, ##__VA_ARGS__); \ } \ } while (0) @@ -54,7 +61,7 @@ */ #define LF_PRINT_DEBUG(format, ...) \ do { \ - if (LOG_LEVEL >= LOG_LEVEL_DEBUG) { \ + if (_lf_log_level_is_debug) { \ lf_print_debug(format, ##__VA_ARGS__); \ } \ } while (0) @@ -100,3 +107,4 @@ } \ } while (0) #endif // NDEBUG +#endif // LOGGING_MACROS_H \ No newline at end of file diff --git a/low_level_platform/impl/src/lf_atomic_windows.c b/low_level_platform/impl/src/lf_atomic_windows.c index 519d225f6..ff5a01750 100644 --- a/low_level_platform/impl/src/lf_atomic_windows.c +++ b/low_level_platform/impl/src/lf_atomic_windows.c @@ -10,18 +10,18 @@ #include "platform/lf_atomic.h" #include -int lf_atomic_fetch_add(int* ptr, int value) { return InterlockedExchangeAdd(ptr, value); } +int lf_atomic_fetch_add(int* ptr, int value) { return InterlockedExchangeAdd((LONG*)ptr, (LONG)value); } int64_t lf_atomic_fetch_add64(int64_t* ptr, int64_t value) { return InterlockedExchangeAdd64(ptr, value); } -int lf_atomic_add_fetch(int* ptr, int value) { return InterlockedAdd(ptr, value); } +int lf_atomic_add_fetch(int* ptr, int value) { return InterlockedAdd((LONG*)ptr, (LONG)value); } int64_t lf_atomic_add_fetch64(int64_t* ptr, int64_t value) { return InterlockedAdd64(ptr, value); } bool lf_atomic_bool_compare_and_swap(int* ptr, int oldval, int newval) { - return (InterlockedCompareExchange(ptr, newval, oldval) == oldval); + return (InterlockedCompareExchange((LONG*)ptr, (LONG)newval, (LONG)oldval) == oldval); } bool lf_atomic_bool_compare_and_swap64(int64_t* ptr, int64_t oldval, int64_t newval) { return (InterlockedCompareExchange64(ptr, newval, oldval) == oldval); } int lf_atomic_val_compare_and_swap(int* ptr, int oldval, int newval) { - return InterlockedCompareExchange(ptr, newval, oldval); + return InterlockedCompareExchange((LONG*)ptr, (LONG)newval, (LONG)oldval); } int64_t lf_atomic_val_compare_and_swap64(int64_t* ptr, int64_t oldval, int64_t newval) { return InterlockedCompareExchange64(ptr, newval, oldval); diff --git a/low_level_platform/impl/src/lf_windows_support.c b/low_level_platform/impl/src/lf_windows_support.c index 61424ac7f..c4524eda4 100644 --- a/low_level_platform/impl/src/lf_windows_support.c +++ b/low_level_platform/impl/src/lf_windows_support.c @@ -39,6 +39,7 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include // For fprintf() #include "platform/lf_windows_support.h" #include "low_level_platform.h" @@ -64,7 +65,7 @@ void _lf_initialize_clock() { if (_lf_use_performance_counter) { _lf_frequency_to_ns = (double)performance_frequency.QuadPart / BILLION; } else { - lf_print_error("High resolution performance counter is not supported on this machine."); + fprintf(stderr, "ERROR: High resolution performance counter is not supported on this machine.\n"); _lf_frequency_to_ns = 0.01; } } @@ -89,9 +90,9 @@ int _lf_clock_gettime(instant_t* t) { } LARGE_INTEGER windows_time; if (_lf_use_performance_counter) { - int result = QueryPerformanceCounter(&windows_time); + result = QueryPerformanceCounter(&windows_time); if (result == 0) { - lf_print_error("_lf_clock_gettime(): Failed to read the value of the physical clock."); + fprintf(stderr, "ERROR: _lf_clock_gettime(): Failed to read the value of the physical clock.\n"); return result; } } else { @@ -140,6 +141,7 @@ int lf_sleep(interval_t sleep_duration) { } int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_time) { + (void)env; // Suppress unused variable warning. interval_t sleep_duration = wakeup_time - lf_time_physical(); if (sleep_duration <= 0) { @@ -165,7 +167,11 @@ int lf_available_cores() { lf_thread_t lf_thread_self() { return GetCurrentThread(); } int lf_thread_create(lf_thread_t* thread, void* (*lf_thread)(void*), void* arguments) { - uintptr_t handle = _beginthreadex(NULL, 0, lf_thread, arguments, 0, NULL); + // _beginthreadex requires a function that returns unsigned rather than void*. + // So the following double cast suppresses the warning: + // '_beginthreadex_proc_type' differs in levels of indirection from 'void *(__cdecl *)(void *)' + uintptr_t handle = + _beginthreadex(NULL, 0, (unsigned(__stdcall*)(void*))(uintptr_t(__stdcall*)(void*))lf_thread, arguments, 0, NULL); *thread = (HANDLE)handle; if (handle == 0) { return errno; @@ -183,6 +189,9 @@ int lf_thread_create(lf_thread_t* thread, void* (*lf_thread)(void*), void* argum */ int lf_thread_join(lf_thread_t thread, void** thread_return) { DWORD retvalue = WaitForSingleObject(thread, INFINITE); + if (thread_return != NULL) { + *thread_return = (void*)retvalue; + } if (retvalue == WAIT_FAILED) { return EINVAL; } @@ -192,11 +201,23 @@ int lf_thread_join(lf_thread_t thread, void** thread_return) { /** * Real-time scheduling API not implemented for Windows. */ -int lf_thread_set_cpu(lf_thread_t thread, size_t cpu_number) { return -1; } +int lf_thread_set_cpu(lf_thread_t thread, size_t cpu_number) { + (void)thread; // Suppress unused variable warning. + (void)cpu_number; // Suppress unused variable warning. + return -1; +} -int lf_thread_set_priority(lf_thread_t thread, int priority) { return -1; } +int lf_thread_set_priority(lf_thread_t thread, int priority) { + (void)thread; // Suppress unused variable warning. + (void)priority; // Suppress unused variable warning. + return -1; +} -int lf_thread_set_scheduling_policy(lf_thread_t thread, lf_scheduling_policy_t* policy) { return -1; } +int lf_thread_set_scheduling_policy(lf_thread_t thread, lf_scheduling_policy_t* policy) { + (void)thread; // Suppress unused variable warning. + (void)policy; // Suppress unused variable warning. + return -1; +} int lf_mutex_init(_lf_critical_section_t* critical_section) { // Set up a recursive mutex @@ -278,10 +299,20 @@ int _lf_cond_timedwait(lf_cond_t* cond, instant_t wakeup_time) { } // convert ns to ms and round up to closest full integer - DWORD wait_duration_ms = (wait_duration + 999999LL) / 1000000LL; + interval_t wait_duration_ms = (wait_duration + 999999LL) / 1000000LL; + DWORD wait_duration_saturated; + if (wait_duration_ms > 0xFFFFFFFFLL) { + // Saturate at 0xFFFFFFFFLL + wait_duration_saturated = (DWORD)0xFFFFFFFFLL; + } else if (wait_duration_ms <= 0) { + // No need to wait. Return indicating that the wait is complete. + return LF_TIMEOUT; + } else { + wait_duration_saturated = (DWORD)wait_duration_ms; + } int return_value = (int)SleepConditionVariableCS((PCONDITION_VARIABLE)&cond->condition, - (PCRITICAL_SECTION)cond->critical_section, wait_duration_ms); + (PCRITICAL_SECTION)cond->critical_section, wait_duration_saturated); if (return_value == 0) { // Error if (GetLastError() == ERROR_TIMEOUT) { diff --git a/trace/impl/src/trace_impl.c b/trace/impl/src/trace_impl.c index 895247e87..02ffbb3bd 100644 --- a/trace/impl/src/trace_impl.c +++ b/trace/impl/src/trace_impl.c @@ -43,46 +43,46 @@ static version_t version = {.build_config = * See trace.h. * @return The number of items written to the object table or -1 for failure. */ -static int write_trace_header(trace_t* trace) { - if (trace->_lf_trace_file != NULL) { - size_t items_written = fwrite(&start_time, sizeof(int64_t), 1, trace->_lf_trace_file); +static int write_trace_header(trace_t* t) { + if (t->_lf_trace_file != NULL) { + size_t items_written = fwrite(&start_time, sizeof(int64_t), 1, t->_lf_trace_file); if (items_written != 1) - _LF_TRACE_FAILURE(trace); + _LF_TRACE_FAILURE(t); // The next item in the header is the size of the // _lf_trace_object_descriptions table. - items_written = fwrite(&trace->_lf_trace_object_descriptions_size, sizeof(int), 1, trace->_lf_trace_file); + items_written = fwrite(&t->_lf_trace_object_descriptions_size, sizeof(int), 1, t->_lf_trace_file); if (items_written != 1) - _LF_TRACE_FAILURE(trace); + _LF_TRACE_FAILURE(t); // Next we write the table. - for (size_t i = 0; i < trace->_lf_trace_object_descriptions_size; i++) { + for (size_t i = 0; i < t->_lf_trace_object_descriptions_size; i++) { // Write the pointer to the self struct. - items_written = fwrite(&trace->_lf_trace_object_descriptions[i].pointer, sizeof(void*), 1, trace->_lf_trace_file); + items_written = fwrite(&t->_lf_trace_object_descriptions[i].pointer, sizeof(void*), 1, t->_lf_trace_file); if (items_written != 1) - _LF_TRACE_FAILURE(trace); + _LF_TRACE_FAILURE(t); // Write the pointer to the trigger_t struct. - items_written = fwrite(&trace->_lf_trace_object_descriptions[i].trigger, sizeof(void*), 1, trace->_lf_trace_file); + items_written = fwrite(&t->_lf_trace_object_descriptions[i].trigger, sizeof(void*), 1, t->_lf_trace_file); if (items_written != 1) - _LF_TRACE_FAILURE(trace); + _LF_TRACE_FAILURE(t); // Write the object type. - items_written = fwrite(&trace->_lf_trace_object_descriptions[i].type, // Write the pointer value. - sizeof(_lf_trace_object_t), 1, trace->_lf_trace_file); + items_written = fwrite(&t->_lf_trace_object_descriptions[i].type, // Write the pointer value. + sizeof(_lf_trace_object_t), 1, t->_lf_trace_file); if (items_written != 1) - _LF_TRACE_FAILURE(trace); + _LF_TRACE_FAILURE(t); // Write the description. - size_t description_size = strlen(trace->_lf_trace_object_descriptions[i].description); - items_written = fwrite(trace->_lf_trace_object_descriptions[i].description, sizeof(char), + size_t description_size = strlen(t->_lf_trace_object_descriptions[i].description); + items_written = fwrite(t->_lf_trace_object_descriptions[i].description, sizeof(char), description_size + 1, // Include null terminator. - trace->_lf_trace_file); + t->_lf_trace_file); if (items_written != description_size + 1) - _LF_TRACE_FAILURE(trace); + _LF_TRACE_FAILURE(t); } } - return trace->_lf_trace_object_descriptions_size; + return (int)t->_lf_trace_object_descriptions_size; } /** @@ -125,37 +125,38 @@ static void flush_trace_locked(trace_t* trace, int worker) { /** * @brief Flush the specified buffer to a file. + * @param t The trace struct. * @param worker Index specifying the trace to flush. */ -static void flush_trace(trace_t* trace, int worker) { +static void flush_trace(trace_t* t, int worker) { // To avoid having more than one worker writing to the file at the same time, // enter a critical section. lf_platform_mutex_lock(trace_mutex); - flush_trace_locked(trace, worker); + flush_trace_locked(t, worker); lf_platform_mutex_unlock(trace_mutex); } -static void start_trace(trace_t* trace, int max_num_local_threads) { +static void start_trace(trace_t* t, int max_num_local_threads) { // Do not write the trace header information to the file yet - // so that startup reactions can register user-defined trace objects. + // so that startup reactions can register user-defined t objects. // write_trace_header(); - trace->_lf_trace_header_written = false; + t->_lf_trace_header_written = false; // Allocate an array of arrays of trace records, one per worker thread plus one // for the 0 thread (the main thread, or in an single-threaded program, the only // thread). - trace->_lf_number_of_trace_buffers = max_num_local_threads; - trace->_lf_trace_buffer = - (trace_record_nodeps_t**)malloc(sizeof(trace_record_nodeps_t*) * (trace->_lf_number_of_trace_buffers + 1)); - trace->_lf_trace_buffer++; // the buffer at index -1 is a fallback for user threads. - for (int i = -1; i < (int)trace->_lf_number_of_trace_buffers; i++) { - trace->_lf_trace_buffer[i] = (trace_record_nodeps_t*)malloc(sizeof(trace_record_nodeps_t) * TRACE_BUFFER_CAPACITY); + t->_lf_number_of_trace_buffers = max_num_local_threads; + t->_lf_trace_buffer = + (trace_record_nodeps_t**)malloc(sizeof(trace_record_nodeps_t*) * (t->_lf_number_of_trace_buffers + 1)); + t->_lf_trace_buffer++; // the buffer at index -1 is a fallback for user threads. + for (int i = -1; i < (int)t->_lf_number_of_trace_buffers; i++) { + t->_lf_trace_buffer[i] = (trace_record_nodeps_t*)malloc(sizeof(trace_record_nodeps_t) * TRACE_BUFFER_CAPACITY); } // Array of counters that track the size of each trace record (per thread). - trace->_lf_trace_buffer_size = (size_t*)calloc(sizeof(size_t), trace->_lf_number_of_trace_buffers + 1); - trace->_lf_trace_buffer_size++; + t->_lf_trace_buffer_size = (size_t*)calloc(sizeof(size_t), t->_lf_number_of_trace_buffers + 1); + t->_lf_trace_buffer_size++; - trace->_lf_trace_stop = 0; + t->_lf_trace_stop = 0; LF_PRINT_DEBUG("Started tracing."); }