diff --git a/headers/private/shared/locks.h b/headers/private/shared/locks.h new file mode 100644 index 0000000000..08ab06bea5 --- /dev/null +++ b/headers/private/shared/locks.h @@ -0,0 +1,97 @@ +/* + * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. + * Distributed under the terms of the MIT License. + */ +#ifndef _LOCKS_H_ +#define _LOCKS_H_ + +#include + +#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 + +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 MutexLocker; +typedef AutoLocker ReadLocker; +typedef AutoLocker WriteLocker; + +#endif // __cplusplus + +#endif // _LOCKS_H_ diff --git a/src/system/libroot/os/Jamfile b/src/system/libroot/os/Jamfile index cc21069cc4..31adfce521 100644 --- a/src/system/libroot/os/Jamfile +++ b/src/system/libroot/os/Jamfile @@ -17,6 +17,7 @@ MergeObject os_main.o : fs_query.cpp fs_volume.c image.cpp + locks.cpp parsedate.cpp port.c scheduler.c diff --git a/src/system/libroot/os/locks.cpp b/src/system/libroot/os/locks.cpp new file mode 100644 index 0000000000..355e0a28fd --- /dev/null +++ b/src/system/libroot/os/locks.cpp @@ -0,0 +1,248 @@ +/* + * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. + * Distributed under the terms of the MIT License. + */ + +#include +#include + +#include + + +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; +}