#include "SDL_internal.h"
#ifdef SDL_THREAD_WINDOWS
#include "../../core/windows/SDL_windows.h"
typedef SDL_Semaphore *(*pfnSDL_CreateSemaphore)(Uint32);
typedef void (*pfnSDL_DestroySemaphore)(SDL_Semaphore *);
typedef bool (*pfnSDL_WaitSemaphoreTimeoutNS)(SDL_Semaphore *, Sint64);
typedef Uint32 (*pfnSDL_GetSemaphoreValue)(SDL_Semaphore *);
typedef void (*pfnSDL_SignalSemaphore)(SDL_Semaphore *);
typedef struct SDL_semaphore_impl_t
{
pfnSDL_CreateSemaphore Create;
pfnSDL_DestroySemaphore Destroy;
pfnSDL_WaitSemaphoreTimeoutNS WaitTimeoutNS;
pfnSDL_GetSemaphoreValue Value;
pfnSDL_SignalSemaphore Signal;
} SDL_sem_impl_t;
static SDL_sem_impl_t SDL_sem_impl_active = { 0 };
typedef BOOL (WINAPI *pfnWaitOnAddress)(volatile VOID *, PVOID, SIZE_T, DWORD);
typedef VOID (WINAPI *pfnWakeByAddressSingle)(PVOID);
static pfnWaitOnAddress pWaitOnAddress = NULL;
static pfnWakeByAddressSingle pWakeByAddressSingle = NULL;
typedef struct SDL_semaphore_atom
{
LONG count;
} SDL_sem_atom;
static SDL_Semaphore *SDL_CreateSemaphore_atom(Uint32 initial_value)
{
SDL_sem_atom *sem;
sem = (SDL_sem_atom *)SDL_malloc(sizeof(*sem));
if (sem) {
sem->count = initial_value;
}
return (SDL_Semaphore *)sem;
}
static void SDL_DestroySemaphore_atom(SDL_Semaphore *sem)
{
SDL_free(sem);
}
static bool SDL_WaitSemaphoreTimeoutNS_atom(SDL_Semaphore *_sem, Sint64 timeoutNS)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
LONG count;
Uint64 now;
Uint64 deadline;
DWORD timeout_eff;
if (!sem) {
return true;
}
if (timeoutNS == 0) {
count = sem->count;
if (count == 0) {
return false;
}
if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
return true;
}
return false;
}
if (timeoutNS < 0) {
for (;;) {
count = sem->count;
while (count == 0) {
if (!pWaitOnAddress(&sem->count, &count, sizeof(sem->count), INFINITE)) {
return false;
}
count = sem->count;
}
if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
return true;
}
}
}
now = SDL_GetTicksNS();
deadline = now + timeoutNS;
for (;;) {
count = sem->count;
while (count == 0) {
now = SDL_GetTicksNS();
if (deadline > now) {
timeout_eff = (DWORD)SDL_NS_TO_MS(deadline - now);
} else {
return false;
}
if (!pWaitOnAddress(&sem->count, &count, sizeof(count), timeout_eff)) {
return false;
}
count = sem->count;
}
if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
return true;
}
}
}
static Uint32 SDL_GetSemaphoreValue_atom(SDL_Semaphore *_sem)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
if (!sem) {
return 0;
}
return (Uint32)sem->count;
}
static void SDL_SignalSemaphore_atom(SDL_Semaphore *_sem)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
if (!sem) {
return;
}
InterlockedIncrement(&sem->count);
pWakeByAddressSingle(&sem->count);
}
static const SDL_sem_impl_t SDL_sem_impl_atom = {
&SDL_CreateSemaphore_atom,
&SDL_DestroySemaphore_atom,
&SDL_WaitSemaphoreTimeoutNS_atom,
&SDL_GetSemaphoreValue_atom,
&SDL_SignalSemaphore_atom,
};
typedef struct SDL_semaphore_kern
{
HANDLE id;
LONG count;
} SDL_sem_kern;
static SDL_Semaphore *SDL_CreateSemaphore_kern(Uint32 initial_value)
{
SDL_sem_kern *sem;
sem = (SDL_sem_kern *)SDL_malloc(sizeof(*sem));
if (sem) {
sem->id = CreateSemaphore(NULL, initial_value, 32 * 1024, NULL);
sem->count = initial_value;
if (!sem->id) {
SDL_SetError("Couldn't create semaphore");
SDL_free(sem);
sem = NULL;
}
}
return (SDL_Semaphore *)sem;
}
static void SDL_DestroySemaphore_kern(SDL_Semaphore *_sem)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
if (sem) {
if (sem->id) {
CloseHandle(sem->id);
sem->id = 0;
}
SDL_free(sem);
}
}
static bool SDL_WaitSemaphoreTimeoutNS_kern(SDL_Semaphore *_sem, Sint64 timeoutNS)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
DWORD dwMilliseconds;
if (!sem) {
return true;
}
if (timeoutNS < 0) {
dwMilliseconds = INFINITE;
} else {
dwMilliseconds = (DWORD)SDL_NS_TO_MS(timeoutNS);
}
switch (WaitForSingleObjectEx(sem->id, dwMilliseconds, FALSE)) {
case WAIT_OBJECT_0:
InterlockedDecrement(&sem->count);
return true;
default:
return false;
}
}
static Uint32 SDL_GetSemaphoreValue_kern(SDL_Semaphore *_sem)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
if (!sem) {
return 0;
}
return (Uint32)sem->count;
}
static void SDL_SignalSemaphore_kern(SDL_Semaphore *_sem)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
if (!sem) {
return;
}
InterlockedIncrement(&sem->count);
if (ReleaseSemaphore(sem->id, 1, NULL) == FALSE) {
InterlockedDecrement(&sem->count); }
}
static const SDL_sem_impl_t SDL_sem_impl_kern = {
&SDL_CreateSemaphore_kern,
&SDL_DestroySemaphore_kern,
&SDL_WaitSemaphoreTimeoutNS_kern,
&SDL_GetSemaphoreValue_kern,
&SDL_SignalSemaphore_kern,
};
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
if (!SDL_sem_impl_active.Create) {
const SDL_sem_impl_t *impl = &SDL_sem_impl_kern;
if (!SDL_GetHintBoolean(SDL_HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL, false)) {
HMODULE synch120 = GetModuleHandle(TEXT("api-ms-win-core-synch-l1-2-0.dll"));
if (synch120) {
pWaitOnAddress = (pfnWaitOnAddress)GetProcAddress(synch120, "WaitOnAddress");
pWakeByAddressSingle = (pfnWakeByAddressSingle)GetProcAddress(synch120, "WakeByAddressSingle");
if (pWaitOnAddress && pWakeByAddressSingle) {
impl = &SDL_sem_impl_atom;
}
}
}
SDL_copyp(&SDL_sem_impl_active, impl);
}
return SDL_sem_impl_active.Create(initial_value);
}
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
SDL_sem_impl_active.Destroy(sem);
}
bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
return SDL_sem_impl_active.WaitTimeoutNS(sem, timeoutNS);
}
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
return SDL_sem_impl_active.Value(sem);
}
void SDL_SignalSemaphore(SDL_Semaphore *sem)
{
SDL_sem_impl_active.Signal(sem);
}
#endif