[PATCH v9 4/9] perf mutex: Add DO_ONCE() for one-time initialization
From: Arnaldo Carvalho de Melo
Date: Sun Oct 04 2026 - 02:07:35 EST
From: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
For lazy init that more than one thread can race into, without
pthread_once() so that it stays within perf's locking primitives and
clang's -Wthread-safety keeps an eye on the mutexes involved.
Mirrors the kernel's include/linux/once.h DO_ONCE(), dropping its
static key fast path, unnecessary at these cold init paths: a per call
site static bool plus a statically initialized mutex, double checked.
As there, code reachable from more than one call site must go through
a common helper.
The lockless fast path reads ___done with an acquire, paired with a
release store after fn(), so that fn()'s side effects are visible to
threads taking the fast path, which the dropped static key used to
guarantee.
The __atomic builtins are used rather than smp_load_acquire()/
smp_store_release() because ThreadSanitizer understands them,
avoiding reports on ___done and on what fn() initialized.
Assisted-by: LLM
Signed-off-by: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
---
tools/perf/util/mutex.h | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/tools/perf/util/mutex.h b/tools/perf/util/mutex.h
index fe04db21d19c5b0b..5554937675886ef3 100644
--- a/tools/perf/util/mutex.h
+++ b/tools/perf/util/mutex.h
@@ -104,6 +104,30 @@ struct LOCKABLE mutex {
#endif
#define DEFINE_MUTEX(name) struct mutex name = { .lock = __PERF_MUTEX_INITIALIZER }
+/*
+ * Runs fn() exactly once however many threads race to it, the rest wait
+ * for it to finish; per call site state, so multiple sites wanting the
+ * same once use a common helper, as in the kernel's DO_ONCE().
+ */
+#define DO_ONCE(fn, ...) \
+ ({ \
+ static bool ___done; \
+ static DEFINE_MUTEX(___once_lock); \
+ bool ___ret = false; \
+ \
+ if (!__atomic_load_n(&___done, __ATOMIC_ACQUIRE)) { \
+ mutex_lock(&___once_lock); \
+ if (!___done) { \
+ fn(__VA_ARGS__); \
+ __atomic_store_n(&___done, true, \
+ __ATOMIC_RELEASE); \
+ ___ret = true; \
+ } \
+ mutex_unlock(&___once_lock); \
+ } \
+ ___ret; \
+ })
+
/* A wrapper around the condition variable implementation. */
struct cond {
pthread_cond_t cond;
--
2.55.0