From 74e1e35e8c55bd5aa838116bf9eddc0758843fc2 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 6 Aug 2026 12:22:52 +0200 Subject: [PATCH] ref(sync): promote shared spinlock helper Rename the Unix-only spinlock header to sentry_spinlock.h and make the primitive available to all platforms. The Unix page allocator continues to use it, and other subsystems can now include the same narrow helper instead of putting spinlock code in sentry_sync.h. Add sentry__spinlock_try_lock and sentry__spinlock_wait alongside the existing lock and unlock operations. The wait variant lets callers bound a spin attempt with their own wait policy while keeping the primitive small and platform-independent. --- src/CMakeLists.txt | 3 +- src/sentry_spinlock.h | 63 +++++++++++++++++++++++++++++++++ src/sentry_unix_pageallocator.c | 2 +- src/sentry_unix_spinlock.h | 28 --------------- tests/unit/test_sync.c | 32 +++++++++++++++++ tests/unit/tests.inc | 1 + 6 files changed, 98 insertions(+), 31 deletions(-) create mode 100644 src/sentry_spinlock.h delete mode 100644 src/sentry_unix_spinlock.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2a27b77071..96759e5ab8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ sentry_target_sources_cwd(sentry sentry_session.h sentry_slice.c sentry_slice.h + sentry_spinlock.h sentry_string.c sentry_string.h sentry_symbolizer.h @@ -98,7 +99,6 @@ if(WIN32) ) elseif(NX OR PROSPERO) sentry_target_sources_cwd(sentry - sentry_unix_spinlock.h path/sentry_path_unix.c process/sentry_process_none.c ) @@ -108,7 +108,6 @@ else() sentry_random.h sentry_unix_pageallocator.c sentry_unix_pageallocator.h - sentry_unix_spinlock.h symbolizer/sentry_symbolizer_unix.c path/sentry_path_unix.c process/sentry_process_unix.c diff --git a/src/sentry_spinlock.h b/src/sentry_spinlock.h new file mode 100644 index 0000000000..16143a098c --- /dev/null +++ b/src/sentry_spinlock.h @@ -0,0 +1,63 @@ +#ifndef SENTRY_SPINLOCK_H_INCLUDED +#define SENTRY_SPINLOCK_H_INCLUDED + +#include "sentry_boot.h" +#include "sentry_cpu_relax.h" + +typedef volatile long sentry_spinlock_t; +typedef bool (*sentry_spinlock_wait_func_t)(int attempt, void *data); + +#define SENTRY__SPINLOCK_INIT 0 + +static inline bool +sentry__spinlock_try_lock(sentry_spinlock_t *spinlock) +{ +#ifdef SENTRY_PLATFORM_WINDOWS +# if SIZEOF_LONG == 8 + return InterlockedCompareExchange64((volatile LONG64 *)spinlock, 1, 0) == 0; +# else + return InterlockedCompareExchange((volatile LONG *)spinlock, 1, 0) == 0; +# endif +#else + long unlocked = 0; + return __atomic_compare_exchange_n( + spinlock, &unlocked, 1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED); +#endif +} + +static inline void +sentry__spinlock_lock(sentry_spinlock_t *spinlock) +{ + while (!sentry__spinlock_try_lock(spinlock)) { + sentry__cpu_relax(); + } +} + +static inline bool +sentry__spinlock_wait(sentry_spinlock_t *spinlock, + sentry_spinlock_wait_func_t wait_func, void *data) +{ + int attempts = 0; + while (!sentry__spinlock_try_lock(spinlock)) { + if (!wait_func || !wait_func(++attempts, data)) { + return false; + } + } + return true; +} + +static inline void +sentry__spinlock_unlock(sentry_spinlock_t *spinlock) +{ +#ifdef SENTRY_PLATFORM_WINDOWS +# if SIZEOF_LONG == 8 + InterlockedExchange64((volatile LONG64 *)spinlock, 0); +# else + InterlockedExchange((volatile LONG *)spinlock, 0); +# endif +#else + __atomic_store_n(spinlock, 0, __ATOMIC_RELEASE); +#endif +} + +#endif diff --git a/src/sentry_unix_pageallocator.c b/src/sentry_unix_pageallocator.c index 7c0e4ec40e..f40564faa7 100644 --- a/src/sentry_unix_pageallocator.c +++ b/src/sentry_unix_pageallocator.c @@ -1,6 +1,6 @@ #include "sentry_unix_pageallocator.h" #include "sentry_core.h" -#include "sentry_unix_spinlock.h" +#include "sentry_spinlock.h" #include #include diff --git a/src/sentry_unix_spinlock.h b/src/sentry_unix_spinlock.h deleted file mode 100644 index b0b9f2b46d..0000000000 --- a/src/sentry_unix_spinlock.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef SENTRY_UNIX_SPINLOCK_H_INCLUDED -#define SENTRY_UNIX_SPINLOCK_H_INCLUDED - -#include "sentry_boot.h" -#include "sentry_cpu_relax.h" - -typedef volatile sig_atomic_t sentry_spinlock_t; - -/** - * On UNIX Systems, inside the signal handler, sentry will switch from standard - * `malloc` to a custom page-based allocator, which is protected by this special - * spinlock. - */ - -#define SENTRY__SPINLOCK_INIT 0 -#define sentry__spinlock_lock(spinlock_ref) \ - for (;;) { \ - while (__atomic_load_n(spinlock_ref, __ATOMIC_RELAXED)) { \ - sentry__cpu_relax(); \ - } \ - if (__atomic_exchange_n(spinlock_ref, 1, __ATOMIC_ACQUIRE) == 0) { \ - break; \ - } \ - } -#define sentry__spinlock_unlock(spinlock_ref) \ - (__atomic_store_n(spinlock_ref, 0, __ATOMIC_RELEASE)) - -#endif diff --git a/tests/unit/test_sync.c b/tests/unit/test_sync.c index cfc2a03fc9..be9e4b657e 100644 --- a/tests/unit/test_sync.c +++ b/tests/unit/test_sync.c @@ -1,4 +1,5 @@ #include "sentry_core.h" +#include "sentry_spinlock.h" #include "sentry_sync.h" #include "sentry_testsupport.h" #include "sentry_utils.h" @@ -669,3 +670,34 @@ SENTRY_TEST(cond_wait_timeout_overflow) pthread_cond_destroy(&cond); #endif } + +static long g_spin_waits = 0; + +static bool +spin_wait(int UNUSED(attempt), void *UNUSED(data)) +{ + sentry__atomic_fetch_and_add(&g_spin_waits, 1); + return sentry__atomic_fetch(&g_spin_waits) < 2; +} + +SENTRY_TEST(spinlock) +{ + sentry_spinlock_t lock = SENTRY__SPINLOCK_INIT; + sentry__atomic_store(&g_spin_waits, 0); + + TEST_CHECK(sentry__spinlock_try_lock(&lock)); + TEST_CHECK_INT_EQUAL(sentry__atomic_fetch(&lock), 1); + TEST_CHECK(!sentry__spinlock_try_lock(&lock)); + sentry__spinlock_unlock(&lock); + TEST_CHECK_INT_EQUAL(sentry__atomic_fetch(&lock), 0); + + sentry__spinlock_lock(&lock); + TEST_CHECK_INT_EQUAL(sentry__atomic_fetch(&lock), 1); + TEST_CHECK(!sentry__spinlock_wait(&lock, spin_wait, NULL)); + TEST_CHECK_INT_EQUAL(sentry__atomic_fetch(&g_spin_waits), 2); + sentry__spinlock_unlock(&lock); + TEST_CHECK_INT_EQUAL(sentry__atomic_fetch(&lock), 0); + TEST_CHECK(sentry__spinlock_wait(&lock, spin_wait, NULL)); + TEST_CHECK_INT_EQUAL(sentry__atomic_fetch(&g_spin_waits), 2); + sentry__spinlock_unlock(&lock); +} diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index e51c7fff36..bc50aec76d 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -372,6 +372,7 @@ XX(span_data_n) XX(span_tagging) XX(span_tagging_n) XX(spans_on_scope) +XX(spinlock) XX(stack_guarantee) XX(stack_guarantee_auto_init) XX(strict_continuation_asymmetric_lenient_continues)