mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-10-14 05:46:00 +00:00
Added SDL_IsMainThread() and SDL_RunOnMainThread()
This commit is contained in:
22
src/SDL.c
22
src/SDL.c
@@ -183,6 +183,7 @@ static bool SDL_MainIsReady = false;
|
||||
#else
|
||||
static bool SDL_MainIsReady = true;
|
||||
#endif
|
||||
static SDL_ThreadID SDL_MainThreadID = 0;
|
||||
static bool SDL_bInMainQuit = false;
|
||||
static Uint8 SDL_SubsystemRefCount[32];
|
||||
|
||||
@@ -250,6 +251,22 @@ static bool SDL_InitOrIncrementSubsystem(Uint32 subsystem)
|
||||
void SDL_SetMainReady(void)
|
||||
{
|
||||
SDL_MainIsReady = true;
|
||||
|
||||
if (SDL_MainThreadID == 0) {
|
||||
SDL_MainThreadID = SDL_GetCurrentThreadID();
|
||||
}
|
||||
}
|
||||
|
||||
bool SDL_IsMainThread(void)
|
||||
{
|
||||
if (SDL_MainThreadID == 0) {
|
||||
// Not initialized yet?
|
||||
return true;
|
||||
}
|
||||
if (SDL_MainThreadID == SDL_GetCurrentThreadID()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize all the subsystems that require initialization before threads start
|
||||
@@ -330,6 +347,11 @@ bool SDL_InitSubSystem(SDL_InitFlags flags)
|
||||
goto quit_and_error;
|
||||
}
|
||||
|
||||
// We initialize video on the main thread
|
||||
// On Apple platforms this is a requirement.
|
||||
// On other platforms, this is the definition.
|
||||
SDL_MainThreadID = SDL_GetCurrentThreadID();
|
||||
|
||||
SDL_IncrementSubsystemRefCount(SDL_INIT_VIDEO);
|
||||
if (!SDL_VideoInit(NULL)) {
|
||||
SDL_DecrementSubsystemRefCount(SDL_INIT_VIDEO);
|
||||
|
@@ -1201,6 +1201,8 @@ SDL3_0.0.0 {
|
||||
SDL_SignalAsyncIOQueue;
|
||||
SDL_LoadFileAsync;
|
||||
SDL_ShowFileDialogWithProperties;
|
||||
SDL_IsMainThread;
|
||||
SDL_RunOnMainThread;
|
||||
# extra symbols go here (don't modify this line)
|
||||
local: *;
|
||||
};
|
||||
|
@@ -1226,3 +1226,5 @@
|
||||
#define SDL_SignalAsyncIOQueue SDL_SignalAsyncIOQueue_REAL
|
||||
#define SDL_LoadFileAsync SDL_LoadFileAsync_REAL
|
||||
#define SDL_ShowFileDialogWithProperties SDL_ShowFileDialogWithProperties_REAL
|
||||
#define SDL_IsMainThread SDL_IsMainThread_REAL
|
||||
#define SDL_RunOnMainThread SDL_RunOnMainThread_REAL
|
||||
|
@@ -1232,3 +1232,5 @@ SDL_DYNAPI_PROC(bool,SDL_WaitAsyncIOResult,(SDL_AsyncIOQueue *a, SDL_AsyncIOOutc
|
||||
SDL_DYNAPI_PROC(void,SDL_SignalAsyncIOQueue,(SDL_AsyncIOQueue *a),(a),)
|
||||
SDL_DYNAPI_PROC(bool,SDL_LoadFileAsync,(const char *a, SDL_AsyncIOQueue *b, void *c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(void,SDL_ShowFileDialogWithProperties,(SDL_FileDialogType a, SDL_DialogFileCallback b, void *c, SDL_PropertiesID d),(a,b,c,d),)
|
||||
SDL_DYNAPI_PROC(bool,SDL_IsMainThread,(void),(),return)
|
||||
SDL_DYNAPI_PROC(bool,SDL_RunOnMainThread,(SDL_MainThreadCallback a,void *b,bool c),(a,b,c),return)
|
||||
|
@@ -1175,6 +1175,177 @@ void SDL_FlushEvents(Uint32 minType, Uint32 maxType)
|
||||
SDL_UnlockMutex(SDL_EventQ.lock);
|
||||
}
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SDL_MAIN_CALLBACK_WAITING,
|
||||
SDL_MAIN_CALLBACK_COMPLETE,
|
||||
SDL_MAIN_CALLBACK_CANCELED,
|
||||
} SDL_MainThreadCallbackState;
|
||||
|
||||
typedef struct SDL_MainThreadCallbackEntry
|
||||
{
|
||||
SDL_MainThreadCallback callback;
|
||||
void *userdata;
|
||||
SDL_AtomicInt state;
|
||||
SDL_Semaphore *semaphore;
|
||||
struct SDL_MainThreadCallbackEntry *next;
|
||||
} SDL_MainThreadCallbackEntry;
|
||||
|
||||
static SDL_Mutex *SDL_main_callbacks_lock;
|
||||
static SDL_MainThreadCallbackEntry *SDL_main_callbacks_head;
|
||||
static SDL_MainThreadCallbackEntry *SDL_main_callbacks_tail;
|
||||
|
||||
static SDL_MainThreadCallbackEntry *SDL_CreateMainThreadCallback(SDL_MainThreadCallback callback, void *userdata, bool wait_complete)
|
||||
{
|
||||
SDL_MainThreadCallbackEntry *entry = (SDL_MainThreadCallbackEntry *)SDL_malloc(sizeof(*entry));
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
entry->callback = callback;
|
||||
entry->userdata = userdata;
|
||||
SDL_SetAtomicInt(&entry->state, SDL_MAIN_CALLBACK_WAITING);
|
||||
if (wait_complete) {
|
||||
entry->semaphore = SDL_CreateSemaphore(0);
|
||||
if (!entry->semaphore) {
|
||||
SDL_free(entry);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
entry->semaphore = NULL;
|
||||
}
|
||||
entry->next = NULL;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void SDL_DestroyMainThreadCallback(SDL_MainThreadCallbackEntry *entry)
|
||||
{
|
||||
if (entry->semaphore) {
|
||||
SDL_DestroySemaphore(entry->semaphore);
|
||||
}
|
||||
SDL_free(entry);
|
||||
}
|
||||
|
||||
static void SDL_InitMainThreadCallbacks(void)
|
||||
{
|
||||
SDL_main_callbacks_lock = SDL_CreateMutex();
|
||||
SDL_assert(SDL_main_callbacks_head == NULL &&
|
||||
SDL_main_callbacks_tail == NULL);
|
||||
}
|
||||
|
||||
static void SDL_QuitMainThreadCallbacks(void)
|
||||
{
|
||||
SDL_MainThreadCallbackEntry *entry;
|
||||
|
||||
SDL_LockMutex(SDL_main_callbacks_lock);
|
||||
{
|
||||
entry = SDL_main_callbacks_head;
|
||||
SDL_main_callbacks_head = NULL;
|
||||
SDL_main_callbacks_tail = NULL;
|
||||
}
|
||||
SDL_UnlockMutex(SDL_main_callbacks_lock);
|
||||
|
||||
while (entry) {
|
||||
SDL_MainThreadCallbackEntry *next = entry->next;
|
||||
|
||||
if (entry->semaphore) {
|
||||
// Let the waiting thread know this is canceled
|
||||
SDL_SetAtomicInt(&entry->state, SDL_MAIN_CALLBACK_CANCELED);
|
||||
SDL_SignalSemaphore(entry->semaphore);
|
||||
} else {
|
||||
// Nobody's waiting for this, clean it up
|
||||
SDL_DestroyMainThreadCallback(entry);
|
||||
}
|
||||
entry = next;
|
||||
}
|
||||
|
||||
SDL_DestroyMutex(SDL_main_callbacks_lock);
|
||||
SDL_main_callbacks_lock = NULL;
|
||||
}
|
||||
|
||||
static void SDL_RunMainThreadCallbacks(void)
|
||||
{
|
||||
SDL_MainThreadCallbackEntry *entry;
|
||||
|
||||
SDL_LockMutex(SDL_main_callbacks_lock);
|
||||
{
|
||||
entry = SDL_main_callbacks_head;
|
||||
SDL_main_callbacks_head = NULL;
|
||||
SDL_main_callbacks_tail = NULL;
|
||||
}
|
||||
SDL_UnlockMutex(SDL_main_callbacks_lock);
|
||||
|
||||
while (entry) {
|
||||
SDL_MainThreadCallbackEntry *next = entry->next;
|
||||
|
||||
entry->callback(entry->userdata);
|
||||
|
||||
if (entry->semaphore) {
|
||||
// Let the waiting thread know this is done
|
||||
SDL_SetAtomicInt(&entry->state, SDL_MAIN_CALLBACK_COMPLETE);
|
||||
SDL_SignalSemaphore(entry->semaphore);
|
||||
} else {
|
||||
// Nobody's waiting for this, clean it up
|
||||
SDL_DestroyMainThreadCallback(entry);
|
||||
}
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
|
||||
bool SDL_RunOnMainThread(SDL_MainThreadCallback callback, void *userdata, bool wait_complete)
|
||||
{
|
||||
if (SDL_IsMainThread() || !SDL_WasInit(SDL_INIT_EVENTS)) {
|
||||
// No need to queue the callback
|
||||
callback(userdata);
|
||||
return true;
|
||||
}
|
||||
|
||||
SDL_MainThreadCallbackEntry *entry = SDL_CreateMainThreadCallback(callback, userdata, wait_complete);
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_LockMutex(SDL_main_callbacks_lock);
|
||||
{
|
||||
if (SDL_main_callbacks_tail) {
|
||||
SDL_main_callbacks_tail->next = entry;
|
||||
SDL_main_callbacks_tail = entry;
|
||||
} else {
|
||||
SDL_main_callbacks_head = entry;
|
||||
SDL_main_callbacks_tail = entry;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(SDL_main_callbacks_lock);
|
||||
|
||||
if (!wait_complete) {
|
||||
// Queued for execution, wait not requested
|
||||
return true;
|
||||
}
|
||||
|
||||
// Maximum wait of 30 seconds to prevent deadlocking forever
|
||||
const Sint32 MAX_CALLBACK_WAIT = 30 * 1000;
|
||||
SDL_WaitSemaphoreTimeout(entry->semaphore, MAX_CALLBACK_WAIT);
|
||||
|
||||
switch (SDL_GetAtomicInt(&entry->state)) {
|
||||
case SDL_MAIN_CALLBACK_COMPLETE:
|
||||
// Execution complete!
|
||||
SDL_DestroyMainThreadCallback(entry);
|
||||
return true;
|
||||
|
||||
case SDL_MAIN_CALLBACK_CANCELED:
|
||||
// The callback was canceled on the main thread
|
||||
SDL_DestroyMainThreadCallback(entry);
|
||||
return SDL_SetError("Callback canceled");
|
||||
|
||||
default:
|
||||
// Probably hit a deadlock in the callback
|
||||
// We can't destroy the entry as the semaphore will be signaled
|
||||
// if it ever comes back, just leak it here.
|
||||
return SDL_SetError("Callback timed out");
|
||||
}
|
||||
}
|
||||
|
||||
// Run the system dependent event loops
|
||||
static void SDL_PumpEventsInternal(bool push_sentinel)
|
||||
{
|
||||
@@ -1184,6 +1355,9 @@ static void SDL_PumpEventsInternal(bool push_sentinel)
|
||||
// Release any keys held down from last frame
|
||||
SDL_ReleaseAutoReleaseKeys();
|
||||
|
||||
// Run any pending main thread callbacks
|
||||
SDL_RunMainThreadCallbacks();
|
||||
|
||||
#ifdef SDL_PLATFORM_ANDROID
|
||||
// Android event processing is independent of the video subsystem
|
||||
Android_PumpEvents(0);
|
||||
@@ -1792,6 +1966,7 @@ bool SDL_InitEvents(void)
|
||||
#endif
|
||||
SDL_AddHintCallback(SDL_HINT_EVENT_LOGGING, SDL_EventLoggingChanged, NULL);
|
||||
SDL_AddHintCallback(SDL_HINT_POLL_SENTINEL, SDL_PollSentinelChanged, NULL);
|
||||
SDL_InitMainThreadCallbacks();
|
||||
if (!SDL_StartEventLoop()) {
|
||||
SDL_RemoveHintCallback(SDL_HINT_EVENT_LOGGING, SDL_EventLoggingChanged, NULL);
|
||||
return false;
|
||||
@@ -1806,6 +1981,7 @@ void SDL_QuitEvents(void)
|
||||
{
|
||||
SDL_QuitQuit();
|
||||
SDL_StopEventLoop();
|
||||
SDL_QuitMainThreadCallbacks();
|
||||
SDL_RemoveHintCallback(SDL_HINT_POLL_SENTINEL, SDL_PollSentinelChanged, NULL);
|
||||
SDL_RemoveHintCallback(SDL_HINT_EVENT_LOGGING, SDL_EventLoggingChanged, NULL);
|
||||
#ifndef SDL_JOYSTICK_DISABLED
|
||||
|
Reference in New Issue
Block a user