#include "SDL_internal.h"
#include "../generic/SDL_syscond_c.h"
#include "SDL_sysmutex_c.h"
typedef SDL_Condition *(*pfnSDL_CreateCondition)(void);
typedef void (*pfnSDL_DestroyCondition)(SDL_Condition *);
typedef void (*pfnSDL_SignalCondition)(SDL_Condition *);
typedef void (*pfnSDL_BroadcastCondition)(SDL_Condition *);
typedef bool (*pfnSDL_WaitConditionTimeoutNS)(SDL_Condition *, SDL_Mutex *, Sint64);
typedef struct SDL_cond_impl_t
{
pfnSDL_CreateCondition Create;
pfnSDL_DestroyCondition Destroy;
pfnSDL_SignalCondition Signal;
pfnSDL_BroadcastCondition Broadcast;
pfnSDL_WaitConditionTimeoutNS WaitTimeoutNS;
} SDL_cond_impl_t;
static SDL_cond_impl_t SDL_cond_impl_active = { 0 };
#ifndef CONDITION_VARIABLE_INIT
#define CONDITION_VARIABLE_INIT \
{ \
0 \
}
typedef struct CONDITION_VARIABLE
{
PVOID Ptr;
} CONDITION_VARIABLE, *PCONDITION_VARIABLE;
#endif
typedef VOID (WINAPI *pfnWakeConditionVariable)(PCONDITION_VARIABLE);
typedef VOID (WINAPI *pfnWakeAllConditionVariable)(PCONDITION_VARIABLE);
typedef BOOL (WINAPI *pfnSleepConditionVariableSRW)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG);
typedef BOOL (WINAPI *pfnSleepConditionVariableCS)(PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD);
static pfnWakeConditionVariable pWakeConditionVariable = NULL;
static pfnWakeAllConditionVariable pWakeAllConditionVariable = NULL;
static pfnSleepConditionVariableSRW pSleepConditionVariableSRW = NULL;
static pfnSleepConditionVariableCS pSleepConditionVariableCS = NULL;
typedef struct SDL_cond_cv
{
CONDITION_VARIABLE cond;
} SDL_cond_cv;
static SDL_Condition *SDL_CreateCondition_cv(void)
{
return (SDL_Condition *)SDL_calloc(1, sizeof(SDL_cond_cv));
}
static void SDL_DestroyCondition_cv(SDL_Condition *cond)
{
SDL_free(cond);
}
static void SDL_SignalCondition_cv(SDL_Condition *_cond)
{
SDL_cond_cv *cond = (SDL_cond_cv *)_cond;
pWakeConditionVariable(&cond->cond);
}
static void SDL_BroadcastCondition_cv(SDL_Condition *_cond)
{
SDL_cond_cv *cond = (SDL_cond_cv *)_cond;
pWakeAllConditionVariable(&cond->cond);
}
static bool SDL_WaitConditionTimeoutNS_cv(SDL_Condition *_cond, SDL_Mutex *_mutex, Sint64 timeoutNS)
{
SDL_cond_cv *cond = (SDL_cond_cv *)_cond;
DWORD timeout;
bool result;
if (timeoutNS < 0) {
timeout = INFINITE;
} else {
timeout = (DWORD)SDL_NS_TO_MS(timeoutNS);
}
if (SDL_mutex_impl_active.Type == SDL_MUTEX_SRW) {
SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex;
if (mutex->count != 1 || mutex->owner != GetCurrentThreadId()) {
return false;
}
mutex->count = 0;
mutex->owner = 0;
result = (pSleepConditionVariableSRW(&cond->cond, &mutex->srw, timeout, 0) == TRUE);
SDL_assert(mutex->count == 0 && mutex->owner == 0);
mutex->count = 1;
mutex->owner = GetCurrentThreadId();
} else {
SDL_mutex_cs *mutex = (SDL_mutex_cs *)_mutex;
SDL_assert(SDL_mutex_impl_active.Type == SDL_MUTEX_CS);
result = (pSleepConditionVariableCS(&cond->cond, &mutex->cs, timeout) == TRUE);
}
return result;
}
static const SDL_cond_impl_t SDL_cond_impl_cv = {
&SDL_CreateCondition_cv,
&SDL_DestroyCondition_cv,
&SDL_SignalCondition_cv,
&SDL_BroadcastCondition_cv,
&SDL_WaitConditionTimeoutNS_cv,
};
static const SDL_cond_impl_t SDL_cond_impl_generic = {
&SDL_CreateCondition_generic,
&SDL_DestroyCondition_generic,
&SDL_SignalCondition_generic,
&SDL_BroadcastCondition_generic,
&SDL_WaitConditionTimeoutNS_generic,
};
SDL_Condition *SDL_CreateCondition(void)
{
if (!SDL_cond_impl_active.Create) {
const SDL_cond_impl_t *impl = NULL;
if (SDL_mutex_impl_active.Type == SDL_MUTEX_INVALID) {
SDL_Mutex *mutex = SDL_CreateMutex();
if (!mutex) {
return NULL;
}
SDL_DestroyMutex(mutex);
SDL_assert(SDL_mutex_impl_active.Type != SDL_MUTEX_INVALID);
}
impl = &SDL_cond_impl_generic;
{
HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
if (kernel32) {
pWakeConditionVariable = (pfnWakeConditionVariable)GetProcAddress(kernel32, "WakeConditionVariable");
pWakeAllConditionVariable = (pfnWakeAllConditionVariable)GetProcAddress(kernel32, "WakeAllConditionVariable");
pSleepConditionVariableSRW = (pfnSleepConditionVariableSRW)GetProcAddress(kernel32, "SleepConditionVariableSRW");
pSleepConditionVariableCS = (pfnSleepConditionVariableCS)GetProcAddress(kernel32, "SleepConditionVariableCS");
if (pWakeConditionVariable && pWakeAllConditionVariable && pSleepConditionVariableSRW && pSleepConditionVariableCS) {
impl = &SDL_cond_impl_cv;
}
}
}
SDL_copyp(&SDL_cond_impl_active, impl);
}
return SDL_cond_impl_active.Create();
}
void SDL_DestroyCondition(SDL_Condition *cond)
{
if (cond) {
SDL_cond_impl_active.Destroy(cond);
}
}
void SDL_SignalCondition(SDL_Condition *cond)
{
if (!cond) {
return;
}
SDL_cond_impl_active.Signal(cond);
}
void SDL_BroadcastCondition(SDL_Condition *cond)
{
if (!cond) {
return;
}
SDL_cond_impl_active.Broadcast(cond);
}
bool SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)
{
if (!cond || !mutex) {
return true;
}
return SDL_cond_impl_active.WaitTimeoutNS(cond, mutex, timeoutNS);
}