From 6aaedb813fa11ba0679c3051bc2eb28646b9506c Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 30 Aug 2025 16:53:58 -0700 Subject: Update to SDL3 --- src/contrib/SDL-3.2.20/test/testsem.c | 333 ++++++++++++++++++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 src/contrib/SDL-3.2.20/test/testsem.c (limited to 'src/contrib/SDL-3.2.20/test/testsem.c') 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 @@ +/* + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely. +*/ + +/* Simple test of the SDL semaphore code */ + +#include + +#include +#include +#include + +#define NUM_THREADS 10 +/* This value should be smaller than the maximum count of the */ +/* semaphore implementation: */ +#define NUM_OVERHEAD_OPS 10000 +#define NUM_OVERHEAD_OPS_MULT 10 + +static SDL_Semaphore *sem; +static int alive; + +typedef struct Thread_State +{ + SDL_Thread *thread; + int number; + bool flag; + int loop_count; + int content_count; +} Thread_State; + +static void log_usage(char *progname, SDLTest_CommonState *state) { + static const char *options[] = { "[--no-threads]", "init_value", NULL }; + SDLTest_CommonLogUsage(state, progname, options); +} + +static void +killed(int sig) +{ + alive = 0; +} + +static int SDLCALL +ThreadFuncRealWorld(void *data) +{ + Thread_State *state = (Thread_State *)data; + while (alive) { + SDL_WaitSemaphore(sem); + SDL_Log("Thread number %d has got the semaphore (value = %" SDL_PRIu32 ")!", + state->number, SDL_GetSemaphoreValue(sem)); + SDL_Delay(200); + SDL_SignalSemaphore(sem); + SDL_Log("Thread number %d has released the semaphore (value = %" SDL_PRIu32 ")!", + state->number, SDL_GetSemaphoreValue(sem)); + ++state->loop_count; + SDL_Delay(1); /* For the scheduler */ + } + SDL_Log("Thread number %d exiting.", state->number); + return 0; +} + +static void +TestRealWorld(int init_sem) +{ + Thread_State thread_states[NUM_THREADS] = { { 0 } }; + int i; + int loop_count; + + sem = SDL_CreateSemaphore(init_sem); + + SDL_Log("Running %d threads, semaphore value = %d", NUM_THREADS, + init_sem); + alive = 1; + /* Create all the threads */ + for (i = 0; i < NUM_THREADS; ++i) { + char name[64]; + (void)SDL_snprintf(name, sizeof(name), "Thread%u", (unsigned int)i); + thread_states[i].number = i; + thread_states[i].thread = SDL_CreateThread(ThreadFuncRealWorld, name, (void *)&thread_states[i]); + } + + /* Wait 10 seconds */ + SDL_Delay(10 * 1000); + + /* Wait for all threads to finish */ + SDL_Log("Waiting for threads to finish"); + alive = 0; + loop_count = 0; + for (i = 0; i < NUM_THREADS; ++i) { + SDL_WaitThread(thread_states[i].thread, NULL); + loop_count += thread_states[i].loop_count; + } + SDL_Log("Finished waiting for threads, ran %d loops in total", loop_count); + SDL_Log("%s", ""); + + SDL_DestroySemaphore(sem); +} + +static void +TestWaitTimeout(void) +{ + Uint64 start_ticks; + Uint64 end_ticks; + Uint64 duration; + bool result; + + sem = SDL_CreateSemaphore(0); + SDL_Log("Waiting 2 seconds on semaphore"); + + start_ticks = SDL_GetTicks(); + result = SDL_WaitSemaphoreTimeout(sem, 2000); + end_ticks = SDL_GetTicks(); + + duration = end_ticks - start_ticks; + + /* Accept a little offset in the effective wait */ + SDL_Log("Wait took %" SDL_PRIu64 " milliseconds", duration); + SDL_Log("%s", ""); + SDL_assert(duration > 1900 && duration < 2050); + + /* Check to make sure the return value indicates timed out */ + if (result) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_WaitSemaphoreTimeout returned: %d; expected: false", result); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", ""); + } + + SDL_DestroySemaphore(sem); +} + +static void +TestOverheadUncontended(void) +{ + Uint64 start_ticks; + Uint64 end_ticks; + Uint64 duration; + int i, j; + + sem = SDL_CreateSemaphore(0); + SDL_Log("Doing %d uncontended Post/Wait operations on semaphore", NUM_OVERHEAD_OPS * NUM_OVERHEAD_OPS_MULT); + + start_ticks = SDL_GetTicks(); + for (i = 0; i < NUM_OVERHEAD_OPS_MULT; i++) { + for (j = 0; j < NUM_OVERHEAD_OPS; j++) { + SDL_SignalSemaphore(sem); + } + for (j = 0; j < NUM_OVERHEAD_OPS; j++) { + SDL_WaitSemaphore(sem); + } + } + end_ticks = SDL_GetTicks(); + + duration = end_ticks - start_ticks; + SDL_Log("Took %" SDL_PRIu64 " milliseconds", duration); + SDL_Log("%s", ""); + + SDL_DestroySemaphore(sem); +} + +static int SDLCALL +ThreadFuncOverheadContended(void *data) +{ + Thread_State *state = (Thread_State *)data; + + if (state->flag) { + while (alive) { + if (!SDL_TryWaitSemaphore(sem)) { + ++state->content_count; + } + ++state->loop_count; + } + } else { + while (alive) { + /* Timeout needed to allow check on alive flag */ + if (!SDL_WaitSemaphoreTimeout(sem, 50)) { + ++state->content_count; + } + ++state->loop_count; + } + } + return 0; +} + +static void +TestOverheadContended(bool try_wait) +{ + Uint64 start_ticks; + Uint64 end_ticks; + Uint64 duration; + Thread_State thread_states[NUM_THREADS] = { { 0 } }; + char textBuffer[1024]; + int loop_count; + int content_count; + int i, j; + size_t len; + + sem = SDL_CreateSemaphore(0); + SDL_Log("Doing %d contended %s operations on semaphore using %d threads", + NUM_OVERHEAD_OPS * NUM_OVERHEAD_OPS_MULT, try_wait ? "Post/TryWait" : "Post/WaitTimeout", NUM_THREADS); + alive = 1; + /* Create multiple threads to starve the semaphore and cause contention */ + for (i = 0; i < NUM_THREADS; ++i) { + char name[64]; + (void)SDL_snprintf(name, sizeof(name), "Thread%u", (unsigned int)i); + thread_states[i].flag = try_wait; + thread_states[i].thread = SDL_CreateThread(ThreadFuncOverheadContended, name, (void *)&thread_states[i]); + } + + start_ticks = SDL_GetTicks(); + for (i = 0; i < NUM_OVERHEAD_OPS_MULT; i++) { + for (j = 0; j < NUM_OVERHEAD_OPS; j++) { + SDL_SignalSemaphore(sem); + } + /* Make sure threads consumed everything */ + while (SDL_GetSemaphoreValue(sem)) { + /* Friendlier with cooperative threading models */ + SDL_DelayNS(1); + } + } + end_ticks = SDL_GetTicks(); + + alive = 0; + loop_count = 0; + content_count = 0; + for (i = 0; i < NUM_THREADS; ++i) { + SDL_WaitThread(thread_states[i].thread, NULL); + loop_count += thread_states[i].loop_count; + content_count += thread_states[i].content_count; + } + SDL_assert_release((loop_count - content_count) == NUM_OVERHEAD_OPS * NUM_OVERHEAD_OPS_MULT); + + duration = end_ticks - start_ticks; + SDL_Log("Took %" SDL_PRIu64 " milliseconds, threads %s %d out of %d times in total (%.2f%%)", + duration, try_wait ? "where contended" : "timed out", content_count, + loop_count, ((float)content_count * 100) / loop_count); + /* Print how many semaphores where consumed per thread */ + (void)SDL_snprintf(textBuffer, sizeof(textBuffer), "{ "); + for (i = 0; i < NUM_THREADS; ++i) { + if (i > 0) { + len = SDL_strlen(textBuffer); + (void)SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, ", "); + } + len = SDL_strlen(textBuffer); + (void)SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, "%d", thread_states[i].loop_count - thread_states[i].content_count); + } + len = SDL_strlen(textBuffer); + (void)SDL_snprintf(textBuffer + len, sizeof(textBuffer) - len, " }"); + SDL_Log("%s", textBuffer); + + SDL_DestroySemaphore(sem); +} + +int main(int argc, char **argv) +{ + int arg_count = 0; + int i; + int init_sem = 0; + bool enable_threads = true; + SDLTest_CommonState *state; + + /* Initialize test framework */ + state = SDLTest_CommonCreateState(argv, 0); + if (!state) { + return 1; + } + + /* Parse commandline */ + for (i = 1; i < argc;) { + int consumed; + + consumed = SDLTest_CommonArg(state, i); + if (consumed == 0) { + consumed = -1; + if (SDL_strcasecmp(argv[i], "--no-threads") == 0) { + enable_threads = false; + consumed = 1; + } else if (arg_count == 0) { + char *endptr; + init_sem = SDL_strtol(argv[i], &endptr, 0); + if (endptr != argv[i] && *endptr == '\0') { + arg_count++; + consumed = 1; + } + } + } + if (consumed <= 0) { + log_usage(argv[0], state); + return 1; + } + + i += consumed; + } + + if (arg_count != 1) { + log_usage(argv[0], state); + return 1; + } + + /* Load the SDL library */ + if (!SDL_Init(0)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError()); + return 1; + } + (void)signal(SIGTERM, killed); + (void)signal(SIGINT, killed); + + if (enable_threads) { + if (init_sem > 0) { + TestRealWorld(init_sem); + } + + TestWaitTimeout(); + } + + TestOverheadUncontended(); + + if (enable_threads) { + TestOverheadContended(false); + + TestOverheadContended(true); + } + + SDL_Quit(); + SDLTest_CommonDestroyState(state); + + return 0; +} -- cgit v1.2.3