diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index 6ed114aaf3..da25244f84 100644 --- a/include/SDL3/SDL_events.h +++ b/include/SDL3/SDL_events.h @@ -1428,14 +1428,20 @@ 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 + * follow the SDL_GetStringRule. 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 + * loop. For other threads you may call this function periodically to * free any temporary memory created by that thread. * + * You can free a specific pointer, to provide more fine grained control over memory management, or you can pass NULL to free all pending temporary allocations. You should *NOT* pass NULL on your main event handling thread, as there may be temporary memory being used by events in-flight. For that thread SDL will call this internally when it's safe to do so. + * * 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. + * thread has finished processing it before calling this function with NULL. + * + * All temporary memory is freed on the main thread in SDL_Quit() and for other threads when they call SDL_CleanupTLS(), which is automatically called at cleanup time for threads created using SDL_CreateThread(). + * + * \param mem a pointer allocated with SDL_AllocateEventMemory(), or NULL to free all pending temporary allocations. * * \threadsafety It is safe to call this function from any thread. * @@ -1443,7 +1449,7 @@ extern SDL_DECLSPEC void * SDLCALL SDL_AllocateEventMemory(size_t size); * * \sa SDL_AllocateEventMemory */ -extern SDL_DECLSPEC void SDLCALL SDL_FreeEventMemory(void); +extern SDL_DECLSPEC void SDLCALL SDL_FreeEventMemory(const void *mem); /* Ends C function definitions when using C++ */ #ifdef __cplusplus diff --git a/include/SDL3/SDL_thread.h b/include/SDL3/SDL_thread.h index ff84a81d58..9f411a9383 100644 --- a/include/SDL3/SDL_thread.h +++ b/include/SDL3/SDL_thread.h @@ -520,6 +520,8 @@ extern SDL_DECLSPEC int SDLCALL SDL_SetTLS(SDL_TLSID *id, const void *value, SDL /** * Cleanup all TLS data for this thread. * + * If you are creating your threads outside of SDL and then calling SDL functions, you should call this function before your thread exits, to properly clean up SDL memory. + * * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.0.0. diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index f384bd9839..1a494d81fe 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -169,7 +169,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_FreeEventMemory,(const void *a),(a),) 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) diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index da4f0881b4..60bf7d6b18 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -110,12 +110,7 @@ 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_FreeEventMemory(NULL); SDL_free(state); } @@ -217,9 +212,46 @@ static void SDL_FlushEventMemory(Uint32 eventID) } } -void SDL_FreeEventMemory(void) +void SDL_FreeEventMemory(const void *mem) { - SDL_FlushEventMemory(0); + SDL_EventMemoryState *state; + + state = SDL_GetEventMemoryState(SDL_FALSE); + if (!state) { + return; + } + + if (mem) { + SDL_EventMemory *prev = NULL, *entry; + + for (entry = state->head; entry; prev = entry, entry = entry->next) { + if (mem == entry->memory) { + if (prev) { + prev->next = entry->next; + } + if (entry == state->head) { + state->head = entry->next; + } + if (entry == state->tail) { + state->tail = prev; + } + SDL_free(entry->memory); + SDL_free(entry); + break; + } + } + } else { + if (state->head) { + while (state->head) { + SDL_EventMemory *entry = state->head; + + state->head = entry->next; + SDL_free(entry->memory); + SDL_free(entry); + } + state->tail = NULL; + } + } } #ifndef SDL_JOYSTICK_DISABLED @@ -741,7 +773,7 @@ void SDL_StopEventLoop(void) SDL_EventQ.free = NULL; SDL_AtomicSet(&SDL_sentinel_pending, 0); - SDL_FlushEventMemory(0); + SDL_FreeEventMemory(NULL); /* Clear disabled event state */ for (i = 0; i < SDL_arraysize(SDL_disabled_events); ++i) {