Files
haiku-beta6/src/kernel/libroot/posix/pthread/pthread_mutex.c
T
Axel Dörfler 74b016ecad Added a basic pthread_mutex and pthread_mutexattr implementation which
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
2003-04-20 18:48:54 +00:00

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;
}