From 370602bff6556f6cafc04a67f5a088a2f377eb4b Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Mon, 12 May 2008 13:56:11 +0000 Subject: [PATCH] Added pthread rwlock support. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25470 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/posix/pthread.h | 47 +- src/system/libroot/posix/pthread/Jamfile | 3 +- .../libroot/posix/pthread/pthread_rwlock.cpp | 430 ++++++++++++++++++ 3 files changed, 477 insertions(+), 3 deletions(-) create mode 100644 src/system/libroot/posix/pthread/pthread_rwlock.cpp diff --git a/headers/posix/pthread.h b/headers/posix/pthread.h index 8893ace9b7..a6a2a74182 100644 --- a/headers/posix/pthread.h +++ b/headers/posix/pthread.h @@ -6,6 +6,7 @@ #define _PTHREAD_H_ +#include #include @@ -17,8 +18,8 @@ typedef struct _pthread_cond *pthread_cond_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; -typedef struct _pthread_rwlockattr *pthread_rwlockattr_t; +typedef struct _pthread_rwlock pthread_rwlock_t; +typedef struct _pthread_rwlockattr pthread_rwlockattr_t; typedef struct _pthread_barrier *pthread_barrier_t; typedef struct _pthread_barrierattr *pthread_barrierattr_t; typedef struct _pthread_spinlock *pthread_spinlock_t; @@ -28,6 +29,26 @@ struct _pthread_once { pthread_mutex_t mutex; }; +struct _pthread_rwlock { + uint32_t flags; + int32_t owner; + union { + struct { + int32_t sem; + } shared; + struct { + int32_t lock_sem; + int32_t lock_count; + int32_t reader_count; + int32_t writer_count; + void* waiters[2]; + } local; + }; +}; + +struct _pthread_rwlockattr { + uint32_t flags; +}; enum pthread_mutex_type { PTHREAD_MUTEX_DEFAULT, @@ -159,6 +180,28 @@ extern int pthread_condattr_getpshared(const pthread_condattr_t *condAttr, extern int pthread_condattr_setpshared(pthread_condattr_t *condAttr, int processShared); +/* rwlock functions */ +extern int pthread_rwlock_init(pthread_rwlock_t *lock, + const pthread_rwlockattr_t *attr); +extern int pthread_rwlock_destroy(pthread_rwlock_t *lock); +extern int pthread_rwlock_rdlock(pthread_rwlock_t *lock); +extern int pthread_rwlock_tryrdlock(pthread_rwlock_t *lock); +extern int pthread_rwlock_timedrdlock(pthread_rwlock_t *lock, + const struct timespec *timeout); +extern int pthread_rwlock_wrlock(pthread_rwlock_t *lock); +extern int pthread_rwlock_trywrlock(pthread_rwlock_t *lock); +extern int pthread_rwlock_timedwrlock(pthread_rwlock_t *lock, + const struct timespec *timeout); +extern int pthread_rwlock_unlock(pthread_rwlock_t *lock); + +/* rwlock attribute functions */ +extern int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); +extern int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); +extern int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, + int *shared); +extern int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, + int shared); + /* 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 680306a5f2..6c0df16c28 100644 --- a/src/system/libroot/posix/pthread/Jamfile +++ b/src/system/libroot/posix/pthread/Jamfile @@ -1,7 +1,7 @@ SubDir HAIKU_TOP src system libroot posix pthread ; UsePrivateKernelHeaders ; -UsePrivateHeaders libroot ; +UsePrivateHeaders libroot shared ; MergeObject posix_pthread.o : pthread.c @@ -15,5 +15,6 @@ MergeObject posix_pthread.o : pthread_mutex.c pthread_mutexattr.c pthread_once.c + pthread_rwlock.cpp ; diff --git a/src/system/libroot/posix/pthread/pthread_rwlock.cpp b/src/system/libroot/posix/pthread/pthread_rwlock.cpp new file mode 100644 index 0000000000..26be6dff01 --- /dev/null +++ b/src/system/libroot/posix/pthread/pthread_rwlock.cpp @@ -0,0 +1,430 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include + + +#define MAX_READER_COUNT 1000000 + +#define RWLOCK_FLAG_SHARED 0x01 + + +struct Waiter : DoublyLinkedListLinkImpl { + Waiter(bool writer) + : + userThread(get_user_thread()), + thread(find_thread(NULL)), + writer(writer), + queued(false) + { + } + + user_thread* userThread; + thread_id thread; + status_t status; + bool writer; + bool queued; +}; + +typedef DoublyLinkedList WaiterList; + + +struct SharedRWLock { + uint32_t flags; + int32_t owner; + int32_t sem; + + status_t Init() + { + flags = RWLOCK_FLAG_SHARED; + owner = -1; + sem = create_sem(MAX_READER_COUNT, "pthread rwlock"); + + return sem >= 0 ? B_OK : EAGAIN; + } + + status_t Destroy() + { + if (sem < 0) + return B_BAD_VALUE; + return delete_sem(sem) == B_OK ? B_OK : B_BAD_VALUE; + } + + status_t ReadLock(bigtime_t timeout) + { + return acquire_sem_etc(sem, 1, + timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout); + } + + status_t WriteLock(bigtime_t timeout) + { + status_t error = acquire_sem_etc(sem, MAX_READER_COUNT, + timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout); + if (error == B_OK) + owner = find_thread(NULL); + return error; + } + + status_t Unlock() + { + if (find_thread(NULL) == owner) { + owner = -1; + return release_sem_etc(sem, MAX_READER_COUNT, 0); + } else + return release_sem(sem); + } +}; + + +struct LocalRWLock { + uint32_t flags; + int32_t owner; + int32_t lock_sem; + int32_t lock_count; + int32_t reader_count; + int32_t writer_count; + WaiterList waiters; + + status_t Init() + { + flags = 0; + owner = -1; + lock_sem = create_sem(0, "pthread rwlock"); + lock_count = 1; + reader_count = 0; + writer_count = 0; + new(&waiters) WaiterList; + + return lock_sem >= 0 ? B_OK : EAGAIN; + } + + status_t Destroy() + { + if (lock_sem < 0) + return B_BAD_VALUE; + return delete_sem(lock_sem) == B_OK ? B_OK : B_BAD_VALUE; + } + + bool StructureLock() + { + if (atomic_add((int32*)&lock_count, -1) <= 0) + acquire_sem(lock_sem); + return true; + } + + void StructureUnlock() + { + if (atomic_add((int32*)&lock_count, 1) < 0) + release_sem(lock_sem); + } + + status_t ReadLock(bigtime_t timeout) + { + Locker locker(this); + + if (writer_count == 0) { + reader_count++; + return B_OK; + } + + return _Wait(false, timeout); + } + + status_t WriteLock(bigtime_t timeout) + { + Locker locker(this); + + if (reader_count == 0 && writer_count == 0) { + writer_count++; + owner = find_thread(NULL); + return B_OK; + } + + return _Wait(true, timeout); + } + + status_t Unlock() + { + Locker locker(this); + + if (find_thread(NULL) == owner) { + writer_count--; + owner = -1; + } else + reader_count--; + + _Unblock(); + + return B_OK; + } + +private: + status_t _Wait(bool writer, bigtime_t timeout) + { + if (timeout == 0) + return B_TIMED_OUT; + + Waiter waiter(writer); + waiters.Add(&waiter); + waiter.queued = true; + waiter.userThread->wait_status = 1; + + if (writer) + writer_count++; + + StructureUnlock(); + status_t error = _kern_block_thread( + timeout >= 0 ? B_ABSOLUTE_REAL_TIME_TIMEOUT : 0, timeout); + StructureLock(); + + if (!waiter.queued) + return waiter.status; + + // we're still queued, which means an error (timeout, interrupt) + // occurred + waiters.Remove(&waiter); + + if (writer) + writer_count--; + + _Unblock(); + + return error; + } + + void _Unblock() + { + // Check whether there any waiting threads at all and whether anyone + // has the write lock + Waiter* waiter = waiters.Head(); + if (waiter == NULL || owner >= 0) + return; + + // writer at head of queue? + if (waiter->writer) { + if (reader_count == 0) { + waiter->status = B_OK; + waiter->queued = false; + waiters.Remove(waiter); + owner = waiter->thread; + + if (waiter->userThread->wait_status > 0) { + waiter->userThread->wait_status = B_OK; + _kern_unblock_thread(waiter->thread, B_OK); + } + } + return; + } + + // wake up one or more readers -- we unblock more than one reader at + // a time to save trips to the kernel + while (!waiters.IsEmpty() && !waiters.Head()->writer) { + static const int kMaxReaderUnblockCount = 128; + thread_id readers[kMaxReaderUnblockCount]; + int readerCount = 0; + + while (readerCount < kMaxReaderUnblockCount + && (waiter = waiters.Head()) != NULL + && !waiter->writer) { + waiter->status = B_OK; + waiter->queued = false; + waiters.Remove(waiter); + + if (waiter->userThread->wait_status > 0) { + waiter->userThread->wait_status = B_OK; + readers[readerCount++] = waiter->thread; + } + } + + if (readerCount > 0) + _kern_unblock_threads(readers, readerCount, B_OK); + } + } + + + struct Locking { + inline bool Lock(LocalRWLock* lockable) + { + return lockable->StructureLock(); + } + + inline void Unlock(LocalRWLock* lockable) + { + lockable->StructureUnlock(); + } + }; + typedef AutoLocker Locker; +}; + + +static void inline +assert_dummy() +{ + STATIC_ASSERT(sizeof(pthread_rwlock_t) >= sizeof(SharedRWLock)); + STATIC_ASSERT(sizeof(pthread_rwlock_t) >= sizeof(LocalRWLock)); +} + + +// #pragma mark - public lock functions + + +int +pthread_rwlock_init(pthread_rwlock_t* lock, const pthread_rwlockattr_t* attr) +{ + bool shared = attr != NULL && (attr->flags & RWLOCK_FLAG_SHARED) != 0; + + if (shared) + return ((SharedRWLock*)lock)->Init(); + else + return ((LocalRWLock*)lock)->Init(); +} + + +int +pthread_rwlock_destroy(pthread_rwlock_t* lock) +{ + if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) + return ((SharedRWLock*)lock)->Destroy(); + else + return ((LocalRWLock*)lock)->Destroy(); +} + + +int +pthread_rwlock_rdlock(pthread_rwlock_t* lock) +{ + if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) + return ((SharedRWLock*)lock)->ReadLock(B_INFINITE_TIMEOUT); + else + return ((LocalRWLock*)lock)->ReadLock(B_INFINITE_TIMEOUT); +} + + +int +pthread_rwlock_tryrdlock(pthread_rwlock_t* lock) +{ + status_t error; + if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) + error = ((SharedRWLock*)lock)->ReadLock(0); + else + error = ((LocalRWLock*)lock)->ReadLock(0); + + return error == B_TIMED_OUT ? EBUSY : error; +} + + +int pthread_rwlock_timedrdlock(pthread_rwlock_t* lock, + const struct timespec *timeout) +{ + bigtime_t timeoutMicros = timeout->tv_sec * 1000000LL + + timeout->tv_nsec / 1000LL; + + status_t error; + if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) + error = ((SharedRWLock*)lock)->ReadLock(timeoutMicros); + else + error = ((LocalRWLock*)lock)->ReadLock(timeoutMicros); + + return error == B_TIMED_OUT ? EBUSY : error; +} + + +int +pthread_rwlock_wrlock(pthread_rwlock_t* lock) +{ + if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) + return ((SharedRWLock*)lock)->WriteLock(B_INFINITE_TIMEOUT); + else + return ((LocalRWLock*)lock)->WriteLock(B_INFINITE_TIMEOUT); +} + + +int +pthread_rwlock_trywrlock(pthread_rwlock_t* lock) +{ + status_t error; + if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) + error = ((SharedRWLock*)lock)->WriteLock(0); + else + error = ((LocalRWLock*)lock)->WriteLock(0); + + return error == B_TIMED_OUT ? EBUSY : error; +} + + +int +pthread_rwlock_timedwrlock(pthread_rwlock_t* lock, + const struct timespec *timeout) +{ + bigtime_t timeoutMicros = timeout->tv_sec * 1000000LL + + timeout->tv_nsec / 1000LL; + + status_t error; + if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) + error = ((SharedRWLock*)lock)->WriteLock(timeoutMicros); + else + error = ((LocalRWLock*)lock)->WriteLock(timeoutMicros); + + return error == B_TIMED_OUT ? EBUSY : error; +} + + +int +pthread_rwlock_unlock(pthread_rwlock_t* lock) +{ + if ((lock->flags & RWLOCK_FLAG_SHARED) != 0) + return ((SharedRWLock*)lock)->Unlock(); + else + return ((LocalRWLock*)lock)->Unlock(); +} + + +// #pragma mark - public attribute functions + + +int +pthread_rwlockattr_init(pthread_rwlockattr_t* attr) +{ + attr->flags = 0; + return 0; +} + + +int +pthread_rwlockattr_destroy(pthread_rwlockattr_t* attr) +{ + return 0; +} + + +int +pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* attr, int* shared) +{ + *shared = (attr->flags & RWLOCK_FLAG_SHARED) != 0; + return 0; +} + + +int +pthread_rwlockattr_setpshared(pthread_rwlockattr_t* attr, int shared) +{ + if (shared) + attr->flags |= RWLOCK_FLAG_SHARED; + else + attr->flags &= ~RWLOCK_FLAG_SHARED; + + return 0; +} +