will come in handy for the glibc stuff - and is great for our libroot.so, too. Everything should work except for: pthread_mutexattr_(set|get)prioceiling/ protocol(). That might be added at a later time, though, I don't consider it a high priority. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3082 a95241bf-73f2-0310-859d-f6bbb57e9c96
239 lines
4.5 KiB
C
239 lines
4.5 KiB
C
#include <pthread.h>
|
|
#include "pthread_private.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
|
|
static const pthread_mutexattr pthread_mutexattr_default = {
|
|
PTHREAD_MUTEX_DEFAULT,
|
|
false
|
|
};
|
|
|
|
|
|
pthread_mutex_t
|
|
_pthread_recursive_mutex_static_initializer(void)
|
|
{
|
|
pthread_mutex_t mutex;
|
|
pthread_mutexattr attr;
|
|
pthread_mutexattr_t attrPointer = &attr;
|
|
|
|
attr.type = PTHREAD_MUTEX_RECURSIVE;
|
|
attr.process_shared = false;
|
|
|
|
if (pthread_mutex_init(&mutex, &attrPointer) == B_OK)
|
|
return mutex;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
pthread_mutex_t
|
|
_pthread_mutex_static_initializer(void)
|
|
{
|
|
pthread_mutex_t mutex;
|
|
if (pthread_mutex_init(&mutex, NULL) == B_OK)
|
|
return mutex;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int
|
|
pthread_mutex_init(pthread_mutex_t *_mutex, const pthread_mutexattr_t *_attr)
|
|
{
|
|
pthread_mutex *mutex;
|
|
const pthread_mutexattr *attr = NULL;
|
|
|
|
if (_mutex == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
mutex = (pthread_mutex *)malloc(sizeof(pthread_mutex));
|
|
if (mutex == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
if (_attr != NULL)
|
|
attr = *_attr;
|
|
else
|
|
attr = &pthread_mutexattr_default;
|
|
|
|
mutex->sem = create_sem(attr && attr->process_shared ? 0 : 1, "pthread_mutex");
|
|
if (mutex->sem < B_OK) {
|
|
free(mutex);
|
|
return B_WOULD_BLOCK;
|
|
// stupid error code (EAGAIN) but demanded by POSIX
|
|
}
|
|
|
|
mutex->count = 0;
|
|
mutex->owner = -1;
|
|
mutex->owner_count = 0;
|
|
memcpy(&mutex->attr, attr, sizeof(pthread_mutexattr));
|
|
|
|
*_mutex = mutex;
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
int
|
|
pthread_mutex_destroy(pthread_mutex_t *_mutex)
|
|
{
|
|
pthread_mutex *mutex;
|
|
|
|
if (_mutex == NULL || (mutex = *_mutex) == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
delete_sem(mutex->sem);
|
|
*_mutex = NULL;
|
|
free(mutex);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
static status_t
|
|
mutex_unlock(pthread_mutex *mutex)
|
|
{
|
|
if (mutex == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
if (mutex->owner != find_thread(NULL)) {
|
|
// this is a bug in the calling application!
|
|
// ToDo: should we handle it in another way?
|
|
fprintf(stderr, "mutex unlocked from foreign thread!\n");
|
|
}
|
|
|
|
if (mutex->attr.type == PTHREAD_MUTEX_RECURSIVE) {
|
|
|
|
if (mutex->owner_count-- > 1)
|
|
return B_OK;
|
|
|
|
mutex->owner = -1;
|
|
}
|
|
|
|
if (!mutex->attr.process_shared || atomic_add(&mutex->count, -1) > 1)
|
|
release_sem(mutex->sem);
|
|
}
|
|
|
|
|
|
static status_t
|
|
mutex_lock(pthread_mutex *mutex, bigtime_t timeout)
|
|
{
|
|
thread_id thisThread = find_thread(NULL);
|
|
status_t status = B_OK;
|
|
|
|
if (mutex == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
if (mutex->attr.type == PTHREAD_MUTEX_ERRORCHECK && mutex->owner == thisThread) {
|
|
// we detect this kind of deadlock and return an error
|
|
return B_BUSY;
|
|
}
|
|
|
|
if (mutex->attr.type == PTHREAD_MUTEX_RECURSIVE && mutex->owner == thisThread) {
|
|
// if we already hold the mutex, we don't need to grab it again
|
|
mutex->owner_count++;
|
|
return B_OK;
|
|
}
|
|
|
|
if (!mutex->attr.process_shared || atomic_add(&mutex->count, 1) > 0) {
|
|
// this mutex is already locked by someone else, so we need
|
|
// to wait
|
|
status = acquire_sem_etc(mutex->sem, 1, timeout == B_INFINITE_TIMEOUT ? 0 : B_ABSOLUTE_TIMEOUT, timeout);
|
|
}
|
|
|
|
if (status == B_OK) {
|
|
// we have locked the mutex for the first time
|
|
mutex->owner = thisThread;
|
|
mutex->owner_count = 1;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
int
|
|
pthread_mutex_lock(pthread_mutex_t *_mutex)
|
|
{
|
|
if (_mutex == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
return mutex_lock(*_mutex, B_INFINITE_TIMEOUT);
|
|
}
|
|
|
|
|
|
int
|
|
pthread_mutex_trylock(pthread_mutex_t *_mutex)
|
|
{
|
|
if (_mutex == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
return mutex_lock(*_mutex, 0);
|
|
}
|
|
|
|
|
|
int
|
|
pthread_mutex_timedlock(pthread_mutex_t *_mutex, const struct timespec *tv)
|
|
{
|
|
bool invalidTime = false;
|
|
status_t status;
|
|
|
|
bigtime_t timeout = 0;
|
|
if (tv && tv->tv_nsec < 1000*1000*1000 && tv->tv_nsec >= 0)
|
|
timeout = tv->tv_sec * 1000000LL + tv->tv_nsec / 1000LL;
|
|
else
|
|
invalidTime = true;
|
|
|
|
if (_mutex == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
status = mutex_lock(*_mutex, timeout);
|
|
if (status != B_OK && invalidTime) {
|
|
// POSIX requires EINVAL (= B_BAD_VALUE) to be returned
|
|
// if the timespec structure was invalid
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
int
|
|
pthread_mutex_unlock(pthread_mutex_t *_mutex)
|
|
{
|
|
if (_mutex == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
return mutex_unlock(*_mutex);
|
|
}
|
|
|
|
|
|
int
|
|
pthread_mutex_getprioceiling(pthread_mutex_t *_mutex, int *_prioCeiling)
|
|
{
|
|
pthread_mutex *mutex;
|
|
|
|
if (_mutex == NULL || (mutex = *_mutex) == NULL || _prioCeiling == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
*_prioCeiling = 0;
|
|
// not implemented
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
int
|
|
pthread_mutex_setprioceiling(pthread_mutex_t *_mutex, int prioCeiling, int *_oldCeiling)
|
|
{
|
|
pthread_mutex *mutex;
|
|
|
|
if (_mutex == NULL || (mutex = *_mutex) == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
// not implemented
|
|
return B_NOT_ALLOWED;
|
|
}
|
|
|
|
|