diff --git a/src/util/debug.h b/src/util/debug.h index bbcc1975545..e6a53e2fdec 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -27,6 +27,8 @@ #include #include +#include "util/simple_mtx.h" + #ifdef __cplusplus extern "C" { #endif @@ -50,4 +52,63 @@ env_var_as_unsigned(const char *var_name, unsigned default_value); } /* extern C */ #endif +/** + * Helper for arbitrary one-time initialization, with additional locking + * to ensure the initialization only happens once (and to make tools like + * helgrind happy). + */ +#define do_once for( \ + struct __do_once_data *__d = ({ \ + static struct __do_once_data __sd = { \ + .lock = _SIMPLE_MTX_INITIALIZER_NP, \ + .done = false, \ + }; \ + simple_mtx_lock(&__sd.lock); \ + &__sd; \ + }); \ + ({ \ + if (__d->done) \ + simple_mtx_unlock(&__d->lock); \ + !__d->done; \ + }); \ + __d->done = true) + +struct __do_once_data { + simple_mtx_t lock; + bool done; +}; + +/** + * Helper for one-time debug value from env-var, and other similar cases, + * where the expression is expected to return the same value each time. + * + * This has additional locking, compared to open-coding the initialization, + * to make tools like helgrind happy. + */ +#define get_once(__expr) ({ \ + static __typeof__(__expr) __val; \ + do_once { \ + __val = __expr; \ + } \ + __val; \ + }) + +/** + * Alternative version of get_once() which has no locking in release builds, + * suitable for hot-paths. + */ +#ifndef NDEBUG +#define get_once_nolock(__expr) ({ \ + static bool __once; \ + static __typeof__(__expr) __val; \ + if (!__once) { \ + __val = __expr; \ + __once = true; \ + } \ + __val; \ + }) +#else +#define get_once_nolock(__expr) get_once(__expr) +#endif + #endif /* _UTIL_DEBUG_H */ diff --git a/src/util/u_debug.h b/src/util/u_debug.h index fc06c582b0d..d4f77c61251 100644 --- a/src/util/u_debug.h +++ b/src/util/u_debug.h @@ -40,6 +40,7 @@ #include #include +#include "util/debug.h" #include "util/os_misc.h" #include "util/detect_os.h" #include "util/macros.h" @@ -407,52 +408,28 @@ debug_get_flags_option(const char *name, static const char * \ debug_get_option_ ## suffix (void) \ { \ - static bool first = true; \ - static const char * value; \ - if (first) { \ - first = false; \ - value = debug_get_option(name, dfault); \ - } \ - return value; \ + return get_once_nolock(debug_get_option(name, dfault)); \ } #define DEBUG_GET_ONCE_BOOL_OPTION(sufix, name, dfault) \ static bool \ debug_get_option_ ## sufix (void) \ { \ - static bool first = true; \ - static bool value; \ - if (first) { \ - first = false; \ - value = debug_get_bool_option(name, dfault); \ - } \ - return value; \ + return get_once_nolock(debug_get_bool_option(name, dfault)); \ } #define DEBUG_GET_ONCE_NUM_OPTION(sufix, name, dfault) \ static long \ debug_get_option_ ## sufix (void) \ { \ - static bool first = true; \ - static long value; \ - if (first) { \ - first = false; \ - value = debug_get_num_option(name, dfault); \ - } \ - return value; \ + return get_once_nolock(debug_get_num_option(name, dfault)); \ } #define DEBUG_GET_ONCE_FLAGS_OPTION(sufix, name, flags, dfault) \ static unsigned long \ debug_get_option_ ## sufix (void) \ { \ - static bool first = true; \ - static unsigned long value; \ - if (first) { \ - first = false; \ - value = debug_get_flags_option(name, flags, dfault); \ - } \ - return value; \ + return get_once_nolock(debug_get_flags_option(name, flags, dfault)); \ }