Atlas - SDL_syssem.c
Home / ext / SDL / src / thread / windows Lines: 1 | Size: 9808 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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#ifdef SDL_THREAD_WINDOWS 24 25/** 26 * Semaphore functions using the Win32 API 27 * There are two implementations available based on: 28 * - Kernel Semaphores. Available on all OS versions. (kern) 29 * Heavy-weight inter-process kernel objects. 30 * - Atomics and WaitOnAddress API. (atom) 31 * Faster due to significantly less context switches. 32 * Requires Windows 8 or newer. 33 * which are chosen at runtime. 34 */ 35 36#include "../../core/windows/SDL_windows.h" 37 38typedef SDL_Semaphore *(*pfnSDL_CreateSemaphore)(Uint32); 39typedef void (*pfnSDL_DestroySemaphore)(SDL_Semaphore *); 40typedef bool (*pfnSDL_WaitSemaphoreTimeoutNS)(SDL_Semaphore *, Sint64); 41typedef Uint32 (*pfnSDL_GetSemaphoreValue)(SDL_Semaphore *); 42typedef void (*pfnSDL_SignalSemaphore)(SDL_Semaphore *); 43 44typedef struct SDL_semaphore_impl_t 45{ 46 pfnSDL_CreateSemaphore Create; 47 pfnSDL_DestroySemaphore Destroy; 48 pfnSDL_WaitSemaphoreTimeoutNS WaitTimeoutNS; 49 pfnSDL_GetSemaphoreValue Value; 50 pfnSDL_SignalSemaphore Signal; 51} SDL_sem_impl_t; 52 53// Implementation will be chosen at runtime based on available Kernel features 54static SDL_sem_impl_t SDL_sem_impl_active = { 0 }; 55 56/** 57 * Atomic + WaitOnAddress implementation 58 */ 59 60// APIs not available on WinPhone 8.1 61// https://www.microsoft.com/en-us/download/details.aspx?id=47328 62 63typedef BOOL (WINAPI *pfnWaitOnAddress)(volatile VOID *, PVOID, SIZE_T, DWORD); 64typedef VOID (WINAPI *pfnWakeByAddressSingle)(PVOID); 65 66static pfnWaitOnAddress pWaitOnAddress = NULL; 67static pfnWakeByAddressSingle pWakeByAddressSingle = NULL; 68 69typedef struct SDL_semaphore_atom 70{ 71 LONG count; 72} SDL_sem_atom; 73 74static SDL_Semaphore *SDL_CreateSemaphore_atom(Uint32 initial_value) 75{ 76 SDL_sem_atom *sem; 77 78 sem = (SDL_sem_atom *)SDL_malloc(sizeof(*sem)); 79 if (sem) { 80 sem->count = initial_value; 81 } 82 return (SDL_Semaphore *)sem; 83} 84 85static void SDL_DestroySemaphore_atom(SDL_Semaphore *sem) 86{ 87 SDL_free(sem); 88} 89 90static bool SDL_WaitSemaphoreTimeoutNS_atom(SDL_Semaphore *_sem, Sint64 timeoutNS) 91{ 92 SDL_sem_atom *sem = (SDL_sem_atom *)_sem; 93 LONG count; 94 Uint64 now; 95 Uint64 deadline; 96 DWORD timeout_eff; 97 98 if (!sem) { 99 return true; 100 } 101 102 if (timeoutNS == 0) { 103 count = sem->count; 104 if (count == 0) { 105 return false; 106 } 107 108 if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) { 109 return true; 110 } 111 112 return false; 113 } 114 115 if (timeoutNS < 0) { 116 for (;;) { 117 count = sem->count; 118 while (count == 0) { 119 if (!pWaitOnAddress(&sem->count, &count, sizeof(sem->count), INFINITE)) { 120 return false; 121 } 122 count = sem->count; 123 } 124 125 if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) { 126 return true; 127 } 128 } 129 } 130 131 /** 132 * WaitOnAddress is subject to spurious and stolen wakeups so we 133 * need to recalculate the effective timeout before every wait 134 */ 135 now = SDL_GetTicksNS(); 136 deadline = now + timeoutNS; 137 138 for (;;) { 139 count = sem->count; 140 // If no semaphore is available we need to wait 141 while (count == 0) { 142 now = SDL_GetTicksNS(); 143 if (deadline > now) { 144 timeout_eff = (DWORD)SDL_NS_TO_MS(deadline - now); 145 } else { 146 return false; 147 } 148 if (!pWaitOnAddress(&sem->count, &count, sizeof(count), timeout_eff)) { 149 return false; 150 } 151 count = sem->count; 152 } 153 154 // Actually the semaphore is only consumed if this succeeds 155 // If it doesn't we need to do everything again 156 if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) { 157 return true; 158 } 159 } 160} 161 162static Uint32 SDL_GetSemaphoreValue_atom(SDL_Semaphore *_sem) 163{ 164 SDL_sem_atom *sem = (SDL_sem_atom *)_sem; 165 166 if (!sem) { 167 return 0; 168 } 169 170 return (Uint32)sem->count; 171} 172 173static void SDL_SignalSemaphore_atom(SDL_Semaphore *_sem) 174{ 175 SDL_sem_atom *sem = (SDL_sem_atom *)_sem; 176 177 if (!sem) { 178 return; 179 } 180 181 InterlockedIncrement(&sem->count); 182 pWakeByAddressSingle(&sem->count); 183} 184 185static const SDL_sem_impl_t SDL_sem_impl_atom = { 186 &SDL_CreateSemaphore_atom, 187 &SDL_DestroySemaphore_atom, 188 &SDL_WaitSemaphoreTimeoutNS_atom, 189 &SDL_GetSemaphoreValue_atom, 190 &SDL_SignalSemaphore_atom, 191}; 192 193/** 194 * Fallback Semaphore implementation using Kernel Semaphores 195 */ 196 197typedef struct SDL_semaphore_kern 198{ 199 HANDLE id; 200 LONG count; 201} SDL_sem_kern; 202 203// Create a semaphore 204static SDL_Semaphore *SDL_CreateSemaphore_kern(Uint32 initial_value) 205{ 206 SDL_sem_kern *sem; 207 208 // Allocate sem memory 209 sem = (SDL_sem_kern *)SDL_malloc(sizeof(*sem)); 210 if (sem) { 211 // Create the semaphore, with max value 32K 212 sem->id = CreateSemaphore(NULL, initial_value, 32 * 1024, NULL); 213 sem->count = initial_value; 214 if (!sem->id) { 215 SDL_SetError("Couldn't create semaphore"); 216 SDL_free(sem); 217 sem = NULL; 218 } 219 } 220 return (SDL_Semaphore *)sem; 221} 222 223// Free the semaphore 224static void SDL_DestroySemaphore_kern(SDL_Semaphore *_sem) 225{ 226 SDL_sem_kern *sem = (SDL_sem_kern *)_sem; 227 if (sem) { 228 if (sem->id) { 229 CloseHandle(sem->id); 230 sem->id = 0; 231 } 232 SDL_free(sem); 233 } 234} 235 236static bool SDL_WaitSemaphoreTimeoutNS_kern(SDL_Semaphore *_sem, Sint64 timeoutNS) 237{ 238 SDL_sem_kern *sem = (SDL_sem_kern *)_sem; 239 DWORD dwMilliseconds; 240 241 if (!sem) { 242 return true; 243 } 244 245 if (timeoutNS < 0) { 246 dwMilliseconds = INFINITE; 247 } else { 248 dwMilliseconds = (DWORD)SDL_NS_TO_MS(timeoutNS); 249 } 250 switch (WaitForSingleObjectEx(sem->id, dwMilliseconds, FALSE)) { 251 case WAIT_OBJECT_0: 252 InterlockedDecrement(&sem->count); 253 return true; 254 default: 255 return false; 256 } 257} 258 259// Returns the current count of the semaphore 260static Uint32 SDL_GetSemaphoreValue_kern(SDL_Semaphore *_sem) 261{ 262 SDL_sem_kern *sem = (SDL_sem_kern *)_sem; 263 if (!sem) { 264 return 0; 265 } 266 return (Uint32)sem->count; 267} 268 269static void SDL_SignalSemaphore_kern(SDL_Semaphore *_sem) 270{ 271 SDL_sem_kern *sem = (SDL_sem_kern *)_sem; 272 273 if (!sem) { 274 return; 275 } 276 277 /* Increase the counter in the first place, because 278 * after a successful release the semaphore may 279 * immediately get destroyed by another thread which 280 * is waiting for this semaphore. 281 */ 282 InterlockedIncrement(&sem->count); 283 if (ReleaseSemaphore(sem->id, 1, NULL) == FALSE) { 284 InterlockedDecrement(&sem->count); // restore 285 } 286} 287 288static const SDL_sem_impl_t SDL_sem_impl_kern = { 289 &SDL_CreateSemaphore_kern, 290 &SDL_DestroySemaphore_kern, 291 &SDL_WaitSemaphoreTimeoutNS_kern, 292 &SDL_GetSemaphoreValue_kern, 293 &SDL_SignalSemaphore_kern, 294}; 295 296/** 297 * Runtime selection and redirection 298 */ 299 300SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value) 301{ 302 if (!SDL_sem_impl_active.Create) { 303 // Default to fallback implementation 304 const SDL_sem_impl_t *impl = &SDL_sem_impl_kern; 305 306 if (!SDL_GetHintBoolean(SDL_HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL, false)) { 307 /* We already statically link to features from this Api 308 * Set (e.g. WaitForSingleObject). Dynamically loading 309 * API Sets is not explicitly documented but according to 310 * Microsoft our specific use case is legal and correct: 311 * https://github.com/microsoft/STL/pull/593#issuecomment-655799859 312 */ 313 HMODULE synch120 = GetModuleHandle(TEXT("api-ms-win-core-synch-l1-2-0.dll")); 314 if (synch120) { 315 // Try to load required functions provided by Win 8 or newer 316 pWaitOnAddress = (pfnWaitOnAddress)GetProcAddress(synch120, "WaitOnAddress"); 317 pWakeByAddressSingle = (pfnWakeByAddressSingle)GetProcAddress(synch120, "WakeByAddressSingle"); 318 319 if (pWaitOnAddress && pWakeByAddressSingle) { 320 impl = &SDL_sem_impl_atom; 321 } 322 } 323 } 324 325 // Copy instead of using pointer to save one level of indirection 326 SDL_copyp(&SDL_sem_impl_active, impl); 327 } 328 return SDL_sem_impl_active.Create(initial_value); 329} 330 331void SDL_DestroySemaphore(SDL_Semaphore *sem) 332{ 333 SDL_sem_impl_active.Destroy(sem); 334} 335 336bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS) 337{ 338 return SDL_sem_impl_active.WaitTimeoutNS(sem, timeoutNS); 339} 340 341Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem) 342{ 343 return SDL_sem_impl_active.Value(sem); 344} 345 346void SDL_SignalSemaphore(SDL_Semaphore *sem) 347{ 348 SDL_sem_impl_active.Signal(sem); 349} 350 351#endif // SDL_THREAD_WINDOWS 352[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.