Atlas - testsem.c

Home / ext / SDL / test Lines: 1 | Size: 9447 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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} 334
[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.