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
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
SubDir OBOS_TOP src kernel libroot posix pthread ;
|
||||
|
||||
KernelMergeObject posix_pthread.o :
|
||||
<$(SOURCE_GRIST)>pthread_mutex.c
|
||||
<$(SOURCE_GRIST)>pthread_mutexattr.c
|
||||
:
|
||||
-fPIC -DPIC
|
||||
;
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
int
|
||||
pthread_mutexattr_init(pthread_mutexattr_t *_mutexAttr)
|
||||
{
|
||||
pthread_mutexattr *attr;
|
||||
|
||||
if (_mutexAttr == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
attr = (pthread_mutexattr *)malloc(sizeof(pthread_mutexattr));
|
||||
if (attr == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
attr->type = PTHREAD_MUTEX_DEFAULT;
|
||||
attr->process_shared = false;
|
||||
|
||||
*_mutexAttr = attr;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_mutexattr_destroy(pthread_mutexattr_t *_mutexAttr)
|
||||
{
|
||||
pthread_mutexattr *attr;
|
||||
|
||||
if (_mutexAttr == NULL || (attr = *_mutexAttr) == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
*_mutexAttr = NULL;
|
||||
free(attr);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_mutexattr_gettype(pthread_mutexattr_t *_mutexAttr, int *_type)
|
||||
{
|
||||
pthread_mutexattr *attr;
|
||||
|
||||
if (_mutexAttr == NULL || (attr = *_mutexAttr) == NULL || _type == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
*_type = attr->type;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_mutexattr_settype(pthread_mutexattr_t *_mutexAttr, int type)
|
||||
{
|
||||
pthread_mutexattr *attr;
|
||||
|
||||
if (_mutexAttr == NULL || (attr = *_mutexAttr) == NULL
|
||||
|| type < PTHREAD_MUTEX_DEFAULT
|
||||
|| type > PTHREAD_MUTEX_RECURSIVE)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
attr->type = type;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_mutexattr_getpshared(pthread_mutexattr_t *_mutexAttr, int *_processShared)
|
||||
{
|
||||
pthread_mutexattr *attr;
|
||||
|
||||
if (_mutexAttr == NULL || (attr = *_mutexAttr) == NULL || _processShared == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
*_processShared = attr->process_shared ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_mutexattr_setpshared(pthread_mutexattr_t *_mutexAttr, int processShared)
|
||||
{
|
||||
pthread_mutexattr *attr;
|
||||
|
||||
if (_mutexAttr == NULL || (attr = *_mutexAttr) == NULL
|
||||
|| processShared < PTHREAD_PROCESS_PRIVATE
|
||||
|| processShared > PTHREAD_PROCESS_SHARED)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
attr->process_shared = processShared == PTHREAD_PROCESS_SHARED ? true : false;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_mutexattr_getprioceiling(pthread_mutexattr_t *_mutexAttr, int *_priorityCeiling)
|
||||
{
|
||||
pthread_mutexattr *attr;
|
||||
|
||||
if (_mutexAttr == NULL || (attr = *_mutexAttr) == NULL || _priorityCeiling == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
*_priorityCeiling = 0;
|
||||
// not implemented
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_mutexattr_setprioceiling(pthread_mutexattr_t *_mutexAttr, int priorityCeiling)
|
||||
{
|
||||
pthread_mutexattr *attr;
|
||||
|
||||
if (_mutexAttr == NULL || (attr = *_mutexAttr) == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// not implemented
|
||||
return B_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_mutexattr_getprotocol(pthread_mutexattr_t *_mutexAttr, int *_protocol)
|
||||
{
|
||||
pthread_mutexattr *attr;
|
||||
|
||||
if (_mutexAttr == NULL || (attr = *_mutexAttr) == NULL || _protocol == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
*_protocol = 0;
|
||||
// not implemented
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pthread_mutexattr_setprotocol(pthread_mutexattr_t *_mutexAttr, int protocol)
|
||||
{
|
||||
pthread_mutexattr *attr;
|
||||
|
||||
if (_mutexAttr == NULL || (attr = *_mutexAttr) == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// not implemented
|
||||
return B_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef _PTHREAD_PRIVATE_H_
|
||||
#define _PTHREAD_PRIVATE_H_
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
// The public *_t types are only pointers to these structures
|
||||
// This way, we are completely free to change them, which might be
|
||||
// necessary in the future (not only due to the incomplete implementation
|
||||
// at this point).
|
||||
|
||||
typedef struct _pthread_mutexattr {
|
||||
int32 type;
|
||||
bool process_shared;
|
||||
} pthread_mutexattr;
|
||||
|
||||
typedef struct _pthread_mutex {
|
||||
vint32 count;
|
||||
sem_id sem;
|
||||
thread_id owner;
|
||||
int32 owner_count;
|
||||
pthread_mutexattr attr;
|
||||
} pthread_mutex;
|
||||
|
||||
#endif /* _PTHREAD_PRIVATE_H_ */
|
||||
Reference in New Issue
Block a user