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:
parent
a3ff5c0561
commit
474d27da32
131
headers/private/shared/RWLockManager.h
Normal file
131
headers/private/shared/RWLockManager.h
Normal 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
|
@ -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
|
||||
;
|
||||
|
259
src/kits/shared/RWLockManager.cpp
Normal file
259
src/kits/shared/RWLockManager.cpp
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user