/* * Copyright (C) 2007 Pingtel Corp., certain elements licensed under a Contributor Agreement. * Contributors retain copyright to elements licensed under a Contributor Agreement. * Licensed to the User under the LGPL license. * * Modified by: Angus Salkeld * Copyright (C) 2012 Red Hat, Inc. * To conform to posix API and support process shared semaphores. * * The bsd posix semaphore implementation does not have support for timing * out while waiting for a synchronization object. This uses the * pthread_cond_timedwait function and a mutex to build all the other * synchronization objecs with timeout capabilities. */ #include "os_base.h" #include #include "rpl_sem.h" #include #include int rpl_sem_init(rpl_sem_t * sem, int pshared, unsigned int count) { int rc; pthread_mutexattr_t mattr; pthread_condattr_t cattr; #ifndef HAVE_RPL_PSHARED_SEMAPHORE if (pshared) { errno = ENOSYS; return -1; } #endif /* HAVE_RPL_PSHARED_SEMAPHORE */ sem->count = count; sem->destroy_request = QB_FALSE; (void)pthread_mutexattr_init(&mattr); (void)pthread_condattr_init(&cattr); #ifdef HAVE_RPL_PSHARED_SEMAPHORE if (pshared) { rc = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED); if (rc != 0) { goto cleanup; } rc = pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED); if (rc != 0) { goto cleanup; } } #endif /* HAVE_RPL_PSHARED_SEMAPHORE */ rc = pthread_mutex_init(&sem->mutex, &mattr); if (rc != 0) { goto cleanup; } rc = pthread_cond_init(&sem->cond, &cattr); if (rc != 0) { goto cleanup_mutex; } return 0; cleanup_mutex: pthread_mutex_destroy(&sem->mutex); cleanup: pthread_mutexattr_destroy(&mattr); pthread_condattr_destroy(&cattr); return rc; } static int _rpl_sem_timedwait(rpl_sem_t * sem, const struct timespec *timeout) { int retval = pthread_mutex_lock(&sem->mutex); if (retval != 0) { return -errno; } if (sem->destroy_request) { retval = -EINVAL; goto unlock_it; } /* wait for sem->count to be not zero, or error */ while (0 == retval && !sem->count) { retval = -pthread_cond_timedwait(&sem->cond, &sem->mutex, timeout); } if (sem->destroy_request) { retval = -EINVAL; goto unlock_it; } switch (retval) { case 0: /* retval is 0 and sem->count is not, the sem is ours */ sem->count--; break; case ETIMEDOUT: /* timedout waiting for count to be not zero */ retval = -EAGAIN; break; default: break; } unlock_it: pthread_mutex_unlock(&sem->mutex); return retval; } int rpl_sem_wait(rpl_sem_t * sem) { struct timespec ts_timeout; int32_t rc; do { qb_util_timespec_from_epoch_get(&ts_timeout); qb_timespec_add_ms(&ts_timeout, 1000); rc = _rpl_sem_timedwait(sem, &ts_timeout); } while (rc == -EAGAIN); if (rc < 0) { errno = -rc; return -1; } return 0; } int rpl_sem_timedwait(rpl_sem_t * sem, const struct timespec *timeout) { int rc = _rpl_sem_timedwait(sem, timeout); if (rc < 0) { errno = -rc; return -1; } return 0; } int rpl_sem_trywait(rpl_sem_t * sem) { int retval = pthread_mutex_lock(&sem->mutex); if (retval != 0) { errno = retval; return -1; } if (sem->count) { sem->count--; pthread_mutex_unlock(&sem->mutex); return 0; } errno = EAGAIN; pthread_mutex_unlock(&sem->mutex); return -1; } int rpl_sem_post(rpl_sem_t * sem) { int retval = pthread_mutex_lock(&sem->mutex); if (retval != 0) { errno = retval; return -1; } sem->count++; retval = pthread_cond_broadcast(&sem->cond); pthread_mutex_unlock(&sem->mutex); if (retval != 0) { errno = retval; return -1; } return 0; } int rpl_sem_getvalue(rpl_sem_t * sem, int *sval) { int retval = pthread_mutex_lock(&sem->mutex); if (retval != 0) { errno = retval; return -1; } *sval = sem->count; pthread_mutex_unlock(&sem->mutex); return 0; } int rpl_sem_destroy(rpl_sem_t * sem) { int retval = pthread_mutex_lock(&sem->mutex); if (retval != 0) { errno = retval; return -1; } sem->destroy_request = QB_TRUE; pthread_mutex_unlock(&sem->mutex); (void)pthread_cond_broadcast(&sem->cond); (void)pthread_cond_destroy(&sem->cond); (void)pthread_mutex_destroy(&sem->mutex); return 0; }