* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* IngoWeinhold <bonefish@cs.tu-berlin.de>
*/
#include "RWLocker.h"
#include <String.h>
struct RWLocker::ReadLockInfo {
thread_id reader;
int32 count;
};
RWLocker::RWLocker()
: fLock(),
fMutex(),
fQueue(),
fReaderCount(0),
fWriterCount(0),
fReadLockInfos(8),
fWriter(B_ERROR),
fWriterWriterCount(0),
fWriterReaderCount(0)
{
_Init(NULL);
}
RWLocker::RWLocker(const char* name)
: fLock(name),
fMutex(),
fQueue(),
fReaderCount(0),
fWriterCount(0),
fReadLockInfos(8),
fWriter(B_ERROR),
fWriterWriterCount(0),
fWriterReaderCount(0)
{
_Init(name);
}
RWLocker::~RWLocker()
{
fLock.Lock();
delete_sem(fMutex.semaphore);
delete_sem(fQueue.semaphore);
for (int32 i = 0; ReadLockInfo* info = _ReadLockInfoAt(i); i++)
delete info;
}
bool
RWLocker::ReadLock()
{
status_t error = _ReadLock(B_INFINITE_TIMEOUT);
return (error == B_OK);
}
status_t
RWLocker::ReadLockWithTimeout(bigtime_t timeout)
{
bigtime_t absoluteTimeout = system_time() + timeout;
if (timeout > 0 && absoluteTimeout < 0)
absoluteTimeout = B_INFINITE_TIMEOUT;
return _ReadLock(absoluteTimeout);
}
void
RWLocker::ReadUnlock()
{
if (fLock.Lock()) {
thread_id thread = find_thread(NULL);
if (thread == fWriter) {
if (fWriterReaderCount > 0)
fWriterReaderCount--;
} else {
int32 index = _IndexOf(thread);
if (ReadLockInfo* info = _ReadLockInfoAt(index)) {
fReaderCount--;
if (--info->count == 0) {
_DeleteReadLockInfo(index);
}
if (fReaderCount == 0) {
_ReleaseBenaphore(fMutex);
}
}
}
fLock.Unlock();
}
}
bool
RWLocker::IsReadLocked() const
{
bool result = false;
if (fLock.Lock()) {
thread_id thread = find_thread(NULL);
result = (thread == fWriter || _IndexOf(thread) >= 0);
fLock.Unlock();
}
return result;
}
bool
RWLocker::WriteLock()
{
status_t error = _WriteLock(B_INFINITE_TIMEOUT);
return (error == B_OK);
}
status_t
RWLocker::WriteLockWithTimeout(bigtime_t timeout)
{
bigtime_t absoluteTimeout = system_time() + timeout;
if (timeout > 0 && absoluteTimeout < 0)
absoluteTimeout = B_INFINITE_TIMEOUT;
return _WriteLock(absoluteTimeout);
}
void
RWLocker::WriteUnlock()
{
if (fLock.Lock()) {
thread_id thread = find_thread(NULL);
if (thread == fWriter) {
fWriterCount--;
if (--fWriterWriterCount == 0) {
fWriter = B_ERROR;
if (fWriterReaderCount > 0) {
_NewReadLockInfo(thread, fWriterReaderCount);
if (fReaderCount > 0)
_ReleaseBenaphore(fMutex);
fReaderCount += fWriterReaderCount;
fWriterReaderCount = 0;
} else {
_ReleaseBenaphore(fMutex);
}
}
}
fLock.Unlock();
}
}
bool
RWLocker::IsWriteLocked() const
{
return (fWriter == find_thread(NULL));
}
void
RWLocker::_Init(const char* name)
{
BString mutexName(name);
mutexName += "_RWLocker_mutex";
fMutex.semaphore = create_sem(0, mutexName.String());
fMutex.counter = 0;
BString queueName(name);
queueName += "_RWLocker_queue";
fQueue.semaphore = create_sem(0, queueName.String());
fQueue.counter = 0;
}
status_t
RWLocker::_ReadLock(bigtime_t timeout)
{
status_t error = B_OK;
thread_id thread = find_thread(NULL);
bool locked = false;
if (fLock.Lock()) {
if (thread == fWriter) {
fWriterReaderCount++;
locked = true;
} else if (ReadLockInfo* info = _ReadLockInfoAt(_IndexOf(thread))) {
info->count++;
fReaderCount++;
locked = true;
}
fLock.Unlock();
} else
error = B_ERROR;
if (error == B_OK && !locked) {
error = _AcquireBenaphore(fQueue, timeout);
if (error == B_OK) {
if (fLock.Lock()) {
bool firstReader = false;
if (++fReaderCount == 1) {
_NewReadLockInfo(thread);
firstReader = true;
} else
_NewReadLockInfo(thread);
fLock.Unlock();
if (firstReader) {
error = _AcquireBenaphore(fMutex, timeout);
switch (error) {
case B_OK:
break;
case B_TIMED_OUT: {
if (fLock.Lock()) {
_DeleteReadLockInfo(_IndexOf(thread));
fReaderCount--;
fLock.Unlock();
}
break;
}
default:
break;
}
}
_ReleaseBenaphore(fQueue);
} else {
error = B_ERROR;
}
}
}
return error;
}
status_t
RWLocker::_WriteLock(bigtime_t timeout)
{
status_t error = B_ERROR;
if (fLock.Lock()) {
bool infiniteTimeout = (timeout == B_INFINITE_TIMEOUT);
bool locked = false;
int32 readerCount = 0;
thread_id thread = find_thread(NULL);
int32 index = _IndexOf(thread);
if (ReadLockInfo* info = _ReadLockInfoAt(index)) {
if (fWriterCount > 0) {
if (infiniteTimeout) {
readerCount = info->count;
fWriterCount++;
fReaderCount -= readerCount;
_DeleteReadLockInfo(index);
error = B_OK;
} else {
error = B_WOULD_BLOCK;
}
} else if (info->count == fReaderCount) {
fWriter = thread;
fWriterCount++;
fWriterWriterCount = 1;
fWriterReaderCount = info->count;
fReaderCount -= fWriterReaderCount;
_DeleteReadLockInfo(index);
locked = true;
error = B_OK;
} else {
if (infiniteTimeout) {
readerCount = info->count;
fWriterCount++;
fReaderCount -= readerCount;
_DeleteReadLockInfo(index);
error = B_OK;
} else
error = B_WOULD_BLOCK;
}
} else {
if (fWriter == thread) {
fWriterCount++;
fWriterWriterCount++;
locked = true;
error = B_OK;
} else {
fWriterCount++;
error = B_OK;
}
}
fLock.Unlock();
if (!locked && error == B_OK) {
error = _AcquireBenaphore(fQueue, timeout);
switch (error) {
case B_OK:
break;
case B_TIMED_OUT: {
if (fLock.Lock()) {
fWriterCount--;
fLock.Unlock();
}
break;
}
default:
break;
}
}
if (!locked && error == B_OK) {
error = _AcquireBenaphore(fMutex, timeout);
switch (error) {
case B_OK: {
fWriter = thread;
fWriterWriterCount = 1;
fWriterReaderCount = readerCount;
break;
}
case B_TIMED_OUT: {
if (fLock.Lock()) {
fWriterCount--;
fLock.Unlock();
}
break;
}
default:
break;
}
_ReleaseBenaphore(fQueue);
}
} else
error = B_ERROR;
return error;
}
int32
RWLocker::_AddReadLockInfo(ReadLockInfo* info)
{
int32 index = fReadLockInfos.CountItems();
fReadLockInfos.AddItem(info, index);
return index;
}
int32
RWLocker::_NewReadLockInfo(thread_id thread, int32 count)
{
ReadLockInfo* info = new ReadLockInfo;
info->reader = thread;
info->count = count;
return _AddReadLockInfo(info);
}
void
RWLocker::_DeleteReadLockInfo(int32 index)
{
if (ReadLockInfo* info = (ReadLockInfo*)fReadLockInfos.RemoveItem(index))
delete info;
}
RWLocker::ReadLockInfo*
RWLocker::_ReadLockInfoAt(int32 index) const
{
return (ReadLockInfo*)fReadLockInfos.ItemAt(index);
}
int32
RWLocker::_IndexOf(thread_id thread) const
{
int32 count = fReadLockInfos.CountItems();
for (int32 i = 0; i < count; i++) {
if (_ReadLockInfoAt(i)->reader == thread)
return i;
}
return -1;
}
status_t
RWLocker::_AcquireBenaphore(Benaphore& benaphore, bigtime_t timeout)
{
status_t error = B_OK;
if (atomic_add(&benaphore.counter, 1) > 0) {
error = acquire_sem_etc(benaphore.semaphore, 1, B_ABSOLUTE_TIMEOUT,
timeout);
}
return error;
}
void
RWLocker::_ReleaseBenaphore(Benaphore& benaphore)
{
if (atomic_add(&benaphore.counter, -1) > 1)
release_sem(benaphore.semaphore);
}