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 (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 struct SignalSemaphore thread_lock; 40 int thread_lock_created = 0; 41 42 int SDL_ThreadsInit(void) 43 { 44 InitSemaphore(&thread_lock); 45 thread_lock_created=1; 46 return 0; 47 } 48 49 /* This should never be called... 50 If this is called by SDL_Quit(), we don't know whether or not we should 51 clean up threads here. If any threads are still running after this call, 52 they will no longer have access to any per-thread data. 53 */ 54 void SDL_ThreadsQuit() 55 { 56 thread_lock_created=0; 57 } 58 59 /* Routines for manipulating the thread list */ 60 static void SDL_AddThread(SDL_Thread *thread) 61 { 62 SDL_Thread **threads; 63 64 /* WARNING: 65 If the very first threads are created simultaneously, then 66 there could be a race condition causing memory corruption. 67 In practice, this isn't a problem because by definition there 68 is only one thread running the first time this is called. 69 */ 70 if ( !thread_lock_created ) { 71 if ( SDL_ThreadsInit() < 0 ) { 72 return; 73 } 74 } 75 ObtainSemaphore(&thread_lock); 76 77 /* Expand the list of threads, if necessary */ 78 #ifdef DEBUG_THREADS 79 printf("Adding thread (%d already - %d max)\n", 80 SDL_numthreads, SDL_maxthreads); 81 #endif 82 if ( SDL_numthreads == SDL_maxthreads ) { 83 threads=(SDL_Thread **)SDL_malloc((SDL_maxthreads+ARRAY_CHUNKSIZE)* 84 (sizeof *threads)); 85 if ( threads == NULL ) { 86 SDL_OutOfMemory(); 87 goto done; 88 } 89 SDL_memcpy(threads, SDL_Threads, SDL_numthreads*(sizeof *threads)); 90 SDL_maxthreads += ARRAY_CHUNKSIZE; 91 if ( SDL_Threads ) { 92 SDL_free(SDL_Threads); 93 } 94 SDL_Threads = threads; 95 } 96 SDL_Threads[SDL_numthreads++] = thread; 97 done: 98 ReleaseSemaphore(&thread_lock); 99 } 100 101 static void SDL_DelThread(SDL_Thread *thread) 102 { 103 int i; 104 105 if ( thread_lock_created ) { 106 ObtainSemaphore(&thread_lock); 107 for ( i=0; i<SDL_numthreads; ++i ) { 108 if ( thread == SDL_Threads[i] ) { 109 break; 110 } 111 } 112 if ( i < SDL_numthreads ) { 113 --SDL_numthreads; 114 while ( i < SDL_numthreads ) { 115 SDL_Threads[i] = SDL_Threads[i+1]; 116 ++i; 117 } 118 #ifdef DEBUG_THREADS 119 printf("Deleting thread (%d left - %d max)\n", 120 SDL_numthreads, SDL_maxthreads); 121 #endif 122 } 123 ReleaseSemaphore(&thread_lock); 124 } 125 } 126 127 /* The default (non-thread-safe) global error variable */ 128 static SDL_error SDL_global_error; 129 130 /* Routine to get the thread-specific error variable */ 131 SDL_error *SDL_GetErrBuf(void) 132 { 133 SDL_error *errbuf; 134 135 errbuf = &SDL_global_error; 136 if ( SDL_Threads ) { 137 int i; 138 Uint32 this_thread; 139 140 this_thread = SDL_ThreadID(); 141 ObtainSemaphore(&thread_lock); 142 for ( i=0; i<SDL_numthreads; ++i ) { 143 if ( this_thread == SDL_Threads[i]->threadid ) { 144 errbuf = &SDL_Threads[i]->errbuf; 145 break; 146 } 147 } 148 ReleaseSemaphore(&thread_lock); 149 } 150 return(errbuf); 151 } 152 153 154 /* Arguments and callback to setup and run the user thread function */ 155 typedef struct { 156 int (*func)(void *); 157 void *data; 158 SDL_Thread *info; 159 struct Task *wait; 160 } thread_args; 161 162 void SDL_RunThread(void *data) 163 { 164 thread_args *args; 165 int (*userfunc)(void *); 166 void *userdata; 167 int *statusloc; 168 169 /* Perform any system-dependent setup 170 - this function cannot fail, and cannot use SDL_SetError() 171 */ 172 SDL_SYS_SetupThread(); 173 174 /* Get the thread id */ 175 args = (thread_args *)data; 176 args->info->threadid = SDL_ThreadID(); 177 178 /* Figure out what function to run */ 179 userfunc = args->func; 180 userdata = args->data; 181 statusloc = &args->info->status; 182 183 /* Wake up the parent thread */ 184 Signal(args->wait,SIGBREAKF_CTRL_E); 185 186 /* Run the function */ 187 *statusloc = userfunc(userdata); 188 } 189 190 SDL_Thread *SDL_CreateThread(int (*fn)(void *), void *data) 191 { 192 SDL_Thread *thread; 193 thread_args *args; 194 int ret; 195 196 /* Allocate memory for the thread info structure */ 197 thread = (SDL_Thread *)SDL_malloc(sizeof(*thread)); 198 if ( thread == NULL ) { 199 SDL_OutOfMemory(); 200 return(NULL); 201 } 202 SDL_memset(thread, 0, (sizeof *thread)); 203 thread->status = -1; 204 205 /* Set up the arguments for the thread */ 206 args = (thread_args *)SDL_malloc(sizeof(*args)); 207 if ( args == NULL ) { 208 SDL_OutOfMemory(); 209 SDL_free(thread); 210 return(NULL); 211 } 212 args->func = fn; 213 args->data = data; 214 args->info = thread; 215 args->wait = FindTask(NULL); 216 if ( args->wait == NULL ) { 217 SDL_free(thread); 218 SDL_free(args); 219 SDL_OutOfMemory(); 220 return(NULL); 221 } 222 223 /* Add the thread to the list of available threads */ 224 SDL_AddThread(thread); 225 226 D(bug("Starting thread...\n")); 227 228 /* Create the thread and go! */ 229 ret = SDL_SYS_CreateThread(thread, args); 230 if ( ret >= 0 ) { 231 D(bug("Waiting for thread CTRL_E...\n")); 232 /* Wait for the thread function to use arguments */ 233 Wait(SIGBREAKF_CTRL_E); 234 D(bug(" Arrived.")); 235 } else { 236 /* Oops, failed. Gotta free everything */ 237 SDL_DelThread(thread); 238 SDL_free(thread); 239 thread = NULL; 240 } 241 SDL_free(args); 242 243 /* Everything is running now */ 244 return(thread); 245 } 246 247 void SDL_WaitThread(SDL_Thread *thread, int *status) 248 { 249 if ( thread ) { 250 SDL_SYS_WaitThread(thread); 251 if ( status ) { 252 *status = thread->status; 253 } 254 SDL_DelThread(thread); 255 SDL_free(thread); 256 } 257 } 258 259 Uint32 SDL_GetThreadID(SDL_Thread *thread) 260 { 261 Uint32 id; 262 263 if ( thread ) { 264 id = thread->threadid; 265 } else { 266 id = SDL_ThreadID(); 267 } 268 return(id); 269 } 270 271 void SDL_KillThread(SDL_Thread *thread) 272 { 273 if ( thread ) { 274 SDL_SYS_KillThread(thread); 275 SDL_WaitThread(thread, NULL); 276 } 277 } 278 279