From f71274580bc2625bf438140839fe38193ece28e6 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Thu, 15 Apr 2010 21:52:49 +0000 Subject: [PATCH] * Private libroot locking primitives: - Reimplemented mutex to use the _kern_mutex*() syscalls. - The initializer functions cannot fail anymore -- changed their return type to void. - Changed the initializer function semantics to not copy the name by default anymore (as in the kernel). Also added *_etc() versions of them that take an additional flags. - Added static initializer macros. - Made the mutex (and thus recursive_lock) lock functions non-interruptable. - Got rid of the "lazy" version. They are no longer needed, since the initialization of the standard types can be done statically and cannot fail. * Adjusted libroot, runtime loader, and other code using the private libroot locking primitives to the new semantics. * pthreads mutexes and condition variables: - Reimplemented using the _kern_mutex*() syscalls. - Consistently use POSIX error codes. - Fixed some not quite POSIX compliant behavior. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36323 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/posix/pthread.h | 8 +- headers/private/libroot/fork.h | 1 - headers/private/libroot/libroot_private.h | 1 - headers/private/shared/locks.h | 56 ++--- src/kits/media/RealtimeAlloc.cpp | 24 +-- src/system/libroot/libroot_init.c | 2 - src/system/libroot/os/locks/mutex.cpp | 156 ++++---------- .../libroot/os/locks/recursive_lock.cpp | 75 +------ src/system/libroot/os/locks/rw_lock.cpp | 12 +- src/system/libroot/posix/pthread/Jamfile | 4 +- .../libroot/posix/pthread/pthread_cond.c | 183 ---------------- .../libroot/posix/pthread/pthread_cond.cpp | 141 +++++++++++++ .../libroot/posix/pthread/pthread_mutex.c | 199 ------------------ .../libroot/posix/pthread/pthread_mutex.cpp | 186 ++++++++++++++++ src/system/libroot/posix/stdlib/env.cpp | 11 +- src/system/libroot/posix/stdlib/exit.c | 23 +- src/system/libroot/posix/unistd/fork.c | 27 ++- .../libroot/posix/user_group_common.cpp | 14 +- src/system/runtime_loader/elf.cpp | 10 +- 19 files changed, 452 insertions(+), 681 deletions(-) delete mode 100644 src/system/libroot/posix/pthread/pthread_cond.c create mode 100644 src/system/libroot/posix/pthread/pthread_cond.cpp delete mode 100644 src/system/libroot/posix/pthread/pthread_mutex.c create mode 100644 src/system/libroot/posix/pthread/pthread_mutex.cpp diff --git a/headers/posix/pthread.h b/headers/posix/pthread.h index 8c4de118b7..b4bb1abb44 100644 --- a/headers/posix/pthread.h +++ b/headers/posix/pthread.h @@ -29,18 +29,18 @@ typedef struct _pthread_spinlock *pthread_spinlock_t; struct _pthread_mutex { uint32_t flags; - int32_t count; - int32_t sem; + int32_t lock; + int32_t unused; int32_t owner; int32_t owner_count; }; struct _pthread_cond { uint32_t flags; - int32_t sem; + int32_t unused; pthread_mutex_t *mutex; int32_t waiter_count; - int32_t event_counter; + int32_t lock; }; struct _pthread_once { diff --git a/headers/private/libroot/fork.h b/headers/private/libroot/fork.h index 236386e0ec..d009bf2228 100644 --- a/headers/private/libroot/fork.h +++ b/headers/private/libroot/fork.h @@ -13,7 +13,6 @@ extern "C" { #endif -extern status_t __init_fork(void); extern status_t __register_atfork(void(*prepare)(void), void(*parent)(void), void(*child)(void)); diff --git a/headers/private/libroot/libroot_private.h b/headers/private/libroot/libroot_private.h index eef754d777..0b1c924061 100644 --- a/headers/private/libroot/libroot_private.h +++ b/headers/private/libroot/libroot_private.h @@ -30,7 +30,6 @@ status_t __flatten_process_args(const char* const* args, int32 argCount, const char* const* env, int32 envCount, char*** _flatArgs, size_t* _flatSize); void _call_atexit_hooks_for_range(addr_t start, addr_t size); -void __init_exit_stack_lock(void); void __init_env(const struct user_space_program_args *args); void __init_heap(void); diff --git a/headers/private/shared/locks.h b/headers/private/shared/locks.h index 39d3d1c537..052f24b6ec 100644 --- a/headers/private/shared/locks.h +++ b/headers/private/shared/locks.h @@ -12,31 +12,22 @@ extern "C" { #endif typedef struct mutex { - int32 benaphore; - sem_id semaphore; + const char* name; + int32 lock; + uint32 flags; } mutex; -status_t mutex_init(mutex *lock, const char *name); +#define MUTEX_FLAG_CLONE_NAME 0x1 +#define MUTEX_INITIALIZER(name) { name, 0, 0 } + +void mutex_init(mutex *lock, const char *name); +void mutex_init_etc(mutex *lock, const char *name, uint32 flags); void mutex_destroy(mutex *lock); status_t mutex_lock(mutex *lock); void mutex_unlock(mutex *lock); -typedef struct lazy_mutex { - int32 benaphore; - sem_id semaphore; - const char* name; -} lazy_mutex; - -status_t lazy_mutex_init(lazy_mutex *lock, const char *name); - // name will not be cloned and must rename valid -void lazy_mutex_destroy(lazy_mutex *lock); -status_t lazy_mutex_lock(lazy_mutex *lock); -void lazy_mutex_unlock(lazy_mutex *lock); - - typedef struct rw_lock { - const char * name; mutex lock; struct rw_lock_waiter * waiters; struct rw_lock_waiter * last_waiter; @@ -46,7 +37,12 @@ typedef struct rw_lock { int32 owner_count; } rw_lock; -status_t rw_lock_init(rw_lock *lock, const char *name); +#define RW_LOCK_FLAG_CLONE_NAME MUTEX_FLAG_CLONE_NAME +#define RW_LOCK_INITIALIZER(name) { MUTEX_INITIALIZER(name), NULL, \ + NULL, -1, 0, 0, 0 } + +void rw_lock_init(rw_lock *lock, const char *name); +void rw_lock_init_etc(rw_lock *lock, const char *name, uint32 flags); void rw_lock_destroy(rw_lock *lock); status_t rw_lock_read_lock(rw_lock *lock); status_t rw_lock_read_unlock(rw_lock *lock); @@ -57,31 +53,21 @@ status_t rw_lock_write_unlock(rw_lock *lock); typedef struct recursive_lock { mutex lock; thread_id holder; - int recursion; + int32 recursion; } recursive_lock; -status_t recursive_lock_init(recursive_lock *lock, const char *name); +#define RECURSIVE_LOCK_FLAG_CLONE_NAME MUTEX_FLAG_CLONE_NAME +#define RECURSIVE_LOCK_INITIALIZER(name) { MUTEX_INITIALIZER(name), -1, 0 } + +void recursive_lock_init(recursive_lock *lock, const char *name); +void recursive_lock_init_etc(recursive_lock *lock, const char *name, + uint32 flags); void recursive_lock_destroy(recursive_lock *lock); status_t recursive_lock_lock(recursive_lock *lock); void recursive_lock_unlock(recursive_lock *lock); int32 recursive_lock_get_recursion(recursive_lock *lock); -typedef struct lazy_recursive_lock { - lazy_mutex lock; - thread_id holder; - int recursion; -} lazy_recursive_lock; - -status_t lazy_recursive_lock_init(lazy_recursive_lock *lock, - const char *name); - // name will not be cloned and must rename valid -void lazy_recursive_lock_destroy(lazy_recursive_lock *lock); -status_t lazy_recursive_lock_lock(lazy_recursive_lock *lock); -void lazy_recursive_lock_unlock(lazy_recursive_lock *lock); -int32 lazy_recursive_lock_get_recursion(lazy_recursive_lock *lock); - - #define INIT_ONCE_UNINITIALIZED -1 #define INIT_ONCE_INITIALIZED -4 diff --git a/src/kits/media/RealtimeAlloc.cpp b/src/kits/media/RealtimeAlloc.cpp index 6f5963d1bf..82fd7532a3 100644 --- a/src/kits/media/RealtimeAlloc.cpp +++ b/src/kits/media/RealtimeAlloc.cpp @@ -75,7 +75,7 @@ typedef DoublyLinkedList PoolList; const static uint32 kAlignment = 256; // all memory chunks will be a multiple of this -static mutex sPoolsLock = {-1, -1}; +static mutex sPoolsLock = MUTEX_INITIALIZER("rtm pools"); static PoolList sPools; @@ -270,13 +270,6 @@ pool_for(void* buffer) } -static void -pool_init(void) -{ - mutex_init(&sPoolsLock, "rtm pools"); -} - - // #pragma mark - public API @@ -287,14 +280,10 @@ rtm_create_pool(rtm_pool** _pool, size_t totalSize, const char* name) if (pool == NULL) return B_NO_MEMORY; - if (name == NULL) - name = "realtime pool"; - - status_t status = mutex_init(&pool->lock, name); - if (status != B_OK) { - free(pool); - return status; - } + if (name != NULL) + mutex_init_etc(&pool->lock, name, MUTEX_FLAG_CLONE_NAME); + else + mutex_init(&pool->lock, "realtime pool"); // Allocate enough space for at least one allocation over \a totalSize pool->max_size = (totalSize + sizeof(FreeChunk) - 1 + B_PAGE_SIZE) @@ -321,9 +310,6 @@ rtm_create_pool(rtm_pool** _pool, size_t totalSize, const char* name) *_pool = pool; - static pthread_once_t sOnce = PTHREAD_ONCE_INIT; - pthread_once(&sOnce, &pool_init); - MutexLocker _(&sPoolsLock); sPools.Add(pool); return B_OK; diff --git a/src/system/libroot/libroot_init.c b/src/system/libroot/libroot_init.c index 01eceb8198..abcbc30e82 100644 --- a/src/system/libroot/libroot_init.c +++ b/src/system/libroot/libroot_init.c @@ -52,9 +52,7 @@ initialize_before(image_id imageID) __gRuntimeLoader->call_atexit_hooks_for_range = _call_atexit_hooks_for_range; - __init_exit_stack_lock(); __init_time(); - __init_fork(); __init_heap(); __init_env(__gRuntimeLoader->program_args); __init_pwd_backend(); diff --git a/src/system/libroot/os/locks/mutex.cpp b/src/system/libroot/os/locks/mutex.cpp index 9cd97e3629..2e755bb9c2 100644 --- a/src/system/libroot/os/locks/mutex.cpp +++ b/src/system/libroot/os/locks/mutex.cpp @@ -1,6 +1,6 @@ /* * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. - * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. * @@ -11,154 +11,72 @@ #include -#include +#include +#include + +#include +#include // #pragma mark - mutex -status_t +void mutex_init(mutex *lock, const char *name) { - if (lock == NULL || name == NULL) - return B_BAD_VALUE; + lock->name = name; + lock->lock = 0; + lock->flags = 0; +} - lock->benaphore = 0; - lock->semaphore = create_sem(0, name); - if (lock->semaphore < 0) - return lock->semaphore; - return B_OK; +void +mutex_init_etc(mutex *lock, const char *name, uint32 flags) +{ + lock->name = (flags & MUTEX_FLAG_CLONE_NAME) != 0 ? strdup(name) : name; + lock->lock = 0; + lock->flags = flags; } void mutex_destroy(mutex *lock) { - delete_sem(lock->semaphore); + if ((lock->flags & MUTEX_FLAG_CLONE_NAME) != 0) + free(const_cast(lock->name)); } status_t mutex_lock(mutex *lock) { - if (atomic_add(&lock->benaphore, 1) == 0) + // set the locked flag + int32 oldValue = atomic_or(&lock->lock, B_USER_MUTEX_LOCKED); + + if ((oldValue & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) == 0 + || (oldValue & B_USER_MUTEX_DISABLED) != 0) { + // No one has the lock or is waiting for it, or the mutex has been + // disabled. return B_OK; + } - status_t result; + // we have to call the kernel + status_t error; do { - result = acquire_sem(lock->semaphore); - } while (result == B_INTERRUPTED); + error = _kern_mutex_lock(&lock->lock, lock->name, 0, 0); + } while (error == B_INTERRUPTED); - return result; + return error; } void mutex_unlock(mutex *lock) { - if (atomic_add(&lock->benaphore, -1) != 1) - release_sem(lock->semaphore); -} - - -// #pragma mark - lazy mutex - - -enum { - STATE_UNINITIALIZED = -1, - STATE_INITIALIZING = -2, - STATE_SPIN_LOCKED = -3, - STATE_SPIN_UNLOCKED = -4 -}; - - -static inline bool -lazy_mutex_ensure_init(lazy_mutex *lock) -{ - int32 value = atomic_test_and_set((vint32*)&lock->semaphore, - STATE_INITIALIZING, STATE_UNINITIALIZED); - - if (value >= 0) - return true; - - if (value == STATE_UNINITIALIZED) { - // we're the first -- perform the initialization - sem_id semaphore = create_sem(0, lock->name); - if (semaphore < 0) - semaphore = STATE_SPIN_UNLOCKED; - atomic_set((vint32*)&lock->semaphore, semaphore); - return semaphore >= 0; - } - - if (value == STATE_INITIALIZING) { - // someone else is initializing -- spin until that is done - while (atomic_get((vint32*)&lock->semaphore) == STATE_INITIALIZING) { - } - } - - return lock->semaphore >= 0; -} - - -status_t -lazy_mutex_init(lazy_mutex *lock, const char *name) -{ - if (lock == NULL || name == NULL) - return B_BAD_VALUE; - - lock->benaphore = 0; - lock->semaphore = STATE_UNINITIALIZED; - lock->name = name; - - return B_OK; -} - - -void -lazy_mutex_destroy(lazy_mutex *lock) -{ - if (lock->semaphore >= 0) - delete_sem(lock->semaphore); -} - - -status_t -lazy_mutex_lock(lazy_mutex *lock) -{ - if (atomic_add(&lock->benaphore, 1) == 0) - return B_OK; - - if (lazy_mutex_ensure_init(lock)) { - // acquire the semaphore - status_t result; - do { - result = acquire_sem(lock->semaphore); - } while (result == B_INTERRUPTED); - - return result; - } else { - // the semaphore creation failed -- so we use it like a spinlock instead - while (atomic_test_and_set((vint32*)&lock->semaphore, - STATE_SPIN_LOCKED, STATE_SPIN_UNLOCKED) - != STATE_SPIN_UNLOCKED) { - } - return B_OK; - } -} - - -void -lazy_mutex_unlock(lazy_mutex *lock) -{ - if (atomic_add(&lock->benaphore, -1) == 1) - return; - - if (lazy_mutex_ensure_init(lock)) { - // release the semaphore - release_sem(lock->semaphore); - } else { - // the semaphore creation failed -- so we use it like a spinlock instead - atomic_set((vint32*)&lock->semaphore, STATE_SPIN_UNLOCKED); + // clear the locked flag + int32 oldValue = atomic_and(&lock->lock, ~(int32)B_USER_MUTEX_LOCKED); + if ((oldValue & B_USER_MUTEX_WAITING) != 0 + && (oldValue & B_USER_MUTEX_DISABLED) == 0) { + _kern_mutex_unlock(&lock->lock, 0); } } diff --git a/src/system/libroot/os/locks/recursive_lock.cpp b/src/system/libroot/os/locks/recursive_lock.cpp index 3d1dc61a4a..bd3b130e3c 100644 --- a/src/system/libroot/os/locks/recursive_lock.cpp +++ b/src/system/libroot/os/locks/recursive_lock.cpp @@ -1,6 +1,6 @@ /* * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. - * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. * @@ -27,12 +27,19 @@ recursive_lock_get_recursion(recursive_lock *lock) } -status_t +void recursive_lock_init(recursive_lock *lock, const char *name) +{ + recursive_lock_init_etc(lock, name, 0); +} + + +void +recursive_lock_init_etc(recursive_lock *lock, const char *name, uint32 flags) { lock->holder = -1; lock->recursion = 0; - return mutex_init(&lock->lock, name != NULL ? name : "recursive lock"); + mutex_init_etc(&lock->lock, name, flags); } @@ -74,65 +81,3 @@ recursive_lock_unlock(recursive_lock *lock) mutex_unlock(&lock->lock); } } - - -// #pragma mark - lazy recursive lock - - -int32 -lazy_recursive_lock_get_recursion(lazy_recursive_lock *lock) -{ - if (lock->holder == find_thread(NULL)) - return lock->recursion; - - return -1; -} - - -status_t -lazy_recursive_lock_init(lazy_recursive_lock *lock, const char *name) -{ - lock->holder = -1; - lock->recursion = 0; - return lazy_mutex_init(&lock->lock, name != NULL ? name : "recursive lock"); -} - - -void -lazy_recursive_lock_destroy(lazy_recursive_lock *lock) -{ - if (lock == NULL) - return; - - lazy_mutex_destroy(&lock->lock); -} - - -status_t -lazy_recursive_lock_lock(lazy_recursive_lock *lock) -{ - thread_id thread = find_thread(NULL); - - if (thread != lock->holder) { - lazy_mutex_lock(&lock->lock); - lock->holder = thread; - } - - lock->recursion++; - return B_OK; -} - - -void -lazy_recursive_lock_unlock(lazy_recursive_lock *lock) -{ - if (find_thread(NULL) != lock->holder) { - debugger("lazy_recursive_lock unlocked by non-holder thread!\n"); - return; - } - - if (--lock->recursion == 0) { - lock->holder = -1; - lazy_mutex_unlock(&lock->lock); - } -} diff --git a/src/system/libroot/os/locks/rw_lock.cpp b/src/system/libroot/os/locks/rw_lock.cpp index d98e4aa5f4..c66adc55b7 100644 --- a/src/system/libroot/os/locks/rw_lock.cpp +++ b/src/system/libroot/os/locks/rw_lock.cpp @@ -79,16 +79,22 @@ rw_lock_unblock(rw_lock *lock) } -status_t +void rw_lock_init(rw_lock *lock, const char *name) { - lock->name = name; + rw_lock_init_etc(lock, name, 0); +} + + +void +rw_lock_init_etc(rw_lock *lock, const char *name, uint32 flags) +{ lock->waiters = NULL; lock->holder = -1; lock->reader_count = 0; lock->writer_count = 0; lock->owner_count = 0; - return mutex_init(&lock->lock, name); + mutex_init_etc(&lock->lock, name, flags); } diff --git a/src/system/libroot/posix/pthread/Jamfile b/src/system/libroot/posix/pthread/Jamfile index 7ce2652172..64294ee377 100644 --- a/src/system/libroot/posix/pthread/Jamfile +++ b/src/system/libroot/posix/pthread/Jamfile @@ -11,10 +11,10 @@ MergeObject posix_pthread.o : pthread_attr.c pthread_cancel.cpp pthread_cleanup.cpp - pthread_cond.c + pthread_cond.cpp pthread_condattr.c pthread_key.cpp - pthread_mutex.c + pthread_mutex.cpp pthread_mutexattr.c pthread_once.cpp pthread_rwlock.cpp diff --git a/src/system/libroot/posix/pthread/pthread_cond.c b/src/system/libroot/posix/pthread/pthread_cond.c deleted file mode 100644 index 112ba7a043..0000000000 --- a/src/system/libroot/posix/pthread/pthread_cond.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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 - - -#define COND_FLAG_SHARED 0x01 - - -static const pthread_condattr pthread_condattr_default = { - false -}; - - -int -pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *_attr) -{ - const pthread_condattr *attr = NULL; - - if (cond == NULL) - return B_BAD_VALUE; - - if (_attr != NULL) - attr = *_attr; - else - attr = &pthread_condattr_default; - - cond->flags = 0; - if (attr->process_shared) - cond->flags |= COND_FLAG_SHARED; - - cond->sem = create_sem(0, "pthread_cond"); - if (cond->sem < B_OK) { - return B_WOULD_BLOCK; - // stupid error code (EAGAIN) but demanded by POSIX - } - - cond->mutex = NULL; - cond->waiter_count = 0; - cond->event_counter = 0; - - return B_OK; -} - - -int -pthread_cond_destroy(pthread_cond_t *cond) -{ - if (cond == NULL) - return B_BAD_VALUE; - - delete_sem(cond->sem); - - return B_OK; -} - - -static status_t -cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex, bigtime_t timeout) -{ - status_t status; - int32 event; - - 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; - - // lazy init - if (cond->sem == -42) { - // Note, this is thread-safe, since another thread would be required to - // hold the same mutex. - sem_id sem = create_sem(0, "pthread_cond"); - if (sem < 0) - return EAGAIN; - - if (atomic_test_and_set((vint32*)&cond->sem, sem, -42) != -42) - delete_sem(sem); - } - - 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++; - - event = atomic_get((vint32*)&cond->event_counter); - - pthread_mutex_unlock(mutex); - - do { - status = acquire_sem_etc(cond->sem, 1, - timeout == B_INFINITE_TIMEOUT ? 0 : B_ABSOLUTE_REAL_TIME_TIMEOUT, - timeout); - } while (status == B_OK - && atomic_get((vint32*)&cond->event_counter) == event); - - 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_t *cond, bool broadcast) -{ - int32 waiterCount; - - if (cond == NULL) - return B_BAD_VALUE; - - waiterCount = cond->waiter_count; - if (waiterCount > 0) { - atomic_add((vint32*)&cond->event_counter, 1); - return release_sem_etc(cond->sem, broadcast ? waiterCount : 1, 0); - } - - return 0; -} - - -int -pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *_mutex) -{ - 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; - - 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) -{ - return cond_signal(cond, true); -} - - -int -pthread_cond_signal(pthread_cond_t *cond) -{ - return cond_signal(cond, false); -} - diff --git a/src/system/libroot/posix/pthread/pthread_cond.cpp b/src/system/libroot/posix/pthread/pthread_cond.cpp new file mode 100644 index 0000000000..0b7f065756 --- /dev/null +++ b/src/system/libroot/posix/pthread/pthread_cond.cpp @@ -0,0 +1,141 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * 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 + +#include +#include + + +#define COND_FLAG_SHARED 0x01 + + +static const pthread_condattr pthread_condattr_default = { + false +}; + + +int +pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* _attr) +{ + const pthread_condattr* attr = _attr != NULL + ? *_attr : &pthread_condattr_default; + + cond->flags = 0; + if (attr->process_shared) + cond->flags |= COND_FLAG_SHARED; + + cond->mutex = NULL; + cond->waiter_count = 0; + cond->lock = 0; + + return 0; +} + + +int +pthread_cond_destroy(pthread_cond_t* cond) +{ + return 0; +} + + +static status_t +cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex, bigtime_t timeout) +{ + if (mutex->owner != find_thread(NULL)) { + // calling thread isn't mutex owner + return EPERM; + } + + if (cond->mutex != NULL && cond->mutex != mutex) { + // condition variable already used with different mutex + return EINVAL; + } + + cond->mutex = mutex; + cond->waiter_count++; + + // make sure the user mutex we use for blocking is locked + atomic_or((int32*)&cond->lock, B_USER_MUTEX_LOCKED); + + // atomically unlock the mutex and start waiting on the user mutex + mutex->owner = -1; + mutex->owner_count = 0; + + status_t status = _kern_mutex_switch_lock((int32*)&mutex->lock, + (int32*)&cond->lock, "pthread condition", + timeout == B_INFINITE_TIMEOUT ? 0 : B_ABSOLUTE_REAL_TIME_TIMEOUT, + timeout); + + if (status == B_INTERRUPTED) { + // EINTR is not an allowed return value. We either have to restart + // waiting -- which we can't atomically -- or return a spurious 0. + status = 0; + } + + 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 inline void +cond_signal(pthread_cond_t* cond, bool broadcast) +{ + if (cond->waiter_count == 0) + return; + + // release the condition lock + _kern_mutex_unlock((int32*)&cond->lock, + broadcast ? B_USER_MUTEX_UNBLOCK_ALL : 0); +} + + +int +pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* _mutex) +{ + return cond_wait(cond, _mutex, B_INFINITE_TIMEOUT); +} + + +int +pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, + const struct timespec* tv) +{ + if (tv == NULL || tv->tv_nsec < 0 || tv->tv_nsec >= 1000 * 1000 * 1000) + return EINVAL; + + return cond_wait(cond, mutex, + tv->tv_sec * 1000000LL + tv->tv_nsec / 1000LL); +} + + +int +pthread_cond_broadcast(pthread_cond_t* cond) +{ + cond_signal(cond, true); + return 0; +} + + +int +pthread_cond_signal(pthread_cond_t* cond) +{ + cond_signal(cond, false); + return 0; +} diff --git a/src/system/libroot/posix/pthread/pthread_mutex.c b/src/system/libroot/posix/pthread/pthread_mutex.c deleted file mode 100644 index 5c40568ba4..0000000000 --- a/src/system/libroot/posix/pthread/pthread_mutex.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2003-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. - * Distributed under the terms of the MIT License. - */ - - -#include -#include "pthread_private.h" - -#include -#include -#include - - -#define MUTEX_FLAG_SHARED 0x80000000 -#define MUTEX_TYPE_BITS 0x0000000f -#define MUTEX_TYPE(mutex) ((mutex)->flags & MUTEX_TYPE_BITS) - - -static const pthread_mutexattr pthread_mutexattr_default = { - PTHREAD_MUTEX_DEFAULT, - false -}; - - -int -pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *_attr) -{ - const pthread_mutexattr *attr = NULL; - - if (mutex == NULL) - return B_BAD_VALUE; - - if (_attr != NULL) - attr = *_attr; - else - attr = &pthread_mutexattr_default; - - mutex->sem = create_sem(0, "pthread_mutex"); - if (mutex->sem < B_OK) { - return B_WOULD_BLOCK; - // stupid error code (EAGAIN) but demanded by POSIX - } - - mutex->count = 0; - mutex->owner = -1; - mutex->owner_count = 0; - mutex->flags = attr->type | (attr->process_shared ? MUTEX_FLAG_SHARED : 0); - - return B_OK; -} - - -int -pthread_mutex_destroy(pthread_mutex_t *mutex) -{ - if (mutex == NULL) - return B_BAD_VALUE; - - delete_sem(mutex->sem); - - return B_OK; -} - - -static status_t -mutex_lock(pthread_mutex_t *mutex, bigtime_t timeout) -{ - thread_id thisThread = find_thread(NULL); - status_t status = B_OK; - - if (mutex == NULL) - return B_BAD_VALUE; - - // If statically initialized, we need to create the semaphore, now. - if (mutex->sem == -42) { - sem_id sem = create_sem(0, "pthread_mutex"); - if (sem < 0) - return EAGAIN; - - if (atomic_test_and_set((vint32*)&mutex->sem, sem, -42) != -42) - delete_sem(sem); - } - - if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_ERRORCHECK - && mutex->owner == thisThread) { - // we detect this kind of deadlock and return an error - return EDEADLK; - } - - if (MUTEX_TYPE(mutex) == 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 (atomic_add((vint32*)&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_REAL_TIME_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) -{ - return mutex_lock(mutex, B_INFINITE_TIMEOUT); -} - - -int -pthread_mutex_trylock(pthread_mutex_t *mutex) -{ - status_t status = mutex_lock(mutex, 0); - return status == B_WOULD_BLOCK ? EBUSY : status; -} - - -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; - - 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; - - if (mutex->owner != find_thread(NULL)) - return EPERM; - - if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_RECURSIVE - && mutex->owner_count-- > 1) { - return B_OK; - } - - mutex->owner = -1; - - if (atomic_add((vint32*)&mutex->count, -1) > 1) - return release_sem(mutex->sem); - - return B_OK; -} - - -int -pthread_mutex_getprioceiling(pthread_mutex_t *mutex, int *_prioCeiling) -{ - if (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) -{ - if (mutex == NULL) - return B_BAD_VALUE; - - // not implemented - return B_NOT_ALLOWED; -} diff --git a/src/system/libroot/posix/pthread/pthread_mutex.cpp b/src/system/libroot/posix/pthread/pthread_mutex.cpp new file mode 100644 index 0000000000..cfdd311867 --- /dev/null +++ b/src/system/libroot/posix/pthread/pthread_mutex.cpp @@ -0,0 +1,186 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2003-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include +#include "pthread_private.h" + +#include +#include +#include + +#include +#include + + +#define MUTEX_FLAG_SHARED 0x80000000 +#define MUTEX_TYPE_BITS 0x0000000f +#define MUTEX_TYPE(mutex) ((mutex)->flags & MUTEX_TYPE_BITS) + + +static const pthread_mutexattr pthread_mutexattr_default = { + PTHREAD_MUTEX_DEFAULT, + false +}; + + +int +pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* _attr) +{ + const pthread_mutexattr* attr = _attr != NULL + ? *_attr : &pthread_mutexattr_default; + + mutex->lock = 0; + mutex->owner = -1; + mutex->owner_count = 0; + mutex->flags = attr->type | (attr->process_shared ? MUTEX_FLAG_SHARED : 0); + + return 0; +} + + +int +pthread_mutex_destroy(pthread_mutex_t* mutex) +{ + return 0; +} + + +static status_t +mutex_lock(pthread_mutex_t* mutex, bigtime_t timeout) +{ + thread_id thisThread = find_thread(NULL); + + if (mutex->owner == thisThread) { + // recursive locking handling + if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_RECURSIVE) { + if (mutex->owner_count == INT32_MAX) + return EAGAIN; + + mutex->owner_count++; + return 0; + } + + // deadlock check (not for PTHREAD_MUTEX_NORMAL as per the specs) + if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_ERRORCHECK + || MUTEX_TYPE(mutex) == PTHREAD_MUTEX_DEFAULT) { + // we detect this kind of deadlock and return an error + return timeout < 0 ? EBUSY : EDEADLK; + } + } + + // set the locked flag + int32 oldValue = atomic_or((int32*)&mutex->lock, B_USER_MUTEX_LOCKED); + + if ((oldValue & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) != 0) { + // someone else has the lock or is at least waiting for it + if (timeout < 0) + return EBUSY; + + // we have to call the kernel + status_t error; + do { + error = _kern_mutex_lock((int32*)&mutex->lock, NULL, + timeout == B_INFINITE_TIMEOUT + ? 0 : B_ABSOLUTE_REAL_TIME_TIMEOUT, + timeout); + } while (error == B_INTERRUPTED); + + if (error != B_OK) + return error; + } + + // we have locked the mutex for the first time + mutex->owner = thisThread; + mutex->owner_count = 1; + + return 0; +} + + +int +pthread_mutex_lock(pthread_mutex_t* mutex) +{ + return mutex_lock(mutex, B_INFINITE_TIMEOUT); +} + + +int +pthread_mutex_trylock(pthread_mutex_t* mutex) +{ + return mutex_lock(mutex, -1); +} + + +int +pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* tv) +{ + // translate the timeout + bool invalidTime = false; + 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; + + status_t status = mutex_lock(mutex, timeout); + if (status != B_OK && invalidTime) { + // The timespec was not valid and the mutex could not be locked + // immediately. + return EINVAL; + } + + return status; +} + + +int +pthread_mutex_unlock(pthread_mutex_t* mutex) +{ + if (mutex->owner != find_thread(NULL)) + return EPERM; + + if (MUTEX_TYPE(mutex) == PTHREAD_MUTEX_RECURSIVE + && --mutex->owner_count > 0) { + // still locked + return 0; + } + + mutex->owner = -1; + + // clear the locked flag + int32 oldValue = atomic_and((int32*)&mutex->lock, + ~(int32)B_USER_MUTEX_LOCKED); + if ((oldValue & B_USER_MUTEX_WAITING) != 0) + _kern_mutex_unlock((int32*)&mutex->lock, 0); + + return 0; +} + + +int +pthread_mutex_getprioceiling(pthread_mutex_t* mutex, int* _prioCeiling) +{ + if (mutex == NULL || _prioCeiling == NULL) + return EINVAL; + + *_prioCeiling = 0; + // not implemented + + return 0; +} + + +int +pthread_mutex_setprioceiling(pthread_mutex_t* mutex, int prioCeiling, + int* _oldCeiling) +{ + if (mutex == NULL) + return EINVAL; + + // not implemented + return EPERM; +} diff --git a/src/system/libroot/posix/stdlib/env.cpp b/src/system/libroot/posix/stdlib/env.cpp index 9f4403ddee..d71e42a5af 100644 --- a/src/system/libroot/posix/stdlib/env.cpp +++ b/src/system/libroot/posix/stdlib/env.cpp @@ -18,7 +18,9 @@ #include -static lazy_mutex sEnvLock; +static const char* const kEnvLockName = "env lock"; + +static mutex sEnvLock = MUTEX_INITIALIZER(kEnvLockName); static char **sManagedEnviron; char **environ = NULL; @@ -27,14 +29,14 @@ char **environ = NULL; static inline void lock_variables(void) { - lazy_mutex_lock(&sEnvLock); + mutex_lock(&sEnvLock); } static inline void unlock_variables(void) { - lazy_mutex_unlock(&sEnvLock); + mutex_unlock(&sEnvLock); } @@ -186,7 +188,7 @@ update_variable(const char *name, int32 length, const char *value, static void environ_fork_hook(void) { - lazy_mutex_init(&sEnvLock, "env lock"); + mutex_init(&sEnvLock, kEnvLockName); } @@ -199,7 +201,6 @@ __init_env(const struct user_space_program_args *args) // Following POSIX, there is no need to make any of the environment // functions thread-safe - but we do it anyway as much as possible to // protect our implementation - lazy_mutex_init(&sEnvLock, "env lock"); environ = args->env; sManagedEnviron = NULL; diff --git a/src/system/libroot/posix/stdlib/exit.c b/src/system/libroot/posix/stdlib/exit.c index 7649383771..b27b5b9220 100644 --- a/src/system/libroot/posix/stdlib/exit.c +++ b/src/system/libroot/posix/stdlib/exit.c @@ -25,26 +25,27 @@ extern void _IO_cleanup(void); extern void _thread_do_exit_work(void); struct exit_stack_info { - void (*exit_stack[ATEXIT_MAX])(void); - int32 stack_size; - lazy_recursive_lock lock; + void (*exit_stack[ATEXIT_MAX])(void); + int32 stack_size; + recursive_lock lock; }; -static struct exit_stack_info sExitStackInfo = { {}, 0, {} }; +static struct exit_stack_info sExitStackInfo + = { {}, 0, RECURSIVE_LOCK_INITIALIZER("exit stack lock") }; static void inline _exit_stack_lock() { - lazy_recursive_lock_lock(&sExitStackInfo.lock); + recursive_lock_lock(&sExitStackInfo.lock); } static void inline _exit_stack_unlock() { - lazy_recursive_lock_unlock(&sExitStackInfo.lock); + recursive_lock_unlock(&sExitStackInfo.lock); } @@ -83,16 +84,6 @@ _call_atexit_hooks_for_range(addr_t start, addr_t size) } -void -__init_exit_stack_lock(void) -{ - status_t error = lazy_recursive_lock_init(&sExitStackInfo.lock, - "exit stack lock"); - if (error != B_OK) - debugger("failed to create exit stack lock"); -} - - // #pragma mark - public API diff --git a/src/system/libroot/posix/unistd/fork.c b/src/system/libroot/posix/unistd/fork.c index 9f28d3632a..26565db1bf 100644 --- a/src/system/libroot/posix/unistd/fork.c +++ b/src/system/libroot/posix/unistd/fork.c @@ -21,9 +21,11 @@ typedef struct fork_hook { void (*function)(void); } fork_hook; +#define FORK_LOCK_NAME "fork lock" + static fork_hook *sPrepareHooks, *sParentHooks, *sChildHooks; static fork_hook *sLastParentHook, *sLastChildHook; -static lazy_mutex sForkLock; +static mutex sForkLock = MUTEX_INITIALIZER(FORK_LOCK_NAME); extern thread_id __main_thread_id; @@ -93,14 +95,6 @@ call_fork_hooks(fork_hook *hook) } -status_t -__init_fork(void) -{ - lazy_mutex_init(&sForkLock, "fork lock"); - return B_OK; -} - - /** Private support function that registers the hooks that will be executed * before and after the team is fork()ed. * It is called from pthread_atfork() and atfork(). @@ -109,7 +103,7 @@ __init_fork(void) status_t __register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) { - status_t status = lazy_mutex_lock(&sForkLock); + status_t status = mutex_lock(&sForkLock); if (status != B_OK) return status; @@ -122,7 +116,7 @@ __register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(voi if (status == B_OK && child) status = add_fork_hook(&sChildHooks, &sLastChildHook, child); - lazy_mutex_unlock(&sForkLock); + mutex_unlock(&sForkLock); return status; } @@ -133,7 +127,7 @@ fork(void) thread_id thread; status_t status; - status = lazy_mutex_lock(&sForkLock); + status = mutex_lock(&sForkLock); if (status != B_OK) return status; @@ -143,7 +137,7 @@ fork(void) thread = _kern_fork(); if (thread < 0) { // something went wrong - lazy_mutex_unlock(&sForkLock); + mutex_unlock(&sForkLock); errno = thread; return -1; } @@ -152,7 +146,10 @@ fork(void) // we are the child // ToDo: initialize child __main_thread_id = find_thread(NULL); - __init_fork(); + mutex_init(&sForkLock, FORK_LOCK_NAME); + // TODO: The lock is already initialized and we in the fork()ing + // process we should make sure that it is in a consistent state when + // calling the kernel. __gRuntimeLoader->reinit_after_fork(); __reinit_pwd_backend_after_fork(); @@ -160,7 +157,7 @@ fork(void) } else { // we are the parent call_fork_hooks(sParentHooks); - lazy_mutex_unlock(&sForkLock); + mutex_unlock(&sForkLock); } return thread; diff --git a/src/system/libroot/posix/user_group_common.cpp b/src/system/libroot/posix/user_group_common.cpp index 3dc4f83f7d..cf2a59235a 100644 --- a/src/system/libroot/posix/user_group_common.cpp +++ b/src/system/libroot/posix/user_group_common.cpp @@ -1,8 +1,9 @@ /* - * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All rights reserved. + * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ + #include #include @@ -24,25 +25,27 @@ using BPrivate::Tokenizer; +static const char* const kUserGroupLockName = "user group"; + const char* BPrivate::kPasswdFile = "/etc/passwd"; const char* BPrivate::kGroupFile = "/etc/group"; const char* BPrivate::kShadowPwdFile = "/etc/shadow"; -static lazy_mutex sUserGroupLock; +static mutex sUserGroupLock = MUTEX_INITIALIZER(kUserGroupLockName); static port_id sRegistrarPort = -1; status_t BPrivate::user_group_lock() { - return lazy_mutex_lock(&sUserGroupLock); + return mutex_lock(&sUserGroupLock); } status_t BPrivate::user_group_unlock() { - lazy_mutex_unlock(&sUserGroupLock); + mutex_unlock(&sUserGroupLock); return B_OK; } @@ -385,12 +388,11 @@ BPrivate::parse_shadow_pwd_line(char* line, char*& name, char*& password, void __init_pwd_backend(void) { - lazy_mutex_init(&sUserGroupLock, "user group"); } void __reinit_pwd_backend_after_fork(void) { - lazy_mutex_init(&sUserGroupLock, "user group"); + mutex_init(&sUserGroupLock, kUserGroupLockName); } diff --git a/src/system/runtime_loader/elf.cpp b/src/system/runtime_loader/elf.cpp index 0e38e077ca..907c6a2ad0 100644 --- a/src/system/runtime_loader/elf.cpp +++ b/src/system/runtime_loader/elf.cpp @@ -38,6 +38,8 @@ // a handle returned by load_library() (dlopen()) #define RLD_GLOBAL_SCOPE ((void*)-2l) +static const char* const kLockName = "runtime loader"; + typedef void (*init_term_function)(image_id); @@ -47,7 +49,7 @@ image_t* gProgramImage; static image_t** sPreloadedImages = NULL; static uint32 sPreloadedImageCount = 0; -static recursive_lock sLock; +static recursive_lock sLock = RECURSIVE_LOCK_INITIALIZER(kLockName); static inline void @@ -928,8 +930,6 @@ terminate_program(void) void rldelf_init(void) { - recursive_lock_init(&sLock, "runtime loader"); - init_add_ons(); // create the debug area @@ -962,9 +962,7 @@ rldelf_init(void) status_t elf_reinit_after_fork(void) { - status_t error = recursive_lock_init(&sLock, "runtime loader"); - if (error != B_OK) - return error; + recursive_lock_init(&sLock, kLockName); // We also need to update the IDs of our images. We are the child and // and have cloned images with different IDs. Since in most cases (fork()