From bda4d6e0d01116db59a0a03b0c703a7af6e11949 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 16 Nov 2020 10:54:20 -0800 Subject: [PATCH] util: Add helpers for various one-time-init patters A fairly common pattern for debug envvars is something like: static int should_print = -1; if (should_print < 0) should_print = env_var_as_unsigned("NIR_PRINT", 0); Unfortunately helgrind doesn't realize that we expect to always get the same return value, so we don't actually care about the race condition here. Add a helper get_once() and do_once macros, with extra locking to make helgrind/drd happy. Note that other than the nir usages (which are limited to debug builds), other usages are not in hot-paths. Signed-off-by: Rob Clark Reviewed-by: Kristian H. Kristensen Part-of: --- src/util/debug.h | 61 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/u_debug.h | 33 ++++--------------------- 2 files changed, 66 insertions(+), 28 deletions(-) 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)); \ }