[PATCH v8 4/9] perf mutex: Add DO_ONCE() for one-time initialization

From: Arnaldo Carvalho de Melo

Date: Fri Oct 02 2026 - 06:33:28 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
__atomic_load_n(..., __ATOMIC_ACQUIRE), paired with the
__atomic_store_n(..., __ATOMIC_RELEASE) store after fn(), so that its
side effects are visible to threads that take the fast path; the
__atomic builtins over the smp_load_acquire()/smp_store_release()
variants because ThreadSanitizer understands the builtins, 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 b85a80ad60386aed..ef1ab6dd8c09556f 100644
--- a/tools/perf/util/mutex.h
+++ b/tools/perf/util/mutex.h
@@ -95,6 +95,30 @@ struct LOCKABLE mutex {
/* Statically initialized mutex, for the file scope ones. */
#define DEFINE_MUTEX(name) struct mutex name = { .lock = PTHREAD_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