1/* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2006 Sam Lantinga 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 19 Sam Lantinga 20 slouken@libsdl.org 21*/ 22#include "SDL_config.h" 23 24/* System independent thread management routines for SDL */ 25 26#include "SDL_mutex.h" 27#include "SDL_thread.h" 28#include "SDL_thread_c.h" 29#include "SDL_systhread.h" 30 31#define ARRAY_CHUNKSIZE 32 32/* The array of threads currently active in the application 33 (except the main thread) 34 The manipulation of an array here is safer than using a linked list. 35*/ 36static int SDL_maxthreads = 0; 37static int SDL_numthreads = 0; 38static SDL_Thread **SDL_Threads = NULL; 39static SDL_mutex *thread_lock = NULL; 40 41int SDL_ThreadsInit(void) 42{ 43 int retval; 44 45 retval = 0; 46 thread_lock = SDL_CreateMutex(); 47 if ( thread_lock == NULL ) { 48 retval = -1; 49 } 50 return(retval); 51} 52 53/* This should never be called... 54 If this is called by SDL_Quit(), we don't know whether or not we should 55 clean up threads here. If any threads are still running after this call, 56 they will no longer have access to any per-thread data. 57 */ 58void SDL_ThreadsQuit(void) 59{ 60 SDL_mutex *mutex; 61 62 mutex = thread_lock; 63 thread_lock = NULL; 64 if ( mutex != NULL ) { 65 SDL_DestroyMutex(mutex); 66 } 67} 68 69/* Routines for manipulating the thread list */ 70static void SDL_AddThread(SDL_Thread *thread) 71{ 72 /* WARNING: 73 If the very first threads are created simultaneously, then 74 there could be a race condition causing memory corruption. 75 In practice, this isn't a problem because by definition there 76 is only one thread running the first time this is called. 77 */ 78 if ( !thread_lock ) { 79 if ( SDL_ThreadsInit() < 0 ) { 80 return; 81 } 82 } 83 SDL_mutexP(thread_lock); 84 85 /* Expand the list of threads, if necessary */ 86#ifdef DEBUG_THREADS 87 printf("Adding thread (%d already - %d max)\n", 88 SDL_numthreads, SDL_maxthreads); 89#endif 90 if ( SDL_numthreads == SDL_maxthreads ) { 91 SDL_Thread **threads; 92 threads = (SDL_Thread **)SDL_realloc(SDL_Threads, 93 (SDL_maxthreads+ARRAY_CHUNKSIZE)*(sizeof *threads)); 94 if ( threads == NULL ) { 95 SDL_OutOfMemory(); 96 goto done; 97 } 98 SDL_maxthreads += ARRAY_CHUNKSIZE; 99 SDL_Threads = threads; 100 } 101 SDL_Threads[SDL_numthreads++] = thread; 102done: 103 SDL_mutexV(thread_lock); 104} 105 106static void SDL_DelThread(SDL_Thread *thread) 107{ 108 int i; 109 110 if ( !thread_lock ) { 111 return; 112 } 113 SDL_mutexP(thread_lock); 114 for ( i=0; i<SDL_numthreads; ++i ) { 115 if ( thread == SDL_Threads[i] ) { 116 break; 117 } 118 } 119 if ( i < SDL_numthreads ) { 120 if ( --SDL_numthreads > 0 ) { 121 while ( i < SDL_numthreads ) { 122 SDL_Threads[i] = SDL_Threads[i+1]; 123 ++i; 124 } 125 } else { 126 SDL_maxthreads = 0; 127 SDL_free(SDL_Threads); 128 SDL_Threads = NULL; 129 } 130#ifdef DEBUG_THREADS 131 printf("Deleting thread (%d left - %d max)\n", 132 SDL_numthreads, SDL_maxthreads); 133#endif 134 } 135 SDL_mutexV(thread_lock); 136 137 if ( SDL_Threads == NULL ) { 138 SDL_ThreadsQuit(); 139 } 140} 141 142/* The default (non-thread-safe) global error variable */ 143static SDL_error SDL_global_error; 144 145/* Routine to get the thread-specific error variable */ 146SDL_error *SDL_GetErrBuf(void) 147{ 148 SDL_error *errbuf; 149 150 errbuf = &SDL_global_error; 151 if ( SDL_Threads ) { 152 int i; 153 Uint32 this_thread; 154 155 this_thread = SDL_ThreadID(); 156 SDL_mutexP(thread_lock); 157 for ( i=0; i<SDL_numthreads; ++i ) { 158 if ( this_thread == SDL_Threads[i]->threadid ) { 159 errbuf = &SDL_Threads[i]->errbuf; 160 break; 161 } 162 } 163 SDL_mutexV(thread_lock); 164 } 165 return(errbuf); 166} 167 168 169/* Arguments and callback to setup and run the user thread function */ 170typedef struct { 171 int (SDLCALL *func)(void *); 172 void *data; 173 SDL_Thread *info; 174 SDL_sem *wait; 175} thread_args; 176 177void SDL_RunThread(void *data) 178{ 179 thread_args *args; 180 int (SDLCALL *userfunc)(void *); 181 void *userdata; 182 int *statusloc; 183 184 /* Perform any system-dependent setup 185 - this function cannot fail, and cannot use SDL_SetError() 186 */ 187 SDL_SYS_SetupThread(); 188 189 /* Get the thread id */ 190 args = (thread_args *)data; 191 args->info->threadid = SDL_ThreadID(); 192 193 /* Figure out what function to run */ 194 userfunc = args->func; 195 userdata = args->data; 196 statusloc = &args->info->status; 197 198 /* Wake up the parent thread */ 199 SDL_SemPost(args->wait); 200 201 /* Run the function */ 202 *statusloc = userfunc(userdata); 203} 204 205#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD 206#undef SDL_CreateThread 207DECLSPEC SDL_Thread * SDLCALL SDL_CreateThread(int (SDLCALL *fn)(void *), void *data, pfnSDL_CurrentBeginThread pfnBeginThread, pfnSDL_CurrentEndThread pfnEndThread) 208#else 209DECLSPEC SDL_Thread * SDLCALL SDL_CreateThread(int (SDLCALL *fn)(void *), void *data) 210#endif 211{ 212 SDL_Thread *thread; 213 thread_args *args; 214 int ret; 215 216 /* Allocate memory for the thread info structure */ 217 thread = (SDL_Thread *)SDL_malloc(sizeof(*thread)); 218 if ( thread == NULL ) { 219 SDL_OutOfMemory(); 220 return(NULL); 221 } 222 SDL_memset(thread, 0, (sizeof *thread)); 223 thread->status = -1; 224 225 /* Set up the arguments for the thread */ 226 args = (thread_args *)SDL_malloc(sizeof(*args)); 227 if ( args == NULL ) { 228 SDL_OutOfMemory(); 229 SDL_free(thread); 230 return(NULL); 231 } 232 args->func = fn; 233 args->data = data; 234 args->info = thread; 235 args->wait = SDL_CreateSemaphore(0); 236 if ( args->wait == NULL ) { 237 SDL_free(thread); 238 SDL_free(args); 239 return(NULL); 240 } 241 242 /* Add the thread to the list of available threads */ 243 SDL_AddThread(thread); 244 245 /* Create the thread and go! */ 246#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD 247 ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread); 248#else 249 ret = SDL_SYS_CreateThread(thread, args); 250#endif 251 if ( ret >= 0 ) { 252 /* Wait for the thread function to use arguments */ 253 SDL_SemWait(args->wait); 254 } else { 255 /* Oops, failed. Gotta free everything */ 256 SDL_DelThread(thread); 257 SDL_free(thread); 258 thread = NULL; 259 } 260 SDL_DestroySemaphore(args->wait); 261 SDL_free(args); 262 263 /* Everything is running now */ 264 return(thread); 265} 266 267void SDL_WaitThread(SDL_Thread *thread, int *status) 268{ 269 if ( thread ) { 270 SDL_SYS_WaitThread(thread); 271 if ( status ) { 272 *status = thread->status; 273 } 274 SDL_DelThread(thread); 275 SDL_free(thread); 276 } 277} 278 279Uint32 SDL_GetThreadID(SDL_Thread *thread) 280{ 281 Uint32 id; 282 283 if ( thread ) { 284 id = thread->threadid; 285 } else { 286 id = SDL_ThreadID(); 287 } 288 return(id); 289} 290 291void SDL_KillThread(SDL_Thread *thread) 292{ 293 if ( thread ) { 294 SDL_SYS_KillThread(thread); 295 SDL_WaitThread(thread, NULL); 296 } 297} 298 299