/ src / libnml / os_intf / _sem.c
_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  }