summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/thread/windows/SDL_syssem.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/thread/windows/SDL_syssem.c
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/thread/windows/SDL_syssem.c')
-rw-r--r--contrib/SDL-3.2.8/src/thread/windows/SDL_syssem.c351
1 files changed, 351 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/thread/windows/SDL_syssem.c b/contrib/SDL-3.2.8/src/thread/windows/SDL_syssem.c
new file mode 100644
index 0000000..ba35add
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/thread/windows/SDL_syssem.c
@@ -0,0 +1,351 @@
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#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