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.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33543 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2009-10-12 11:19:33 +00:00
parent d75e3d6c23
commit 93b63126e1
3 changed files with 346 additions and 0 deletions

View File

@ -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 <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_

View File

@ -17,6 +17,7 @@ MergeObject os_main.o :
fs_query.cpp
fs_volume.c
image.cpp
locks.cpp
parsedate.cpp
port.c
scheduler.c

View File

@ -0,0 +1,248 @@
/*
* Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
* 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;
}