diff --git a/src/engine/engine_crossplatform.h b/src/engine/engine_crossplatform.h index 2cf6dff9..5f6b1b5d 100644 --- a/src/engine/engine_crossplatform.h +++ b/src/engine/engine_crossplatform.h @@ -85,6 +85,20 @@ __atomic_fetch_add(ptr, val, __ATOMIC_RELAXED) #endif +// Atomics helpers for mjtBool (1-byte _Bool) with acquire/release semantics. +#if defined(_MSC_VER) && !defined(__clang__) + #define mj_atomic_load_bool(ptr) \ + (mjtBool) _InterlockedCompareExchange8((volatile char*)(ptr), 0, 0) + #define mj_atomic_store_bool(ptr, val) \ + (void)_InterlockedExchange8((volatile char*)(ptr), (char)(val)) + #define mj_atomic_exchange_bool(ptr, val) \ + (mjtBool) _InterlockedExchange8((volatile char*)(ptr), (char)(val)) +#else + #define mj_atomic_load_bool(ptr) __atomic_load_n(ptr, __ATOMIC_ACQUIRE) + #define mj_atomic_store_bool(ptr, val) __atomic_store_n(ptr, val, __ATOMIC_RELEASE) + #define mj_atomic_exchange_bool(ptr, val) __atomic_exchange_n(ptr, val, __ATOMIC_ACQ_REL) +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/src/engine/engine_util_errmem.c b/src/engine/engine_util_errmem.c index f5e7f8a4..f6c51988 100644 --- a/src/engine/engine_util_errmem.c +++ b/src/engine/engine_util_errmem.c @@ -104,7 +104,8 @@ static mjLogConfig log_config = {.logto_console = true, .logto_file = true, .logfile = "MUJOCO_LOG.TXT", .topics = 0}; -static mjtBool env_checked = 0; +static mjtBool env_init_claimed = 0; // claimed by first thread to enter init +static mjtBool env_init_done = 0; // set after log_config init completes // parse MUJOCO_LOG_TOPICS env var to seed initial topic bitmask // example: MUJOCO_LOG_TOPICS="time_stp,sleep" @@ -143,9 +144,17 @@ static void mju_initLogTopicsFromEnv(void) { // private pointer getter encapsulates lazy init with zero copy overhead static const mjLogConfig* mju_getLogConfigPtr(void) { - if (!env_checked) { - mju_initLogTopicsFromEnv(); - env_checked = 1; + if (!mj_atomic_load_bool(&env_init_done)) { + // atomically claim right to initialize; only one thread gets old value 0 + if (!mj_atomic_exchange_bool(&env_init_claimed, 1)) { + mju_initLogTopicsFromEnv(); + mj_atomic_store_bool(&env_init_done, 1); + } else { + // another thread is initializing; spin until it completes + while (!mj_atomic_load_bool(&env_init_done)) { + // empty + } + } } return &log_config; } @@ -171,8 +180,9 @@ mjLogConfig mju_getLogConfig(void) { // set default handler configuration void mju_setLogConfig(mjLogConfig config) { - env_checked = 1; log_config = config; + mj_atomic_store_bool(&env_init_claimed, 1); + mj_atomic_store_bool(&env_init_done, 1); } // restore default processing @@ -182,8 +192,9 @@ void mju_clearHandlers(void) { .logto_file = true, .logfile = "MUJOCO_LOG.TXT", .topics = 0}; - env_checked = 1; + mj_atomic_store_bool(&env_init_claimed, 1); mju_initLogTopicsFromEnv(); + mj_atomic_store_bool(&env_init_done, 1); mju_user_error = 0; mju_user_warning = 0;