Removed SDL_FreeTemporaryMemory()

This was just causing confusion and anxiety. SDL temporary memory will be automatically freed on the main thread when processing events and on other threads when it ages out after a second. The application can free it directly by calling SDL_ClaimTemporaryMemory() to get ownership of the pointer, if necessary.
This commit is contained in:
Sam Lantinga 2024-07-22 07:18:00 -07:00
parent 70c1012e8c
commit ff1d351390
6 changed files with 34 additions and 88 deletions

View File

@ -1426,42 +1426,18 @@ extern SDL_DECLSPEC Uint32 SDLCALL SDL_RegisterEvents(int numevents);
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_ClaimTemporaryMemory
* \sa SDL_FreeTemporaryMemory
*/
extern SDL_DECLSPEC void * SDLCALL SDL_AllocateTemporaryMemory(size_t size);
/**
* Claim ownership of temporary memory.
*
* This function changes ownership of temporary memory allocated for events
* and functions that return temporary memory. If this function succeeds, the
* memory will no longer be automatically freed by SDL, it must be freed using
* SDL_free() by the application.
* Some functions return temporary memory which SDL will automatically clean up later. Normally you won't save these results beyond the current function scope, but if you want to use them later, you can call this function to get a pointer that the application owns, and should be freed using SDL_free().
*
* If the memory isn't temporary, or it was allocated on a different thread,
* or if it is associated with an event currently in the event queue, this
* If the memory isn't temporary, or it was returned from an SDL function called on a different thread, this
* will return NULL, and the application does not have ownership of the
* memory.
*
* Essentially you have 3 options for handling temporary memory:
*
* 1. After calling a function that returns temporary memory, pass that
* pointer to SDL_FreeTemporaryMemory(). This gives you full control over the
* management of allocations.
*
* 2. On your main thread, temporary memory is automatically cleaned up when
* processing events. On other threads, you can periodically call
* SDL_FreeTemporaryMemory(NULL) to clean up any temporary memory that has
* accumulated.
*
* 3. After calling a function that returns temporary memory, pass that
* pointer to SDL_ClaimTemporaryMemory(). This transfers ownership of the
* memory to your application and you can pass it to other threads or save it
* indefinitely, calling SDL_free() to free it when you're ready.
*
* Any of the three options are valid, and you can mix and match them to suit
* your application.
*
* \param mem a pointer allocated with SDL_AllocateTemporaryMemory().
* \returns a pointer to the memory now owned by the application, which must
* be freed using SDL_free(), or NULL if the memory is not temporary
@ -1476,34 +1452,6 @@ extern SDL_DECLSPEC void * SDLCALL SDL_AllocateTemporaryMemory(size_t size);
*/
extern SDL_DECLSPEC void * SDLCALL SDL_ClaimTemporaryMemory(const void *mem);
/**
* Free temporary memory.
*
* This function frees temporary memory allocated for events and functions
* that return temporary memory. This memory is local to the thread that
* creates it and is automatically freed for the main thread when processing
* events. 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.
*
* 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_AllocateTemporaryMemory(), or NULL
* to free all pending temporary allocations.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_AllocateTemporaryMemory
*/
extern SDL_DECLSPEC void SDLCALL SDL_FreeTemporaryMemory(const void *mem);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}

View File

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

View File

@ -155,7 +155,6 @@
#define SDL_FlushEvent SDL_FlushEvent_REAL
#define SDL_FlushEvents SDL_FlushEvents_REAL
#define SDL_FlushRenderer SDL_FlushRenderer_REAL
#define SDL_FreeTemporaryMemory SDL_FreeTemporaryMemory_REAL
#define SDL_GDKSuspendComplete SDL_GDKSuspendComplete_REAL
#define SDL_GL_CreateContext SDL_GL_CreateContext_REAL
#define SDL_GL_DestroyContext SDL_GL_DestroyContext_REAL

View File

@ -175,7 +175,6 @@ 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_FreeTemporaryMemory,(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)

View File

@ -74,6 +74,7 @@ static Uint32 SDL_userevents = SDL_EVENT_USER;
typedef struct SDL_TemporaryMemory
{
void *memory;
Uint64 timestamp;
struct SDL_TemporaryMemory *prev;
struct SDL_TemporaryMemory *next;
} SDL_TemporaryMemory;
@ -105,11 +106,13 @@ static struct
SDL_EventEntry *free;
} SDL_EventQ = { NULL, SDL_FALSE, { 0 }, 0, NULL, NULL, NULL };
static void SDL_FreeTemporaryMemory(void);
static void SDL_CleanupTemporaryMemory(void *data)
{
SDL_TemporaryMemoryState *state = (SDL_TemporaryMemoryState *)data;
SDL_FreeTemporaryMemory(NULL);
SDL_FreeTemporaryMemory();
SDL_free(state);
}
@ -260,6 +263,23 @@ static void SDL_TransferTemporaryMemoryFromEvent(SDL_EventEntry *event)
event->memory = NULL;
}
static void SDL_CollectTemporaryMemory(SDL_TemporaryMemoryState *state, Uint64 now)
{
// Temporary memory will age out and be collected after 1 second
const int TEMPORARY_MEMORY_COLLECT_TIME_MS = 1000;
while (state->head) {
SDL_TemporaryMemory *entry = state->head;
if ((now - entry->timestamp) < TEMPORARY_MEMORY_COLLECT_TIME_MS) {
break;
}
SDL_UnlinkTemporaryMemoryEntry(state, entry);
SDL_FreeTemporaryMemoryEntry(state, entry, SDL_TRUE);
}
}
void *SDL_FreeLater(void *memory)
{
SDL_TemporaryMemoryState *state;
@ -276,12 +296,16 @@ void *SDL_FreeLater(void *memory)
return memory; // this is now a leak, but you probably have bigger problems if malloc failed.
}
Uint64 now = SDL_GetTicks();
SDL_CollectTemporaryMemory(state, now);
SDL_TemporaryMemory *entry = (SDL_TemporaryMemory *)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.
}
entry->memory = memory;
entry->timestamp = now;
SDL_LinkTemporaryMemoryEntry(state, entry);
@ -317,7 +341,7 @@ void *SDL_ClaimTemporaryMemory(const void *mem)
return NULL;
}
void SDL_FreeTemporaryMemory(const void *mem)
void SDL_FreeTemporaryMemory(void)
{
SDL_TemporaryMemoryState *state;
@ -326,19 +350,11 @@ void SDL_FreeTemporaryMemory(const void *mem)
return;
}
if (mem) {
SDL_TemporaryMemory *entry = SDL_GetTemporaryMemoryEntry(state, mem);
if (entry) {
SDL_UnlinkTemporaryMemoryEntry(state, entry);
SDL_FreeTemporaryMemoryEntry(state, entry, SDL_TRUE);
}
} else {
while (state->head) {
SDL_TemporaryMemory *entry = state->head;
while (state->head) {
SDL_TemporaryMemory *entry = state->head;
SDL_UnlinkTemporaryMemoryEntry(state, entry);
SDL_FreeTemporaryMemoryEntry(state, entry, SDL_TRUE);
}
SDL_UnlinkTemporaryMemoryEntry(state, entry);
SDL_FreeTemporaryMemoryEntry(state, entry, SDL_TRUE);
}
}
@ -862,7 +878,7 @@ void SDL_StopEventLoop(void)
SDL_EventQ.free = NULL;
SDL_AtomicSet(&SDL_sentinel_pending, 0);
SDL_FreeTemporaryMemory(NULL);
SDL_FreeTemporaryMemory();
/* Clear disabled event state */
for (i = 0; i < SDL_arraysize(SDL_disabled_events); ++i) {
@ -1159,7 +1175,7 @@ static void SDL_PumpEventsInternal(SDL_bool push_sentinel)
SDL_VideoDevice *_this = SDL_GetVideoDevice();
/* Free old event memory */
SDL_FreeTemporaryMemory(NULL);
SDL_FreeTemporaryMemory();
/* Release any keys held down from last frame */
SDL_ReleaseAutoReleaseKeys();

View File

@ -197,25 +197,10 @@ static int events_temporaryMemory(void *arg)
tmp = SDL_ClaimTemporaryMemory(mem);
SDLTest_AssertCheck(tmp == NULL, "SDL_ClaimTemporaryMemory() can't claim memory twice");
/* Verify that freeing the original pointer does nothing */
SDL_FreeTemporaryMemory(mem);
SDLTest_AssertCheck(*(char *)mem == '1', "SDL_FreeTemporaryMemory() on claimed memory has no effect");
/* Clean up */
SDL_free(claimed);
}
{
/* Create and free event memory */
mem = SDL_AllocateTemporaryMemory(1);
SDLTest_AssertCheck(mem != NULL, "SDL_AllocateTemporaryMemory()");
*(char *)mem = '1';
SDL_FreeTemporaryMemory(mem);
claimed = SDL_ClaimTemporaryMemory(mem);
SDLTest_AssertCheck(claimed == NULL, "SDL_ClaimTemporaryMemory() can't claim memory after it's been freed");
}
{
/* Create event memory and queue it */
mem = SDL_AllocateTemporaryMemory(1);