Added RWLockable and RWLockManager classes. The former is basically a

light-weight read-write lock, that doesn't use a semaphore itself. Locking
and unlocking has to be done via methods of RWLockManager. This combo allows
lots of locks without risking to hit the semaphore limit.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29686 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2009-03-24 23:13:56 +00:00
parent a3ff5c0561
commit 474d27da32
3 changed files with 395 additions and 0 deletions

View File

@ -0,0 +1,131 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef _RW_LOCK_MANAGER_H
#define _RW_LOCK_MANAGER_H
#include <Locker.h>
#include <util/DoublyLinkedList.h>
namespace BPrivate {
class RWLockManager;
class RWLockable {
public:
RWLockable();
private:
struct Waiter : DoublyLinkedListLinkImpl<Waiter> {
Waiter(bool writer)
:
thread(find_thread(NULL)),
writer(writer),
queued(false)
{
}
thread_id thread;
status_t status;
bool writer;
bool queued;
};
typedef DoublyLinkedList<Waiter> WaiterList;
friend class RWLockManager;
private:
thread_id fOwner;
int32 fOwnerCount;
int32 fReaderCount;
WaiterList fWaiters;
};
class RWLockManager {
public:
RWLockManager();
~RWLockManager();
status_t Init() { return fLock.InitCheck(); }
bool Lock() { return fLock.Lock(); }
void Unlock() { return fLock.Unlock(); }
bool ReadLock(RWLockable* lockable);
bool TryReadLock(RWLockable* lockable);
status_t ReadLockWithTimeout(RWLockable* lockable,
bigtime_t timeout);
void ReadUnlock(RWLockable* lockable);
bool WriteLock(RWLockable* lockable);
bool TryWriteLock(RWLockable* lockable);
status_t WriteLockWithTimeout(RWLockable* lockable,
bigtime_t timeout);
void WriteUnlock(RWLockable* lockable);
inline bool GenericLock(bool write, RWLockable* lockable);
inline bool TryGenericLock(bool write,
RWLockable* lockable);
inline status_t GenericLockWithTimeout(bool write,
RWLockable* lockable, bigtime_t timeout);
inline void GenericUnlock(bool write, RWLockable* lockable);
private:
status_t _Wait(RWLockable* lockable, bool writer,
bigtime_t timeout);
void _Unblock(RWLockable* lockable);
private:
BLocker fLock;
};
inline bool
RWLockManager::GenericLock(bool write, RWLockable* lockable)
{
return write ? WriteLock(lockable) : ReadLock(lockable);
}
inline bool
RWLockManager::TryGenericLock(bool write, RWLockable* lockable)
{
return write ? TryWriteLock(lockable) : TryReadLock(lockable);
}
inline status_t
RWLockManager::GenericLockWithTimeout(bool write, RWLockable* lockable,
bigtime_t timeout)
{
return write
? WriteLockWithTimeout(lockable, timeout)
: ReadLockWithTimeout(lockable, timeout);
}
inline void
RWLockManager::GenericUnlock(bool write, RWLockable* lockable)
{
if (write)
WriteUnlock(lockable);
else
ReadUnlock(lockable);
}
} // namespace BPrivate
using BPrivate::RWLockable;
using BPrivate::RWLockManager;
#endif // _RW_LOCK_MANAGER_H

View File

@ -6,6 +6,10 @@ AddSubDirSupportedPlatforms libbe_test ;
UseLibraryHeaders agg ;
UsePrivateHeaders shared ;
# for RWLockManager only
UsePrivateSystemHeaders ;
UsePrivateHeaders kernel libroot ;
StaticLibrary libshared.a :
AboutWindow.cpp
AffineTransform.cpp
@ -14,4 +18,5 @@ StaticLibrary libshared.a :
CommandPipe.cpp
DateTime.cpp
HashString.cpp
RWLockManager.cpp
;

View File

@ -0,0 +1,259 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <RWLockManager.h>
#include <AutoLocker.h>
#include <syscalls.h>
#include <user_thread.h>
RWLockable::RWLockable()
:
fOwner(-1),
fOwnerCount(0),
fReaderCount(0)
{
}
RWLockManager::RWLockManager()
:
fLock("r/w lock manager")
{
}
RWLockManager::~RWLockManager()
{
}
bool
RWLockManager::ReadLock(RWLockable* lockable)
{
AutoLocker<RWLockManager> locker(this);
if (lockable->fWaiters.IsEmpty()) {
lockable->fReaderCount++;
return true;
}
return _Wait(lockable, false, B_INFINITE_TIMEOUT) == B_OK;
}
bool
RWLockManager::TryReadLock(RWLockable* lockable)
{
AutoLocker<RWLockManager> locker(this);
if (lockable->fWaiters.IsEmpty()) {
lockable->fReaderCount++;
return true;
}
return false;
}
status_t
RWLockManager::ReadLockWithTimeout(RWLockable* lockable, bigtime_t timeout)
{
AutoLocker<RWLockManager> locker(this);
if (lockable->fWaiters.IsEmpty()) {
lockable->fReaderCount++;
return B_OK;
}
return _Wait(lockable, false, timeout);
}
void
RWLockManager::ReadUnlock(RWLockable* lockable)
{
AutoLocker<RWLockManager> locker(this);
if (lockable->fReaderCount <= 0) {
debugger("RWLockManager::ReadUnlock(): Not read-locked!");
return;
}
if (--lockable->fReaderCount == 0)
_Unblock(lockable);
}
bool
RWLockManager::WriteLock(RWLockable* lockable)
{
AutoLocker<RWLockManager> locker(this);
thread_id thread = find_thread(NULL);
if (lockable->fOwner == thread) {
lockable->fOwnerCount++;
return true;
}
if (lockable->fReaderCount == 0 && lockable->fWaiters.IsEmpty()) {
lockable->fOwnerCount = 1;
lockable->fOwner = find_thread(NULL);
return true;
}
return _Wait(lockable, true, B_INFINITE_TIMEOUT) == B_OK;
}
bool
RWLockManager::TryWriteLock(RWLockable* lockable)
{
AutoLocker<RWLockManager> locker(this);
thread_id thread = find_thread(NULL);
if (lockable->fOwner == thread) {
lockable->fOwnerCount++;
return true;
}
if (lockable->fReaderCount == 0 && lockable->fWaiters.IsEmpty()) {
lockable->fOwnerCount++;
lockable->fOwner = thread;
return true;
}
return false;
}
status_t
RWLockManager::WriteLockWithTimeout(RWLockable* lockable, bigtime_t timeout)
{
AutoLocker<RWLockManager> locker(this);
thread_id thread = find_thread(NULL);
if (lockable->fOwner == thread) {
lockable->fOwnerCount++;
return B_OK;
}
if (lockable->fReaderCount == 0 && lockable->fWaiters.IsEmpty()) {
lockable->fOwnerCount++;
lockable->fOwner = thread;
return B_OK;
}
return _Wait(lockable, true, timeout);
}
void
RWLockManager::WriteUnlock(RWLockable* lockable)
{
AutoLocker<RWLockManager> locker(this);
if (find_thread(NULL) != lockable->fOwner) {
debugger("RWLockManager::WriteUnlock(): Not write-locked by calling "
"thread!");
return;
}
if (--lockable->fOwnerCount == 0) {
lockable->fOwner = -1;
_Unblock(lockable);
}
}
status_t
RWLockManager::_Wait(RWLockable* lockable, bool writer, bigtime_t timeout)
{
if (timeout == 0)
return B_TIMED_OUT;
// enqueue a waiter
RWLockable::Waiter waiter(writer);
lockable->fWaiters.Add(&waiter);
waiter.queued = true;
get_user_thread()->wait_status = 1;
// wait
Unlock();
status_t error;
do {
error = _kern_block_thread(
timeout >= 0 ? B_RELATIVE_TIMEOUT : 0, timeout);
// TODO: When interrupted we should adjust the timeout, respectively
// convert to an absolute timeout in the first place!
} while (error == B_INTERRUPTED);
Lock();
if (!waiter.queued)
return waiter.status;
// we're still queued, which means an error (timeout, interrupt)
// occurred
lockable->fWaiters.Remove(&waiter);
_Unblock(lockable);
return error;
}
void
RWLockManager::_Unblock(RWLockable* lockable)
{
// Check whether there any waiting threads at all and whether anyone
// has the write lock
RWLockable::Waiter* waiter = lockable->fWaiters.Head();
if (waiter == NULL || lockable->fOwner >= 0)
return;
// writer at head of queue?
if (waiter->writer) {
if (lockable->fReaderCount == 0) {
waiter->status = B_OK;
waiter->queued = false;
lockable->fWaiters.Remove(waiter);
lockable->fOwner = waiter->thread;
lockable->fOwnerCount = 1;
_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 (!lockable->fWaiters.IsEmpty()
&& !lockable->fWaiters.Head()->writer) {
static const int kMaxReaderUnblockCount = 128;
thread_id readers[kMaxReaderUnblockCount];
int readerCount = 0;
while (readerCount < kMaxReaderUnblockCount
&& (waiter = lockable->fWaiters.Head()) != NULL
&& !waiter->writer) {
waiter->status = B_OK;
waiter->queued = false;
lockable->fWaiters.Remove(waiter);
readers[readerCount++] = waiter->thread;
lockable->fReaderCount++;
}
if (readerCount > 0)
_kern_unblock_threads(readers, readerCount, B_OK);
}
}