_sem.c
1 /******************************************************************** 2 * Description: _sem.c 3 * 4 * Derived from a work by Fred Proctor & Will Shackleford 5 * 6 * Author: 7 * License: LGPL Version 2 8 * System: Linux 9 * 10 * Copyright (c) 2004 All rights reserved. 11 * 12 * Last change: 13 ********************************************************************/ 14 15 #include "config.h" 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <errno.h> /* errno */ 19 #include <string.h> /* strerror() */ 20 #include <stdarg.h> /* va_list, va_arg(), va_start(), va_end() */ 21 #include <sys/types.h> 22 #include <sys/ipc.h> /* IPC_CREATE, IPC_NOWAIT */ 23 #include <inttypes.h> 24 25 /* There are two types of posix semaphores named and unnamed. 26 unamed semaphores can either have the pshared flag set or not 27 determining whether it can be shared between processes. 28 Currently (12/27/02), Linux implements only unnamed posix semaphores 29 that are not shared between processes. This is useless to RCSLIB so 30 on Linux System V semaphores will be used instead. 31 */ 32 33 #include <sys/sem.h> /* struct sembuf */ 34 #include <math.h> /* fmod() */ 35 #include <signal.h> 36 #include <sys/time.h> 37 38 typedef int rcs_sem_t; 39 #define rcs_sem_t_defined 40 41 #include "_sem.h" 42 #include "_timer.h" /* etime() */ 43 #include "rcs_print.hh" 44 45 #define SEM_TAKE (-1) /* decrement sembuf.sem_op */ 46 #define SEM_GIVE (1) /* increment sembuf.sem_op */ 47 48 #ifdef _SEM_SEMUN_UNDEFINED 49 /* The user should define a union like the following to use it for arguments 50 for `semctl'. Previous versions of bits/sem.h used to define this union 51 but this is incorrect. One can test the macro _SEM_SEMUN_UNDEFINED to 52 see whether one must define the union or not. */ 53 54 union semun { 55 int val; /* value for SETVAL */ 56 struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ 57 unsigned short int *array; /* array for GETALL, SETALL */ 58 struct seminfo *__buf; /* buffer for IPC_INFO */ 59 }; 60 61 #endif 62 63 /* remove semaphore from OS-- this must be done *before* sem_close, 64 since rcs_sem_close frees the storage allocated for the rcs_sem_t */ 65 int rcs_sem_destroy(rcs_sem_t * sem) 66 { 67 /* remove OS semaphore */ 68 if (semctl(*sem, 0, IPC_RMID, 0) == -1) { 69 rcs_print_error("semctl(%d,0,%d) failed: (errno = %d) %s\n", 70 *sem, IPC_RMID, errno, strerror(errno)); 71 return -1; 72 } 73 return 0; 74 } 75 76 int sem_clear_bus_errors = 0; 77 void sem_clear_bus_error_handler(int sig) 78 { 79 sem_clear_bus_errors++; 80 } 81 82 int rcs_sem_clear(rcs_sem_t * sem) 83 { 84 union semun sem_arg; 85 sem_arg.val = 1; 86 semctl(*sem, 1, SETVAL, sem_arg); 87 return (0); 88 } 89 90 static int rcs_sem_open_val = 0; 91 92 /* create a named binary semaphore */ 93 rcs_sem_t *rcs_sem_open(key_t name, int oflag, /* int mode */ ...) 94 { 95 va_list ap; 96 int mode; /* optional last arg */ 97 key_t key; /* name converted to a key */ 98 rcs_sem_t semid, *retval; /* semaphore id returned */ 99 int semflg = 0; /* flag for perms, create, etc. */ 100 101 /* if IPC_CREAT is specified for creating the sempahore, then the 102 optional arg is the mode */ 103 if (oflag & IPC_CREAT) { 104 va_start(ap, oflag); 105 mode = va_arg(ap, int); 106 va_end(ap); 107 semflg |= mode; 108 semflg |= IPC_CREAT; 109 } else { 110 semflg &= ~IPC_CREAT; 111 } 112 113 key = name; 114 115 if (key < 1) { 116 rcs_print_error("rcs_sem_open: invalid key %jd\n", (intmax_t)key); 117 return NULL; 118 } 119 120 if ((semid = (rcs_sem_t) semget((key_t) key, 1, semflg)) == -1) { 121 rcs_print_error("semget"); 122 rcs_puts((char *) strerror(errno)); 123 return NULL; 124 } 125 126 /* we have a valid semid-- semantics say we return a pointer to the id, 127 so we need to allocate space that users will free later with 128 rcs_sem_close */ 129 retval = (rcs_sem_t *) malloc(sizeof(rcs_sem_t)); 130 *retval = semid; 131 return retval; 132 } 133 134 int rcs_sem_close(rcs_sem_t * sem) 135 { 136 if (sem != 0) { 137 free(sem); 138 } 139 return 0; 140 } 141 142 int rcs_sem_wait_notimeout(rcs_sem_t * sem) 143 { 144 int retval = -1; 145 struct sembuf sops; 146 sops.sem_num = 0; 147 sops.sem_op = SEM_TAKE; 148 sops.sem_flg = 0; 149 retval = semop(*sem, &sops, 1); 150 if (errno == EINTR) { 151 rcs_print_debug(PRINT_SEMAPHORE_ACTIVITY, "%s %d semop interrupted\n", 152 __FILE__, __LINE__); 153 return retval; 154 } 155 156 if (retval == -1) { 157 rcs_print_error 158 ("semop(semid=%d, {sem_num=%d,sem_op=%d,sem_flg=%d},nsops=1): ERROR: %s %d\n", 159 *sem, sops.sem_num, sops.sem_op, sops.sem_flg, strerror(errno), 160 errno); 161 } 162 163 return retval; 164 } 165 166 int rcs_sem_trywait(rcs_sem_t * sem) 167 { 168 struct sembuf sops; 169 sops.sem_num = 0; 170 sops.sem_op = SEM_TAKE; 171 sops.sem_flg = IPC_NOWAIT; 172 return semop(*sem, &sops, 1); 173 } 174 175 #ifndef _GNU_SOURCE 176 #error Must compile with -D_GNU_SOURCE else impliment your own semtimedop() \ 177 function. 178 #endif 179 180 #if !defined (HAVE_SEMTIMEDOP) 181 #undef HAVE_SEMTIMEDOP 182 void itimer_handler(int signum) 183 { 184 #ifdef DEBUG 185 rcs_print_error(stderr, "semop timed out\n"); 186 #endif 187 } 188 #endif 189 190 int rcs_sem_wait(rcs_sem_t * sem, double timeout) 191 { 192 #ifdef HAVE_SEMTIMEDOP 193 struct timespec time = {1,0}; 194 #else 195 struct sigaction sa; 196 struct itimerval time; 197 #endif 198 #if DEBUG 199 double start_time = etime(); 200 double end_time = 0; 201 int error = 0; 202 #endif 203 struct sembuf sops; 204 int retval = -1; 205 206 sops.sem_num = 0; 207 sops.sem_op = SEM_TAKE; 208 sops.sem_flg = 0; 209 210 if (0 == sem) { 211 return -1; 212 } 213 214 #ifdef HAVE_SEMTIMEDOP 215 if (timeout > 0 ) { 216 time.tv_sec = (long int) timeout; 217 time.tv_nsec = (long int) ((timeout - time.tv_sec) * 1e9); 218 } 219 retval = semtimedop(*sem, &sops, 1, &time); 220 #else 221 /* semtimedop was introduced with 2.4.22 kernels, prior to that, we need 222 to mess around with timers & signals.. */ 223 if (timeout > 0 ) { 224 memset(&sa, 0, sizeof(sa)); 225 sa.sa_handler = &itimer_handler; 226 sa.sa_flags = SA_RESETHAND; 227 /* itimers are limited to three per process, better hope the limit 228 is not exceeded. If it is, chances are, the HMI will exhibit 229 random lockups */ 230 time.it_value.tv_sec = (long int) timeout; 231 time.it_interval.tv_sec = 0; 232 time.it_interval.tv_usec = 0; 233 time.it_value.tv_usec = (long int) ((timeout - time.it_value.tv_sec) * 1e6); 234 sigaction(SIGALRM, &sa, NULL); 235 setitimer(ITIMER_REAL, &time, NULL); 236 } 237 retval = semop(*sem, &sops, 1); 238 #endif 239 240 #if DEBUG 241 error = errno; 242 end_time = etime(); 243 if (retval < 0) { 244 perror("_sem.c: rcs_sem_wait()"); 245 printf("rcs_sem_wait(%d,%f) returned %d (errno %d) after %f\n", *sem, timeout, retval, error, end_time - start_time); 246 } 247 #endif 248 return retval; 249 250 } 251 252 int rcs_sem_post(rcs_sem_t * sem) 253 { 254 struct sembuf sops; 255 union semun sem_arg; 256 sem_arg.val = 0; 257 258 rcs_print_debug(PRINT_SEMAPHORE_ACTIVITY, "rcs_sem_post(%d) called.\n", 259 *sem); 260 261 sops.sem_num = 0; /* only one semaphore in set */ 262 sops.sem_flg = 0; /* wait indefinitely */ 263 sops.sem_op = SEM_GIVE; 264 265 if (semctl(*sem, 0, GETVAL, sem_arg) == 1) { 266 /* it's given-- leave it alone */ 267 return 0; 268 } 269 270 /* it's taken-- suppose now others take it again before we give it? they 271 block, and this semgive will release one of them */ 272 while (1) { 273 if (semop(*sem, &sops, 1) == -1) { 274 if (errno == EINTR) { 275 /* interrupted system call-- restart it */ 276 rcs_print_error("semop:"); 277 rcs_print_error("errno=%d : %s\n", errno, strerror(errno)); 278 rcs_puts("restarting"); 279 continue; 280 } else { 281 rcs_print_error("semop"); 282 rcs_print_error("errno=%d : %s\n", errno, strerror(errno)); 283 return -1; 284 } 285 } else { 286 return 0; 287 } 288 } 289 return (0); 290 } 291 292 int rcs_sem_flush(rcs_sem_t * sem) 293 { 294 int semval; 295 int sems_to_give; 296 int ncount = -1; 297 struct sembuf sops; 298 union semun sem_arg; 299 sem_arg.val = 0; 300 301 sops.sem_num = 0; /* only one semaphore in set */ 302 sops.sem_flg = IPC_NOWAIT; /* wait indefinitely */ 303 sops.sem_op = SEM_GIVE; 304 semval = semctl(*sem, 0, GETVAL, sem_arg); 305 ncount = semctl(*sem, 0, GETNCNT, sem_arg); 306 307 /* Neither ncount nor semval should ever be less than zero any way, so 308 this is just paranoid */ 309 if (semval < 0) { 310 semval = 0; 311 } 312 if (ncount < 0) { 313 ncount = 0; 314 } 315 if (semval > ncount) { 316 return 0; 317 } 318 319 sems_to_give = ncount - semval + 1; 320 321 /* it's taken-- suppose now others take it again before we give it? they 322 block, and this semgive will release one of them until semval = 1; */ 323 sops.sem_op = sems_to_give; 324 while (sems_to_give > 0) { 325 if (semop(*sem, &sops, 1) == -1) { 326 if (errno == EINTR) { 327 /* interrupted system call-- restart it */ 328 rcs_print_error("semop:"); 329 rcs_print_error("errno=%d : %s\n", errno, strerror(errno)); 330 rcs_puts("restarting"); 331 continue; 332 } else { 333 rcs_print_error("semop"); 334 rcs_print_error("errno=%d : %s\n", errno, strerror(errno)); 335 return -1; 336 } 337 } 338 sems_to_give -= sops.sem_op; 339 } 340 return (0); 341 } 342 343 rcs_sem_t *rcs_sem_create(key_t id, int mode, int state) 344 { 345 union semun sem_arg; 346 rcs_sem_t *sem; 347 348 if (id < 1) { 349 rcs_print_error("rcs_sem_create: invalid id %lu\n", (unsigned long)id); 350 return NULL; 351 } 352 353 rcs_sem_open_val = state; 354 355 sem = rcs_sem_open(id, IPC_CREAT, mode); 356 357 if (NULL == sem) { 358 rcs_print_error("sem_init: Pointer to semaphore object is NULL.\n"); 359 return NULL; 360 } 361 sem_arg.val = state; 362 semctl(*sem, 0, SETVAL, sem_arg); 363 return sem; 364 }