with the new design, there is always only one redraw message in the WindowLayers message queue, therefor, ReadLockWithTimeout() is no longer needed, removed RWLocker again as MultiLocker suits our needs.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15232 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2005-11-29 20:53:00 +00:00
parent c63a78aafa
commit 312345bcb1
6 changed files with 22 additions and 632 deletions

View File

@ -491,18 +491,21 @@ Desktop::SendToBack(WindowLayer* window)
void
Desktop::SetFocusWindow(WindowLayer* window)
{
// TODO: find bug, this invalidates too many regions
if (fFocusWindow == window)
return;
if (fFocusWindow)
fFocusWindow->SetFocus(false);
if (LockClipping()) {
fFocusWindow = window;
if (fFocusWindow)
fFocusWindow->SetFocus(false);
fFocusWindow = window;
if (fFocusWindow)
fFocusWindow->SetFocus(true);
if (fFocusWindow)
fFocusWindow->SetFocus(true);
UnlockClipping();
}
}

View File

@ -12,13 +12,10 @@
#define SHOW_GLOBAL_DIRTY_REGION 0
#define SHOW_WINDOW_CONTENT_DIRTY_REGION 0
#define MULTI_LOCKER 0
#define RW_LOCKER 0
#define MULTI_LOCKER 1
#if MULTI_LOCKER
# include "MultiLocker.h"
#elif RW_LOCKER
# include "RWLocker.h"
#else
# include <Locker.h>
#endif
@ -68,23 +65,14 @@ class Desktop : public BLooper {
void SetFocusWindow(WindowLayer* window);
#if RW_LOCKER
#if MULTI_LOCKER
bool ReadLockClipping() { return fClippingLock.ReadLock(); }
bool ReadLockClippingWithTimeout() { return fClippingLock.ReadLockWithTimeout(10000) >= B_OK; }
void ReadUnlockClipping() { fClippingLock.ReadUnlock(); }
bool LockClipping() { return fClippingLock.WriteLock(); }
void UnlockClipping() { fClippingLock.WriteUnlock(); }
#elif MULTI_LOCKER
bool ReadLockClipping() { return fClippingLock.ReadLock(); }
bool ReadLockClippingWithTimeout() { return fClippingLock.ReadLock(); }
void ReadUnlockClipping() { fClippingLock.ReadUnlock(); }
bool LockClipping() { return fClippingLock.WriteLock(); }
void UnlockClipping() { fClippingLock.WriteUnlock(); }
#else // BLocker
bool ReadLockClipping() { return fClippingLock.Lock(); }
bool ReadLockClippingWithTimeout() { return fClippingLock.LockWithTimeout(10000) >= B_OK; }
void ReadUnlockClipping() { fClippingLock.Unlock(); }
bool LockClipping() { return fClippingLock.Lock(); }

View File

@ -1,465 +0,0 @@
// RWLocker.cpp
#include <String.h>
#include "RWLocker.h"
// info about a read lock owner
struct RWLocker::ReadLockInfo {
thread_id reader;
int32 count;
};
// constructor
RWLocker::RWLocker()
: fLock(),
fMutex(),
fQueue(),
fReaderCount(0),
fWriterCount(0),
fReadLockInfos(8),
fWriter(B_ERROR),
fWriterWriterCount(0),
fWriterReaderCount(0)
{
_Init(NULL);
}
// constructor
RWLocker::RWLocker(const char* name)
: fLock(name),
fMutex(),
fQueue(),
fReaderCount(0),
fWriterCount(0),
fReadLockInfos(8),
fWriter(B_ERROR),
fWriterWriterCount(0),
fWriterReaderCount(0)
{
_Init(name);
}
// destructor
RWLocker::~RWLocker()
{
fLock.Lock();
delete_sem(fMutex.semaphore);
delete_sem(fQueue.semaphore);
for (int32 i = 0; ReadLockInfo* info = _ReadLockInfoAt(i); i++)
delete info;
}
// ReadLock
bool
RWLocker::ReadLock()
{
status_t error = _ReadLock(B_INFINITE_TIMEOUT);
return (error == B_OK);
}
// ReadLockWithTimeout
status_t
RWLocker::ReadLockWithTimeout(bigtime_t timeout)
{
bigtime_t absoluteTimeout = system_time() + timeout;
// take care of overflow
if (timeout > 0 && absoluteTimeout < 0)
absoluteTimeout = B_INFINITE_TIMEOUT;
return _ReadLock(absoluteTimeout);
}
// ReadUnlock
void
RWLocker::ReadUnlock()
{
if (fLock.Lock()) {
thread_id thread = find_thread(NULL);
if (thread == fWriter) {
// We (also) have a write lock.
if (fWriterReaderCount > 0)
fWriterReaderCount--;
// else: error: unmatched ReadUnlock()
} else {
int32 index = _IndexOf(thread);
if (ReadLockInfo* info = _ReadLockInfoAt(index)) {
fReaderCount--;
if (--info->count == 0) {
// The outer read lock bracket for the thread has been
// reached. Dispose the info.
_DeleteReadLockInfo(index);
}
if (fReaderCount == 0) {
// The last reader needs to unlock the mutex.
_ReleaseBenaphore(fMutex);
}
} // else: error: caller has no read lock
}
fLock.Unlock();
} // else: we are probably going to be destroyed
}
// IsReadLocked
//
// Returns whether or not the calling thread owns a read lock or even a
// write lock.
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;
}
// WriteLock
bool
RWLocker::WriteLock()
{
status_t error = _WriteLock(B_INFINITE_TIMEOUT);
return (error == B_OK);
}
// WriteLockWithTimeout
status_t
RWLocker::WriteLockWithTimeout(bigtime_t timeout)
{
bigtime_t absoluteTimeout = system_time() + timeout;
// take care of overflow
if (timeout > 0 && absoluteTimeout < 0)
absoluteTimeout = B_INFINITE_TIMEOUT;
return _WriteLock(absoluteTimeout);
}
// WriteUnlock
void
RWLocker::WriteUnlock()
{
if (fLock.Lock()) {
thread_id thread = find_thread(NULL);
if (thread == fWriter) {
fWriterCount--;
if (--fWriterWriterCount == 0) {
// The outer write lock bracket for the thread has been
// reached.
fWriter = B_ERROR;
if (fWriterReaderCount > 0) {
// We still own read locks.
_NewReadLockInfo(thread, fWriterReaderCount);
// A reader that expects to be the first reader may wait
// at the mutex semaphore. We need to wake it up.
if (fReaderCount > 0)
_ReleaseBenaphore(fMutex);
fReaderCount += fWriterReaderCount;
fWriterReaderCount = 0;
} else {
// We don't own any read locks. So we have to release the
// mutex benaphore.
_ReleaseBenaphore(fMutex);
}
}
} // else: error: unmatched WriteUnlock()
fLock.Unlock();
} // else: We're probably going to die.
}
// IsWriteLocked
//
// Returns whether or not the calling thread owns a write lock.
bool
RWLocker::IsWriteLocked() const
{
return (fWriter == find_thread(NULL));
}
// _Init
void
RWLocker::_Init(const char* name)
{
// init the mutex benaphore
BString mutexName(name);
mutexName += "_RWLocker_mutex";
fMutex.semaphore = create_sem(0, mutexName.String());
fMutex.counter = 0;
// init the queueing benaphore
BString queueName(name);
queueName += "_RWLocker_queue";
fQueue.semaphore = create_sem(0, queueName.String());
fQueue.counter = 0;
}
// _ReadLock
//
// /timeout/ -- absolute timeout
status_t
RWLocker::_ReadLock(bigtime_t timeout)
{
status_t error = B_OK;
thread_id thread = find_thread(NULL);
bool locked = false;
if (fLock.Lock()) {
// Check, if we already own a read (or write) lock. In this case we
// can skip the usual locking procedure.
if (thread == fWriter) {
// We already own a write lock.
fWriterReaderCount++;
locked = true;
} else if (ReadLockInfo* info = _ReadLockInfoAt(_IndexOf(thread))) {
// We already own a read lock.
info->count++;
fReaderCount++;
locked = true;
}
fLock.Unlock();
} else // failed to lock the data
error = B_ERROR;
// Usual locking, i.e. we do not already own a read or write lock.
if (error == B_OK && !locked) {
error = _AcquireBenaphore(fQueue, timeout);
if (error == B_OK) {
if (fLock.Lock()) {
bool firstReader = false;
if (++fReaderCount == 1) {
// We are the first reader.
_NewReadLockInfo(thread);
firstReader = true;
} else
_NewReadLockInfo(thread);
fLock.Unlock();
// The first reader needs to lock the mutex.
if (firstReader) {
error = _AcquireBenaphore(fMutex, timeout);
switch (error) {
case B_OK:
// fine
break;
case B_TIMED_OUT: {
// clean up
if (fLock.Lock()) {
_DeleteReadLockInfo(_IndexOf(thread));
fReaderCount--;
fLock.Unlock();
}
break;
}
default:
// Probably we are going to be destroyed.
break;
}
}
// Let the next candidate enter the game.
_ReleaseBenaphore(fQueue);
} else {
// We couldn't lock the data, which can only happen, if
// we're going to be destroyed.
error = B_ERROR;
}
}
}
return error;
}
// _WriteLock
//
// /timeout/ -- absolute timeout
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)) {
// We already own a read lock.
if (fWriterCount > 0) {
// There are writers before us.
if (infiniteTimeout) {
// Timeout is infinite and there are writers before us.
// Unregister the read locks and lock as usual.
readerCount = info->count;
fWriterCount++;
fReaderCount -= readerCount;
_DeleteReadLockInfo(index);
error = B_OK;
} else {
// The timeout is finite and there are readers before us:
// let the write lock request fail.
error = B_WOULD_BLOCK;
}
} else if (info->count == fReaderCount) {
// No writers before us.
// We are the only read lock owners. Just move the read lock
// info data to the special writer fields and then we are done.
// Note: At this point we may overtake readers that already
// have acquired the queueing benaphore, but have not yet
// locked the data. But that doesn't harm.
fWriter = thread;
fWriterCount++;
fWriterWriterCount = 1;
fWriterReaderCount = info->count;
fReaderCount -= fWriterReaderCount;
_DeleteReadLockInfo(index);
locked = true;
error = B_OK;
} else {
// No writers before us, but other readers.
// Note, we're quite restrictive here. If there are only
// readers before us, we could reinstall our readers, if
// our request times out. Unfortunately it is not easy
// to ensure, that no writer overtakes us between unlocking
// the data and acquiring the queuing benaphore.
if (infiniteTimeout) {
// Unregister the readers and lock as usual.
readerCount = info->count;
fWriterCount++;
fReaderCount -= readerCount;
_DeleteReadLockInfo(index);
error = B_OK;
} else
error = B_WOULD_BLOCK;
}
} else {
// We don't own a read lock.
if (fWriter == thread) {
// ... but a write lock.
fWriterCount++;
fWriterWriterCount++;
locked = true;
error = B_OK;
} else {
// We own neither read nor write locks.
// Lock as usual.
fWriterCount++;
error = B_OK;
}
}
fLock.Unlock();
// Usual locking...
// First step: acquire the queueing benaphore.
if (!locked && error == B_OK) {
error = _AcquireBenaphore(fQueue, timeout);
switch (error) {
case B_OK:
break;
case B_TIMED_OUT: {
// clean up
if (fLock.Lock()) {
fWriterCount--;
fLock.Unlock();
} // else: failed to lock the data: we're probably going
// to die.
break;
}
default:
// Probably we're going to die.
break;
}
}
// Second step: acquire the mutex benaphore.
if (!locked && error == B_OK) {
error = _AcquireBenaphore(fMutex, timeout);
switch (error) {
case B_OK: {
// Yeah, we made it. Set the special writer fields.
fWriter = thread;
fWriterWriterCount = 1;
fWriterReaderCount = readerCount;
break;
}
case B_TIMED_OUT: {
// clean up
if (fLock.Lock()) {
fWriterCount--;
fLock.Unlock();
} // else: failed to lock the data: we're probably going
// to die.
break;
}
default:
// Probably we're going to die.
break;
}
// Whatever happened, we have to release the queueing benaphore.
_ReleaseBenaphore(fQueue);
}
} else // failed to lock the data
error = B_ERROR;
return error;
}
// _AddReadLockInfo
int32
RWLocker::_AddReadLockInfo(ReadLockInfo* info)
{
int32 index = fReadLockInfos.CountItems();
fReadLockInfos.AddItem(info, index);
return index;
}
// _NewReadLockInfo
//
// Create a new read lock info for the supplied thread and add it to the
// list. Returns the index of the info.
int32
RWLocker::_NewReadLockInfo(thread_id thread, int32 count)
{
ReadLockInfo* info = new ReadLockInfo;
info->reader = thread;
info->count = count;
return _AddReadLockInfo(info);
}
// _DeleteReadLockInfo
void
RWLocker::_DeleteReadLockInfo(int32 index)
{
if (ReadLockInfo* info = (ReadLockInfo*)fReadLockInfos.RemoveItem(index))
delete info;
}
// _ReadLockInfoAt
RWLocker::ReadLockInfo*
RWLocker::_ReadLockInfoAt(int32 index) const
{
return (ReadLockInfo*)fReadLockInfos.ItemAt(index);
}
// _IndexOf
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;
}
// _AcquireBenaphore
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;
}
// _ReleaseBenaphore
void
RWLocker::_ReleaseBenaphore(Benaphore& benaphore)
{
if (atomic_add(&benaphore.counter, -1) > 1)
release_sem(benaphore.semaphore);
}

View File

@ -1,126 +0,0 @@
// RWLocker.h
//
// This class provides a reader/writer locking mechanism:
// * A writer needs an exclusive lock.
// * For a reader a non-exclusive lock to be shared with other readers is
// sufficient.
// * The ownership of a lock is bound to the thread that requested the lock;
// the same thread has to call Unlock() later.
// * Nested locking is supported: a number of XXXLock() calls needs to be
// bracketed by the same number of XXXUnlock() calls.
// * The lock acquiration strategy is fair: a lock applicant needs to wait
// only for those threads that already own a lock or requested one before
// the current thread. No one can overtake. E.g. if a thread owns a read
// lock, another one is waiting for a write lock, then a third one
// requesting a read lock has to wait until the write locker is done.
// This does not hold for threads that already own a lock (nested locking).
// A read lock owner is immediately granted another read lock and a write
// lock owner another write or a read lock.
// * A write lock owner is allowed to request a read lock and a read lock
// owner a write lock. While the first case is not problematic, the
// second one needs some further explanation: A read lock owner requesting
// a write lock temporarily looses its read lock(s) until the write lock
// is granted. Otherwise two read lock owning threads trying to get
// write locks at the same time would dead lock each other. The only
// problem with this solution is, that the write lock acquiration must
// not fail, because in that case the thread could not be given back
// its read lock(s), since another thread may have been given a write lock
// in the mean time. Fortunately locking can fail only either, if the
// locker has been deleted, or, if a timeout occured. Therefore
// WriteLockWithTimeout() immediatlely returns with a B_WOULD_BLOCK error
// code, if the caller already owns a read lock (but no write lock) and
// another thread already owns or has requested a read or write lock.
// * Calls to read and write locking methods may interleave arbitrarily,
// e.g.: ReadLock(); WriteLock(); ReadUnlock(); WriteUnlock();
//
// Important note: Read/WriteLock() can fail only, if the locker has been
// deleted. However, it is NOT save to invoke any method on a deleted
// locker object.
//
// Implementation details:
// A locker needs three semaphores (a BLocker and two semaphores): one
// to protect the lockers data, one as a reader/writer mutex (to be
// acquired by each writer and the first reader) and one for queueing
// waiting readers and writers. The simplified locking/unlocking
// algorithm is the following:
//
// writer reader
// queue.acquire() queue.acquire()
// mutex.acquire() if (first reader) mutex.acquire()
// queue.release() queue.release()
// ... ...
// mutex.release() if (last reader) mutex.release()
//
// One thread at maximum waits at the mutex, the others at the queueing
// semaphore. Unfortunately features as nested locking and timeouts make
// things more difficult. Therefore readers as well as writers need to check
// whether they already own a lock before acquiring the queueing semaphore.
// The data for the readers are stored in a list of ReadLockInfo structures;
// the writer data are stored in some special fields. /fReaderCount/ and
// /fWriterCount/ contain the total count of unbalanced Read/WriteLock()
// calls, /fWriterReaderCount/ and /fWriterWriterCount/ only from those of
// the current write lock owner (/fWriter/). To be a bit more precise:
// /fWriterReaderCount/ is not contained in /fReaderCount/, but
// /fWriterWriterCount/ is contained in /fWriterCount/. Therefore
// /fReaderCount/ can be considered to be the count of true reader's read
// locks.
#ifndef RW_LOCKER_H
#define RW_LOCKER_H
#include <List.h>
#include <Locker.h>
class RWLocker {
public:
RWLocker();
RWLocker(const char* name);
virtual ~RWLocker();
bool ReadLock();
status_t ReadLockWithTimeout(bigtime_t timeout);
void ReadUnlock();
bool IsReadLocked() const;
bool WriteLock();
status_t WriteLockWithTimeout(bigtime_t timeout);
void WriteUnlock();
bool IsWriteLocked() const;
private:
struct ReadLockInfo;
struct Benaphore {
sem_id semaphore;
int32 counter;
};
private:
void _Init(const char* name);
status_t _ReadLock(bigtime_t timeout);
status_t _WriteLock(bigtime_t timeout);
int32 _AddReadLockInfo(ReadLockInfo* info);
int32 _NewReadLockInfo(thread_id thread,
int32 count = 1);
void _DeleteReadLockInfo(int32 index);
ReadLockInfo* _ReadLockInfoAt(int32 index) const;
int32 _IndexOf(thread_id thread) const;
static status_t _AcquireBenaphore(Benaphore& benaphore,
bigtime_t timeout);
static void _ReleaseBenaphore(Benaphore& benaphore);
private:
mutable BLocker fLock; // data lock
Benaphore fMutex; // critical code mutex
Benaphore fQueue; // queueing semaphore
int32 fReaderCount; // total count...
int32 fWriterCount; // total count...
BList fReadLockInfos;
thread_id fWriter; // current write lock owner
int32 fWriterWriterCount; // write lock owner count
int32 fWriterReaderCount; // writer read lock owner
// count
};
#endif // RW_LOCKER_H

View File

@ -83,14 +83,9 @@ WindowLayer::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_REDRAW: {
if (!MessageQueue()->FindMessage(MSG_REDRAW, 0)) {
while (!fDesktop->ReadLockClippingWithTimeout()) {
//printf("%s MSG_REDRAW -> timeout\n", Name());
if (MessageQueue()->FindMessage(MSG_REDRAW, 0)) {
//printf("%s MSG_REDRAW -> timeout - leaving because there are pending redraws\n", Name());
return;
}
}
// there is only one MSG_REDRAW in the queue at anytime
if (fDesktop->ReadLockClipping()) {
_DrawBorder();
_TriggerContentRedraw();
@ -208,19 +203,15 @@ void
WindowLayer::SetFocus(bool focus)
{
// executed from Desktop thread
// it holds the clipping write lock,
// so the window thread cannot be
// accessing fFocus
// since we don't mark parts dirty that
// don't intersect with our own visible
// region, readlocking is fine
if (fDesktop->ReadLockClipping()) {
BRegion dirty(fBorderRegion);
dirty.IntersectWith(&fVisibleRegion);
fDesktop->MarkDirty(&dirty);
BRegion dirty(fBorderRegion);
dirty.IntersectWith(&fVisibleRegion);
fDesktop->MarkDirty(&dirty);
fFocus = focus;
fDesktop->ReadUnlockClipping();
}
fFocus = focus;
}
// MoveBy

View File

@ -35,7 +35,6 @@ SRCS= ClientLooper.cpp \
DrawingEngine.cpp \
main.cpp \
MultiLocker.cpp \
RWLocker.cpp \
ViewLayer.cpp \
WindowLayer.cpp