From 550a30a0b77dcf07a0098b8a0db51afe9b4cd313 Mon Sep 17 00:00:00 2001 From: Ryan Leavengood Date: Sat, 22 Sep 2007 16:30:22 +0000 Subject: [PATCH] I needed Pthread condition variables for WebKit, so I implemented them. I have tested this with a simple test program I downloaded from the internet. We get the same result as on Linux, so I think this is good. I will test it more with WebKit later. Our Pthreads implementation is still missing some stuff, but this adds a good chunk. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22274 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/posix/pthread.h | 28 ++- src/system/libroot/posix/pthread/Jamfile | 2 + .../libroot/posix/pthread/pthread_cond.c | 183 ++++++++++++++++++ .../libroot/posix/pthread/pthread_condattr.c | 73 +++++++ .../libroot/posix/pthread/pthread_private.h | 18 +- 5 files changed, 295 insertions(+), 9 deletions(-) create mode 100644 src/system/libroot/posix/pthread/pthread_cond.c create mode 100644 src/system/libroot/posix/pthread/pthread_condattr.c diff --git a/headers/posix/pthread.h b/headers/posix/pthread.h index 432a44fde1..cd9f722807 100644 --- a/headers/posix/pthread.h +++ b/headers/posix/pthread.h @@ -12,7 +12,7 @@ typedef struct _pthread_attr *pthread_attr_t; typedef struct _pthread_mutex *pthread_mutex_t; typedef struct _pthread_mutexattr *pthread_mutexattr_t; typedef struct _pthread_cond *pthread_cond_t; -typedef struct _pthread_cond_attr *pthread_condattr_t; +typedef struct _pthread_condattr *pthread_condattr_t; typedef int pthread_key_t; typedef struct _pthread_once pthread_once_t; typedef struct _pthread_rwlock *pthread_rwlock_t; @@ -42,10 +42,10 @@ enum pthread_process_shared { /* * Flags for threads and thread attributes. */ -#define PTHREAD_DETACHED 0x1 +#define PTHREAD_DETACHED 0x1 #define PTHREAD_SCOPE_SYSTEM 0x2 #define PTHREAD_INHERIT_SCHED 0x4 -#define PTHREAD_NOFLOAT 0x8 +#define PTHREAD_NOFLOAT 0x8 #define PTHREAD_CREATE_DETACHED PTHREAD_DETACHED #define PTHREAD_CREATE_JOINABLE 0 @@ -61,14 +61,12 @@ enum pthread_process_shared { #define PTHREAD_CANCEL_ASYNCHRONOUS 2 #define PTHREAD_CANCELED ((void *) 1) -#define PTHREAD_COND_INITIALIZER NULL - #define PTHREAD_NEEDS_INIT 0 #define PTHREAD_DONE_INIT 1 #define PTHREAD_ONCE_INIT { PTHREAD_NEEDS_INIT, NULL } #define PTHREAD_BARRIER_SERIAL_THREAD -1 -#define PTHREAD_PRIO_NONE 0 +#define PTHREAD_PRIO_NONE 0 #define PTHREAD_PRIO_INHERIT 1 #define PTHREAD_PRIO_PROTECT 2 @@ -84,6 +82,9 @@ extern pthread_mutex_t _pthread_recursive_mutex_static_initializer(void); #define PTHREAD_RECURSIVE_MUTEX_INITIALIZER \ pthread_recursive_mutex_static_initializer(); +extern pthread_cond_t _pthread_cond_static_initializer(void); +#define PTHREAD_COND_INITIALIZER _pthread_cond_static_initializer(); + /* mutex functions */ extern int pthread_mutex_destroy(pthread_mutex_t *mutex); extern int pthread_mutex_getprioceiling(pthread_mutex_t *mutex, int *_priorityCeiling); @@ -107,6 +108,21 @@ extern int pthread_mutexattr_setprotocol(pthread_mutexattr_t *mutexAttr, int pro extern int pthread_mutexattr_setpshared(pthread_mutexattr_t *mutexAttr, int processShared); extern int pthread_mutexattr_settype(pthread_mutexattr_t *mutexAttr, int type); +/* condition variable functions */ +extern int pthread_cond_destroy(pthread_cond_t *cond); +extern int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); +extern int pthread_cond_broadcast(pthread_cond_t *cond); +extern int pthread_cond_signal(pthread_cond_t *cond); +extern int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime); +extern int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); + +/* condition variable attribute functions */ +extern int pthread_condattr_destroy(pthread_condattr_t *condAttr); +extern int pthread_condattr_init(pthread_condattr_t *condAttr); +extern int pthread_condattr_getpshared(const pthread_condattr_t *condAttr, int *processShared); +extern int pthread_condattr_setpshared(pthread_condattr_t *condAttr, int processShared); + /* misc. functions */ extern int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)); diff --git a/src/system/libroot/posix/pthread/Jamfile b/src/system/libroot/posix/pthread/Jamfile index 964b3cdf2e..a680515df1 100644 --- a/src/system/libroot/posix/pthread/Jamfile +++ b/src/system/libroot/posix/pthread/Jamfile @@ -6,6 +6,8 @@ MergeObject posix_pthread.o : pthread.c pthread_atfork.c pthread_attr.c + pthread_cond.c + pthread_condattr.c pthread_key.c pthread_mutex.c pthread_mutexattr.c diff --git a/src/system/libroot/posix/pthread/pthread_cond.c b/src/system/libroot/posix/pthread/pthread_cond.c new file mode 100644 index 0000000000..a632174a77 --- /dev/null +++ b/src/system/libroot/posix/pthread/pthread_cond.c @@ -0,0 +1,183 @@ +/* + * Copyright 2007, Ryan Leavengood, leavengood@gmail.com. + * All rights reserved. Distributed under the terms of the MIT License. + */ + + +#include +#include "pthread_private.h" + +#include +#include +#include + + +static const pthread_condattr pthread_condattr_default = { + false +}; + + +pthread_cond_t +_pthread_cond_static_initializer(void) +{ + pthread_cond_t cond; + if (pthread_cond_init(&cond, NULL) == B_OK) + return cond; + + return NULL; +} + + +int +pthread_cond_init(pthread_cond_t *_cond, const pthread_condattr_t *_attr) +{ + pthread_cond *cond; + const pthread_condattr *attr = NULL; + + if (_cond == NULL) + return B_BAD_VALUE; + + cond = (pthread_cond *)malloc(sizeof(pthread_cond)); + if (cond == NULL) + return B_NO_MEMORY; + + if (_attr != NULL) + attr = *_attr; + else + attr = &pthread_condattr_default; + + // TODO: What about the process_shared attribute? + cond->sem = create_sem(0, "pthread_cond"); + if (cond->sem < B_OK) { + free(cond); + return B_WOULD_BLOCK; + // stupid error code (EAGAIN) but demanded by POSIX + } + + cond->mutex = NULL; + cond->waiter_count = 0; + memcpy(&cond->attr, attr, sizeof(pthread_condattr)); + + *_cond = cond; + return B_OK; +} + + +int +pthread_cond_destroy(pthread_cond_t *_cond) +{ + pthread_cond *cond; + + if (_cond == NULL || (cond = *_cond) == NULL) + return B_BAD_VALUE; + + delete_sem(cond->sem); + *_cond = NULL; + free(cond); + + return B_OK; +} + + +static status_t +cond_wait(pthread_cond *cond, pthread_mutex_t *_mutex, bigtime_t timeout) +{ + status_t status = B_OK; + + if (cond == NULL || *_mutex == NULL) + return B_BAD_VALUE; + + if ((*_mutex)->owner != find_thread(NULL)) + // POSIX suggests EPERM (= B_NOT_ALLOWED) to be returned + // if this thread does not own the mutex + return B_NOT_ALLOWED; + + if (cond->mutex && cond->mutex != _mutex) + // POSIX suggests EINVAL (= B_BAD_VALUE) to be returned if + // the same condition variable is used with multiple mutexes + return B_BAD_VALUE; + + cond->mutex = _mutex; + cond->waiter_count++; + pthread_mutex_unlock(_mutex); + status = acquire_sem_etc(cond->sem, 1, timeout == B_INFINITE_TIMEOUT ? 0 : B_ABSOLUTE_TIMEOUT, timeout); + pthread_mutex_lock(_mutex); + cond->waiter_count--; + // If there are no more waiters, we can change mutexes + if (cond->waiter_count == 0) + cond->mutex = NULL; + + return status; +} + +static status_t +cond_signal(pthread_cond *cond, bool broadcast) +{ + if (cond == NULL) + return B_BAD_VALUE; + + return release_sem_etc(cond->sem, broadcast ? cond->waiter_count : 1, 0); +} + +int +pthread_cond_wait(pthread_cond_t *_cond, pthread_mutex_t *_mutex) +{ + if (_cond == NULL || _mutex == NULL) + return B_BAD_VALUE; + + if (*_cond == NULL) + pthread_cond_init(_cond, NULL); + + return cond_wait(*_cond, _mutex, B_INFINITE_TIMEOUT); +} + + +int +pthread_cond_timedwait(pthread_cond_t *_cond, 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 (_cond == NULL || _mutex == NULL) + return B_BAD_VALUE; + + if (*_cond == NULL) + pthread_cond_init(_cond, NULL); + + status = cond_wait(*_cond, _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_cond_broadcast(pthread_cond_t *_cond) +{ + if (_cond == NULL) + return B_BAD_VALUE; + + return cond_signal(*_cond, true); +} + + +int +pthread_cond_signal(pthread_cond_t *_cond) +{ + if (_cond == NULL) + return B_BAD_VALUE; + + return cond_signal(*_cond, false); +} + diff --git a/src/system/libroot/posix/pthread/pthread_condattr.c b/src/system/libroot/posix/pthread/pthread_condattr.c new file mode 100644 index 0000000000..24634a41a1 --- /dev/null +++ b/src/system/libroot/posix/pthread/pthread_condattr.c @@ -0,0 +1,73 @@ +/* + * Copyright 2007, Ryan Leavengood, leavengood@gmail.com. + * All rights reserved. Distributed under the terms of the MIT License. + */ + + +#include +#include "pthread_private.h" + +#include + + +int +pthread_condattr_init(pthread_condattr_t *_condAttr) +{ + pthread_condattr *attr; + + if (_condAttr == NULL) + return B_BAD_VALUE; + + attr = (pthread_condattr *)malloc(sizeof(pthread_condattr)); + if (attr == NULL) + return B_NO_MEMORY; + + attr->process_shared = false; + + *_condAttr = attr; + return B_OK; +} + + +int +pthread_condattr_destroy(pthread_condattr_t *_condAttr) +{ + pthread_condattr *attr; + + if (_condAttr == NULL || (attr = *_condAttr) == NULL) + return B_BAD_VALUE; + + *_condAttr = NULL; + free(attr); + + return B_OK; +} + + +int +pthread_condattr_getpshared(const pthread_condattr_t *_condAttr, int *_processShared) +{ + pthread_condattr *attr; + + if (_condAttr == NULL || (attr = *_condAttr) == NULL || _processShared == NULL) + return B_BAD_VALUE; + + *_processShared = attr->process_shared ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE; + return B_OK; +} + + +int +pthread_condattr_setpshared(pthread_condattr_t *_condAttr, int processShared) +{ + pthread_condattr *attr; + + if (_condAttr == NULL || (attr = *_condAttr) == 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; +} + diff --git a/src/system/libroot/posix/pthread/pthread_private.h b/src/system/libroot/posix/pthread/pthread_private.h index 4a45b11916..30bf0da949 100644 --- a/src/system/libroot/posix/pthread/pthread_private.h +++ b/src/system/libroot/posix/pthread/pthread_private.h @@ -1,9 +1,10 @@ #ifndef _PTHREAD_PRIVATE_H_ #define _PTHREAD_PRIVATE_H_ /* -** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. -** Distributed under the terms of the OpenBeOS License. -*/ + * Copyright 2003, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2007, Ryan Leavengood, leavengood@gmail.com. + * All rights reserved. Distributed under the terms of the MIT License. + */ #include @@ -12,6 +13,17 @@ // necessary in the future (not only due to the incomplete implementation // at this point). +typedef struct _pthread_condattr { + bool process_shared; +} pthread_condattr; + +typedef struct _pthread_cond { + sem_id sem; + pthread_mutex_t *mutex; + int32 waiter_count; + pthread_condattr attr; +} pthread_cond; + typedef struct _pthread_mutexattr { int32 type; bool process_shared;