comparison src/thread/linux/SDL_syssem.c @ 0:74212992fb08

Initial revision
author Sam Lantinga <slouken@lokigames.com>
date Thu, 26 Apr 2001 16:45:43 +0000
parents
children d9e3595b63d5
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 #include <stdlib.h>
29 #include "SDL_error.h"
30 #include "SDL_thread.h"
31 #include "SDL_timer.h"
32
33 #ifdef linux
34 /* Look to see if glibc is available, and if so, what version */
35 #include <features.h>
36
37 #if (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0)
38 #warning Working around a bug in glibc 2.0 pthreads
39 #undef SDL_USE_PTHREADS
40 /* The bug is actually a problem where threads are suspended, but don't
41 wake up when the thread manager sends them a signal. This is a problem
42 with thread creation too, but it happens less often. :-/
43 We avoid this by using System V IPC for semaphores.
44 */
45 #endif /* glibc 2.0 */
46 #endif /* linux */
47
48 #ifdef SDL_USE_PTHREADS
49
50 #ifdef SDL_NO_PTHREAD_SEMAPHORES
51 #include "generic/SDL_sem.c"
52 #else
53
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <unistd.h> /* For getpid() */
57 #include <semaphore.h>
58
59 /* Wrapper around POSIX 1003.1b semaphores */
60
61 #ifdef MACOSX
62 #define USE_NAMED_SEMAPHORES
63 /* Broken sem_getvalue() in MacOS X Public Beta */
64 #define BROKEN_SEMGETVALUE
65 #endif /* MACOSX */
66
67 struct SDL_semaphore {
68 sem_t *sem;
69 #ifndef USE_NAMED_SEMAPHORES
70 sem_t sem_data;
71 #endif
72 #ifdef BROKEN_SEMGETVALUE
73 /* This is a little hack for MacOS X -
74 It's not thread-safe, but it's better than nothing
75 */
76 int sem_value;
77 #endif
78 };
79
80 /* Create a semaphore, initialized with value */
81 SDL_sem *SDL_CreateSemaphore(Uint32 initial_value)
82 {
83 SDL_sem *sem = (SDL_sem *) malloc(sizeof(SDL_sem));
84 if ( sem ) {
85 #ifdef USE_NAMED_SEMAPHORES
86 static int semnum = 0;
87 char name[32];
88
89 sprintf(name, "/SDL_sem-%d-%4.4d", getpid(), semnum++);
90 sem->sem = sem_open(name, O_CREAT, 0600, initial_value);
91 if ( sem->sem == (sem_t *)SEM_FAILED ) {
92 SDL_SetError("sem_open(%s) failed", name);
93 free(sem);
94 sem = NULL;
95 } else {
96 sem_unlink(name);
97 }
98 #else
99 if ( sem_init(&sem->sem_data, 0, initial_value) < 0 ) {
100 SDL_SetError("sem_init() failed");
101 free(sem);
102 sem = NULL;
103 } else {
104 sem->sem = &sem->sem_data;
105 }
106 #endif /* USE_NAMED_SEMAPHORES */
107
108 #ifdef BROKEN_SEMGETVALUE
109 if ( sem ) {
110 sem->sem_value = initial_value;
111 }
112 #endif /* BROKEN_SEMGETVALUE */
113 } else {
114 SDL_OutOfMemory();
115 }
116 return sem;
117 }
118
119 void SDL_DestroySemaphore(SDL_sem *sem)
120 {
121 if ( sem ) {
122 #ifdef USE_NAMED_SEMAPHORES
123 sem_close(sem->sem);
124 #else
125 sem_destroy(sem->sem);
126 #endif
127 free(sem);
128 }
129 }
130
131 int SDL_SemTryWait(SDL_sem *sem)
132 {
133 int retval;
134
135 if ( ! sem ) {
136 SDL_SetError("Passed a NULL semaphore");
137 return -1;
138 }
139 retval = SDL_MUTEX_TIMEDOUT;
140 if ( sem_trywait(sem->sem) == 0 ) {
141 #ifdef BROKEN_SEMGETVALUE
142 --sem->sem_value;
143 #endif
144 retval = 0;
145 }
146 return retval;
147 }
148
149 int SDL_SemWait(SDL_sem *sem)
150 {
151 int retval;
152
153 if ( ! sem ) {
154 SDL_SetError("Passed a NULL semaphore");
155 return -1;
156 }
157
158 #ifdef BROKEN_SEMGETVALUE
159 --sem->sem_value;
160 #endif
161 retval = sem_wait(sem->sem);
162 if ( retval < 0 ) {
163 SDL_SetError("sem_wait() failed");
164 }
165 return retval;
166 }
167
168 int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout)
169 {
170 int retval;
171
172 if ( ! sem ) {
173 SDL_SetError("Passed a NULL semaphore");
174 return -1;
175 }
176
177 /* Try the easy cases first */
178 if ( timeout == 0 ) {
179 return SDL_SemTryWait(sem);
180 }
181 if ( timeout == SDL_MUTEX_MAXWAIT ) {
182 return SDL_SemWait(sem);
183 }
184
185 /* Ack! We have to busy wait... */
186 timeout += SDL_GetTicks();
187 do {
188 retval = SDL_SemTryWait(sem);
189 if ( retval == 0 ) {
190 break;
191 }
192 SDL_Delay(1);
193 } while ( SDL_GetTicks() < timeout );
194
195 return retval;
196 }
197
198 Uint32 SDL_SemValue(SDL_sem *sem)
199 {
200 int ret = 0;
201 if ( sem ) {
202 #ifdef BROKEN_SEMGETVALUE
203 ret = sem->sem_value;
204 #else
205 sem_getvalue(sem->sem, &ret);
206 #endif
207 if ( ret < 0 ) {
208 ret = 0;
209 }
210 }
211 return (Uint32)ret;
212 }
213
214 int SDL_SemPost(SDL_sem *sem)
215 {
216 int retval;
217
218 if ( ! sem ) {
219 SDL_SetError("Passed a NULL semaphore");
220 return -1;
221 }
222
223 #ifdef BROKEN_SEMGETVALUE
224 ++sem->sem_value;
225 #endif
226 retval = sem_post(sem->sem);
227 if ( retval < 0 ) {
228 SDL_SetError("sem_post() failed");
229 }
230 return retval;
231 }
232
233 #endif /* NO_PTHREAD_SEMAPHORES */
234
235 #else /* System V IPC implementation */
236
237 #include <stdio.h>
238 #include <stdlib.h>
239 #include <sys/types.h>
240 #include <sys/ipc.h>
241 #include <sys/sem.h>
242 #include <errno.h>
243
244 #include "SDL_error.h"
245 #include "SDL_thread.h"
246
247
248 struct SDL_semaphore {
249 int id;
250 };
251
252 /* Not defined by many operating systems, use configure to detect */
253 #if !defined(HAVE_SEMUN)
254 union semun {
255 int val;
256 struct semid_ds *buf;
257 ushort *array;
258 };
259 #endif
260
261 static struct sembuf op_trywait[2] = {
262 { 0, -1, (IPC_NOWAIT|SEM_UNDO) } /* Decrement semaphore, no block */
263 };
264 static struct sembuf op_wait[2] = {
265 { 0, -1, SEM_UNDO } /* Decrement semaphore */
266 };
267 static struct sembuf op_post[1] = {
268 { 0, 1, (IPC_NOWAIT|SEM_UNDO) } /* Increment semaphore */
269 };
270
271 /* Create a blockable semaphore */
272 SDL_sem *SDL_CreateSemaphore(Uint32 initial_value)
273 {
274 extern int _creating_thread_lock; /* SDL_threads.c */
275 SDL_sem *sem;
276 union semun init;
277 key_t key;
278
279 sem = (SDL_sem *)malloc(sizeof(*sem));
280 if ( sem == NULL ) {
281 SDL_OutOfMemory();
282 return(NULL);
283 }
284 /* This flag is true if we are creating the thread manager sem,
285 which is never freed. This allows us to reuse the same sem.
286 */
287 if ( _creating_thread_lock ) {
288 key = 'S'+'D'+'L';
289 } else {
290 key = IPC_PRIVATE;
291 }
292 /* Keep trying to create sem while we don't own the requested key */
293 do {
294 if ( key != IPC_PRIVATE ) {
295 ++key;
296 }
297 sem->id = semget(key, 1, (0600|IPC_CREAT));
298 } while ((sem->id < 0) && (key != IPC_PRIVATE) && (errno == EACCES));
299
300 /* Report the error if we eventually failed */
301 if ( sem->id < 0 ) {
302 SDL_SetError("Couldn't create semaphore");
303 free(sem);
304 return(NULL);
305 }
306 init.val = initial_value; /* Initialize semaphore */
307 semctl(sem->id, 0, SETVAL, init);
308 return(sem);
309 }
310
311 void SDL_DestroySemaphore(SDL_sem *sem)
312 {
313 if ( sem ) {
314 #ifdef _SGI_SOURCE
315 semctl(sem->id, 0, IPC_RMID);
316 #else
317 union semun dummy;
318 dummy.val = 0;
319 semctl(sem->id, 0, IPC_RMID, dummy);
320 #endif
321 free(sem);
322 }
323 }
324
325 int SDL_SemTryWait(SDL_sem *sem)
326 {
327 int retval;
328
329 if ( ! sem ) {
330 SDL_SetError("Passed a NULL semaphore");
331 return -1;
332 }
333
334 retval = 0;
335 tryagain:
336 if ( semop(sem->id, op_trywait, 1) < 0 ) {
337 if ( errno == EINTR ) {
338 goto tryagain;
339 }
340 retval = SDL_MUTEX_TIMEDOUT;
341 }
342 return retval;
343 }
344
345 int SDL_SemWait(SDL_sem *sem)
346 {
347 int retval;
348
349 if ( ! sem ) {
350 SDL_SetError("Passed a NULL semaphore");
351 return -1;
352 }
353
354 retval = 0;
355 tryagain:
356 if ( semop(sem->id, op_wait, 1) < 0 ) {
357 if ( errno == EINTR ) {
358 goto tryagain;
359 }
360 SDL_SetError("Semaphore operation error");
361 retval = -1;
362 }
363 return retval;
364 }
365
366 int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout)
367 {
368 int retval;
369
370 if ( ! sem ) {
371 SDL_SetError("Passed a NULL semaphore");
372 return -1;
373 }
374
375 /* Try the easy cases first */
376 if ( timeout == 0 ) {
377 return SDL_SemTryWait(sem);
378 }
379 if ( timeout == SDL_MUTEX_MAXWAIT ) {
380 return SDL_SemWait(sem);
381 }
382
383 /* Ack! We have to busy wait... */
384 timeout += SDL_GetTicks();
385 do {
386 retval = SDL_SemTryWait(sem);
387 if ( retval == 0 ) {
388 break;
389 }
390 SDL_Delay(1);
391 } while ( SDL_GetTicks() < timeout );
392
393 return retval;
394 }
395
396 Uint32 SDL_SemValue(SDL_sem *sem)
397 {
398 int semval;
399 Uint32 value;
400
401 value = 0;
402 if ( sem ) {
403 tryagain:
404 #ifdef _SGI_SOURCE
405 semval = semctl(sem->id, 0, GETVAL);
406 #else
407 {
408 union semun arg;
409 arg.val = 0;
410 semval = semctl(sem->id, 0, GETVAL, arg);
411 }
412 #endif
413 if ( semval < 0 ) {
414 if ( errno == EINTR ) {
415 goto tryagain;
416 }
417 } else {
418 value = (Uint32)semval;
419 }
420 }
421 return value;
422 }
423
424 int SDL_SemPost(SDL_sem *sem)
425 {
426 int retval;
427
428 if ( ! sem ) {
429 SDL_SetError("Passed a NULL semaphore");
430 return -1;
431 }
432
433 retval = 0;
434 tryagain:
435 if ( semop(sem->id, op_post, 1) < 0 ) {
436 if ( errno == EINTR ) {
437 goto tryagain;
438 }
439 SDL_SetError("Semaphore operation error");
440 retval = -1;
441 }
442 return retval;
443 }
444
445 #endif /* SDL_USE_PTHREADS */