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:
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* 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
|
||||||
@@ -6,6 +6,10 @@ AddSubDirSupportedPlatforms libbe_test ;
|
|||||||
UseLibraryHeaders agg ;
|
UseLibraryHeaders agg ;
|
||||||
UsePrivateHeaders shared ;
|
UsePrivateHeaders shared ;
|
||||||
|
|
||||||
|
# for RWLockManager only
|
||||||
|
UsePrivateSystemHeaders ;
|
||||||
|
UsePrivateHeaders kernel libroot ;
|
||||||
|
|
||||||
StaticLibrary libshared.a :
|
StaticLibrary libshared.a :
|
||||||
AboutWindow.cpp
|
AboutWindow.cpp
|
||||||
AffineTransform.cpp
|
AffineTransform.cpp
|
||||||
@@ -14,4 +18,5 @@ StaticLibrary libshared.a :
|
|||||||
CommandPipe.cpp
|
CommandPipe.cpp
|
||||||
DateTime.cpp
|
DateTime.cpp
|
||||||
HashString.cpp
|
HashString.cpp
|
||||||
|
RWLockManager.cpp
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user