1 /* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2012 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 (at) 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 */ 36 static int SDL_maxthreads = 0; 37 static int SDL_numthreads = 0; 38 static SDL_Thread **SDL_Threads = NULL; 39 static SDL_mutex *thread_lock = NULL; 40 41 int 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 */ 58 void 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 */ 70 static 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; 102 done: 103 SDL_mutexV(thread_lock); 104 } 105 106 static 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 0 /* There could be memory corruption if another thread is starting */ 138 if ( SDL_Threads == NULL ) { 139 SDL_ThreadsQuit(); 140 } 141 #endif 142 } 143 144 /* The default (non-thread-safe) global error variable */ 145 static SDL_error SDL_global_error; 146 147 /* Routine to get the thread-specific error variable */ 148 SDL_error *SDL_GetErrBuf(void) 149 { 150 SDL_error *errbuf; 151 152 errbuf = &SDL_global_error; 153 if ( SDL_Threads ) { 154 int i; 155 Uint32 this_thread; 156 157 this_thread = SDL_ThreadID(); 158 SDL_mutexP(thread_lock); 159 for ( i=0; i<SDL_numthreads; ++i ) { 160 if ( this_thread == SDL_Threads[i]->threadid ) { 161 errbuf = &SDL_Threads[i]->errbuf; 162 break; 163 } 164 } 165 SDL_mutexV(thread_lock); 166 } 167 return(errbuf); 168 } 169 170 171 /* Arguments and callback to setup and run the user thread function */ 172 typedef struct { 173 int (SDLCALL *func)(void *); 174 void *data; 175 SDL_Thread *info; 176 SDL_sem *wait; 177 } thread_args; 178 179 void SDL_RunThread(void *data) 180 { 181 thread_args *args; 182 int (SDLCALL *userfunc)(void *); 183 void *userdata; 184 int *statusloc; 185 186 /* Perform any system-dependent setup 187 - this function cannot fail, and cannot use SDL_SetError() 188 */ 189 SDL_SYS_SetupThread(); 190 191 /* Get the thread id */ 192 args = (thread_args *)data; 193 args->info->threadid = SDL_ThreadID(); 194 195 /* Figure out what function to run */ 196 userfunc = args->func; 197 userdata = args->data; 198 statusloc = &args->info->status; 199 200 /* Wake up the parent thread */ 201 SDL_SemPost(args->wait); 202 203 /* Run the function */ 204 *statusloc = userfunc(userdata); 205 } 206 207 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD 208 #undef SDL_CreateThread 209 DECLSPEC SDL_Thread * SDLCALL SDL_CreateThread(int (SDLCALL *fn)(void *), void *data, pfnSDL_CurrentBeginThread pfnBeginThread, pfnSDL_CurrentEndThread pfnEndThread) 210 #else 211 DECLSPEC SDL_Thread * SDLCALL SDL_CreateThread(int (SDLCALL *fn)(void *), void *data) 212 #endif 213 { 214 SDL_Thread *thread; 215 thread_args *args; 216 int ret; 217 218 /* Allocate memory for the thread info structure */ 219 thread = (SDL_Thread *)SDL_malloc(sizeof(*thread)); 220 if ( thread == NULL ) { 221 SDL_OutOfMemory(); 222 return(NULL); 223 } 224 SDL_memset(thread, 0, (sizeof *thread)); 225 thread->status = -1; 226 227 /* Set up the arguments for the thread */ 228 args = (thread_args *)SDL_malloc(sizeof(*args)); 229 if ( args == NULL ) { 230 SDL_OutOfMemory(); 231 SDL_free(thread); 232 return(NULL); 233 } 234 args->func = fn; 235 args->data = data; 236 args->info = thread; 237 args->wait = SDL_CreateSemaphore(0); 238 if ( args->wait == NULL ) { 239 SDL_free(thread); 240 SDL_free(args); 241 return(NULL); 242 } 243 244 /* Add the thread to the list of available threads */ 245 SDL_AddThread(thread); 246 247 /* Create the thread and go! */ 248 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD 249 ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread); 250 #else 251 ret = SDL_SYS_CreateThread(thread, args); 252 #endif 253 if ( ret >= 0 ) { 254 /* Wait for the thread function to use arguments */ 255 SDL_SemWait(args->wait); 256 } else { 257 /* Oops, failed. Gotta free everything */ 258 SDL_DelThread(thread); 259 SDL_free(thread); 260 thread = NULL; 261 } 262 SDL_DestroySemaphore(args->wait); 263 SDL_free(args); 264 265 /* Everything is running now */ 266 return(thread); 267 } 268 269 void SDL_WaitThread(SDL_Thread *thread, int *status) 270 { 271 if ( thread ) { 272 SDL_SYS_WaitThread(thread); 273 if ( status ) { 274 *status = thread->status; 275 } 276 SDL_DelThread(thread); 277 SDL_free(thread); 278 } 279 } 280 281 Uint32 SDL_GetThreadID(SDL_Thread *thread) 282 { 283 Uint32 id; 284 285 if ( thread ) { 286 id = thread->threadid; 287 } else { 288 id = SDL_ThreadID(); 289 } 290 return(id); 291 } 292 293 void SDL_KillThread(SDL_Thread *thread) 294 { 295 if ( thread ) { 296 SDL_SYS_KillThread(thread); 297 SDL_WaitThread(thread, NULL); 298 } 299 } 300 301