Simple Directmedia Layer
at main 238 lines 7.4 kB view raw
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2024 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#ifdef SDL_THREAD_WINDOWS 24 25/** 26 * Mutex functions using the Win32 API 27 * There are two implementations available based on: 28 * - Critical Sections. Available on all OS versions since Windows XP. 29 * - Slim Reader/Writer Locks. Requires Windows 7 or newer. 30 * which are chosen at runtime. 31 */ 32 33#include "SDL_sysmutex_c.h" 34 35// Implementation will be chosen at runtime based on available Kernel features 36SDL_mutex_impl_t SDL_mutex_impl_active = { 0 }; 37 38/** 39 * Implementation based on Slim Reader/Writer (SRW) Locks for Win 7 and newer. 40 */ 41 42typedef VOID(WINAPI *pfnInitializeSRWLock)(PSRWLOCK); 43typedef VOID(WINAPI *pfnReleaseSRWLockExclusive)(PSRWLOCK); 44typedef VOID(WINAPI *pfnAcquireSRWLockExclusive)(PSRWLOCK); 45typedef BOOLEAN(WINAPI *pfnTryAcquireSRWLockExclusive)(PSRWLOCK); 46static pfnInitializeSRWLock pInitializeSRWLock = NULL; 47static pfnReleaseSRWLockExclusive pReleaseSRWLockExclusive = NULL; 48static pfnAcquireSRWLockExclusive pAcquireSRWLockExclusive = NULL; 49static pfnTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive = NULL; 50 51static SDL_Mutex *SDL_CreateMutex_srw(void) 52{ 53 SDL_mutex_srw *mutex = (SDL_mutex_srw *)SDL_calloc(1, sizeof(*mutex)); 54 if (mutex) { 55 pInitializeSRWLock(&mutex->srw); 56 } 57 return (SDL_Mutex *)mutex; 58} 59 60static void SDL_DestroyMutex_srw(SDL_Mutex *mutex) 61{ 62 // There are no kernel allocated resources 63 SDL_free(mutex); 64} 65 66static void SDL_LockMutex_srw(SDL_Mutex *_mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes 67{ 68 SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex; 69 const DWORD this_thread = GetCurrentThreadId(); 70 71 if (mutex->owner == this_thread) { 72 ++mutex->count; 73 } else { 74 /* The order of operations is important. 75 We set the locking thread id after we obtain the lock 76 so unlocks from other threads will fail. 77 */ 78 pAcquireSRWLockExclusive(&mutex->srw); 79 SDL_assert(mutex->count == 0 && mutex->owner == 0); 80 mutex->owner = this_thread; 81 mutex->count = 1; 82 } 83} 84 85static bool SDL_TryLockMutex_srw(SDL_Mutex *_mutex) 86{ 87 SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex; 88 const DWORD this_thread = GetCurrentThreadId(); 89 bool retval = true; 90 91 if (mutex->owner == this_thread) { 92 ++mutex->count; 93 } else { 94 if (pTryAcquireSRWLockExclusive(&mutex->srw) != 0) { 95 SDL_assert(mutex->count == 0 && mutex->owner == 0); 96 mutex->owner = this_thread; 97 mutex->count = 1; 98 } else { 99 retval = false; 100 } 101 } 102 return retval; 103} 104 105static void SDL_UnlockMutex_srw(SDL_Mutex *_mutex) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes 106{ 107 SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex; 108 109 if (mutex->owner == GetCurrentThreadId()) { 110 if (--mutex->count == 0) { 111 mutex->owner = 0; 112 pReleaseSRWLockExclusive(&mutex->srw); 113 } 114 } else { 115 SDL_assert(!"mutex not owned by this thread"); // undefined behavior...! 116 } 117} 118 119static const SDL_mutex_impl_t SDL_mutex_impl_srw = { 120 &SDL_CreateMutex_srw, 121 &SDL_DestroyMutex_srw, 122 &SDL_LockMutex_srw, 123 &SDL_TryLockMutex_srw, 124 &SDL_UnlockMutex_srw, 125 SDL_MUTEX_SRW, 126}; 127 128/** 129 * Fallback Mutex implementation using Critical Sections (before Win 7) 130 */ 131 132static SDL_Mutex *SDL_CreateMutex_cs(void) 133{ 134 SDL_mutex_cs *mutex = (SDL_mutex_cs *)SDL_malloc(sizeof(*mutex)); 135 if (mutex) { 136 // Initialize 137 // On SMP systems, a non-zero spin count generally helps performance 138 // This function always succeeds 139 (void)InitializeCriticalSectionAndSpinCount(&mutex->cs, 2000); 140 } 141 return (SDL_Mutex *)mutex; 142} 143 144static void SDL_DestroyMutex_cs(SDL_Mutex *mutex_) 145{ 146 SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_; 147 DeleteCriticalSection(&mutex->cs); 148 SDL_free(mutex); 149} 150 151static void SDL_LockMutex_cs(SDL_Mutex *mutex_) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes 152{ 153 SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_; 154 EnterCriticalSection(&mutex->cs); 155} 156 157static bool SDL_TryLockMutex_cs(SDL_Mutex *mutex_) 158{ 159 SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_; 160 return (TryEnterCriticalSection(&mutex->cs) == TRUE); 161} 162 163static void SDL_UnlockMutex_cs(SDL_Mutex *mutex_) SDL_NO_THREAD_SAFETY_ANALYSIS // clang doesn't know about NULL mutexes 164{ 165 SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_; 166 LeaveCriticalSection(&mutex->cs); 167} 168 169static const SDL_mutex_impl_t SDL_mutex_impl_cs = { 170 &SDL_CreateMutex_cs, 171 &SDL_DestroyMutex_cs, 172 &SDL_LockMutex_cs, 173 &SDL_TryLockMutex_cs, 174 &SDL_UnlockMutex_cs, 175 SDL_MUTEX_CS, 176}; 177 178/** 179 * Runtime selection and redirection 180 */ 181 182SDL_Mutex *SDL_CreateMutex(void) 183{ 184 if (!SDL_mutex_impl_active.Create) { 185 const SDL_mutex_impl_t *impl = &SDL_mutex_impl_cs; 186 187 // Try faster implementation for Windows 7 and newer 188 HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll")); 189 if (kernel32) { 190 // Requires Vista: 191 pInitializeSRWLock = (pfnInitializeSRWLock)GetProcAddress(kernel32, "InitializeSRWLock"); 192 pReleaseSRWLockExclusive = (pfnReleaseSRWLockExclusive)GetProcAddress(kernel32, "ReleaseSRWLockExclusive"); 193 pAcquireSRWLockExclusive = (pfnAcquireSRWLockExclusive)GetProcAddress(kernel32, "AcquireSRWLockExclusive"); 194 // Requires 7: 195 pTryAcquireSRWLockExclusive = (pfnTryAcquireSRWLockExclusive)GetProcAddress(kernel32, "TryAcquireSRWLockExclusive"); 196 if (pInitializeSRWLock && pReleaseSRWLockExclusive && pAcquireSRWLockExclusive && pTryAcquireSRWLockExclusive) { 197 impl = &SDL_mutex_impl_srw; 198 } 199 } 200 201 // Copy instead of using pointer to save one level of indirection 202 SDL_copyp(&SDL_mutex_impl_active, impl); 203 } 204 return SDL_mutex_impl_active.Create(); 205} 206 207void SDL_DestroyMutex(SDL_Mutex *mutex) 208{ 209 if (mutex) { 210 SDL_mutex_impl_active.Destroy(mutex); 211 } 212} 213 214void SDL_LockMutex(SDL_Mutex *mutex) 215{ 216 if (mutex) { 217 SDL_mutex_impl_active.Lock(mutex); 218 } 219} 220 221bool SDL_TryLockMutex(SDL_Mutex *mutex) 222{ 223 bool result = true; 224 225 if (mutex) { 226 result = SDL_mutex_impl_active.TryLock(mutex); 227 } 228 return result; 229} 230 231void SDL_UnlockMutex(SDL_Mutex *mutex) 232{ 233 if (mutex) { 234 SDL_mutex_impl_active.Unlock(mutex); 235 } 236} 237 238#endif // SDL_THREAD_WINDOWS