#include "namespace.h"
#include <machine/tls.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <pthread.h>
#include "un-namespace.h"
#include "thr_private.h"
#ifdef _PTHREADS_DEBUGGING
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <sys/file.h>
#endif
#define MAX_READ_LOCKS (INT_MAX - 1)
umtx_t _rwlock_static_lock;
#ifdef _PTHREADS_DEBUGGING
static
void
rwlock_log(const char *ctl, ...)
{
char buf[256];
va_list va;
size_t len;
va_start(va, ctl);
len = vsnprintf(buf, sizeof(buf), ctl, va);
va_end(va);
_thr_log(buf, len);
}
#else
static __inline
void
rwlock_log(const char *ctl __unused, ...)
{
}
#endif
static int
rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr __unused)
{
pthread_rwlock_t prwlock;
int ret;
prwlock = __malloc(sizeof(struct __pthread_rwlock_s));
if (prwlock == NULL)
return (ENOMEM);
if ((ret = _pthread_mutex_init(&prwlock->lock, NULL)) != 0) {
__free(prwlock);
} else {
ret = _pthread_cond_init(&prwlock->read_signal, NULL);
if (ret != 0) {
_pthread_mutex_destroy(&prwlock->lock);
__free(prwlock);
} else {
ret = _pthread_cond_init(&prwlock->write_signal, NULL);
if (ret != 0) {
_pthread_cond_destroy(&prwlock->read_signal);
_pthread_mutex_destroy(&prwlock->lock);
__free(prwlock);
} else {
prwlock->state = 0;
prwlock->blocked_writers = 0;
*rwlock = prwlock;
}
}
}
return (ret);
}
#if 0
void
_rwlock_reinit(pthread_rwlock_t prwlock)
{
_mutex_reinit(&prwlock->lock);
_cond_reinit(prwlock->read_signal);
prwlock->state = 0;
prwlock->blocked_writers = 0;
}
#endif
int
_pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
{
int ret;
if (rwlock == NULL) {
ret = EINVAL;
} else if (*rwlock == NULL) {
ret = 0;
} else {
pthread_rwlock_t prwlock;
prwlock = *rwlock;
rwlock_log("rwlock_destroy %p\n", prwlock);
_pthread_mutex_destroy(&prwlock->lock);
_pthread_cond_destroy(&prwlock->read_signal);
_pthread_cond_destroy(&prwlock->write_signal);
__free(prwlock);
*rwlock = NULL;
ret = 0;
}
return (ret);
}
static int
init_static(pthread_t thread, pthread_rwlock_t *rwlock)
{
int ret;
THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock);
if (*rwlock == NULL)
ret = rwlock_init(rwlock, NULL);
else
ret = 0;
THR_LOCK_RELEASE(thread, &_rwlock_static_lock);
return (ret);
}
int
_pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
{
*rwlock = NULL;
return (rwlock_init(rwlock, attr));
}
static int
rwlock_rdlock_common(pthread_rwlock_t *rwlock, const struct timespec *abstime)
{
pthread_t curthread = tls_get_curthread();
pthread_rwlock_t prwlock;
int ret;
if (rwlock == NULL)
return (EINVAL);
prwlock = *rwlock;
if (prwlock == NULL) {
if ((ret = init_static(curthread, rwlock)) != 0)
return (ret);
prwlock = *rwlock;
}
rwlock_log("rwlock_rdlock_common %p\n", prwlock);
if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) {
rwlock_log("rwlock_rdlock_common %p (failedA)\n", prwlock);
return (ret);
}
if (prwlock->state == MAX_READ_LOCKS) {
_pthread_mutex_unlock(&prwlock->lock);
rwlock_log("rwlock_rdlock_common %p (failedB)\n", prwlock);
return (EAGAIN);
}
curthread = tls_get_curthread();
if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
;
} else {
while (prwlock->blocked_writers || prwlock->state < 0) {
if (abstime) {
ret = _pthread_cond_timedwait(
&prwlock->read_signal,
&prwlock->lock, abstime);
} else {
ret = _pthread_cond_wait(
&prwlock->read_signal,
&prwlock->lock);
}
if (ret != 0) {
_pthread_mutex_unlock(&prwlock->lock);
rwlock_log("rwlock_rdlock_common %p "
"(failedC)\n", prwlock);
return (ret);
}
}
}
curthread->rdlock_count++;
prwlock->state++;
_pthread_mutex_unlock(&prwlock->lock);
rwlock_log("rwlock_rdlock_common %p (return %d)\n", prwlock, ret);
return (ret);
}
int
_pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
{
return (rwlock_rdlock_common(rwlock, NULL));
}
int
_pthread_rwlock_timedrdlock (pthread_rwlock_t * __restrict rwlock,
const struct timespec * __restrict abstime)
{
return (rwlock_rdlock_common(rwlock, abstime));
}
int
_pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
{
pthread_t curthread = tls_get_curthread();
pthread_rwlock_t prwlock;
int ret;
if (rwlock == NULL)
return (EINVAL);
prwlock = *rwlock;
if (prwlock == NULL) {
if ((ret = init_static(curthread, rwlock)) != 0)
return (ret);
prwlock = *rwlock;
}
if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
return (ret);
curthread = tls_get_curthread();
if (prwlock->state == MAX_READ_LOCKS)
ret = EAGAIN;
else if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
curthread->rdlock_count++;
prwlock->state++;
}
else if (prwlock->blocked_writers || prwlock->state < 0)
ret = EBUSY;
else {
curthread->rdlock_count++;
prwlock->state++;
}
_pthread_mutex_unlock(&prwlock->lock);
return (ret);
}
int
_pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
{
pthread_t curthread = tls_get_curthread();
pthread_rwlock_t prwlock;
int ret;
if (rwlock == NULL)
return (EINVAL);
prwlock = *rwlock;
if (prwlock == NULL) {
if ((ret = init_static(curthread, rwlock)) != 0)
return (ret);
prwlock = *rwlock;
}
if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
return (ret);
if (prwlock->state != 0)
ret = EBUSY;
else
prwlock->state = -1;
_pthread_mutex_unlock(&prwlock->lock);
return (ret);
}
int
_pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
{
pthread_t curthread;
pthread_rwlock_t prwlock;
int ret;
if (rwlock == NULL)
return (EINVAL);
prwlock = *rwlock;
if (prwlock == NULL)
return (EINVAL);
rwlock_log("rwlock_unlock %p\n", prwlock);
if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
return (ret);
curthread = tls_get_curthread();
if (prwlock->state > 0) {
curthread->rdlock_count--;
prwlock->state--;
if (prwlock->state == 0 && prwlock->blocked_writers)
ret = _pthread_cond_signal(&prwlock->write_signal);
} else if (prwlock->state < 0) {
prwlock->state = 0;
if (prwlock->blocked_writers)
ret = _pthread_cond_signal(&prwlock->write_signal);
else
ret = _pthread_cond_broadcast(&prwlock->read_signal);
} else {
ret = EINVAL;
}
_pthread_mutex_unlock(&prwlock->lock);
rwlock_log("rwlock_unlock %p (return %d)\n", prwlock, ret);
return (ret);
}
static int
rwlock_wrlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime)
{
pthread_t curthread = tls_get_curthread();
pthread_rwlock_t prwlock;
int ret;
if (rwlock == NULL)
return (EINVAL);
prwlock = *rwlock;
if (prwlock == NULL) {
if ((ret = init_static(curthread, rwlock)) != 0)
return (ret);
prwlock = *rwlock;
}
rwlock_log("rwlock_wrlock_common %p\n", prwlock);
if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) {
rwlock_log("rwlock_wrlock_common %p (failedA)\n", prwlock);
return (ret);
}
while (prwlock->state != 0) {
prwlock->blocked_writers++;
if (abstime != NULL) {
ret = _pthread_cond_timedwait(&prwlock->write_signal,
&prwlock->lock,
abstime);
} else {
ret = _pthread_cond_wait(&prwlock->write_signal,
&prwlock->lock);
}
if (ret != 0) {
prwlock->blocked_writers--;
if (prwlock->blocked_writers == 0 &&
prwlock->state >= 0) {
_pthread_cond_broadcast(&prwlock->read_signal);
}
_pthread_mutex_unlock(&prwlock->lock);
rwlock_log("rwlock_wrlock_common %p (failedB %d)\n",
prwlock, ret);
return (ret);
}
prwlock->blocked_writers--;
}
prwlock->state = -1;
_pthread_mutex_unlock(&prwlock->lock);
rwlock_log("rwlock_wrlock_common %p (returns %d)\n", prwlock, ret);
return (ret);
}
int
_pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
{
return (rwlock_wrlock_common (rwlock, NULL));
}
int
_pthread_rwlock_timedwrlock (pthread_rwlock_t * __restrict rwlock,
const struct timespec * __restrict abstime)
{
return (rwlock_wrlock_common (rwlock, abstime));
}
__strong_reference(_pthread_rwlock_destroy, pthread_rwlock_destroy);
__strong_reference(_pthread_rwlock_init, pthread_rwlock_init);
__strong_reference(_pthread_rwlock_rdlock, pthread_rwlock_rdlock);
__strong_reference(_pthread_rwlock_timedrdlock, pthread_rwlock_timedrdlock);
__strong_reference(_pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock);
__strong_reference(_pthread_rwlock_trywrlock, pthread_rwlock_trywrlock);
__strong_reference(_pthread_rwlock_unlock, pthread_rwlock_unlock);
__strong_reference(_pthread_rwlock_wrlock, pthread_rwlock_wrlock);
__strong_reference(_pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock);