Bladeren bron

Add SDL_cond implementation using Windows Condition Variables
Is automatically used when the SRW SDL_mutex implementation is active.
Otherwise falls back to the generic implementation.

v2: - Rebase onto master fa3ea1051a4b

Joel Linn 4 jaren geleden
bovenliggende
commit
8fc0baad98

+ 1 - 0
VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj

@@ -133,6 +133,7 @@
     <ClInclude Include="..\..\src\thread\SDL_systhread.h" />
     <ClInclude Include="..\..\src\thread\SDL_thread_c.h" />
     <ClInclude Include="..\..\src\thread\generic\SDL_syscond_c.h" />
+    <ClInclude Include="..\..\src\thread\windows\SDL_sysmutex_c.h" />
     <ClInclude Include="..\..\src\thread\windows\SDL_systhread_c.h" />
     <ClInclude Include="..\..\src\timer\SDL_timer_c.h" />
     <ClInclude Include="..\..\src\video\dummy\SDL_nullevents_c.h" />

+ 3 - 0
VisualC-WinRT/WinPhone81_VS2013/SDL-WinPhone81.vcxproj.filters

@@ -384,6 +384,9 @@
     <ClInclude Include="..\..\src\thread\generic\SDL_syscond_c.h">
       <Filter>Source Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\thread\windows\SDL_sysmutex_c.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\src\thread\windows\SDL_systhread_c.h">
       <Filter>Source Files</Filter>
     </ClInclude>

+ 1 - 0
VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj

@@ -147,6 +147,7 @@
     <ClInclude Include="..\..\src\sensor\SDL_syssensor.h" />
     <ClInclude Include="..\..\src\thread\SDL_thread_c.h" />
     <ClInclude Include="..\..\src\thread\generic\SDL_syscond_c.h" />
+    <ClInclude Include="..\..\src\thread\windows\SDL_sysmutex_c.h" />
     <ClInclude Include="..\..\src\thread\windows\SDL_systhread_c.h" />
     <ClInclude Include="..\..\src\timer\SDL_timer_c.h" />
     <ClInclude Include="..\..\src\video\dummy\SDL_nullevents_c.h" />

+ 3 - 0
VisualC-WinRT/WinRT81_VS2013/SDL-WinRT81.vcxproj.filters

@@ -393,6 +393,9 @@
     <ClInclude Include="..\..\src\thread\generic\SDL_syscond_c.h">
       <Filter>Source Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\thread\windows\SDL_sysmutex_c.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\src\thread\windows\SDL_systhread_c.h">
       <Filter>Source Files</Filter>
     </ClInclude>

+ 1 - 0
VisualC/SDL/SDL.vcxproj

@@ -360,6 +360,7 @@
     <ClInclude Include="..\..\src\thread\SDL_systhread.h" />
     <ClInclude Include="..\..\src\thread\SDL_thread_c.h" />
     <ClInclude Include="..\..\src\thread\generic\SDL_syscond_c.h" />
+    <ClInclude Include="..\..\src\thread\windows\SDL_sysmutex_c.h" />
     <ClInclude Include="..\..\src\thread\windows\SDL_systhread_c.h" />
     <ClInclude Include="..\..\src\timer\SDL_timer_c.h" />
     <ClInclude Include="..\..\src\video\dummy\SDL_nullevents_c.h" />

+ 1 - 0
VisualC/SDL/SDL.vcxproj.filters

@@ -302,6 +302,7 @@
     <ClInclude Include="..\..\src\thread\SDL_systhread.h" />
     <ClInclude Include="..\..\src\thread\SDL_thread_c.h" />
     <ClInclude Include="..\..\src\thread\generic\SDL_syscond_c.h" />
+    <ClInclude Include="..\..\src\thread\windows\SDL_sysmutex_c.h" />
     <ClInclude Include="..\..\src\thread\windows\SDL_systhread_c.h" />
     <ClInclude Include="..\..\src\timer\SDL_timer_c.h" />
     <ClInclude Include="..\..\src\video\dummy\SDL_nullevents_c.h" />

+ 3 - 0
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)

+ 168 - 1
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();
 }

+ 4 - 32
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,
 };
 
 

+ 69 - 0
src/thread/windows/SDL_sysmutex_c.h

@@ -0,0 +1,69 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
+
+  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: */