1 /* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2012 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 (at) libsdl.org 21 */ 22 #include "SDL_config.h" 23 24 #include "SDL_thread.h" 25 #include "SDL_timer.h" 26 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <sys/types.h> 31 #include <sys/ipc.h> 32 #include <sys/sem.h> 33 #include <errno.h> 34 35 #include "SDL_error.h" 36 #include "SDL_thread.h" 37 38 39 struct SDL_semaphore { 40 int id; 41 }; 42 43 /* Not defined by many operating systems, use configure to detect */ 44 /* 45 #if !defined(HAVE_SEMUN) 46 union semun { 47 int val; 48 struct semid_ds *buf; 49 ushort *array; 50 }; 51 #endif 52 */ 53 54 static struct sembuf op_trywait[2] = { 55 { 0, -1, (IPC_NOWAIT|SEM_UNDO) } /* Decrement semaphore, no block */ 56 }; 57 static struct sembuf op_wait[2] = { 58 { 0, -1, SEM_UNDO } /* Decrement semaphore */ 59 }; 60 static struct sembuf op_post[1] = { 61 { 0, 1, (IPC_NOWAIT|SEM_UNDO) } /* Increment semaphore */ 62 }; 63 64 /* Create a blockable semaphore */ 65 SDL_sem *SDL_CreateSemaphore(Uint32 initial_value) 66 { 67 extern int _creating_thread_lock; /* SDL_threads.c */ 68 SDL_sem *sem; 69 union semun init; 70 71 sem = (SDL_sem *)SDL_malloc(sizeof(*sem)); 72 if ( sem == NULL ) { 73 SDL_OutOfMemory(); 74 return(NULL); 75 } 76 sem->id = semget(IPC_PRIVATE, 1, (0600|IPC_CREAT)); 77 if ( sem->id < 0 ) { 78 SDL_SetError("Couldn't create semaphore"); 79 SDL_free(sem); 80 return(NULL); 81 } 82 init.val = initial_value; /* Initialize semaphore */ 83 semctl(sem->id, 0, SETVAL, init); 84 return(sem); 85 } 86 87 void SDL_DestroySemaphore(SDL_sem *sem) 88 { 89 if ( sem ) { 90 #ifdef __IRIX__ 91 semctl(sem->id, 0, IPC_RMID); 92 #else 93 union semun dummy; 94 dummy.val = 0; 95 semctl(sem->id, 0, IPC_RMID, dummy); 96 #endif 97 SDL_free(sem); 98 } 99 } 100 101 int SDL_SemTryWait(SDL_sem *sem) 102 { 103 int retval; 104 105 if ( ! sem ) { 106 SDL_SetError("Passed a NULL semaphore"); 107 return -1; 108 } 109 110 retval = 0; 111 tryagain: 112 if ( semop(sem->id, op_trywait, 1) < 0 ) { 113 if ( errno == EINTR ) { 114 goto tryagain; 115 } 116 retval = SDL_MUTEX_TIMEDOUT; 117 } 118 return retval; 119 } 120 121 int SDL_SemWait(SDL_sem *sem) 122 { 123 int retval; 124 125 if ( ! sem ) { 126 SDL_SetError("Passed a NULL semaphore"); 127 return -1; 128 } 129 130 retval = 0; 131 tryagain: 132 if ( semop(sem->id, op_wait, 1) < 0 ) { 133 if ( errno == EINTR ) { 134 goto tryagain; 135 } 136 SDL_SetError("Semaphore operation error"); 137 retval = -1; 138 } 139 return retval; 140 } 141 142 int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout) 143 { 144 int retval; 145 146 if ( ! sem ) { 147 SDL_SetError("Passed a NULL semaphore"); 148 return -1; 149 } 150 151 /* Try the easy cases first */ 152 if ( timeout == 0 ) { 153 return SDL_SemTryWait(sem); 154 } 155 if ( timeout == SDL_MUTEX_MAXWAIT ) { 156 return SDL_SemWait(sem); 157 } 158 159 /* Ack! We have to busy wait... */ 160 timeout += SDL_GetTicks(); 161 do { 162 retval = SDL_SemTryWait(sem); 163 if ( retval == 0 ) { 164 break; 165 } 166 SDL_Delay(1); 167 } while ( SDL_GetTicks() < timeout ); 168 169 return retval; 170 } 171 172 Uint32 SDL_SemValue(SDL_sem *sem) 173 { 174 int semval; 175 Uint32 value; 176 177 value = 0; 178 if ( sem ) { 179 tryagain: 180 #ifdef __IRIX__ 181 semval = semctl(sem->id, 0, GETVAL); 182 #else 183 { 184 union semun arg; 185 arg.val = 0; 186 semval = semctl(sem->id, 0, GETVAL, arg); 187 } 188 #endif 189 if ( semval < 0 ) { 190 if ( errno == EINTR ) { 191 goto tryagain; 192 } 193 } else { 194 value = (Uint32)semval; 195 } 196 } 197 return value; 198 } 199 200 int SDL_SemPost(SDL_sem *sem) 201 { 202 int retval; 203 204 if ( ! sem ) { 205 SDL_SetError("Passed a NULL semaphore"); 206 return -1; 207 } 208 209 retval = 0; 210 tryagain: 211 if ( semop(sem->id, op_post, 1) < 0 ) { 212 if ( errno == EINTR ) { 213 goto tryagain; 214 } 215 SDL_SetError("Semaphore operation error"); 216 retval = -1; 217 } 218 return retval; 219 } 220