#include "../../SDL_internal.h"
#if SDL_THREAD_WINDOWS
#include "../../core/windows/SDL_windows.h"
#include "SDL_hints.h"
#include "SDL_thread.h"
#include "SDL_timer.h"
typedef SDL_sem * (*pfnSDL_CreateSemaphore)(Uint32);
typedef void (*pfnSDL_DestroySemaphore)(SDL_sem *);
typedef int (*pfnSDL_SemWaitTimeout)(SDL_sem *, Uint32);
typedef int (*pfnSDL_SemTryWait)(SDL_sem *);
typedef int (*pfnSDL_SemWait)(SDL_sem *);
typedef Uint32 (*pfnSDL_SemValue)(SDL_sem *);
typedef int (*pfnSDL_SemPost)(SDL_sem *);
typedef struct SDL_semaphore_impl_t
{
pfnSDL_CreateSemaphore Create;
pfnSDL_DestroySemaphore Destroy;
pfnSDL_SemWaitTimeout WaitTimeout;
pfnSDL_SemTryWait TryWait;
pfnSDL_SemWait Wait;
pfnSDL_SemValue Value;
pfnSDL_SemPost Post;
} SDL_sem_impl_t;
static SDL_sem_impl_t SDL_sem_impl_active = {0};
#if (HAVE_WINAPIFAMILY_H) && defined(WINAPI_FAMILY_PHONE_APP)
#define SDL_WINAPI_FAMILY_PHONE (WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
#else
#define SDL_WINAPI_FAMILY_PHONE 0
#endif
#if !SDL_WINAPI_FAMILY_PHONE
#if __WINRT__
#define pWaitOnAddress WaitOnAddress
#define pWakeByAddressSingle WakeByAddressSingle
#else
typedef BOOL(WINAPI *pfnWaitOnAddress)(volatile VOID*, PVOID, SIZE_T, DWORD);
typedef VOID(WINAPI *pfnWakeByAddressSingle)(PVOID);
static pfnWaitOnAddress pWaitOnAddress = NULL;
static pfnWakeByAddressSingle pWakeByAddressSingle = NULL;
#endif
typedef struct SDL_semaphore_atom
{
LONG count;
} SDL_sem_atom;
static SDL_sem *
SDL_CreateSemaphore_atom(Uint32 initial_value)
{
SDL_sem_atom *sem;
sem = (SDL_sem_atom *) SDL_malloc(sizeof(*sem));
if (sem) {
sem->count = initial_value;
} else {
SDL_OutOfMemory();
}
return (SDL_sem *)sem;
}
static void
SDL_DestroySemaphore_atom(SDL_sem * sem)
{
if (sem) {
SDL_free(sem);
}
}
static int
SDL_SemTryWait_atom(SDL_sem * _sem)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
LONG count;
if (!sem) {
return SDL_InvalidParamError("sem");
}
count = sem->count;
if (count == 0) {
return SDL_MUTEX_TIMEDOUT;
}
if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
return 0;
}
return SDL_MUTEX_TIMEDOUT;
}
static int
SDL_SemWait_atom(SDL_sem * _sem)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
LONG count;
if (!sem) {
return SDL_InvalidParamError("sem");
}
for (;;) {
count = sem->count;
while (count == 0) {
if (pWaitOnAddress(&sem->count, &count, sizeof(sem->count), INFINITE) == FALSE) {
return SDL_SetError("WaitOnAddress() failed");
}
count = sem->count;
}
if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
return 0;
}
}
}
static int
SDL_SemWaitTimeout_atom(SDL_sem * _sem, Uint32 timeout)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
LONG count;
Uint32 now;
Uint32 deadline;
DWORD timeout_eff;
if (timeout == SDL_MUTEX_MAXWAIT) {
return SDL_SemWait_atom(_sem);
}
if (!sem) {
return SDL_InvalidParamError("sem");
}
now = SDL_GetTicks();
deadline = now + (DWORD) timeout;
for (;;) {
count = sem->count;
while (count == 0) {
now = SDL_GetTicks();
if (deadline > now) {
timeout_eff = deadline - now;
} else {
return SDL_MUTEX_TIMEDOUT;
}
if (pWaitOnAddress(&sem->count, &count, sizeof(count), timeout_eff) == FALSE) {
if (GetLastError() == ERROR_TIMEOUT) {
return SDL_MUTEX_TIMEDOUT;
}
return SDL_SetError("WaitOnAddress() failed");
}
count = sem->count;
}
if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
return 0;
}
}
}
static Uint32
SDL_SemValue_atom(SDL_sem * _sem)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
if (!sem) {
SDL_InvalidParamError("sem");
return 0;
}
return (Uint32)sem->count;
}
static int
SDL_SemPost_atom(SDL_sem * _sem)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
if (!sem) {
return SDL_InvalidParamError("sem");
}
InterlockedIncrement(&sem->count);
pWakeByAddressSingle(&sem->count);
return 0;
}
static const SDL_sem_impl_t SDL_sem_impl_atom =
{
&SDL_CreateSemaphore_atom,
&SDL_DestroySemaphore_atom,
&SDL_SemWaitTimeout_atom,
&SDL_SemTryWait_atom,
&SDL_SemWait_atom,
&SDL_SemValue_atom,
&SDL_SemPost_atom,
};
#endif
typedef struct SDL_semaphore_kern
{
HANDLE id;
LONG count;
} SDL_sem_kern;
static SDL_sem *
SDL_CreateSemaphore_kern(Uint32 initial_value)
{
SDL_sem_kern *sem;
sem = (SDL_sem_kern *) SDL_malloc(sizeof(*sem));
if (sem) {
#if __WINRT__
sem->id = CreateSemaphoreEx(NULL, initial_value, 32 * 1024, NULL, 0, SEMAPHORE_ALL_ACCESS);
#else
sem->id = CreateSemaphore(NULL, initial_value, 32 * 1024, NULL);
#endif
sem->count = initial_value;
if (!sem->id) {
SDL_SetError("Couldn't create semaphore");
SDL_free(sem);
sem = NULL;
}
} else {
SDL_OutOfMemory();
}
return (SDL_sem *)sem;
}
static void
SDL_DestroySemaphore_kern(SDL_sem * _sem)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
if (sem) {
if (sem->id) {
CloseHandle(sem->id);
sem->id = 0;
}
SDL_free(sem);
}
}
static int
SDL_SemWaitTimeout_kern(SDL_sem * _sem, Uint32 timeout)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
int retval;
DWORD dwMilliseconds;
if (!sem) {
return SDL_InvalidParamError("sem");
}
if (timeout == SDL_MUTEX_MAXWAIT) {
dwMilliseconds = INFINITE;
} else {
dwMilliseconds = (DWORD) timeout;
}
switch (WaitForSingleObjectEx(sem->id, dwMilliseconds, FALSE)) {
case WAIT_OBJECT_0:
InterlockedDecrement(&sem->count);
retval = 0;
break;
case WAIT_TIMEOUT:
retval = SDL_MUTEX_TIMEDOUT;
break;
default:
retval = SDL_SetError("WaitForSingleObject() failed");
break;
}
return retval;
}
static int
SDL_SemTryWait_kern(SDL_sem * sem)
{
return SDL_SemWaitTimeout_kern(sem, 0);
}
static int
SDL_SemWait_kern(SDL_sem * sem)
{
return SDL_SemWaitTimeout_kern(sem, SDL_MUTEX_MAXWAIT);
}
static Uint32
SDL_SemValue_kern(SDL_sem * _sem)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
if (!sem) {
SDL_InvalidParamError("sem");
return 0;
}
return (Uint32)sem->count;
}
static int
SDL_SemPost_kern(SDL_sem * _sem)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
if (!sem) {
return SDL_InvalidParamError("sem");
}
InterlockedIncrement(&sem->count);
if (ReleaseSemaphore(sem->id, 1, NULL) == FALSE) {
InterlockedDecrement(&sem->count);
return SDL_SetError("ReleaseSemaphore() failed");
}
return 0;
}
static const SDL_sem_impl_t SDL_sem_impl_kern =
{
&SDL_CreateSemaphore_kern,
&SDL_DestroySemaphore_kern,
&SDL_SemWaitTimeout_kern,
&SDL_SemTryWait_kern,
&SDL_SemWait_kern,
&SDL_SemValue_kern,
&SDL_SemPost_kern,
};
SDL_sem *
SDL_CreateSemaphore(Uint32 initial_value)
{
if (SDL_sem_impl_active.Create == NULL) {
const SDL_sem_impl_t * impl = &SDL_sem_impl_kern;
#if !SDL_WINAPI_FAMILY_PHONE
if (!SDL_GetHintBoolean(SDL_HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL, SDL_FALSE)) {
#if __WINRT__
impl = &SDL_sem_impl_atom;
#else
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;
}
}
#endif
}
#endif
SDL_copyp(&SDL_sem_impl_active, impl);
}
return SDL_sem_impl_active.Create(initial_value);
}
void
SDL_DestroySemaphore(SDL_sem * sem)
{
SDL_sem_impl_active.Destroy(sem);
}
int
SDL_SemWaitTimeout(SDL_sem * sem, Uint32 timeout)
{
return SDL_sem_impl_active.WaitTimeout(sem, timeout);
}
int
SDL_SemTryWait(SDL_sem * sem)
{
return SDL_sem_impl_active.TryWait(sem);
}
int
SDL_SemWait(SDL_sem * sem)
{
return SDL_sem_impl_active.Wait(sem);
}
Uint32
SDL_SemValue(SDL_sem * sem)
{
return SDL_sem_impl_active.Value(sem);
}
int
SDL_SemPost(SDL_sem * sem)
{
return SDL_sem_impl_active.Post(sem);
}
#endif