SDL: testrwlock: Redesign to fix some flaws.

From 8e5388c72fdbbca2af82a52e80dc5f7a2c6ad76e Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Mon, 12 Oct 2026 19:37:44 -0400
Subject: [PATCH] testrwlock: Redesign to fix some flaws.

- Now the writer increments a global variable, and readers report on its
  current value, which shows not just what order things lock, but also that
  serialization is happening appropriately.
- Readers will, once we hit the timeout, sleep and never try to grab the lock,
  so even if normal operation results in complete starvation (launch with
  `--nbthreads 1000`, etc), the writer will be able to obtain the lock
  shortly after the time limit and everything will shut down in orderly
  fashion.
- Other minor cleanups and tweaks.

Fixes #15346.
---
 test/testrwlock.c | 72 +++++++++++++++++++++++++----------------------
 1 file changed, 38 insertions(+), 34 deletions(-)

diff --git a/test/testrwlock.c b/test/testrwlock.c
index 94e6c77b23f10..2a94a6498b087 100644
--- a/test/testrwlock.c
+++ b/test/testrwlock.c
@@ -19,50 +19,43 @@
 #include <SDL3/SDL_test.h>
 
 static SDL_RWLock *rwlock = NULL;
-static SDL_ThreadID mainthread;
 static SDL_AtomicInt doterminate;
 static int nb_threads = 6;
 static SDL_Thread **threads;
-static int worktime = 1000;
-static int writerworktime = 100;
+static int TheVariable = 0;
+static int worktime = 100;
+static int writerworktime = 10;
 static int timeout = 10000;
 static SDLTest_CommonState *state;
 
-static void DoWork(const int workticks)  /* "Work" */
+static int SDLCALL ReaderRun(void *data)
 {
-    const SDL_ThreadID tid = SDL_GetCurrentThreadID();
-    const bool is_reader = tid != mainthread;
-    const char *typestr = is_reader ? "Reader" : "Writer";
+    const int threadidx = (int) (uintptr_t) data;
+    SDL_Log("Reader Thread %d: starting up!", threadidx);
+    while (!SDL_GetAtomicInt(&doterminate)) {
+        /* if we're past the timeout, just sleep and wait for doterminate. This makes sure we can't drown out the writer with so many read locks that it never gets control. */
+        if (SDL_GetTicks() > ((Uint32) timeout)) {
+            SDL_Delay(10);
+            continue;
+        }
 
-    SDL_Log("%s Thread %" SDL_PRIu64 ": ready to work", typestr, tid);
-    if (is_reader) {
+        SDL_Log("Reader Thread %d: Ready to grab lock!", threadidx);
         SDL_LockRWLockForReading(rwlock);
-    } else {
-        SDL_LockRWLockForWriting(rwlock);
-    }
-
-    SDL_Log("%s Thread %" SDL_PRIu64 ": start work!", typestr, tid);
-    SDL_Delay(workticks);
-    SDL_Log("%s Thread %" SDL_PRIu64 ": work done!", typestr, tid);
-    SDL_UnlockRWLock(rwlock);
-
-    /* If this sleep isn't done, then threads may starve */
-    SDL_Delay(10);
-}
-
-static int SDLCALL
-ReaderRun(void *data)
-{
-    SDL_Log("Reader Thread %" SDL_PRIu64 ": starting up", SDL_GetCurrentThreadID());
-    while (!SDL_GetAtomicInt(&doterminate)) {
-        DoWork(worktime);
+        SDL_Log("Reader Thread %d: TheVariable=%d", threadidx, TheVariable);
+        if (worktime) {
+            SDL_Delay(worktime);  /* hold the lock awhile. */
+        }
+        SDL_Log("Reader Thread %d: Releasing lock!", threadidx);
+        SDL_UnlockRWLock(rwlock);
+        SDL_Delay(200 + SDL_rand(200)); /* If this sleep isn't done, then threads may starve */
     }
-    SDL_Log("Reader Thread %" SDL_PRIu64 ": exiting!", SDL_GetCurrentThreadID());
+    SDL_Log("Reader Thread %d: terminating!", threadidx);
     return 0;
 }
 
 int main(int argc, char *argv[])
 {
+    int remaining_ticks;
     int i;
 
     /* Initialize test framework */
@@ -146,19 +139,30 @@ int main(int argc, char *argv[])
         return 1;
     }
 
-    mainthread = SDL_GetCurrentThreadID();
-    SDL_Log("Writer thread: %" SDL_PRIu64, mainthread);
     for (i = 0; i < nb_threads; ++i) {
         char name[64];
         (void)SDL_snprintf(name, sizeof(name), "Reader%d", i);
-        threads[i] = SDL_CreateThread(ReaderRun, name, NULL);
+        threads[i] = SDL_CreateThread(ReaderRun, name, (void *)(uintptr_t) (i+1));
         if (threads[i] == NULL) {
             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create reader thread! %s", SDL_GetError());
         }
     }
 
-    while (!SDL_GetAtomicInt(&doterminate) && (SDL_GetTicks() < ((Uint64) timeout))) {
-        DoWork(writerworktime);
+    remaining_ticks = timeout;
+    while (!SDL_GetAtomicInt(&doterminate) && (remaining_ticks > 0)) {
+        Uint64 now;
+        SDL_Log("## New iteration! (%d ticks remaining.)", remaining_ticks);
+        SDL_Log("Writer Thread: Ready to grab lock!");
+        SDL_LockRWLockForWriting(rwlock);
+        SDL_Log("Writer Thread: increment TheVariable to %d", ++TheVariable);
+        if (writerworktime) {
+            SDL_Delay(writerworktime);
+        }
+        SDL_Log("Writer Thread: Releasing lock!");
+        SDL_UnlockRWLock(rwlock);
+        SDL_Delay(50); /* If this sleep isn't done, then threads may starve */
+        now = SDL_GetTicks();
+        remaining_ticks = ((now < ((Uint64) timeout)) ? ((int) (((Uint64) timeout) - now)) : 0);
     }
 
     SDL_SetAtomicInt(&doterminate, 1);