Author: mmlr Date: 2009-10-12 13:19:33 +0200 (Mon, 12 Oct 2009) New Revision: 33543 Changeset: http://dev.haiku-os.org/changeset/33543/haiku Added: haiku/trunk/headers/private/shared/locks.h haiku/trunk/src/system/libroot/os/locks.cpp Modified: haiku/trunk/src/system/libroot/os/Jamfile Log: Adding mutex and rw_lock with the same interface as the kernel versions to libroot. The mutex is a simple benaphore, the rw_lock is pretty much the same as the one from libkernelland_emu but uses a mutex per lock instead of emulating a global thread lock. Also added MutexLocking and RWLock{Read|Write}Locking and AutoLockers based on them. It's cased with __cplusplus so the locks are also usable from C. Everything's currently exposed in shared/private/locks.h but I think we should make these locking primitves public. Added: haiku/trunk/headers/private/shared/locks.h =================================================================== --- haiku/trunk/headers/private/shared/locks.h (rev 0) +++ haiku/trunk/headers/private/shared/locks.h 2009-10-12 11:19:33 UTC (rev 33543) @@ -0,0 +1,97 @@ +/* + * Copyright 2009, Michael Lotz, mmlr@xxxxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef _LOCKS_H_ +#define _LOCKS_H_ + +#include <OS.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct mutex { + int32 benaphore; + sem_id semaphore; +} mutex; + +status_t mutex_init(mutex *lock, const char *name); +void mutex_destroy(mutex *lock); +status_t mutex_lock(mutex *lock); +void mutex_unlock(mutex *lock); + + +typedef struct rw_lock { + const char * name; + mutex lock; + struct rw_lock_waiter * waiters; + struct rw_lock_waiter * last_waiter; + thread_id holder; + int32 reader_count; + int32 writer_count; + int32 owner_count; +} rw_lock; + +status_t rw_lock_init(rw_lock *lock, const char *name); +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); +status_t rw_lock_write_lock(rw_lock *lock); +status_t rw_lock_write_unlock(rw_lock *lock); + +#ifdef __cplusplus +} // extern "C" + + +#include <AutoLocker.h> + +class MutexLocking { +public: + inline bool Lock(struct mutex *lock) + { + return mutex_lock(lock) == B_OK; + } + + inline void Unlock(struct mutex *lock) + { + mutex_unlock(lock); + } +}; + + +class RWLockReadLocking { +public: + inline bool Lock(struct rw_lock *lock) + { + return rw_lock_read_lock(lock) == B_OK; + } + + inline void Unlock(struct rw_lock *lock) + { + rw_lock_read_unlock(lock); + } +}; + + +class RWLockWriteLocking { +public: + inline bool Lock(struct rw_lock *lock) + { + return rw_lock_write_lock(lock) == B_OK; + } + + inline void Unlock(struct rw_lock *lock) + { + rw_lock_write_unlock(lock); + } +}; + + +typedef AutoLocker<mutex, MutexLocking> MutexLocker; +typedef AutoLocker<rw_lock, RWLockReadLocking> ReadLocker; +typedef AutoLocker<rw_lock, RWLockWriteLocking> WriteLocker; + +#endif // __cplusplus + +#endif // _LOCKS_H_ Modified: haiku/trunk/src/system/libroot/os/Jamfile =================================================================== --- haiku/trunk/src/system/libroot/os/Jamfile 2009-10-12 03:17:17 UTC (rev 33542) +++ haiku/trunk/src/system/libroot/os/Jamfile 2009-10-12 11:19:33 UTC (rev 33543) @@ -17,6 +17,7 @@ fs_query.cpp fs_volume.c image.cpp + locks.cpp parsedate.cpp port.c scheduler.c Added: haiku/trunk/src/system/libroot/os/locks.cpp =================================================================== --- haiku/trunk/src/system/libroot/os/locks.cpp (rev 0) +++ haiku/trunk/src/system/libroot/os/locks.cpp 2009-10-12 11:19:33 UTC (rev 33543) @@ -0,0 +1,248 @@ +/* + * Copyright 2009, Michael Lotz, mmlr@xxxxxxxxx + * Distributed under the terms of the MIT License. + */ + +#include <locks.h> +#include <syscalls.h> + +#include <OS.h> + + +status_t +mutex_init(mutex *lock, const char *name) +{ + if (lock == NULL || name == NULL) + return B_BAD_VALUE; + + lock->benaphore = 0; + lock->semaphore = create_sem(0, name); + if (lock->semaphore < 0) + return lock->semaphore; + + return B_OK; +} + + +void +mutex_destroy(mutex *lock) +{ + delete_sem(lock->semaphore); +} + + +status_t +mutex_lock(mutex *lock) +{ + if (atomic_add(&lock->benaphore, 1) == 0) + return B_OK; + + status_t result; + do { + result = acquire_sem(lock->semaphore); + } while (result == B_INTERRUPTED); + + return result; +} + + +void +mutex_unlock(mutex *lock) +{ + if (atomic_add(&lock->benaphore, -1) != 1) + release_sem(lock->semaphore); +} + + +typedef struct rw_lock_waiter { + rw_lock_waiter * next; + thread_id thread; + bool writer; +} rw_lock_waiter; + + +static status_t +rw_lock_wait(rw_lock *lock, bool writer) +{ + rw_lock_waiter waiter; + waiter.thread = find_thread(NULL); + waiter.next = NULL; + waiter.writer = writer; + + if (lock->waiters != NULL) + lock->last_waiter->next = &waiter; + else + lock->waiters = &waiter; + + lock->last_waiter = &waiter; + + // the rw_lock is locked when entering, release it before blocking + mutex_unlock(&lock->lock); + + status_t result; + do { + result = _kern_block_thread(0, 0); + } while (result == B_INTERRUPTED); + + // and lock it again before returning + mutex_lock(&lock->lock); + return result; +} + + +static void +rw_lock_unblock(rw_lock *lock) +{ + // this is called locked + if (lock->holder >= 0) + return; + + rw_lock_waiter *waiter = lock->waiters; + if (waiter == NULL) + return; + + if (waiter->writer) { + if (lock->reader_count > 0) + return; + + lock->waiters = waiter->next; + lock->holder = waiter->thread; + _kern_unblock_thread(waiter->thread, B_OK); + return; + } + + while (waiter != NULL && !waiter->writer) { + lock->reader_count++; + lock->waiters = waiter->next; + _kern_unblock_thread(waiter->thread, B_OK); + waiter = lock->waiters; + } +} + + +status_t +rw_lock_init(rw_lock *lock, const char *name) +{ + lock->name = name; + lock->waiters = NULL; + lock->holder = -1; + lock->reader_count = 0; + lock->writer_count = 0; + lock->owner_count = 0; + return mutex_init(&lock->lock, name); +} + + +void +rw_lock_destroy(rw_lock *lock) +{ + mutex_lock(&lock->lock); + + rw_lock_waiter *waiter = lock->waiters; + while (waiter != NULL) { + _kern_unblock_thread(waiter->thread, B_ERROR); + waiter = waiter->next; + } + + mutex_destroy(&lock->lock); +} + + +status_t +rw_lock_read_lock(rw_lock *lock) +{ + MutexLocker locker(lock->lock); + + if (lock->writer_count == 0) { + lock->reader_count++; + return B_OK; + } + + if (lock->holder == find_thread(NULL)) { + lock->owner_count++; + return B_OK; + } + + return rw_lock_wait(lock, false); +} + + +status_t +rw_lock_read_unlock(rw_lock *lock) +{ + MutexLocker locker(lock->lock); + + if (lock->holder == find_thread(NULL)) { + if (--lock->owner_count > 0) + return B_OK; + + // this originally has been a write lock + lock->writer_count--; + lock->holder = -1; + + rw_lock_unblock(lock); + return B_OK; + } + + if (lock->reader_count <= 0) { + debugger("rw_lock not read locked"); + return B_ERROR; + } + + lock->reader_count--; + rw_lock_unblock(lock); + return B_OK; +} + + +status_t +rw_lock_write_lock(rw_lock *lock) +{ + MutexLocker locker(lock->lock); + + if (lock->reader_count == 0 && lock->writer_count == 0) { + lock->writer_count++; + lock->holder = find_thread(NULL); + lock->owner_count = 1; + return B_OK; + } + + if (lock->holder == find_thread(NULL)) { + lock->owner_count++; + return B_OK; + } + + lock->writer_count++; + + status_t result = rw_lock_wait(lock, true); + if (result != B_OK) + return result; + + if (lock->holder != find_thread(NULL)) { + debugger("write locked but holder not set"); + return B_ERROR; + } + + lock->owner_count = 1; + return B_OK; +} + + +status_t +rw_lock_write_unlock(rw_lock *lock) +{ + MutexLocker locker(lock->lock); + + if (lock->holder != find_thread(NULL)) { + debugger("rw_lock not write locked"); + return B_ERROR; + } + + if (--lock->owner_count > 0) + return B_OK; + + lock->writer_count--; + lock->holder = -1; + rw_lock_unblock(lock); + return B_OK; +}