Parcourir la source

Made event and temporary string memory thread-local and added SDL_FreeEventMemory()

Fixes https://github.com/libsdl-org/SDL/issues/10283
Sam Lantinga il y a 9 mois
Parent
commit
4aeabb3c3b

+ 18 - 1
include/SDL3/SDL_events.h

@@ -1407,7 +1407,7 @@ extern SDL_DECLSPEC SDL_bool SDLCALL SDL_EventEnabled(Uint32 type);
 extern SDL_DECLSPEC Uint32 SDLCALL SDL_RegisterEvents(int numevents);
 
 /**
- * Allocate dynamic memory for an SDL event.
+ * Allocate temporary memory for an SDL event.
  *
  * You can use this to allocate memory for user events that will be
  * automatically freed after the event is processed.
@@ -1419,9 +1419,26 @@ extern SDL_DECLSPEC Uint32 SDLCALL SDL_RegisterEvents(int numevents);
  * \threadsafety It is safe to call this function from any thread.
  *
  * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_FreeEventMemory
  */
 extern SDL_DECLSPEC void * SDLCALL SDL_AllocateEventMemory(size_t size);
 
+/**
+ * Free temporary event memory allocated by SDL.
+ *
+ * This function frees temporary memory allocated for events and APIs that return temporary strings. This memory is local to the thread that creates it and is automatically freed for the main thread when pumping the event loop. For other threads you may want to call this function periodically to free any temporary memory created by that thread.
+ *
+ * Note that if you call SDL_AllocateEventMemory() on one thread and pass it to another thread, e.g. via a user event, then you should be sure the other thread has finished processing it before calling this function.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_AllocateEventMemory
+ */
+extern SDL_DECLSPEC void SDLCALL SDL_FreeEventMemory(void);
+
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
 }

+ 0 - 1
src/SDL.c

@@ -582,7 +582,6 @@ void SDL_Quit(void)
      */
     SDL_memset(SDL_SubsystemRefCount, 0x0, sizeof(SDL_SubsystemRefCount));
 
-    SDL_FlushEventMemory(0);
     SDL_FreeEnvironmentMemory();
 
     SDL_QuitMainThread();

+ 1 - 0
src/dynapi/SDL_dynapi.sym

@@ -123,6 +123,7 @@ SDL3_0.0.0 {
     SDL_FlushEvent;
     SDL_FlushEvents;
     SDL_FlushRenderer;
+    SDL_FreeEventMemory;
     SDL_GDKSuspendComplete;
     SDL_GL_CreateContext;
     SDL_GL_DestroyContext;

+ 1 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -148,6 +148,7 @@
 #define SDL_FlushEvent SDL_FlushEvent_REAL
 #define SDL_FlushEvents SDL_FlushEvents_REAL
 #define SDL_FlushRenderer SDL_FlushRenderer_REAL
+#define SDL_FreeEventMemory SDL_FreeEventMemory_REAL
 #define SDL_GDKSuspendComplete SDL_GDKSuspendComplete_REAL
 #define SDL_GL_CreateContext SDL_GL_CreateContext_REAL
 #define SDL_GL_DestroyContext SDL_GL_DestroyContext_REAL

+ 1 - 0
src/dynapi/SDL_dynapi_procs.h

@@ -168,6 +168,7 @@ SDL_DYNAPI_PROC(int,SDL_FlushAudioStream,(SDL_AudioStream *a),(a),return)
 SDL_DYNAPI_PROC(void,SDL_FlushEvent,(Uint32 a),(a),)
 SDL_DYNAPI_PROC(void,SDL_FlushEvents,(Uint32 a, Uint32 b),(a,b),)
 SDL_DYNAPI_PROC(int,SDL_FlushRenderer,(SDL_Renderer *a),(a),return)
+SDL_DYNAPI_PROC(void,SDL_FreeEventMemory,(void),(),)
 SDL_DYNAPI_PROC(void,SDL_GDKSuspendComplete,(void),(),)
 SDL_DYNAPI_PROC(SDL_GLContext,SDL_GL_CreateContext,(SDL_Window *a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_GL_DestroyContext,(SDL_GLContext a),(a),return)

+ 86 - 46
src/events/SDL_events.c

@@ -97,35 +97,79 @@ typedef struct SDL_EventMemory
     struct SDL_EventMemory *next;
 } SDL_EventMemory;
 
-static SDL_Mutex *SDL_event_memory_lock;
-static SDL_EventMemory *SDL_event_memory_head;
-static SDL_EventMemory *SDL_event_memory_tail;
+typedef struct SDL_EventMemoryState
+{
+    SDL_EventMemory *head;
+    SDL_EventMemory *tail;
+} SDL_EventMemoryState;
+
+static SDL_TLSID SDL_event_memory;
+
+
+static void SDL_CleanupEventMemory(void *data)
+{
+    SDL_EventMemoryState *state = (SDL_EventMemoryState *)data;
+
+    while (state->head) {
+        SDL_EventMemory *entry = state->head;
+        state->head = entry->next;
+        SDL_free(entry->memory);
+        SDL_free(entry);
+    }
+    SDL_free(state);
+}
+
+static SDL_EventMemoryState *SDL_GetEventMemoryState(SDL_bool create)
+{
+    SDL_EventMemoryState *state;
+
+    state = SDL_GetTLS(&SDL_event_memory);
+    if (!state) {
+        if (!create) {
+            return NULL;
+        }
+
+        state = (SDL_EventMemoryState *)SDL_calloc(1, sizeof(*state));
+        if (!state) {
+            return NULL;
+        }
+
+        if (SDL_SetTLS(&SDL_event_memory, state, SDL_CleanupEventMemory) < 0) {
+            SDL_free(state);
+            return NULL;
+        }
+    }
+    return state;
+}
 
 void *SDL_FreeLater(void *memory)
 {
+    SDL_EventMemoryState *state;
+
     if (memory == NULL) {
         return NULL;
     }
 
+    state = SDL_GetEventMemoryState(SDL_TRUE);
+    if (!state) {
+        return memory;  // this is now a leak, but you probably have bigger problems if malloc failed.
+    }
+
     SDL_EventMemory *entry = (SDL_EventMemory *)SDL_malloc(sizeof(*entry));
     if (!entry) {
         return memory;  // this is now a leak, but you probably have bigger problems if malloc failed. We could probably pool up and reuse entries, though.
     }
 
-    SDL_LockMutex(SDL_event_memory_lock);
-    {
-        entry->eventID = SDL_last_event_id;
-        entry->memory = memory;
-        entry->next = NULL;
+    entry->eventID = SDL_last_event_id;
+    entry->memory = memory;
+    entry->next = NULL;
 
-        if (SDL_event_memory_tail) {
-            SDL_event_memory_tail->next = entry;
-        } else {
-            SDL_event_memory_head = entry;
-        }
-        SDL_event_memory_tail = entry;
+    if (state->tail) {
+        state->tail->next = entry;
+    } else {
+        state->head = entry;
     }
-    SDL_UnlockMutex(SDL_event_memory_lock);
+    state->tail = entry;
 
     return memory;
 }
@@ -143,31 +187,39 @@ const char *SDL_AllocateEventString(const char *string)
     return NULL;
 }
 
-void SDL_FlushEventMemory(Uint32 eventID)
+static void SDL_FlushEventMemory(Uint32 eventID)
 {
-    SDL_LockMutex(SDL_event_memory_lock);
-    {
-        if (SDL_event_memory_head) {
-            while (SDL_event_memory_head) {
-                SDL_EventMemory *entry = SDL_event_memory_head;
+    SDL_EventMemoryState *state;
 
-                if (eventID && (Sint32)(eventID - entry->eventID) < 0) {
-                    break;
-                }
+    state = SDL_GetEventMemoryState(SDL_FALSE);
+    if (!state) {
+        return;
+    }
 
-                /* If you crash here, your application has memory corruption
-                 * or freed memory in an event, which is no longer necessary.
-                 */
-                SDL_event_memory_head = entry->next;
-                SDL_free(entry->memory);
-                SDL_free(entry);
-            }
-            if (!SDL_event_memory_head) {
-                SDL_event_memory_tail = NULL;
+    if (state->head) {
+        while (state->head) {
+            SDL_EventMemory *entry = state->head;
+
+            if (eventID && (Sint32)(eventID - entry->eventID) < 0) {
+                break;
             }
+
+            /* If you crash here, your application has memory corruption
+             * or freed memory in an event, which is no longer necessary.
+             */
+            state->head = entry->next;
+            SDL_free(entry->memory);
+            SDL_free(entry);
+        }
+        if (!state->head) {
+            state->tail = NULL;
         }
     }
-    SDL_UnlockMutex(SDL_event_memory_lock);
+}
+
+void SDL_FreeEventMemory(void)
+{
+    SDL_FlushEventMemory(0);
 }
 
 #ifndef SDL_JOYSTICK_DISABLED
@@ -697,10 +749,6 @@ void SDL_StopEventLoop(void)
         SDL_disabled_events[i] = NULL;
     }
 
-    if (SDL_event_memory_lock) {
-        SDL_DestroyMutex(SDL_event_memory_lock);
-        SDL_event_memory_lock = NULL;
-    }
     if (SDL_event_watchers_lock) {
         SDL_DestroyMutex(SDL_event_watchers_lock);
         SDL_event_watchers_lock = NULL;
@@ -746,14 +794,6 @@ int SDL_StartEventLoop(void)
             return -1;
         }
     }
-
-    if (SDL_event_memory_lock == NULL) {
-        SDL_event_memory_lock = SDL_CreateMutex();
-        if (SDL_event_memory_lock == NULL) {
-            SDL_UnlockMutex(SDL_EventQ.lock);
-            return -1;
-        }
-    }
 #endif /* !SDL_THREADS_DISABLED */
 
     SDL_EventQ.active = SDL_TRUE;

+ 0 - 1
src/events/SDL_events_c.h

@@ -38,7 +38,6 @@
 /* Start and stop the event processing loop */
 extern int SDL_StartEventLoop(void);
 extern void SDL_StopEventLoop(void);
-extern void SDL_FlushEventMemory(Uint32 eventID);
 extern void SDL_QuitInterrupt(void);
 
 extern const char *SDL_AllocateEventString(const char *string);