#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <inttypes.h>
#include <sys/sem.h>
#include <math.h>
#include <signal.h>
#include <sys/time.h>
typedef int rcs_sem_t;
#define rcs_sem_t_defined
#include "_sem.h"
#include "_timer.h"
#include "rcs_print.hh"
#define SEM_TAKE (-1)
#define SEM_GIVE (1)
#ifdef _SEM_SEMUN_UNDEFINED
union semun {
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
#endif
int rcs_sem_destroy(rcs_sem_t * sem)
{
if (semctl(*sem, 0, IPC_RMID, 0) == -1) {
rcs_print_error("semctl(%d,0,%d) failed: (errno = %d) %s\n",
*sem, IPC_RMID, errno, strerror(errno));
return -1;
}
return 0;
}
int sem_clear_bus_errors = 0;
void sem_clear_bus_error_handler(int sig)
{
sem_clear_bus_errors++;
}
int rcs_sem_clear(rcs_sem_t * sem)
{
union semun sem_arg;
sem_arg.val = 1;
semctl(*sem, 1, SETVAL, sem_arg);
return (0);
}
static int rcs_sem_open_val = 0;
rcs_sem_t *rcs_sem_open(key_t name, int oflag, ...)
{
va_list ap;
int mode;
key_t key;
rcs_sem_t semid, *retval;
int semflg = 0;
if (oflag & IPC_CREAT) {
va_start(ap, oflag);
mode = va_arg(ap, int);
va_end(ap);
semflg |= mode;
semflg |= IPC_CREAT;
} else {
semflg &= ~IPC_CREAT;
}
key = name;
if (key < 1) {
rcs_print_error("rcs_sem_open: invalid key %jd\n", (intmax_t)key);
return NULL;
}
if ((semid = (rcs_sem_t) semget((key_t) key, 1, semflg)) == -1) {
rcs_print_error("semget");
rcs_puts((char *) strerror(errno));
return NULL;
}
retval = (rcs_sem_t *) malloc(sizeof(rcs_sem_t));
*retval = semid;
return retval;
}
int rcs_sem_close(rcs_sem_t * sem)
{
if (sem != 0) {
free(sem);
}
return 0;
}
int rcs_sem_wait_notimeout(rcs_sem_t * sem)
{
int retval = -1;
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = SEM_TAKE;
sops.sem_flg = 0;
retval = semop(*sem, &sops, 1);
if (errno == EINTR) {
rcs_print_debug(PRINT_SEMAPHORE_ACTIVITY, "%s %d semop interrupted\n",
__FILE__, __LINE__);
return retval;
}
if (retval == -1) {
rcs_print_error
("semop(semid=%d, {sem_num=%d,sem_op=%d,sem_flg=%d},nsops=1): ERROR: %s %d\n",
*sem, sops.sem_num, sops.sem_op, sops.sem_flg, strerror(errno),
errno);
}
return retval;
}
int rcs_sem_trywait(rcs_sem_t * sem)
{
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = SEM_TAKE;
sops.sem_flg = IPC_NOWAIT;
return semop(*sem, &sops, 1);
}
#ifndef _GNU_SOURCE
#error Must compile with -D_GNU_SOURCE else impliment your own semtimedop() \
function.
#endif
#if !defined (HAVE_SEMTIMEDOP)
#undef HAVE_SEMTIMEDOP
void itimer_handler(int signum)
{
#ifdef DEBUG
rcs_print_error(stderr, "semop timed out\n");
#endif
}
#endif
int rcs_sem_wait(rcs_sem_t * sem, double timeout)
{
#ifdef HAVE_SEMTIMEDOP
struct timespec time = {1,0};
#else
struct sigaction sa;
struct itimerval time;
#endif
#if DEBUG
double start_time = etime();
double end_time = 0;
int error = 0;
#endif
struct sembuf sops;
int retval = -1;
sops.sem_num = 0;
sops.sem_op = SEM_TAKE;
sops.sem_flg = 0;
if (0 == sem) {
return -1;
}
#ifdef HAVE_SEMTIMEDOP
if (timeout > 0 ) {
time.tv_sec = (long int) timeout;
time.tv_nsec = (long int) ((timeout - time.tv_sec) * 1e9);
}
retval = semtimedop(*sem, &sops, 1, &time);
#else
if (timeout > 0 ) {
memset(&sa, 0, sizeof(sa));
sa.sa_handler = &itimer_handler;
sa.sa_flags = SA_RESETHAND;
time.it_value.tv_sec = (long int) timeout;
time.it_interval.tv_sec = 0;
time.it_interval.tv_usec = 0;
time.it_value.tv_usec = (long int) ((timeout - time.it_value.tv_sec) * 1e6);
sigaction(SIGALRM, &sa, NULL);
setitimer(ITIMER_REAL, &time, NULL);
}
retval = semop(*sem, &sops, 1);
#endif
#if DEBUG
error = errno;
end_time = etime();
if (retval < 0) {
perror("_sem.c: rcs_sem_wait()");
printf("rcs_sem_wait(%d,%f) returned %d (errno %d) after %f\n", *sem, timeout, retval, error, end_time - start_time);
}
#endif
return retval;
}
int rcs_sem_post(rcs_sem_t * sem)
{
struct sembuf sops;
union semun sem_arg;
sem_arg.val = 0;
rcs_print_debug(PRINT_SEMAPHORE_ACTIVITY, "rcs_sem_post(%d) called.\n",
*sem);
sops.sem_num = 0;
sops.sem_flg = 0;
sops.sem_op = SEM_GIVE;
if (semctl(*sem, 0, GETVAL, sem_arg) == 1) {
return 0;
}
while (1) {
if (semop(*sem, &sops, 1) == -1) {
if (errno == EINTR) {
rcs_print_error("semop:");
rcs_print_error("errno=%d : %s\n", errno, strerror(errno));
rcs_puts("restarting");
continue;
} else {
rcs_print_error("semop");
rcs_print_error("errno=%d : %s\n", errno, strerror(errno));
return -1;
}
} else {
return 0;
}
}
return (0);
}
int rcs_sem_flush(rcs_sem_t * sem)
{
int semval;
int sems_to_give;
int ncount = -1;
struct sembuf sops;
union semun sem_arg;
sem_arg.val = 0;
sops.sem_num = 0;
sops.sem_flg = IPC_NOWAIT;
sops.sem_op = SEM_GIVE;
semval = semctl(*sem, 0, GETVAL, sem_arg);
ncount = semctl(*sem, 0, GETNCNT, sem_arg);
if (semval < 0) {
semval = 0;
}
if (ncount < 0) {
ncount = 0;
}
if (semval > ncount) {
return 0;
}
sems_to_give = ncount - semval + 1;
sops.sem_op = sems_to_give;
while (sems_to_give > 0) {
if (semop(*sem, &sops, 1) == -1) {
if (errno == EINTR) {
rcs_print_error("semop:");
rcs_print_error("errno=%d : %s\n", errno, strerror(errno));
rcs_puts("restarting");
continue;
} else {
rcs_print_error("semop");
rcs_print_error("errno=%d : %s\n", errno, strerror(errno));
return -1;
}
}
sems_to_give -= sops.sem_op;
}
return (0);
}
rcs_sem_t *rcs_sem_create(key_t id, int mode, int state)
{
union semun sem_arg;
rcs_sem_t *sem;
if (id < 1) {
rcs_print_error("rcs_sem_create: invalid id %lu\n", (unsigned long)id);
return NULL;
}
rcs_sem_open_val = state;
sem = rcs_sem_open(id, IPC_CREAT, mode);
if (NULL == sem) {
rcs_print_error("sem_init: Pointer to semaphore object is NULL.\n");
return NULL;
}
sem_arg.val = state;
semctl(*sem, 0, SETVAL, sem_arg);
return sem;
}