#ifndef SENTRY_SYNC_H_INCLUDED
#define SENTRY_SYNC_H_INCLUDED
#include "sentry_boot.h"
#include "sentry_core.h"
#include <assert.h>
#include <stdio.h>
#ifdef SENTRY_PLATFORM_WINDOWS
# if _WIN32_WINNT >= 0x0600
# include <synchapi.h>
# endif
# include <winnt.h>
# if _WIN32_WINNT < 0x0600
# define INIT_ONCE_STATIC_INIT \
{ \
0 \
}
typedef union {
PVOID Ptr;
} INIT_ONCE, *PINIT_ONCE;
typedef struct {
HANDLE Semaphore;
HANDLE ContinueEvent;
LONG Waiters;
LONG Target;
} CONDITION_VARIABLE_PREVISTA, *PCONDITION_VARIABLE_PREVISTA;
typedef BOOL(WINAPI *PINIT_ONCE_FN)(
PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context);
inline BOOL
InitOnceExecuteOnce(
PINIT_ONCE InitOnce, PINIT_ONCE_FN InitFn, PVOID Parameter, LPVOID *Context)
{
for (;;) {
switch ((ULONG_PTR)InitOnce->Ptr) {
case 0: if (InterlockedCompareExchangePointer(
&InitOnce->Ptr, (PVOID)1, (PVOID)0)
!= 0) {
break;
}
if (InitFn(InitOnce, Parameter, Context)) {
InitOnce->Ptr = (PVOID)2;
return TRUE;
}
InitOnce->Ptr = 0;
return FALSE;
case 1: Sleep(1);
break;
case 2: return TRUE;
default: return FALSE;
}
}
}
inline void
InitializeConditionVariable_PREVISTA(
PCONDITION_VARIABLE_PREVISTA ConditionVariable)
{
ConditionVariable->Target = 0;
ConditionVariable->Waiters = 0;
ConditionVariable->Semaphore = CreateSemaphoreW(NULL, 0, MAXLONG, NULL);
ConditionVariable->ContinueEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
}
inline BOOL
SleepConditionVariableCS_PREVISTA(
PCONDITION_VARIABLE_PREVISTA cv, PCRITICAL_SECTION cs, DWORD timeout)
{
DWORD result = 0;
LeaveCriticalSection(cs);
InterlockedIncrement((LONG *)&cv->Waiters);
result = WaitForSingleObject(cv->Semaphore, timeout);
if (InterlockedDecrement((LONG *)&cv->Waiters) == cv->Target
&& result == WAIT_OBJECT_0) {
SetEvent(cv->ContinueEvent);
}
EnterCriticalSection(cs);
return result;
}
inline void
WakeConditionVariable_PREVISTA(PCONDITION_VARIABLE_PREVISTA ConditionVariable)
{
if (!ConditionVariable) {
return;
}
if (ConditionVariable->Waiters == 0) {
return;
}
ConditionVariable->Target = ConditionVariable->Waiters - 1;
SignalObjectAndWait(ConditionVariable->Semaphore,
ConditionVariable->ContinueEvent, INFINITE, FALSE);
}
# endif
struct sentry__winmutex_s {
INIT_ONCE init_once;
CRITICAL_SECTION critical_section;
};
static inline BOOL CALLBACK
sentry__winmutex_init(
PINIT_ONCE UNUSED(InitOnce), PVOID cs, PVOID *UNUSED(lpContext))
{
InitializeCriticalSection((LPCRITICAL_SECTION)cs);
return TRUE;
}
static inline void
sentry__winmutex_lock(struct sentry__winmutex_s *mutex)
{
InitOnceExecuteOnce(&mutex->init_once, sentry__winmutex_init,
&mutex->critical_section, NULL);
EnterCriticalSection(&mutex->critical_section);
}
typedef HANDLE sentry_threadid_t;
typedef struct sentry__winmutex_s sentry_mutex_t;
# define SENTRY__MUTEX_INIT \
{ \
INIT_ONCE_STATIC_INIT, { 0 } \
}
# define sentry__mutex_lock(Lock) sentry__winmutex_lock(Lock)
# define sentry__mutex_unlock(Lock) \
LeaveCriticalSection(&(Lock)->critical_section)
# define sentry__mutex_free(Lock) \
DeleteCriticalSection(&(Lock)->critical_section)
# define sentry__thread_spawn(ThreadId, Func, Data) \
(*ThreadId = CreateThread(NULL, 0, Func, Data, 0, NULL), \
*ThreadId == INVALID_HANDLE_VALUE ? 1 : 0)
# define sentry__thread_join(ThreadId) \
WaitForSingleObject(ThreadId, INFINITE)
# if _WIN32_WINNT < 0x0600
typedef CONDITION_VARIABLE_PREVISTA sentry_cond_t;
# define sentry__cond_init(CondVar) \
InitializeConditionVariable_PREVISTA(CondVar)
# define sentry__cond_wake WakeConditionVariable_PREVISTA
# define sentry__cond_wait_timeout(CondVar, Lock, Timeout) \
SleepConditionVariableCS_PREVISTA( \
CondVar, &(Lock)->critical_section, Timeout)
# else
typedef CONDITION_VARIABLE sentry_cond_t;
# define sentry__cond_init(CondVar) InitializeConditionVariable(CondVar)
# define sentry__cond_wake WakeConditionVariable
# define sentry__cond_wait_timeout(CondVar, Lock, Timeout) \
SleepConditionVariableCS( \
CondVar, &(Lock)->critical_section, Timeout)
# endif
# define sentry__cond_wait(CondVar, Lock) \
sentry__cond_wait_timeout(CondVar, Lock, INFINITE)
#else
# include <errno.h>
# include <pthread.h>
# include <sys/time.h>
bool sentry__block_for_signal_handler(void);
void sentry__enter_signal_handler(void);
void sentry__leave_signal_handler(void);
typedef pthread_t sentry_threadid_t;
typedef pthread_mutex_t sentry_mutex_t;
typedef pthread_cond_t sentry_cond_t;
# ifdef SENTRY_PLATFORM_LINUX
# define SENTRY__MUTEX_INIT PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
# else
# define SENTRY__MUTEX_INIT PTHREAD_RECURSIVE_MUTEX_INITIALIZER
# endif
# define sentry__mutex_lock(Mutex) \
do { \
if (sentry__block_for_signal_handler()) { \
int rv = pthread_mutex_lock(Mutex); \
(void)rv; \
assert(rv == 0); \
} \
} while (0)
# define sentry__mutex_unlock(Mutex) \
do { \
if (sentry__block_for_signal_handler()) { \
pthread_mutex_unlock(Mutex); \
} \
} while (0)
# define sentry__mutex_free(Lock) pthread_mutex_destroy(Lock)
# define sentry__cond_init(CondVar) \
do { \
sentry_cond_t tmp = PTHREAD_COND_INITIALIZER; \
*(CondVar) = tmp; \
} while (0)
# define sentry__cond_wait(Cond, Mutex) \
do { \
if (sentry__block_for_signal_handler()) { \
pthread_cond_wait(Cond, Mutex); \
} \
} while (0)
# define sentry__cond_wake pthread_cond_signal
# define sentry__thread_spawn(ThreadId, Func, Data) \
(pthread_create(ThreadId, NULL, Func, Data) == 0 ? 0 : 1)
# define sentry__thread_join(ThreadId) pthread_join(ThreadId, NULL)
# define sentry__threadid_equal pthread_equal
# define sentry__current_thread pthread_self
static inline int
sentry__cond_wait_timeout(
sentry_cond_t *cv, sentry_mutex_t *mutex, uint64_t msecs)
{
if (!sentry__block_for_signal_handler()) {
return 0;
}
struct timeval now;
struct timespec lock_time;
gettimeofday(&now, NULL);
lock_time.tv_sec = now.tv_sec + msecs / 1000ULL;
lock_time.tv_nsec = (now.tv_usec + 1000ULL * (msecs % 1000)) * 1000ULL;
return pthread_cond_timedwait(cv, mutex, &lock_time);
}
#endif
#define sentry__mutex_init(Mutex) \
do { \
sentry_mutex_t tmp = SENTRY__MUTEX_INIT; \
*(Mutex) = tmp; \
} while (0)
static inline long
sentry__atomic_fetch_and_add(volatile long *val, long diff)
{
#ifdef SENTRY_PLATFORM_WINDOWS
# if SIZEOF_LONG == 8
return InterlockedExchangeAdd64((LONG64 *)val, diff);
# else
return InterlockedExchangeAdd((LONG *)val, diff);
# endif
#else
return __atomic_fetch_add(val, diff, __ATOMIC_SEQ_CST);
#endif
}
static inline long
sentry__atomic_store(volatile long *val, long value)
{
#ifdef SENTRY_PLATFORM_WINDOWS
# if SIZEOF_LONG == 8
return InterlockedExchange64((LONG64 *)val, value);
# else
return InterlockedExchange((LONG *)val, value);
# endif
#else
return __atomic_exchange_n(val, value, __ATOMIC_SEQ_CST);
#endif
}
static inline long
sentry__atomic_fetch(volatile long *val)
{
return sentry__atomic_fetch_and_add(val, 0);
}
struct sentry_bgworker_s;
typedef struct sentry_bgworker_s sentry_bgworker_t;
typedef void (*sentry_task_exec_func_t)(void *task_data, void *state);
sentry_bgworker_t *sentry__bgworker_new(
void *state, void (*free_state)(void *state));
void *sentry__bgworker_get_state(sentry_bgworker_t *bgw);
void sentry__bgworker_decref(sentry_bgworker_t *bgw);
int sentry__bgworker_start(sentry_bgworker_t *bgw);
int sentry__bgworker_shutdown(sentry_bgworker_t *bgw, uint64_t timeout);
void sentry__bgworker_setname(sentry_bgworker_t *bgw, const char *thread_name);
int sentry__bgworker_submit(sentry_bgworker_t *bgw,
sentry_task_exec_func_t exec_func, void (*cleanup_func)(void *task_data),
void *task_data);
size_t sentry__bgworker_foreach_matching(sentry_bgworker_t *bgw,
sentry_task_exec_func_t exec_func,
bool (*callback)(void *task_data, void *data), void *data);
#endif