/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2006 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Sam Lantinga slouken@libsdl.org */ #include "SDL_config.h" /* System independent thread management routines for SDL */ #include "SDL_mutex.h" #include "SDL_thread.h" #include "SDL_thread_c.h" #include "SDL_systhread.h" #define ARRAY_CHUNKSIZE 32 /* The array of threads currently active in the application (except the main thread) The manipulation of an array here is safer than using a linked list. */ static int SDL_maxthreads = 0; static int SDL_numthreads = 0; static SDL_Thread **SDL_Threads = NULL; static SDL_mutex *thread_lock = NULL; int _creating_thread_lock = 0; int SDL_ThreadsInit(void) { int retval; retval = 0; /* Set the thread lock creation flag so that we can reuse an existing lock on the system - since this mutex never gets destroyed (see SDL_ThreadsQuit()), we want to reuse it. */ _creating_thread_lock = 1; thread_lock = SDL_CreateMutex(); _creating_thread_lock = 0; if ( thread_lock == NULL ) { retval = -1; } return(retval); } /* This should never be called... If this is called by SDL_Quit(), we don't know whether or not we should clean up threads here. If any threads are still running after this call, they will no longer have access to any per-thread data. */ void SDL_ThreadsQuit() { SDL_mutex *mutex; mutex = thread_lock; thread_lock = NULL; if ( mutex != NULL ) { SDL_DestroyMutex(mutex); } } /* Routines for manipulating the thread list */ static void SDL_AddThread(SDL_Thread *thread) { SDL_Thread **threads; /* WARNING: If the very first threads are created simultaneously, then there could be a race condition causing memory corruption. In practice, this isn't a problem because by definition there is only one thread running the first time this is called. */ if ( thread_lock == NULL ) { if ( SDL_ThreadsInit() < 0 ) { return; } } SDL_mutexP(thread_lock); /* Expand the list of threads, if necessary */ #ifdef DEBUG_THREADS printf("Adding thread (%d already - %d max)\n", SDL_numthreads, SDL_maxthreads); #endif if ( SDL_numthreads == SDL_maxthreads ) { threads=(SDL_Thread **)SDL_malloc((SDL_maxthreads+ARRAY_CHUNKSIZE)* (sizeof *threads)); if ( threads == NULL ) { SDL_OutOfMemory(); goto done; } SDL_memcpy(threads, SDL_Threads, SDL_numthreads*(sizeof *threads)); SDL_maxthreads += ARRAY_CHUNKSIZE; if ( SDL_Threads ) { SDL_free(SDL_Threads); } SDL_Threads = threads; } SDL_Threads[SDL_numthreads++] = thread; done: SDL_mutexV(thread_lock); } static void SDL_DelThread(SDL_Thread *thread) { int i; if ( thread_lock ) { SDL_mutexP(thread_lock); for ( i=0; i 0 ) { while ( i < SDL_numthreads ) { SDL_Threads[i] = SDL_Threads[i+1]; ++i; } } else { SDL_maxthreads = 0; SDL_free(SDL_Threads); SDL_Threads = NULL; } #ifdef DEBUG_THREADS printf("Deleting thread (%d left - %d max)\n", SDL_numthreads, SDL_maxthreads); #endif } SDL_mutexV(thread_lock); } } /* The default (non-thread-safe) global error variable */ static SDL_error SDL_global_error; /* Routine to get the thread-specific error variable */ SDL_error *SDL_GetErrBuf(void) { SDL_error *errbuf; errbuf = &SDL_global_error; if ( SDL_Threads ) { int i; Uint32 this_thread; this_thread = SDL_ThreadID(); SDL_mutexP(thread_lock); for ( i=0; ithreadid ) { errbuf = &SDL_Threads[i]->errbuf; break; } } SDL_mutexV(thread_lock); } return(errbuf); } /* Arguments and callback to setup and run the user thread function */ typedef struct { int (*func)(void *); void *data; SDL_Thread *info; SDL_sem *wait; } thread_args; void SDL_RunThread(void *data) { thread_args *args; int (*userfunc)(void *); void *userdata; int *statusloc; /* Perform any system-dependent setup - this function cannot fail, and cannot use SDL_SetError() */ SDL_SYS_SetupThread(); /* Get the thread id */ args = (thread_args *)data; args->info->threadid = SDL_ThreadID(); /* Figure out what function to run */ userfunc = args->func; userdata = args->data; statusloc = &args->info->status; /* Wake up the parent thread */ SDL_SemPost(args->wait); /* Run the function */ *statusloc = userfunc(userdata); } #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD #undef SDL_CreateThread DECLSPEC SDL_Thread * SDLCALL SDL_CreateThread(int (*fn)(void *), void *data, pfnSDL_CurrentBeginThread pfnBeginThread, pfnSDL_CurrentEndThread pfnEndThread) #else DECLSPEC SDL_Thread * SDLCALL SDL_CreateThread(int (*fn)(void *), void *data) #endif { SDL_Thread *thread; thread_args *args; int ret; /* Allocate memory for the thread info structure */ thread = (SDL_Thread *)SDL_malloc(sizeof(*thread)); if ( thread == NULL ) { SDL_OutOfMemory(); return(NULL); } SDL_memset(thread, 0, (sizeof *thread)); thread->status = -1; /* Set up the arguments for the thread */ args = (thread_args *)SDL_malloc(sizeof(*args)); if ( args == NULL ) { SDL_OutOfMemory(); SDL_free(thread); return(NULL); } args->func = fn; args->data = data; args->info = thread; args->wait = SDL_CreateSemaphore(0); if ( args->wait == NULL ) { SDL_free(thread); SDL_free(args); return(NULL); } /* Add the thread to the list of available threads */ SDL_AddThread(thread); /* Create the thread and go! */ #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread); #else ret = SDL_SYS_CreateThread(thread, args); #endif if ( ret >= 0 ) { /* Wait for the thread function to use arguments */ SDL_SemWait(args->wait); } else { /* Oops, failed. Gotta free everything */ SDL_DelThread(thread); SDL_free(thread); thread = NULL; } SDL_DestroySemaphore(args->wait); SDL_free(args); /* Everything is running now */ return(thread); } void SDL_WaitThread(SDL_Thread *thread, int *status) { if ( thread ) { SDL_SYS_WaitThread(thread); if ( status ) { *status = thread->status; } SDL_DelThread(thread); SDL_free(thread); } } Uint32 SDL_GetThreadID(SDL_Thread *thread) { Uint32 id; if ( thread ) { id = thread->threadid; } else { id = SDL_ThreadID(); } return(id); } void SDL_KillThread(SDL_Thread *thread) { if ( thread ) { SDL_SYS_KillThread(thread); SDL_WaitThread(thread, NULL); } }