src/thread/linux/SDL_syssem.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. SDL_CreateSemaphore
  2. SDL_DestroySemaphore
  3. SDL_SemTryWait
  4. SDL_SemWait
  5. SDL_SemWaitTimeout
  6. SDL_SemValue
  7. SDL_SemPost
  8. SDL_CreateSemaphore
  9. SDL_DestroySemaphore
  10. SDL_SemTryWait
  11. SDL_SemWait
  12. SDL_SemWaitTimeout
  13. SDL_SemValue
  14. SDL_SemPost

   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: SDL_syssem.c,v 1.1.2.12 2001/02/10 07:20:04 hercules Exp $";
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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 */

/* [<][>][^][v][top][bottom][index][help] */