Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/thread/windows/SDL_syssem.c
9905 views
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
38
typedef SDL_Semaphore *(*pfnSDL_CreateSemaphore)(Uint32);
39
typedef void (*pfnSDL_DestroySemaphore)(SDL_Semaphore *);
40
typedef bool (*pfnSDL_WaitSemaphoreTimeoutNS)(SDL_Semaphore *, Sint64);
41
typedef Uint32 (*pfnSDL_GetSemaphoreValue)(SDL_Semaphore *);
42
typedef void (*pfnSDL_SignalSemaphore)(SDL_Semaphore *);
43
44
typedef 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
54
static 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
63
typedef BOOL(WINAPI *pfnWaitOnAddress)(volatile VOID *, PVOID, SIZE_T, DWORD);
64
typedef VOID(WINAPI *pfnWakeByAddressSingle)(PVOID);
65
66
static pfnWaitOnAddress pWaitOnAddress = NULL;
67
static pfnWakeByAddressSingle pWakeByAddressSingle = NULL;
68
69
typedef struct SDL_semaphore_atom
70
{
71
LONG count;
72
} SDL_sem_atom;
73
74
static 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
85
static void SDL_DestroySemaphore_atom(SDL_Semaphore *sem)
86
{
87
SDL_free(sem);
88
}
89
90
static 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
162
static 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
173
static 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
185
static 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
197
typedef struct SDL_semaphore_kern
198
{
199
HANDLE id;
200
LONG count;
201
} SDL_sem_kern;
202
203
// Create a semaphore
204
static 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
224
static 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
236
static 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
260
static 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
269
static 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
288
static 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
300
SDL_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
331
void SDL_DestroySemaphore(SDL_Semaphore *sem)
332
{
333
SDL_sem_impl_active.Destroy(sem);
334
}
335
336
bool SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
337
{
338
return SDL_sem_impl_active.WaitTimeoutNS(sem, timeoutNS);
339
}
340
341
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
342
{
343
return SDL_sem_impl_active.Value(sem);
344
}
345
346
void SDL_SignalSemaphore(SDL_Semaphore *sem)
347
{
348
SDL_sem_impl_active.Signal(sem);
349
}
350
351
#endif // SDL_THREAD_WINDOWS
352
353