thread: Put all important SDL_CreateThread internal data into SDL_Thread.

This avoids the need to malloc something extra, use a semaphore, etc, and
fixes Emscripten with pthreads support, which might not spin up a web worker
until after SDL_CreateThread returns and thus can't wait on a semaphore at
this point in any case.

Fixes Bugzilla #5064.
This commit is contained in:
Ryan C. Gordon 2020-03-26 22:14:59 -04:00
parent abdc5cbf24
commit 46bb47cf04
8 changed files with 44 additions and 97 deletions

View File

@ -33,11 +33,11 @@
on success. on success.
*/ */
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
extern int SDL_SYS_CreateThread(SDL_Thread * thread, void *args, extern int SDL_SYS_CreateThread(SDL_Thread * thread,
pfnSDL_CurrentBeginThread pfnBeginThread, pfnSDL_CurrentBeginThread pfnBeginThread,
pfnSDL_CurrentEndThread pfnEndThread); pfnSDL_CurrentEndThread pfnEndThread);
#else #else
extern int SDL_SYS_CreateThread(SDL_Thread * thread, void *args); extern int SDL_SYS_CreateThread(SDL_Thread * thread);
#endif #endif
/* This function does any necessary setup in the child thread */ /* This function does any necessary setup in the child thread */

View File

@ -258,22 +258,12 @@ SDL_GetErrBuf(void)
} }
/* Arguments and callback to setup and run the user thread function */
typedef struct
{
int (SDLCALL * func) (void *);
void *data;
SDL_Thread *info;
SDL_sem *wait;
} thread_args;
void void
SDL_RunThread(void *data) SDL_RunThread(SDL_Thread *thread)
{ {
thread_args *args = (thread_args *) data; void *userdata = thread->userdata;
int (SDLCALL * userfunc) (void *) = args->func; int (SDLCALL * userfunc) (void *) = thread->userfunc;
void *userdata = args->data;
SDL_Thread *thread = args->info;
int *statusloc = &thread->status; int *statusloc = &thread->status;
/* Perform any system-dependent setup - this function may not fail */ /* Perform any system-dependent setup - this function may not fail */
@ -282,9 +272,6 @@ SDL_RunThread(void *data)
/* Get the thread id */ /* Get the thread id */
thread->threadid = SDL_ThreadID(); thread->threadid = SDL_ThreadID();
/* Wake up the parent thread */
SDL_SemPost(args->wait);
/* Run the function */ /* Run the function */
*statusloc = userfunc(userdata); *statusloc = userfunc(userdata);
@ -325,16 +312,14 @@ SDL_CreateThreadWithStackSize(int (SDLCALL * fn) (void *),
#endif #endif
{ {
SDL_Thread *thread; SDL_Thread *thread;
thread_args *args;
int ret; int ret;
/* Allocate memory for the thread info structure */ /* Allocate memory for the thread info structure */
thread = (SDL_Thread *) SDL_malloc(sizeof(*thread)); thread = (SDL_Thread *) SDL_calloc(1, sizeof(*thread));
if (thread == NULL) { if (thread == NULL) {
SDL_OutOfMemory(); SDL_OutOfMemory();
return (NULL); return NULL;
} }
SDL_zerop(thread);
thread->status = -1; thread->status = -1;
SDL_AtomicSet(&thread->state, SDL_THREAD_STATE_ALIVE); SDL_AtomicSet(&thread->state, SDL_THREAD_STATE_ALIVE);
@ -344,57 +329,29 @@ SDL_CreateThreadWithStackSize(int (SDLCALL * fn) (void *),
if (thread->name == NULL) { if (thread->name == NULL) {
SDL_OutOfMemory(); SDL_OutOfMemory();
SDL_free(thread); SDL_free(thread);
return (NULL); return NULL;
} }
} }
/* Set up the arguments for the thread */ thread->userfunc = fn;
args = (thread_args *) SDL_malloc(sizeof(*args)); thread->userdata = data;
if (args == NULL) {
SDL_OutOfMemory();
if (thread->name) {
SDL_free(thread->name);
}
SDL_free(thread);
return (NULL);
}
args->func = fn;
args->data = data;
args->info = thread;
args->wait = SDL_CreateSemaphore(0);
if (args->wait == NULL) {
if (thread->name) {
SDL_free(thread->name);
}
SDL_free(thread);
SDL_free(args);
return (NULL);
}
thread->stacksize = stacksize; thread->stacksize = stacksize;
/* Create the thread and go! */ /* Create the thread and go! */
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread); ret = SDL_SYS_CreateThread(thread, pfnBeginThread, pfnEndThread);
#else #else
ret = SDL_SYS_CreateThread(thread, args); ret = SDL_SYS_CreateThread(thread);
#endif #endif
if (ret >= 0) { if (ret < 0) {
/* Wait for the thread function to use arguments */
SDL_SemWait(args->wait);
} else {
/* Oops, failed. Gotta free everything */ /* Oops, failed. Gotta free everything */
if (thread->name) { SDL_free(thread->name);
SDL_free(thread->name);
}
SDL_free(thread); SDL_free(thread);
thread = NULL; thread = NULL;
} }
SDL_DestroySemaphore(args->wait);
SDL_free(args);
/* Everything is running now */ /* Everything is running now */
return (thread); return thread;
} }
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD

View File

@ -60,11 +60,14 @@ struct SDL_Thread
SDL_error errbuf; SDL_error errbuf;
char *name; char *name;
size_t stacksize; /* 0 for default, >0 for user-specified stack size. */ size_t stacksize; /* 0 for default, >0 for user-specified stack size. */
int (SDLCALL * userfunc) (void *);
void *userdata;
void *data; void *data;
void *endfunc; /* only used on some platforms. */
}; };
/* This is the function called to run a thread */ /* This is the function called to run a thread */
extern void SDL_RunThread(void *data); extern void SDL_RunThread(SDL_Thread *thread);
/* This is the system-independent thread local storage structure */ /* This is the system-independent thread local storage structure */
typedef struct { typedef struct {

View File

@ -27,12 +27,12 @@
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
int int
SDL_SYS_CreateThread(SDL_Thread * thread, void *args, SDL_SYS_CreateThread(SDL_Thread * thread,
pfnSDL_CurrentBeginThread pfnBeginThread, pfnSDL_CurrentBeginThread pfnBeginThread,
pfnSDL_CurrentEndThread pfnEndThread) pfnSDL_CurrentEndThread pfnEndThread)
#else #else
int int
SDL_SYS_CreateThread(SDL_Thread * thread, void *args) SDL_SYS_CreateThread(SDL_Thread * thread)
#endif /* SDL_PASSED_BEGINTHREAD_ENDTHREAD */ #endif /* SDL_PASSED_BEGINTHREAD_ENDTHREAD */
{ {
return SDL_SetError("Threads are not supported on this platform"); return SDL_SetError("Threads are not supported on this platform");

View File

@ -37,11 +37,11 @@
static int ThreadEntry(SceSize args, void *argp) static int ThreadEntry(SceSize args, void *argp)
{ {
SDL_RunThread(*(void **) argp); SDL_RunThread(*(SDL_Thread **) argp);
return 0; return 0;
} }
int SDL_SYS_CreateThread(SDL_Thread *thread, void *args) int SDL_SYS_CreateThread(SDL_Thread *thread)
{ {
SceKernelThreadInfo status; SceKernelThreadInfo status;
int priority = 32; int priority = 32;
@ -59,7 +59,7 @@ int SDL_SYS_CreateThread(SDL_Thread *thread, void *args)
return SDL_SetError("sceKernelCreateThread() failed"); return SDL_SetError("sceKernelCreateThread() failed");
} }
sceKernelStartThread(thread->handle, 4, &args); sceKernelStartThread(thread->handle, 4, &thread);
return 0; return 0;
} }

View File

@ -76,7 +76,7 @@ RunThread(void *data)
#ifdef __ANDROID__ #ifdef __ANDROID__
Android_JNI_SetupThread(); Android_JNI_SetupThread();
#endif #endif
SDL_RunThread(data); SDL_RunThread((SDL_Thread *) data);
return NULL; return NULL;
} }
@ -88,7 +88,7 @@ static SDL_bool checked_setname = SDL_FALSE;
static int (*ppthread_setname_np)(pthread_t, const char*) = NULL; static int (*ppthread_setname_np)(pthread_t, const char*) = NULL;
#endif #endif
int int
SDL_SYS_CreateThread(SDL_Thread * thread, void *args) SDL_SYS_CreateThread(SDL_Thread * thread)
{ {
pthread_attr_t type; pthread_attr_t type;
@ -117,7 +117,7 @@ SDL_SYS_CreateThread(SDL_Thread * thread, void *args)
} }
/* Create the thread and go! */ /* Create the thread and go! */
if (pthread_create(&thread->handle, &type, RunThread, args) != 0) { if (pthread_create(&thread->handle, &type, RunThread, thread) != 0) {
return SDL_SetError("Not enough resources to create thread"); return SDL_SetError("Not enough resources to create thread");
} }

View File

@ -40,16 +40,16 @@ extern "C" {
static void static void
RunThread(void *args) RunThread(void *args)
{ {
SDL_RunThread(args); SDL_RunThread((SDL_Thread *) args);
} }
extern "C" extern "C"
int int
SDL_SYS_CreateThread(SDL_Thread * thread, void *args) SDL_SYS_CreateThread(SDL_Thread * thread)
{ {
try { try {
// !!! FIXME: no way to set a thread stack size here. // !!! FIXME: no way to set a thread stack size here.
std::thread cpp_thread(RunThread, args); std::thread cpp_thread(RunThread, thread);
thread->handle = (void *) new std::thread(std::move(cpp_thread)); thread->handle = (void *) new std::thread(std::move(cpp_thread));
return 0; return 0;
} catch (std::system_error & ex) { } catch (std::system_error & ex) {

View File

@ -74,23 +74,16 @@ typedef void (__cdecl * pfnSDL_CurrentEndThread) (unsigned code);
#endif /* !SDL_PASSED_BEGINTHREAD_ENDTHREAD */ #endif /* !SDL_PASSED_BEGINTHREAD_ENDTHREAD */
typedef struct ThreadStartParms
{
void *args;
pfnSDL_CurrentEndThread pfnCurrentEndThread;
} tThreadStartParms, *pThreadStartParms;
static DWORD static DWORD
RunThread(void *data) RunThread(void *data)
{ {
pThreadStartParms pThreadParms = (pThreadStartParms) data; SDL_Thread *thread = (SDL_Thread *) data;
pfnSDL_CurrentEndThread pfnEndThread = pThreadParms->pfnCurrentEndThread; pfnSDL_CurrentEndThread pfnEndThread = (pfnSDL_CurrentEndThread) thread->endfunc;
void *args = pThreadParms->args; SDL_RunThread(thread);
SDL_free(pThreadParms); if (pfnEndThread != NULL) {
SDL_RunThread(args);
if (pfnEndThread != NULL)
pfnEndThread(0); pfnEndThread(0);
return (0); }
return 0;
} }
static DWORD WINAPI static DWORD WINAPI
@ -107,33 +100,27 @@ RunThreadViaBeginThreadEx(void *data)
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
int int
SDL_SYS_CreateThread(SDL_Thread * thread, void *args, SDL_SYS_CreateThread(SDL_Thread * thread,
pfnSDL_CurrentBeginThread pfnBeginThread, pfnSDL_CurrentBeginThread pfnBeginThread,
pfnSDL_CurrentEndThread pfnEndThread) pfnSDL_CurrentEndThread pfnEndThread)
{ {
#elif defined(__CYGWIN__) || defined(__WINRT__) #elif defined(__CYGWIN__) || defined(__WINRT__)
int int
SDL_SYS_CreateThread(SDL_Thread * thread, void *args) SDL_SYS_CreateThread(SDL_Thread * thread)
{ {
pfnSDL_CurrentBeginThread pfnBeginThread = NULL; pfnSDL_CurrentBeginThread pfnBeginThread = NULL;
pfnSDL_CurrentEndThread pfnEndThread = NULL; pfnSDL_CurrentEndThread pfnEndThread = NULL;
#else #else
int int
SDL_SYS_CreateThread(SDL_Thread * thread, void *args) SDL_SYS_CreateThread(SDL_Thread * thread)
{ {
pfnSDL_CurrentBeginThread pfnBeginThread = (pfnSDL_CurrentBeginThread)_beginthreadex; pfnSDL_CurrentBeginThread pfnBeginThread = (pfnSDL_CurrentBeginThread)_beginthreadex;
pfnSDL_CurrentEndThread pfnEndThread = (pfnSDL_CurrentEndThread)_endthreadex; pfnSDL_CurrentEndThread pfnEndThread = (pfnSDL_CurrentEndThread)_endthreadex;
#endif /* SDL_PASSED_BEGINTHREAD_ENDTHREAD */ #endif /* SDL_PASSED_BEGINTHREAD_ENDTHREAD */
pThreadStartParms pThreadParms =
(pThreadStartParms) SDL_malloc(sizeof(tThreadStartParms));
const DWORD flags = thread->stacksize ? STACK_SIZE_PARAM_IS_A_RESERVATION : 0; const DWORD flags = thread->stacksize ? STACK_SIZE_PARAM_IS_A_RESERVATION : 0;
if (!pThreadParms) {
return SDL_OutOfMemory();
}
/* Save the function which we will have to call to clear the RTL of calling app! */ /* Save the function which we will have to call to clear the RTL of calling app! */
pThreadParms->pfnCurrentEndThread = pfnEndThread; thread->endfunc = pfnEndThread;
/* Also save the real parameters we have to pass to thread function */
pThreadParms->args = args;
/* thread->stacksize == 0 means "system default", same as win32 expects */ /* thread->stacksize == 0 means "system default", same as win32 expects */
if (pfnBeginThread) { if (pfnBeginThread) {
@ -141,12 +128,12 @@ SDL_SYS_CreateThread(SDL_Thread * thread, void *args)
thread->handle = (SYS_ThreadHandle) thread->handle = (SYS_ThreadHandle)
((size_t) pfnBeginThread(NULL, (unsigned int) thread->stacksize, ((size_t) pfnBeginThread(NULL, (unsigned int) thread->stacksize,
RunThreadViaBeginThreadEx, RunThreadViaBeginThreadEx,
pThreadParms, flags, &threadid)); thread, flags, &threadid));
} else { } else {
DWORD threadid = 0; DWORD threadid = 0;
thread->handle = CreateThread(NULL, thread->stacksize, thread->handle = CreateThread(NULL, thread->stacksize,
RunThreadViaCreateThread, RunThreadViaCreateThread,
pThreadParms, flags, &threadid); thread, flags, &threadid);
} }
if (thread->handle == NULL) { if (thread->handle == NULL) {
return SDL_SetError("Not enough resources to create thread"); return SDL_SetError("Not enough resources to create thread");