From 74b016ecad01d13c1265b8901d5a9b5cc606db76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sun, 20 Apr 2003 18:48:54 +0000 Subject: [PATCH] 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 --- src/kernel/libroot/posix/pthread/Jamfile | 9 + .../libroot/posix/pthread/pthread_mutex.c | 238 ++++++++++++++++++ .../libroot/posix/pthread/pthread_mutexattr.c | 152 +++++++++++ .../libroot/posix/pthread/pthread_private.h | 24 ++ 4 files changed, 423 insertions(+) create mode 100644 src/kernel/libroot/posix/pthread/Jamfile create mode 100644 src/kernel/libroot/posix/pthread/pthread_mutex.c create mode 100644 src/kernel/libroot/posix/pthread/pthread_mutexattr.c create mode 100644 src/kernel/libroot/posix/pthread/pthread_private.h diff --git a/src/kernel/libroot/posix/pthread/Jamfile b/src/kernel/libroot/posix/pthread/Jamfile new file mode 100644 index 0000000000..85ab33ced7 --- /dev/null +++ b/src/kernel/libroot/posix/pthread/Jamfile @@ -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 + ; + diff --git a/src/kernel/libroot/posix/pthread/pthread_mutex.c b/src/kernel/libroot/posix/pthread/pthread_mutex.c new file mode 100644 index 0000000000..53d330a1aa --- /dev/null +++ b/src/kernel/libroot/posix/pthread/pthread_mutex.c @@ -0,0 +1,238 @@ +#include +#include "pthread_private.h" + +#include +#include +#include + + +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; +} + + diff --git a/src/kernel/libroot/posix/pthread/pthread_mutexattr.c b/src/kernel/libroot/posix/pthread/pthread_mutexattr.c new file mode 100644 index 0000000000..ccfa5b2ae1 --- /dev/null +++ b/src/kernel/libroot/posix/pthread/pthread_mutexattr.c @@ -0,0 +1,152 @@ +#include +#include "pthread_private.h" + +#include + + +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; +} + diff --git a/src/kernel/libroot/posix/pthread/pthread_private.h b/src/kernel/libroot/posix/pthread/pthread_private.h new file mode 100644 index 0000000000..a3d397744e --- /dev/null +++ b/src/kernel/libroot/posix/pthread/pthread_private.h @@ -0,0 +1,24 @@ +#ifndef _PTHREAD_PRIVATE_H_ +#define _PTHREAD_PRIVATE_H_ + +#include + +// 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_ */