diff --git a/src/sentry_sync.h b/src/sentry_sync.h index 66309886a..1a9c48a6c 100644 --- a/src/sentry_sync.h +++ b/src/sentry_sync.h @@ -135,6 +135,22 @@ WakeConditionVariable_PREVISTA(PCONDITION_VARIABLE_PREVISTA ConditionVariable) ConditionVariable->ContinueEvent, INFINITE, FALSE); } +inline void +WakeAllConditionVariable_PREVISTA( + PCONDITION_VARIABLE_PREVISTA ConditionVariable) +{ + if (!ConditionVariable) { + return; + } + + LONG waiters = InterlockedCompareExchange( + (volatile LONG *)&ConditionVariable->Waiters, 0, 0); + if (waiters > 0) { + ConditionVariable->Target = -1; + ReleaseSemaphore(ConditionVariable->Semaphore, waiters, NULL); + } +} + # endif /* _WIN32_WINNT < 0x0600 */ struct sentry__winmutex_s { @@ -195,6 +211,7 @@ typedef CONDITION_VARIABLE_PREVISTA sentry_cond_t; # define sentry__cond_init(CondVar) \ InitializeConditionVariable_PREVISTA(CondVar) # define sentry__cond_wake WakeConditionVariable_PREVISTA +# define sentry__cond_wake_all WakeAllConditionVariable_PREVISTA # define sentry__cond_wait_timeout(CondVar, Lock, Timeout) \ SleepConditionVariableCS_PREVISTA( \ CondVar, &(Lock)->critical_section, Timeout) @@ -202,6 +219,7 @@ typedef CONDITION_VARIABLE_PREVISTA sentry_cond_t; typedef CONDITION_VARIABLE sentry_cond_t; # define sentry__cond_init(CondVar) InitializeConditionVariable(CondVar) # define sentry__cond_wake WakeConditionVariable +# define sentry__cond_wake_all WakeAllConditionVariable # define sentry__cond_wait_timeout(CondVar, Lock, Timeout) \ SleepConditionVariableCS( \ CondVar, &(Lock)->critical_section, Timeout) @@ -344,6 +362,7 @@ typedef pthread_cond_t sentry_cond_t; } \ } while (0) # define sentry__cond_wake pthread_cond_signal +# define sentry__cond_wake_all pthread_cond_broadcast # define sentry__thread_init(ThreadId) \ memset(ThreadId, 0, sizeof(sentry_threadid_t)) # define sentry__thread_spawn(ThreadId, Func, Data) \ diff --git a/tests/unit/test_sync.c b/tests/unit/test_sync.c index 98c5eeef9..cfc2a03fc 100644 --- a/tests/unit/test_sync.c +++ b/tests/unit/test_sync.c @@ -581,6 +581,72 @@ SENTRY_TEST(bgworker_delayed_shutdown) sentry__bgworker_decref(bgw); } +#define COND_WAKE_ALL_THREADS 2 + +struct cond_wake_all_state { + sentry_mutex_t mutex; + sentry_cond_t waiting_cond; + sentry_cond_t ready_cond; + volatile long waiting; + volatile long woke; + bool ready; +}; + +SENTRY_THREAD_FN +cond_wake_all_thread(void *data) +{ + struct cond_wake_all_state *state = data; + + sentry__mutex_lock(&state->mutex); + sentry__atomic_fetch_and_add(&state->waiting, 1); + sentry__cond_wake(&state->waiting_cond); + while (!state->ready) { + sentry__cond_wait(&state->ready_cond, &state->mutex); + } + sentry__atomic_fetch_and_add(&state->woke, 1); + sentry__mutex_unlock(&state->mutex); + + return 0; +} + +SENTRY_TEST(cond_wake_all) +{ + struct cond_wake_all_state state = { 0 }; + sentry_threadid_t threads[COND_WAKE_ALL_THREADS]; + + sentry__mutex_init(&state.mutex); + sentry__cond_init(&state.waiting_cond); + sentry__cond_init(&state.ready_cond); + + for (int i = 0; i < COND_WAKE_ALL_THREADS; i++) { + sentry__thread_init(&threads[i]); + TEST_ASSERT( + sentry__thread_spawn(&threads[i], cond_wake_all_thread, &state) + == 0); + } + + sentry__mutex_lock(&state.mutex); + while (sentry__atomic_fetch(&state.waiting) < COND_WAKE_ALL_THREADS) { + sentry__cond_wait(&state.waiting_cond, &state.mutex); + } + state.ready = true; + sentry__cond_wake_all(&state.ready_cond); + sentry__mutex_unlock(&state.mutex); + + for (int i = 0; i < COND_WAKE_ALL_THREADS; i++) { + sentry__thread_join(threads[i]); + } + + TEST_CHECK_INT_EQUAL( + sentry__atomic_fetch(&state.woke), COND_WAKE_ALL_THREADS); + +#ifndef SENTRY_PLATFORM_WINDOWS + pthread_cond_destroy(&state.ready_cond); + pthread_cond_destroy(&state.waiting_cond); +#endif + sentry__mutex_free(&state.mutex); +} + SENTRY_TEST(cond_wait_timeout_overflow) { #if !(defined(SENTRY_PLATFORM_MACOS) \ diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 5f0265802..e51c7fff3 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -102,6 +102,7 @@ XX(client_report_save_raw_envelope) XX(concurrent_init) XX(concurrent_uninit) XX(cond_wait_timeout_overflow) +XX(cond_wake_all) XX(continuation_no_baggage_uses_sdk_dsc) XX(count_sampled_events) XX(crash_context_handler_path_propagation)