* Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
* Distributed under the terms of the MIT License.
*/
#include <locks.h>
#include <OS.h>
#include <syscalls.h>
#include <user_thread.h>
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;
get_user_thread()->wait_status = 1;
mutex_unlock(&lock->lock);
status_t result;
do {
result = _kern_block_thread(0, 0);
} while (result == B_INTERRUPTED);
mutex_lock(&lock->lock);
return result;
}
static void
rw_lock_unblock(rw_lock *lock)
{
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;
}
}
void
__rw_lock_init(rw_lock *lock, const char *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;
mutex_init_etc(&lock->lock, name, flags);
}
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;
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;
}