summaryrefslogtreecommitdiff
path: root/src/contrib/SDL-3.2.20/test/testsem.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-08-30 16:53:58 -0700
committer3gg <3gg@shellblade.net>2025-08-30 16:53:58 -0700
commit6aaedb813fa11ba0679c3051bc2eb28646b9506c (patch)
tree34acbfc9840e02cb4753e6306ea7ce978bf8b58e /src/contrib/SDL-3.2.20/test/testsem.c
parent8f228ade99dd3d4c8da9b78ade1815c9adf85c8f (diff)
Update to SDL3
Diffstat (limited to 'src/contrib/SDL-3.2.20/test/testsem.c')
-rw-r--r--src/contrib/SDL-3.2.20/test/testsem.c333
1 files changed, 333 insertions, 0 deletions
diff --git a/src/contrib/SDL-3.2.20/test/testsem.c b/src/contrib/SDL-3.2.20/test/testsem.c
new file mode 100644
index 0000000..78de533
--- /dev/null
+++ b/src/contrib/SDL-3.2.20/test/testsem.c
@@ -0,0 +1,333 @@
1/*
2 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely.
11*/
12
13/* Simple test of the SDL semaphore code */
14
15#include <signal.h>
16
17#include <SDL3/SDL.h>
18#include <SDL3/SDL_main.h>
19#include <SDL3/SDL_test.h>
20
21#define NUM_THREADS 10
22/* This value should be smaller than the maximum count of the */
23/* semaphore implementation: */
24#define NUM_OVERHEAD_OPS 10000
25#define NUM_OVERHEAD_OPS_MULT 10
26
27static SDL_Semaphore *sem;
28static int alive;
29
30typedef struct Thread_State
31{
32 SDL_Thread *thread;
33 int number;
34 bool flag;
35 int loop_count;
36 int content_count;
37} Thread_State;
38
39static void log_usage(char *progname, SDLTest_CommonState *state) {
40 static const char *options[] = { "[--no-threads]", "init_value", NULL };
41 SDLTest_CommonLogUsage(state, progname, options);
42}
43
44static void
45killed(int sig)
46{
47 alive = 0;
48}
49
50static int SDLCALL
51ThreadFuncRealWorld(void *data)
52{
53 Thread_State *state = (Thread_State *)data;
54 while (alive) {
55 SDL_WaitSemaphore(sem);
56 SDL_Log("Thread number %d has got the semaphore (value = %" SDL_PRIu32 ")!",
57 state->number, SDL_GetSemaphoreValue(sem));
58 SDL_Delay(200);
59 SDL_SignalSemaphore(sem);
60 SDL_Log("Thread number %d has released the semaphore (value = %" SDL_PRIu32 ")!",
61 state->number, SDL_GetSemaphoreValue(sem));
62 ++state->loop_count;
63 SDL_Delay(1); /* For the scheduler */
64 }
65 SDL_Log("Thread number %d exiting.", state->number);
66 return 0;
67}
68
69static void
70TestRealWorld(int init_sem)
71{
72 Thread_State thread_states[NUM_THREADS] = { { 0 } };
73 int i;
74 int loop_count;
75
76 sem = SDL_CreateSemaphore(init_sem);
77
78 SDL_Log("Running %d threads, semaphore value = %d", NUM_THREADS,
79 init_sem);
80 alive = 1;
81 /* Create all the threads */
82 for (i = 0; i < NUM_THREADS; ++i) {
83 char name[64];
84 (void)SDL_snprintf(name, sizeof(name), "Thread%u", (unsigned int)i);
85 thread_states[i].number = i;
86 thread_states[i].thread = SDL_CreateThread(ThreadFuncRealWorld, name, (void *)&thread_states[i]);
87 }
88
89 /* Wait 10 seconds */
90 SDL_Delay(10 * 1000);
91
92 /* Wait for all threads to finish */
93 SDL_Log("Waiting for threads to finish");
94 alive = 0;
95 loop_count = 0;
96 for (i = 0; i < NUM_THREADS; ++i) {
97 SDL_WaitThread(thread_states[i].thread, NULL);
98 loop_count += thread_states[i].loop_count;
99 }
100 SDL_Log("Finished waiting for threads, ran %d loops in total", loop_count);
101 SDL_Log("%s", "");
102
103 SDL_DestroySemaphore(sem);
104}
105
106static void
107TestWaitTimeout(void)
108{
109 Uint64 start_ticks;
110 Uint64 end_ticks;
111 Uint64 duration;
112 bool result;
113
114 sem = SDL_CreateSemaphore(0);
115 SDL_Log("Waiting 2 seconds on semaphore");
116
117 start_ticks = SDL_GetTicks();
118 result = SDL_WaitSemaphoreTimeout(sem, 2000);
119 end_ticks = SDL_GetTicks();
120
121 duration = end_ticks - start_ticks;
122
123 /* Accept a little offset in the effective wait */
124 SDL_Log("Wait took %" SDL_PRIu64 " milliseconds", duration);
125 SDL_Log("%s", "");
126 SDL_assert(duration > 1900 && duration < 2050);
127
128 /* Check to make sure the return value indicates timed out */
129 if (result) {
130 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_WaitSemaphoreTimeout returned: %d; expected: false", result);
131 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "");
132 }
133
134 SDL_DestroySemaphore(sem);
135}
136
137static void
138TestOverheadUncontended(void)
139{
140 Uint64 start_ticks;
141 Uint64 end_ticks;
142 Uint64 duration;
143 int i, j;
144
145 sem = SDL_CreateSemaphore(0);
146 SDL_Log("Doing %d uncontended Post/Wait operations on semaphore", NUM_OVERHEAD_OPS * NUM_OVERHEAD_OPS_MULT);
147
148 start_ticks = SDL_GetTicks();
149 for (i = 0; i < NUM_OVERHEAD_OPS_MULT; i++) {
150 for (j = 0; j < NUM_OVERHEAD_OPS; j++) {
151 SDL_SignalSemaphore(sem);
152 }
153 for (j = 0; j < NUM_OVERHEAD_OPS; j++) {
154 SDL_WaitSemaphore(sem);
155 }
156 }
157 end_ticks = SDL_GetTicks();
158
159 duration = end_ticks - start_ticks;
160 SDL_Log("Took %" SDL_PRIu64 " milliseconds", duration);
161 SDL_Log("%s", "");
162
163 SDL_DestroySemaphore(sem);
164}
165
166static int SDLCALL
167ThreadFuncOverheadContended(void *data)
168{
169 Thread_State *state = (Thread_State *)data;
170
171 if (state->flag) {
172 while (alive) {
173 if (!SDL_TryWaitSemaphore(sem)) {
174 ++state->content_count;
175 }
176 ++state->loop_count;
177 }
178 } else {
179 while (alive) {
180 /* Timeout needed to allow check on alive flag */
181 if (!SDL_WaitSemaphoreTimeout(sem, 50)) {
182 ++state->content_count;
183 }
184 ++state->loop_count;
185 }
186 }
187 return 0;
188}
189
190static void
191TestOverheadContended(bool try_wait)
192{
193 Uint64 start_ticks;
194 Uint64 end_ticks;
195 Uint64 duration;
196 Thread_State thread_states[NUM_THREADS] = { { 0 } };
197 char textBuffer[1024];
198 int loop_count;
199 int content_count;
200 int i, j;
201 size_t len;
202
203 sem = SDL_CreateSemaphore(0);
204 SDL_Log("Doing %d contended %s operations on semaphore using %d threads",
205 NUM_OVERHEAD_OPS * NUM_OVERHEAD_OPS_MULT, try_wait ? "Post/TryWait" : "Post/WaitTimeout", NUM_THREADS);
206 alive = 1;
207 /* Create multiple threads to starve the semaphore and cause contention */
208 for (i = 0; i < NUM_THREADS; ++i) {
209 char name[64];
210 (void)SDL_snprintf(name, sizeof(name), "Thread%u", (unsigned int)i);
211 thread_states[i].flag = try_wait;
212 thread_states[i].thread = SDL_CreateThread(ThreadFuncOverheadContended, name, (void *)&thread_states[i]);
213 }
214
215 start_ticks = SDL_GetTicks();
216 for (i = 0; i < NUM_OVERHEAD_OPS_MULT; i++) {
217 for (j = 0; j < NUM_OVERHEAD_OPS; j++) {
218 SDL_SignalSemaphore(sem);
219 }
220 /* Make sure threads consumed everything */
221 while (SDL_GetSemaphoreValue(sem)) {
222 /* Friendlier with cooperative threading models */
223 SDL_DelayNS(1);
224 }
225 }
226 end_ticks = SDL_GetTicks();
227
228 alive = 0;
229 loop_count = 0;
230 content_count = 0;
231 for (i = 0; i < NUM_THREADS; ++i) {
232 SDL_WaitThread(thread_states[i].thread, NULL);
233 loop_count += thread_states[i].loop_count;
234 content_count += thread_states[i].content_count;
235 }
236 SDL_assert_release((loop_count - content_count) == NUM_OVERHEAD_OPS * NUM_OVERHEAD_OPS_MULT);
237
238 duration = end_ticks - start_ticks;
239 SDL_Log("Took %" SDL_PRIu64 " milliseconds, threads %s %d out of %d times in total (%.2f%%)",
240 duration, try_wait ? "where contended" : "timed out", content_count,
241 loop_count, ((float)content_count * 100) / loop_count);
242 /* Print how many semaphores where consumed per thread */
243 (void)SDL_snprintf(textBuffer, sizeof(textBuffer), "{ ");
244 for (i = 0; i < NUM_THREADS; ++i) {
245 if (i > 0) {
246 len = SDL_strlen(textBuffer);
247 (void)SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, ", ");
248 }
249 len = SDL_strlen(textBuffer);
250 (void)SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, "%d", thread_states[i].loop_count - thread_states[i].content_count);
251 }
252 len = SDL_strlen(textBuffer);
253 (void)SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, " }");
254 SDL_Log("%s", textBuffer);
255
256 SDL_DestroySemaphore(sem);
257}
258
259int main(int argc, char **argv)
260{
261 int arg_count = 0;
262 int i;
263 int init_sem = 0;
264 bool enable_threads = true;
265 SDLTest_CommonState *state;
266
267 /* Initialize test framework */
268 state = SDLTest_CommonCreateState(argv, 0);
269 if (!state) {
270 return 1;
271 }
272
273 /* Parse commandline */
274 for (i = 1; i < argc;) {
275 int consumed;
276
277 consumed = SDLTest_CommonArg(state, i);
278 if (consumed == 0) {
279 consumed = -1;
280 if (SDL_strcasecmp(argv[i], "--no-threads") == 0) {
281 enable_threads = false;
282 consumed = 1;
283 } else if (arg_count == 0) {
284 char *endptr;
285 init_sem = SDL_strtol(argv[i], &endptr, 0);
286 if (endptr != argv[i] && *endptr == '\0') {
287 arg_count++;
288 consumed = 1;
289 }
290 }
291 }
292 if (consumed <= 0) {
293 log_usage(argv[0], state);
294 return 1;
295 }
296
297 i += consumed;
298 }
299
300 if (arg_count != 1) {
301 log_usage(argv[0], state);
302 return 1;
303 }
304
305 /* Load the SDL library */
306 if (!SDL_Init(0)) {
307 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError());
308 return 1;
309 }
310 (void)signal(SIGTERM, killed);
311 (void)signal(SIGINT, killed);
312
313 if (enable_threads) {
314 if (init_sem > 0) {
315 TestRealWorld(init_sem);
316 }
317
318 TestWaitTimeout();
319 }
320
321 TestOverheadUncontended();
322
323 if (enable_threads) {
324 TestOverheadContended(false);
325
326 TestOverheadContended(true);
327 }
328
329 SDL_Quit();
330 SDLTest_CommonDestroyState(state);
331
332 return 0;
333}