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)