SDL-1.2.14
[sdl_omap.git] / src / thread / SDL_thread.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 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 */
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