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 */
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
SDL_ThreadsInit(void)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 */
SDL_ThreadsQuit()54 void SDL_ThreadsQuit()
55 {
56 thread_lock_created=0;
57 }
58
59 /* Routines for manipulating the thread list */
SDL_AddThread(SDL_Thread * thread)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
SDL_DelThread(SDL_Thread * thread)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 */
SDL_GetErrBuf(void)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
SDL_RunThread(void * data)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
SDL_CreateThread(int (* fn)(void *),void * data)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
SDL_WaitThread(SDL_Thread * thread,int * status)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
SDL_GetThreadID(SDL_Thread * thread)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
SDL_KillThread(SDL_Thread * thread)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