blob: 7daeb4f2df4714c9f0838bbccaec63286a741e7b [file] [log] [blame]
/*
* 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 <asalkeld@redhat.com>
* 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 <pthread.h>
#include "rpl_sem.h"
#include <qb/qbdefs.h>
#include <qb/qbutil.h>
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;
}