diff options
Diffstat (limited to 'contrib/SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c')
| -rw-r--r-- | contrib/SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c b/contrib/SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c new file mode 100644 index 0000000..b29ef63 --- /dev/null +++ b/contrib/SDL-3.2.8/src/thread/windows/SDL_syscond_cv.c | |||
| @@ -0,0 +1,226 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> | ||
| 4 | |||
| 5 | This software is provided 'as-is', without any express or implied | ||
| 6 | warranty. In no event will the authors be held liable for any damages | ||
| 7 | arising from the use of this software. | ||
| 8 | |||
| 9 | Permission is granted to anyone to use this software for any purpose, | ||
| 10 | including commercial applications, and to alter it and redistribute it | ||
| 11 | freely, subject to the following restrictions: | ||
| 12 | |||
| 13 | 1. The origin of this software must not be misrepresented; you must not | ||
| 14 | claim that you wrote the original software. If you use this software | ||
| 15 | in a product, an acknowledgment in the product documentation would be | ||
| 16 | appreciated but is not required. | ||
| 17 | 2. Altered source versions must be plainly marked as such, and must not be | ||
| 18 | misrepresented as being the original software. | ||
| 19 | 3. This notice may not be removed or altered from any source distribution. | ||
| 20 | */ | ||
| 21 | #include "SDL_internal.h" | ||
| 22 | |||
| 23 | #include "../generic/SDL_syscond_c.h" | ||
| 24 | #include "SDL_sysmutex_c.h" | ||
| 25 | |||
| 26 | typedef SDL_Condition *(*pfnSDL_CreateCondition)(void); | ||
| 27 | typedef void (*pfnSDL_DestroyCondition)(SDL_Condition *); | ||
| 28 | typedef void (*pfnSDL_SignalCondition)(SDL_Condition *); | ||
| 29 | typedef void (*pfnSDL_BroadcastCondition)(SDL_Condition *); | ||
| 30 | typedef bool (*pfnSDL_WaitConditionTimeoutNS)(SDL_Condition *, SDL_Mutex *, Sint64); | ||
| 31 | |||
| 32 | typedef struct SDL_cond_impl_t | ||
| 33 | { | ||
| 34 | pfnSDL_CreateCondition Create; | ||
| 35 | pfnSDL_DestroyCondition Destroy; | ||
| 36 | pfnSDL_SignalCondition Signal; | ||
| 37 | pfnSDL_BroadcastCondition Broadcast; | ||
| 38 | pfnSDL_WaitConditionTimeoutNS WaitTimeoutNS; | ||
| 39 | } SDL_cond_impl_t; | ||
| 40 | |||
| 41 | // Implementation will be chosen at runtime based on available Kernel features | ||
| 42 | static SDL_cond_impl_t SDL_cond_impl_active = { 0 }; | ||
| 43 | |||
| 44 | /** | ||
| 45 | * Native Windows Condition Variable (SRW Locks) | ||
| 46 | */ | ||
| 47 | |||
| 48 | #ifndef CONDITION_VARIABLE_INIT | ||
| 49 | #define CONDITION_VARIABLE_INIT \ | ||
| 50 | { \ | ||
| 51 | 0 \ | ||
| 52 | } | ||
| 53 | typedef struct CONDITION_VARIABLE | ||
| 54 | { | ||
| 55 | PVOID Ptr; | ||
| 56 | } CONDITION_VARIABLE, *PCONDITION_VARIABLE; | ||
| 57 | #endif | ||
| 58 | |||
| 59 | typedef VOID(WINAPI *pfnWakeConditionVariable)(PCONDITION_VARIABLE); | ||
| 60 | typedef VOID(WINAPI *pfnWakeAllConditionVariable)(PCONDITION_VARIABLE); | ||
| 61 | typedef BOOL(WINAPI *pfnSleepConditionVariableSRW)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG); | ||
| 62 | typedef BOOL(WINAPI *pfnSleepConditionVariableCS)(PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD); | ||
| 63 | |||
| 64 | static pfnWakeConditionVariable pWakeConditionVariable = NULL; | ||
| 65 | static pfnWakeAllConditionVariable pWakeAllConditionVariable = NULL; | ||
| 66 | static pfnSleepConditionVariableSRW pSleepConditionVariableSRW = NULL; | ||
| 67 | static pfnSleepConditionVariableCS pSleepConditionVariableCS = NULL; | ||
| 68 | |||
| 69 | typedef struct SDL_cond_cv | ||
| 70 | { | ||
| 71 | CONDITION_VARIABLE cond; | ||
| 72 | } SDL_cond_cv; | ||
| 73 | |||
| 74 | static SDL_Condition *SDL_CreateCondition_cv(void) | ||
| 75 | { | ||
| 76 | // Relies on CONDITION_VARIABLE_INIT == 0. | ||
| 77 | return (SDL_Condition *)SDL_calloc(1, sizeof(SDL_cond_cv)); | ||
| 78 | } | ||
| 79 | |||
| 80 | static void SDL_DestroyCondition_cv(SDL_Condition *cond) | ||
| 81 | { | ||
| 82 | // There are no kernel allocated resources | ||
| 83 | SDL_free(cond); | ||
| 84 | } | ||
| 85 | |||
| 86 | static void SDL_SignalCondition_cv(SDL_Condition *_cond) | ||
| 87 | { | ||
| 88 | SDL_cond_cv *cond = (SDL_cond_cv *)_cond; | ||
| 89 | pWakeConditionVariable(&cond->cond); | ||
| 90 | } | ||
| 91 | |||
| 92 | static void SDL_BroadcastCondition_cv(SDL_Condition *_cond) | ||
| 93 | { | ||
| 94 | SDL_cond_cv *cond = (SDL_cond_cv *)_cond; | ||
| 95 | pWakeAllConditionVariable(&cond->cond); | ||
| 96 | } | ||
| 97 | |||
| 98 | static bool SDL_WaitConditionTimeoutNS_cv(SDL_Condition *_cond, SDL_Mutex *_mutex, Sint64 timeoutNS) | ||
| 99 | { | ||
| 100 | SDL_cond_cv *cond = (SDL_cond_cv *)_cond; | ||
| 101 | DWORD timeout; | ||
| 102 | bool result; | ||
| 103 | |||
| 104 | if (timeoutNS < 0) { | ||
| 105 | timeout = INFINITE; | ||
| 106 | } else { | ||
| 107 | timeout = (DWORD)SDL_NS_TO_MS(timeoutNS); | ||
| 108 | } | ||
| 109 | |||
| 110 | if (SDL_mutex_impl_active.Type == SDL_MUTEX_SRW) { | ||
| 111 | SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex; | ||
| 112 | |||
| 113 | if (mutex->count != 1 || mutex->owner != GetCurrentThreadId()) { | ||
| 114 | // Passed mutex is not locked or locked recursively" | ||
| 115 | return false; | ||
| 116 | } | ||
| 117 | |||
| 118 | // The mutex must be updated to the released state | ||
| 119 | mutex->count = 0; | ||
| 120 | mutex->owner = 0; | ||
| 121 | |||
| 122 | result = (pSleepConditionVariableSRW(&cond->cond, &mutex->srw, timeout, 0) == TRUE); | ||
| 123 | |||
| 124 | // The mutex is owned by us again, regardless of status of the wait | ||
| 125 | SDL_assert(mutex->count == 0 && mutex->owner == 0); | ||
| 126 | mutex->count = 1; | ||
| 127 | mutex->owner = GetCurrentThreadId(); | ||
| 128 | } else { | ||
| 129 | SDL_mutex_cs *mutex = (SDL_mutex_cs *)_mutex; | ||
| 130 | |||
| 131 | SDL_assert(SDL_mutex_impl_active.Type == SDL_MUTEX_CS); | ||
| 132 | |||
| 133 | result = (pSleepConditionVariableCS(&cond->cond, &mutex->cs, timeout) == TRUE); | ||
| 134 | } | ||
| 135 | |||
| 136 | return result; | ||
| 137 | } | ||
| 138 | |||
| 139 | static const SDL_cond_impl_t SDL_cond_impl_cv = { | ||
| 140 | &SDL_CreateCondition_cv, | ||
| 141 | &SDL_DestroyCondition_cv, | ||
| 142 | &SDL_SignalCondition_cv, | ||
| 143 | &SDL_BroadcastCondition_cv, | ||
| 144 | &SDL_WaitConditionTimeoutNS_cv, | ||
| 145 | }; | ||
| 146 | |||
| 147 | |||
| 148 | // Generic Condition Variable implementation using SDL_Mutex and SDL_Semaphore | ||
| 149 | static const SDL_cond_impl_t SDL_cond_impl_generic = { | ||
| 150 | &SDL_CreateCondition_generic, | ||
| 151 | &SDL_DestroyCondition_generic, | ||
| 152 | &SDL_SignalCondition_generic, | ||
| 153 | &SDL_BroadcastCondition_generic, | ||
| 154 | &SDL_WaitConditionTimeoutNS_generic, | ||
| 155 | }; | ||
| 156 | |||
| 157 | SDL_Condition *SDL_CreateCondition(void) | ||
| 158 | { | ||
| 159 | if (!SDL_cond_impl_active.Create) { | ||
| 160 | const SDL_cond_impl_t *impl = NULL; | ||
| 161 | |||
| 162 | if (SDL_mutex_impl_active.Type == SDL_MUTEX_INVALID) { | ||
| 163 | // The mutex implementation isn't decided yet, trigger it | ||
| 164 | SDL_Mutex *mutex = SDL_CreateMutex(); | ||
| 165 | if (!mutex) { | ||
| 166 | return NULL; | ||
| 167 | } | ||
| 168 | SDL_DestroyMutex(mutex); | ||
| 169 | |||
| 170 | SDL_assert(SDL_mutex_impl_active.Type != SDL_MUTEX_INVALID); | ||
| 171 | } | ||
| 172 | |||
| 173 | // Default to generic implementation, works with all mutex implementations | ||
| 174 | impl = &SDL_cond_impl_generic; | ||
| 175 | { | ||
| 176 | HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll")); | ||
| 177 | if (kernel32) { | ||
| 178 | pWakeConditionVariable = (pfnWakeConditionVariable)GetProcAddress(kernel32, "WakeConditionVariable"); | ||
| 179 | pWakeAllConditionVariable = (pfnWakeAllConditionVariable)GetProcAddress(kernel32, "WakeAllConditionVariable"); | ||
| 180 | pSleepConditionVariableSRW = (pfnSleepConditionVariableSRW)GetProcAddress(kernel32, "SleepConditionVariableSRW"); | ||
| 181 | pSleepConditionVariableCS = (pfnSleepConditionVariableCS)GetProcAddress(kernel32, "SleepConditionVariableCS"); | ||
| 182 | if (pWakeConditionVariable && pWakeAllConditionVariable && pSleepConditionVariableSRW && pSleepConditionVariableCS) { | ||
| 183 | // Use the Windows provided API | ||
| 184 | impl = &SDL_cond_impl_cv; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | SDL_copyp(&SDL_cond_impl_active, impl); | ||
| 190 | } | ||
| 191 | return SDL_cond_impl_active.Create(); | ||
| 192 | } | ||
| 193 | |||
| 194 | void SDL_DestroyCondition(SDL_Condition *cond) | ||
| 195 | { | ||
| 196 | if (cond) { | ||
| 197 | SDL_cond_impl_active.Destroy(cond); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | void SDL_SignalCondition(SDL_Condition *cond) | ||
| 202 | { | ||
| 203 | if (!cond) { | ||
| 204 | return; | ||
| 205 | } | ||
| 206 | |||
| 207 | SDL_cond_impl_active.Signal(cond); | ||
| 208 | } | ||
| 209 | |||
| 210 | void SDL_BroadcastCondition(SDL_Condition *cond) | ||
| 211 | { | ||
| 212 | if (!cond) { | ||
| 213 | return; | ||
| 214 | } | ||
| 215 | |||
| 216 | SDL_cond_impl_active.Broadcast(cond); | ||
| 217 | } | ||
| 218 | |||
| 219 | bool SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS) | ||
| 220 | { | ||
| 221 | if (!cond || !mutex) { | ||
| 222 | return true; | ||
| 223 | } | ||
| 224 | |||
| 225 | return SDL_cond_impl_active.WaitTimeoutNS(cond, mutex, timeoutNS); | ||
| 226 | } | ||
