comparison src/thread/SDL_thread.c @ 0:74212992fb08

Initial revision
author Sam Lantinga <slouken@lokigames.com>
date Thu, 26 Apr 2001 16:45:43 +0000
parents
children e8157fcb3114
comparison
equal deleted inserted replaced
-1:000000000000 0:74212992fb08
1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 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 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Sam Lantinga
20 slouken@devolution.com
21 */
22
23 #ifdef SAVE_RCSID
24 static char rcsid =
25 "@(#) $Id$";
26 #endif
27
28 /* System independent thread management routines for SDL */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "SDL_error.h"
35 #include "SDL_mutex.h"
36 #include "SDL_thread.h"
37 #include "SDL_thread_c.h"
38 #include "SDL_systhread.h"
39
40 #define ARRAY_CHUNKSIZE 32
41 /* The array of threads currently active in the application
42 (except the main thread)
43 The manipulation of an array here is safer than using a linked list.
44 */
45 static int SDL_maxthreads = 0;
46 static int SDL_numthreads = 0;
47 static SDL_Thread **SDL_Threads = NULL;
48 static SDL_mutex *thread_lock = NULL;
49 int _creating_thread_lock = 0;
50
51 int SDL_ThreadsInit(void)
52 {
53 int retval;
54
55 retval = 0;
56 /* Set the thread lock creation flag so that we can reuse an
57 existing lock on the system - since this mutex never gets
58 destroyed (see SDL_ThreadsQuit()), we want to reuse it.
59 */
60 _creating_thread_lock = 1;
61 thread_lock = SDL_CreateMutex();
62 _creating_thread_lock = 0;
63 if ( thread_lock == NULL ) {
64 retval = -1;
65 }
66 return(retval);
67 }
68
69 /* This should never be called...
70 If this is called by SDL_Quit(), we don't know whether or not we should
71 clean up threads here. If any threads are still running after this call,
72 they will no longer have access to any per-thread data.
73 */
74 void SDL_ThreadsQuit()
75 {
76 SDL_mutex *mutex;
77
78 mutex = thread_lock;
79 thread_lock = NULL;
80 if ( mutex != NULL ) {
81 SDL_DestroyMutex(mutex);
82 }
83 }
84
85 /* Routines for manipulating the thread list */
86 static void SDL_AddThread(SDL_Thread *thread)
87 {
88 SDL_Thread **threads;
89
90 /* WARNING:
91 If the very first threads are created simultaneously, then
92 there could be a race condition causing memory corruption.
93 In practice, this isn't a problem because by definition there
94 is only one thread running the first time this is called.
95 */
96 if ( thread_lock == NULL ) {
97 if ( SDL_ThreadsInit() < 0 ) {
98 return;
99 }
100 }
101 SDL_mutexP(thread_lock);
102
103 /* Expand the list of threads, if necessary */
104 #ifdef DEBUG_THREADS
105 printf("Adding thread (%d already - %d max)\n",
106 SDL_numthreads, SDL_maxthreads);
107 #endif
108 if ( SDL_numthreads == SDL_maxthreads ) {
109 threads=(SDL_Thread **)malloc((SDL_maxthreads+ARRAY_CHUNKSIZE)*
110 (sizeof *threads));
111 if ( threads == NULL ) {
112 SDL_OutOfMemory();
113 goto done;
114 }
115 memcpy(threads, SDL_Threads, SDL_numthreads*(sizeof *threads));
116 SDL_maxthreads += ARRAY_CHUNKSIZE;
117 if ( SDL_Threads ) {
118 free(SDL_Threads);
119 }
120 SDL_Threads = threads;
121 }
122 SDL_Threads[SDL_numthreads++] = thread;
123 done:
124 SDL_mutexV(thread_lock);
125 }
126
127 static void SDL_DelThread(SDL_Thread *thread)
128 {
129 int i;
130
131 if ( thread_lock ) {
132 SDL_mutexP(thread_lock);
133 for ( i=0; i<SDL_numthreads; ++i ) {
134 if ( thread == SDL_Threads[i] ) {
135 break;
136 }
137 }
138 if ( i < SDL_numthreads ) {
139 --SDL_numthreads;
140 while ( i < SDL_numthreads ) {
141 SDL_Threads[i] = SDL_Threads[i+1];
142 ++i;
143 }
144 #ifdef DEBUG_THREADS
145 printf("Deleting thread (%d left - %d max)\n",
146 SDL_numthreads, SDL_maxthreads);
147 #endif
148 }
149 SDL_mutexV(thread_lock);
150 }
151 }
152
153 /* The default (non-thread-safe) global error variable */
154 static SDL_error SDL_global_error;
155
156 /* Routine to get the thread-specific error variable */
157 SDL_error *SDL_GetErrBuf(void)
158 {
159 SDL_error *errbuf;
160
161 errbuf = &SDL_global_error;
162 if ( SDL_Threads ) {
163 int i;
164 Uint32 this_thread;
165
166 this_thread = SDL_ThreadID();
167 SDL_mutexP(thread_lock);
168 for ( i=0; i<SDL_numthreads; ++i ) {
169 if ( this_thread == SDL_Threads[i]->threadid ) {
170 errbuf = &SDL_Threads[i]->errbuf;
171 break;
172 }
173 }
174 SDL_mutexV(thread_lock);
175 }
176 return(errbuf);
177 }
178
179
180 /* Arguments and callback to setup and run the user thread function */
181 typedef struct {
182 int (*func)(void *);
183 void *data;
184 SDL_Thread *info;
185 SDL_sem *wait;
186 } thread_args;
187
188 void SDL_RunThread(void *data)
189 {
190 thread_args *args;
191 int (*userfunc)(void *);
192 void *userdata;
193 int *statusloc;
194
195 /* Perform any system-dependent setup
196 - this function cannot fail, and cannot use SDL_SetError()
197 */
198 SDL_SYS_SetupThread();
199
200 /* Get the thread id */
201 args = (thread_args *)data;
202 args->info->threadid = SDL_ThreadID();
203
204 /* Figure out what function to run */
205 userfunc = args->func;
206 userdata = args->data;
207 statusloc = &args->info->status;
208
209 /* Wake up the parent thread */
210 SDL_SemPost(args->wait);
211
212 /* Run the function */
213 *statusloc = userfunc(userdata);
214 }
215
216 SDL_Thread *SDL_CreateThread(int (*fn)(void *), void *data)
217 {
218 SDL_Thread *thread;
219 thread_args *args;
220 int ret;
221
222 /* Allocate memory for the thread info structure */
223 thread = (SDL_Thread *)malloc(sizeof(*thread));
224 if ( thread == NULL ) {
225 SDL_OutOfMemory();
226 return(NULL);
227 }
228 memset(thread, 0, (sizeof *thread));
229 thread->status = -1;
230
231 /* Set up the arguments for the thread */
232 args = (thread_args *)malloc(sizeof(*args));
233 if ( args == NULL ) {
234 SDL_OutOfMemory();
235 free(thread);
236 return(NULL);
237 }
238 args->func = fn;
239 args->data = data;
240 args->info = thread;
241 args->wait = SDL_CreateSemaphore(0);
242 if ( args->wait == NULL ) {
243 free(thread);
244 free(args);
245 return(NULL);
246 }
247
248 /* Add the thread to the list of available threads */
249 SDL_AddThread(thread);
250
251 /* Create the thread and go! */
252 ret = SDL_SYS_CreateThread(thread, args);
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 free(thread);
260 thread = NULL;
261 }
262 SDL_DestroySemaphore(args->wait);
263 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 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