SDL: Allow initializing hints and properties from any thread

From 807b8a9d4deefc4993f4fefc185918ecbef01c9f Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 17 Sep 2024 02:55:23 -0700
Subject: [PATCH] Allow initializing hints and properties from any thread

---
 src/SDL.c            | 11 +++----
 src/SDL_hints.c      | 39 +++++++++++++++---------
 src/SDL_properties.c | 72 +++++++++++++++++++++++++++++---------------
 3 files changed, 77 insertions(+), 45 deletions(-)

diff --git a/src/SDL.c b/src/SDL.c
index d5a12885098a9..0116ba9c30ea5 100644
--- a/src/SDL.c
+++ b/src/SDL.c
@@ -252,21 +252,14 @@ void SDL_InitMainThread(void)
 {
     SDL_InitTLSData();
     SDL_InitEnvironment();
-    SDL_InitProperties();
-    SDL_InitHints();
     SDL_InitTicks();
     SDL_InitFilesystem();
-    SDL_InitLog();
-    SDL_GetGlobalProperties();
 }
 
 static void SDL_QuitMainThread(void)
 {
-    SDL_QuitLog();
     SDL_QuitFilesystem();
     SDL_QuitTicks();
-    SDL_QuitHints();
-    SDL_QuitProperties();
     SDL_QuitEnvironment();
     SDL_QuitTLSData();
 }
@@ -625,6 +618,10 @@ void SDL_Quit(void)
      */
     SDL_memset(SDL_SubsystemRefCount, 0x0, sizeof(SDL_SubsystemRefCount));
 
+    SDL_QuitLog();
+    SDL_QuitHints();
+    SDL_QuitProperties();
+
     SDL_QuitMainThread();
 
     SDL_bInMainQuit = false;
diff --git a/src/SDL_hints.c b/src/SDL_hints.c
index 8dd9c551ccb09..2d1d5b872a9c9 100644
--- a/src/SDL_hints.c
+++ b/src/SDL_hints.c
@@ -36,20 +36,37 @@ typedef struct SDL_Hint
     SDL_HintWatch *callbacks;
 } SDL_Hint;
 
-static SDL_PropertiesID SDL_hint_props = 0;
+static SDL_AtomicU32 SDL_hint_props;
 
-static SDL_PropertiesID GetHintProperties(bool create)
+
+void SDL_InitHints(void)
+{
+}
+
+void SDL_QuitHints(void)
 {
-    if (!SDL_hint_props && create) {
-        SDL_hint_props = SDL_CreateProperties();
+    SDL_PropertiesID props;
+    do {
+        props = SDL_GetAtomicU32(&SDL_hint_props);
+    } while (!SDL_CompareAndSwapAtomicU32(&SDL_hint_props, props, 0));
+
+    if (props) {
+        SDL_DestroyProperties(props);
     }
-    return SDL_hint_props;
 }
 
-void SDL_InitHints(void)
+static SDL_PropertiesID GetHintProperties(bool create)
 {
-    // Just make sure the hint properties are created on the main thread
-    (void)GetHintProperties(true);
+    SDL_PropertiesID props = SDL_GetAtomicU32(&SDL_hint_props);
+    if (!props && create) {
+        props = SDL_CreateProperties();
+        if (!SDL_CompareAndSwapAtomicU32(&SDL_hint_props, 0, props)) {
+            // Somebody else created hint properties before us, just use those
+            SDL_DestroyProperties(props);
+            props = SDL_GetAtomicU32(&SDL_hint_props);
+        }
+    }
+    return props;
 }
 
 static void SDLCALL CleanupHintProperty(void *userdata, void *value)
@@ -336,9 +353,3 @@ void SDL_RemoveHintCallback(const char *name, SDL_HintCallback callback, void *u
     SDL_UnlockProperties(hints);
 }
 
-void SDL_QuitHints(void)
-{
-    SDL_DestroyProperties(SDL_hint_props);
-    SDL_hint_props = 0;
-}
-
diff --git a/src/SDL_properties.c b/src/SDL_properties.c
index 2a4f2aca83982..eacbc588f4859 100644
--- a/src/SDL_properties.c
+++ b/src/SDL_properties.c
@@ -48,10 +48,11 @@ typedef struct
     SDL_Mutex *lock;
 } SDL_Properties;
 
+static SDL_InitState SDL_properties_init;
 static SDL_HashTable *SDL_properties;
 static SDL_Mutex *SDL_properties_lock;
 static SDL_PropertiesID SDL_last_properties_id;
-static SDL_PropertiesID SDL_global_properties;
+static SDL_AtomicU32 SDL_global_properties;
 
 
 static void SDL_FreePropertyWithCleanup(const void *key, const void *value, void *data, bool cleanup)
@@ -99,27 +100,42 @@ static void SDL_FreeProperties(const void *key, const void *value, void *data)
 
 bool SDL_InitProperties(void)
 {
-    if (!SDL_properties_lock) {
-        SDL_properties_lock = SDL_CreateMutex();
-        if (!SDL_properties_lock) {
-            return false;
-        }
+    if (!SDL_ShouldInit(&SDL_properties_init)) {
+        return true;
     }
+
+    // If this fails we'll continue without it.
+    SDL_properties_lock = SDL_CreateMutex();
+
+    SDL_properties = SDL_CreateHashTable(NULL, 16, SDL_HashID, SDL_KeyMatchID, SDL_FreeProperties, false);
     if (!SDL_properties) {
-        SDL_properties = SDL_CreateHashTable(NULL, 16, SDL_HashID, SDL_KeyMatchID, SDL_FreeProperties, false);
-        if (!SDL_properties) {
-            return false;
-        }
+        goto error;
     }
+
+    SDL_SetInitialized(&SDL_properties_init, true);
     return true;
+
+error:
+    SDL_SetInitialized(&SDL_properties_init, true);
+    SDL_QuitProperties();
+    return false;
 }
 
 void SDL_QuitProperties(void)
 {
-    if (SDL_global_properties) {
-        SDL_DestroyProperties(SDL_global_properties);
-        SDL_global_properties = 0;
+    if (!SDL_ShouldQuit(&SDL_properties_init)) {
+        return;
+    }
+
+    SDL_PropertiesID props;
+    do {
+        props = SDL_GetAtomicU32(&SDL_global_properties);
+    } while (!SDL_CompareAndSwapAtomicU32(&SDL_global_properties, props, 0));
+
+    if (props) {
+        SDL_DestroyProperties(props);
     }
+
     if (SDL_properties) {
         SDL_DestroyHashTable(SDL_properties);
         SDL_properties = NULL;
@@ -128,14 +144,27 @@ void SDL_QuitProperties(void)
         SDL_DestroyMutex(SDL_properties_lock);
         SDL_properties_lock = NULL;
     }
+
+    SDL_SetInitialized(&SDL_properties_init, false);
+}
+
+static bool SDL_CheckInitProperties(void)
+{
+    return SDL_InitProperties();
 }
 
 SDL_PropertiesID SDL_GetGlobalProperties(void)
 {
-    if (!SDL_global_properties) {
-        SDL_global_properties = SDL_CreateProperties();
+    SDL_PropertiesID props = SDL_GetAtomicU32(&SDL_global_properties);
+    if (!props) {
+        props = SDL_CreateProperties();
+        if (!SDL_CompareAndSwapAtomicU32(&SDL_global_properties, 0, props)) {
+            // Somebody else created global properties before us, just use those
+            SDL_DestroyProperties(props);
+            props = SDL_GetAtomicU32(&SDL_global_properties);
+        }
     }
-    return SDL_global_properties;
+    return props;
 }
 
 SDL_PropertiesID SDL_CreateProperties(void)
@@ -144,7 +173,7 @@ SDL_PropertiesID SDL_CreateProperties(void)
     SDL_Properties *properties = NULL;
     bool inserted = false;
 
-    if (!SDL_properties && !SDL_InitProperties()) {
+    if (!SDL_CheckInitProperties()) {
         return 0;
     }
 
@@ -156,14 +185,9 @@ SDL_PropertiesID SDL_CreateProperties(void)
     if (!properties->props) {
         goto error;
     }
-    properties->lock = SDL_CreateMutex();
-    if (!properties->lock) {
-        goto error;
-    }
 
-    if (!SDL_InitProperties()) {
-        goto error;
-    }
+    // If this fails we'll continue without it.
+    properties->lock = SDL_CreateMutex();
 
     SDL_LockMutex(SDL_properties_lock);
     ++SDL_last_properties_id;