diff --git a/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj b/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj index d684c8e23..61060211c 100644 --- a/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj +++ b/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj @@ -133,6 +133,7 @@ + diff --git a/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters b/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters index 0019ae0f1..c041cac46 100644 --- a/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters +++ b/VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters @@ -384,6 +384,9 @@ Source Files + + Source Files + Source Files diff --git a/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj b/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj index 3d756ceab..e774d6720 100644 --- a/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj +++ b/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj @@ -147,6 +147,7 @@ + diff --git a/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters b/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters index aa52d8a5c..56db7083a 100644 --- a/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters +++ b/VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters @@ -393,6 +393,9 @@ Source Files + + Source Files + Source Files diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index c5739535d..9915d0dcf 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -360,6 +360,7 @@ + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index d7cddb140..c7ec35c08 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -302,6 +302,7 @@ + diff --git a/include/SDL_hints.h b/include/SDL_hints.h index bac14a736..80b572bdb 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -1202,6 +1202,9 @@ extern "C" { * They offer better performance, allocate no kernel ressources and * use less memory. SDL will fall back to Critical Sections on older * OS versions or if forced to by this hint. + * This also affects Condition Variables. When SRW mutexes are used, + * SDL will use Windows Condition Variables as well. Else, a generic + * SDL_cond implementation will be used that works with all mutexes. * * This variable can be set to the following values: * "0" - Use SRW Locks when available. If not, fall back to Critical Sections. (default) diff --git a/src/thread/windows/SDL_syscond_srw.c b/src/thread/windows/SDL_syscond_srw.c index ef8c802f3..e2093c91a 100644 --- a/src/thread/windows/SDL_syscond_srw.c +++ b/src/thread/windows/SDL_syscond_srw.c @@ -24,6 +24,7 @@ #include "SDL_thread.h" #include "../generic/SDL_syscond_c.h" +#include "SDL_sysmutex_c.h" typedef SDL_cond * (*pfnSDL_CreateCond)(void); typedef void (*pfnSDL_DestroyCond)(SDL_cond *); @@ -45,6 +46,136 @@ typedef struct SDL_cond_impl_t /* Implementation will be chosen at runtime based on available Kernel features */ static SDL_cond_impl_t SDL_cond_impl_active = {0}; + +/** + * Native Windows Condition Variable (SRW Locks) + */ + +#ifndef CONDITION_VARIABLE_INIT +#define CONDITION_VARIABLE_INIT {0} +typedef struct CONDITION_VARIABLE { + PVOID Ptr; +} CONDITION_VARIABLE, *PCONDITION_VARIABLE; +#endif + +#if __WINRT__ +#define pWakeConditionVariable WakeConditionVariable +#define pWakeAllConditionVariable WakeAllConditionVariable +#define pSleepConditionVariableSRW SleepConditionVariableSRW +#else +typedef VOID(WINAPI *pfnWakeConditionVariable)(PCONDITION_VARIABLE); +typedef VOID(WINAPI *pfnWakeAllConditionVariable)(PCONDITION_VARIABLE); +typedef BOOL(WINAPI *pfnSleepConditionVariableSRW)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG); + +static pfnWakeConditionVariable pWakeConditionVariable = NULL; +static pfnWakeAllConditionVariable pWakeAllConditionVariable = NULL; +static pfnSleepConditionVariableSRW pSleepConditionVariableSRW = NULL; +#endif + +typedef struct SDL_cond_srw +{ + CONDITION_VARIABLE cond; +} SDL_cond_srw; + + +static SDL_cond * +SDL_CreateCond_srw(void) +{ + SDL_cond_srw *cond; + + /* Relies on CONDITION_VARIABLE_INIT == 0. */ + cond = (SDL_cond_srw *) SDL_calloc(1, sizeof(*cond)); + if (!cond) { + SDL_OutOfMemory(); + } + + return (SDL_cond *)cond; +} + +static void +SDL_DestroyCond_srw(SDL_cond * cond) +{ + if (cond) { + /* There are no kernel allocated resources */ + SDL_free(cond); + } +} + +static int +SDL_CondSignal_srw(SDL_cond * _cond) +{ + SDL_cond_srw *cond = (SDL_cond_srw *)_cond; + if (!cond) { + return SDL_SetError("Passed a NULL condition variable"); + } + + pWakeConditionVariable(&cond->cond); + + return 0; +} + +static int +SDL_CondBroadcast_srw(SDL_cond * _cond) +{ + SDL_cond_srw *cond = (SDL_cond_srw *)_cond; + if (!cond) { + return SDL_SetError("Passed a NULL condition variable"); + } + + pWakeAllConditionVariable(&cond->cond); + + return 0; +} + +static int +SDL_CondWaitTimeout_srw(SDL_cond * _cond, SDL_mutex * _mutex, Uint32 ms) +{ + SDL_cond_srw *cond = (SDL_cond_srw *)_cond; + SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex; + DWORD timeout; + + if (!cond) { + return SDL_SetError("Passed a NULL condition variable"); + } + if (!mutex) { + return SDL_SetError("Passed a NULL mutex"); + } + + if (mutex->count != 1) { + return SDL_SetError("Passed mutex is not locked or locked recursively"); + } + + if (ms == SDL_MUTEX_MAXWAIT) { + timeout = INFINITE; + } else { + timeout = (DWORD) ms; + } + + if (pSleepConditionVariableSRW(&cond->cond, &mutex->srw, timeout, 0) == FALSE) { + if (GetLastError() == ERROR_TIMEOUT) { + return SDL_MUTEX_TIMEDOUT; + } + return SDL_SetError("SleepConditionVariableSRW() failed"); + } + + return 0; +} + +static int +SDL_CondWait_srw(SDL_cond * cond, SDL_mutex * mutex) { + return SDL_CondWaitTimeout_srw(cond, mutex, SDL_MUTEX_MAXWAIT); +} + +static const SDL_cond_impl_t SDL_cond_impl_srw = +{ + &SDL_CreateCond_srw, + &SDL_DestroyCond_srw, + &SDL_CondSignal_srw, + &SDL_CondBroadcast_srw, + &SDL_CondWait_srw, + &SDL_CondWaitTimeout_srw, +}; + /** * Generic Condition Variable implementation using SDL_mutex and SDL_sem */ @@ -64,7 +195,43 @@ SDL_cond * SDL_CreateCond(void) { if (SDL_cond_impl_active.Create == NULL) { - SDL_memcpy(&SDL_cond_impl_active, &SDL_cond_impl_generic, sizeof(SDL_cond_impl_active)); + /* Default to generic implementation, works with all mutex implementations */ + const SDL_cond_impl_t * impl = &SDL_cond_impl_generic; + + if (SDL_mutex_impl_active.Type == SDL_MUTEX_INVALID) { + /* The mutex implementation isn't decided yet, trigger it */ + SDL_mutex *mutex = SDL_CreateMutex(); + if (!mutex) { + return NULL; + } + SDL_DestroyMutex(mutex); + + SDL_assert(SDL_mutex_impl_active.Type != SDL_MUTEX_INVALID); + } + + /* It is required SRW Locks are used */ + if (SDL_mutex_impl_active.Type == SDL_MUTEX_SRW) { +#if __WINRT__ + /* Link statically on this platform */ + impl = &SDL_cond_impl_srw; +#else + HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); + if (kernel32) { + pWakeConditionVariable = (pfnWakeConditionVariable) GetProcAddress(kernel32, "WakeConditionVariable"); + pWakeAllConditionVariable = (pfnWakeAllConditionVariable) GetProcAddress(kernel32, "WakeAllConditionVariable"); + pSleepConditionVariableSRW = (pfnSleepConditionVariableSRW) GetProcAddress(kernel32, "SleepConditionVariableSRW"); + if (pWakeConditionVariable && pWakeAllConditionVariable && pSleepConditionVariableSRW) { + /* Use the Windows provided API */ + impl = &SDL_cond_impl_srw; + } + } + if (!(kernel32 && pWakeConditionVariable && pWakeAllConditionVariable && pSleepConditionVariableSRW)) { + SDL_LogWarn(SDL_LOG_CATEGORY_SYSTEM, "Could not load required imports for SRW Condition Variables although SRW Locks are used!"); + } +#endif + } + + SDL_memcpy(&SDL_cond_impl_active, impl, sizeof(SDL_cond_impl_active)); } return SDL_cond_impl_active.Create(); } diff --git a/src/thread/windows/SDL_sysmutex.c b/src/thread/windows/SDL_sysmutex.c index eb4df31ae..8d5dd3591 100644 --- a/src/thread/windows/SDL_sysmutex.c +++ b/src/thread/windows/SDL_sysmutex.c @@ -31,41 +31,19 @@ */ -#include "../../core/windows/SDL_windows.h" - #include "SDL_hints.h" -#include "SDL_mutex.h" -typedef SDL_mutex * (*pfnSDL_CreateMutex)(void); -typedef int (*pfnSDL_LockMutex)(SDL_mutex *); -typedef int (*pfnSDL_TryLockMutex)(SDL_mutex *); -typedef int (*pfnSDL_UnlockMutex)(SDL_mutex *); -typedef void (*pfnSDL_DestroyMutex)(SDL_mutex *); +#include "SDL_sysmutex_c.h" -typedef struct SDL_mutex_impl_t -{ - pfnSDL_CreateMutex Create; - pfnSDL_DestroyMutex Destroy; - pfnSDL_LockMutex Lock; - pfnSDL_TryLockMutex TryLock; - pfnSDL_UnlockMutex Unlock; -} SDL_mutex_impl_t; /* Implementation will be chosen at runtime based on available Kernel features */ -static SDL_mutex_impl_t SDL_mutex_impl_active = {0}; +SDL_mutex_impl_t SDL_mutex_impl_active = {0}; /** * Implementation based on Slim Reader/Writer (SRW) Locks for Win 7 and newer. */ -#ifndef SRWLOCK_INIT -#define SRWLOCK_INIT {0} -typedef struct _SRWLOCK { - PVOID Ptr; -} SRWLOCK, *PSRWLOCK; -#endif - #if __WINRT__ /* Functions are guaranteed to be available */ #define pReleaseSRWLockExclusive ReleaseSRWLockExclusive @@ -80,14 +58,6 @@ static pfnAcquireSRWLockExclusive pAcquireSRWLockExclusive = NULL; static pfnTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive = NULL; #endif -typedef struct SDL_mutex_srw -{ - SRWLOCK srw; - /* SRW Locks are not recursive, that has to be handled by SDL: */ - DWORD count; - DWORD owner; -} SDL_mutex_srw; - static SDL_mutex * SDL_CreateMutex_srw(void) { @@ -189,6 +159,7 @@ static const SDL_mutex_impl_t SDL_mutex_impl_srw = &SDL_LockMutex_srw, &SDL_TryLockMutex_srw, &SDL_UnlockMutex_srw, + SDL_MUTEX_SRW, }; @@ -283,6 +254,7 @@ static const SDL_mutex_impl_t SDL_mutex_impl_cs = &SDL_LockMutex_cs, &SDL_TryLockMutex_cs, &SDL_UnlockMutex_cs, + SDL_MUTEX_CS, }; diff --git a/src/thread/windows/SDL_sysmutex_c.h b/src/thread/windows/SDL_sysmutex_c.h new file mode 100644 index 000000000..ddd519669 --- /dev/null +++ b/src/thread/windows/SDL_sysmutex_c.h @@ -0,0 +1,69 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2020 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#include "../../core/windows/SDL_windows.h" + +#include "SDL_mutex.h" + +typedef SDL_mutex * (*pfnSDL_CreateMutex)(void); +typedef int (*pfnSDL_LockMutex)(SDL_mutex *); +typedef int (*pfnSDL_TryLockMutex)(SDL_mutex *); +typedef int (*pfnSDL_UnlockMutex)(SDL_mutex *); +typedef void (*pfnSDL_DestroyMutex)(SDL_mutex *); + +typedef enum +{ + SDL_MUTEX_INVALID = 0, + SDL_MUTEX_SRW, + SDL_MUTEX_CS, +} SDL_MutexType; + +typedef struct SDL_mutex_impl_t +{ + pfnSDL_CreateMutex Create; + pfnSDL_DestroyMutex Destroy; + pfnSDL_LockMutex Lock; + pfnSDL_TryLockMutex TryLock; + pfnSDL_UnlockMutex Unlock; + /* Needed by SDL_cond: */ + SDL_MutexType Type; +} SDL_mutex_impl_t; + +extern SDL_mutex_impl_t SDL_mutex_impl_active; + + +#ifndef SRWLOCK_INIT +#define SRWLOCK_INIT {0} +typedef struct _SRWLOCK { + PVOID Ptr; +} SRWLOCK, *PSRWLOCK; +#endif + +typedef struct SDL_mutex_srw +{ + SRWLOCK srw; + /* SRW Locks are not recursive, that has to be handled by SDL: */ + DWORD count; + DWORD owner; +} SDL_mutex_srw; + +/* vi: set ts=4 sw=4 expandtab: */