* Replaced DEBUG MultiLocker with an implementation that actually helps debugging

locking problems (instead of debugging the locker class).
* MultiLocker::IsReadLocked() is now only exported with DEBUG mode turned on, as
  it only works correctly in this case.
* Made MultiLocker safe against B_INTERRUPTED, ie. it now just tries to lock again
  instead of failing for no obvious reason.
* Removed bogus arguments to acquire_sem_etc() in MultiLocker (like B_DO_NOT_RESCHEDULE).
* Applied coding style to MultiLocker.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20055 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2007-02-02 19:16:18 +00:00
parent cb134e2550
commit 4d1fd46cdf
4 changed files with 534 additions and 442 deletions
+4 -1
View File
@@ -456,8 +456,11 @@ DesktopSettings::DesktopSettings(Desktop* desktop)
: :
fSettings(desktop->fSettings) fSettings(desktop->fSettings)
{ {
if (!desktop->fWindowLock.IsReadLocked() && !desktop->fWindowLock.IsWriteLocked()) #if DEBUG
if (!desktop->fWindowLock.IsWriteLocked()
&& !desktop->fWindowLock.IsReadLocked())
debugger("desktop not locked when trying to access settings"); debugger("desktop not locked when trying to access settings");
#endif
} }
+402 -331
View File
@@ -1,8 +1,11 @@
/* MultiLocker.cpp */
/* /*
Copyright 1999, Be Incorporated. All Rights Reserved. * Copyright 2005-2007, Haiku, Inc. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License. * Distributed under the terms of the MIT license.
*/ *
* Copyright 1999, Be Incorporated. All Rights Reserved.
* This file may be used under the terms of the Be Sample Code License.
*/
#include "MultiLocker.h" #include "MultiLocker.h"
@@ -10,32 +13,38 @@
#include <Errors.h> #include <Errors.h>
#include <OS.h> #include <OS.h>
//#define TIMING 1
//#define DEBUG 1 #define TIMING MULTI_LOCKER_TIMING
#define DEBUG MULTI_LOCKER_DEBUG
MultiLocker::MultiLocker(const char* semaphoreBaseName) const int32 LARGE_NUMBER = 100000;
: fInit(B_NO_INIT),
fReadCount(0),
fReadSem(-1), MultiLocker::MultiLocker(const char* baseName)
fWriteCount(0), :
fWriteSem(-1), #if DEBUG
fLockCount(0), fDebugArray(NULL),
fWriterLock(-1), fMaxThreads(0),
fWriterNest(0), #else
fWriterThread(-1), fReadCount(0),
fWriterStackBase(0), fWriteCount(0),
fDebugArray(NULL), fLockCount(0),
fMaxThreads(0) #endif
fInit(B_NO_INIT),
fWriterNest(0),
fWriterThread(-1),
fWriterStackBase(0)
{ {
//build the semaphores // build the semaphores
if (semaphoreBaseName) { #if !DEBUG
if (baseName) {
char name[128]; char name[128];
sprintf(name, "%s-%s", semaphoreBaseName, "ReadSem"); sprintf(name, "%s-%s", baseName, "ReadSem");
fReadSem = create_sem(0, name); fReadSem = create_sem(0, name);
sprintf(name, "%s-%s", semaphoreBaseName, "WriteSem"); sprintf(name, "%s-%s", baseName, "WriteSem");
fWriteSem = create_sem(0, name); fWriteSem = create_sem(0, name);
sprintf(name, "%s-%s", semaphoreBaseName, "WriterLock"); sprintf(name, "%s-%s", baseName, "WriterLock");
fWriterLock = create_sem(0, name); fWriterLock = create_sem(0, name);
} else { } else {
fReadSem = create_sem(0, "MultiLocker_ReadSem"); fReadSem = create_sem(0, "MultiLocker_ReadSem");
@@ -45,383 +54,474 @@ MultiLocker::MultiLocker(const char* semaphoreBaseName)
if (fReadSem >= 0 && fWriteSem >=0 && fWriterLock >= 0) if (fReadSem >= 0 && fWriteSem >=0 && fWriterLock >= 0)
fInit = B_OK; fInit = B_OK;
#else
fLock = create_sem(LARGE_NUMBER, baseName != NULL ? baseName : "MultiLocker");
if (fLock >= 0)
fInit = B_OK;
// we are in debug mode!
// create the reader tracking list
// the array needs to be large enough to hold all possible threads
system_info sys;
get_system_info(&sys);
fMaxThreads = sys.max_threads;
fDebugArray = (int32 *) malloc(fMaxThreads * sizeof(int32));
for (int32 i = 0; i < fMaxThreads; i++) {
fDebugArray[i] = 0;
}
#endif
#if TIMING
//initialize the counter variables
rl_count = ru_count = wl_count = wu_count = islock_count = 0;
rl_time = ru_time = wl_time = wu_time = islock_time = 0;
#if DEBUG #if DEBUG
//we are in debug mode! reg_count = unreg_count = 0;
//create the reader tracking list reg_time = unreg_time = 0;
//the array needs to be large enough to hold all possible threads
system_info sys;
get_system_info(&sys);
fMaxThreads = sys.max_threads;
fDebugArray = (int32 *) malloc(fMaxThreads * sizeof(int32));
for (int32 i = 0; i < fMaxThreads; i++) {
fDebugArray[i] = 0;
}
#endif
#if TIMING
//initialize the counter variables
rl_count = ru_count = wl_count = wu_count = islock_count = 0;
rl_time = ru_time = wl_time = wu_time = islock_time = 0;
#if DEBUG
reg_count = unreg_count = 0;
reg_time = unreg_time = 0;
#endif
#endif #endif
#endif
} }
MultiLocker::~MultiLocker() MultiLocker::~MultiLocker()
{ {
//become the writer // become the writer
if (!IsWriteLocked()) WriteLock(); if (!IsWriteLocked())
WriteLock();
//set locker to be uninitialized
// set locker to be uninitialized
fInit = B_NO_INIT; fInit = B_NO_INIT;
//delete the semaphores #if !DEBUG
// delete the semaphores
delete_sem(fReadSem); delete_sem(fReadSem);
delete_sem(fWriteSem); delete_sem(fWriteSem);
delete_sem(fWriterLock); delete_sem(fWriterLock);
#else
#if DEBUG delete_sem(fLock);
//we are in debug mode! free(fDebugArray);
//clear and delete the reader tracking list #endif
free(fDebugArray); #if TIMING
#endif // let's produce some performance numbers
#if TIMING printf("MultiLocker Statistics:\n"
//let's produce some performance numbers "Avg ReadLock: %lld\n"
printf("MultiLocker Statistics:\n" "Avg ReadUnlock: %lld\n"
"Avg ReadLock: %lld\n" "Avg WriteLock: %lld\n"
"Avg ReadUnlock: %lld\n" "Avg WriteUnlock: %lld\n"
"Avg WriteLock: %lld\n" "Avg IsWriteLocked: %lld\n",
"Avg WriteUnlock: %lld\n" rl_count > 0 ? rl_time / rl_count : 0,
"Avg IsWriteLocked: %lld\n", ru_count > 0 ? ru_time / ru_count : 0,
rl_count > 0 ? rl_time / rl_count : 0, wl_count > 0 ? wl_time / wl_count : 0,
ru_count > 0 ? ru_time / ru_count : 0, wu_count > 0 ? wu_time / wu_count : 0,
wl_count > 0 ? wl_time / wl_count : 0, islock_count > 0 ? islock_time / islock_count : 0);
wu_count > 0 ? wu_time / wu_count : 0, #endif
islock_count > 0 ? islock_time / islock_count : 0
);
#if DEBUG
printf( "Avg register_thread: %lld\n"
"Avg unregister_thread: %lld\n",
reg_count > 0 ? reg_time / reg_count : 0,
unreg_count > 0 ? unreg_time / unreg_count : 0
);
#endif
#endif
} }
status_t
status_t
MultiLocker::InitCheck() MultiLocker::InitCheck()
{ {
return fInit; return fInit;
} }
bool
/*!
This function demonstrates a nice method of determining if the current thread
is the writer or not. The method involves caching the index of the page in memory
where the thread's stack is located. Each time a new writer acquires the lock,
its thread_id and stack_page are recorded. IsWriteLocked gets the stack_page of the
current thread and sees if it is a match. If the stack_page matches you are guaranteed
to have the matching thread. If the stack page doesn't match the more traditional
find_thread(NULL) method of matching the thread_ids is used.
This technique is very useful when dealing with a lock that is acquired in a nested fashion.
It could be expanded to cache the information of the last thread in the lock, and then if
the same thread returns while there is no one in the lock, it could save some time, if the
same thread is likely to acquire the lock again and again.
I should note another shortcut that could be implemented here
If fWriterThread is set to -1 then there is no writer in the lock, and we could
return from this function much faster. However the function is currently set up
so all of the stack_base and thread_id info is determined here. WriteLock passes
in some variables so that if the lock is not held it does not have to get the thread_id
and stack base again. Instead this function returns that information. So this shortcut
would only move this information gathering outside of this function, and I like it all
contained.
*/
bool
MultiLocker::IsWriteLocked(uint32* _stackBase, thread_id* _thread)
{
#if TIMING
bigtime_t start = system_time();
#endif
// get a variable on the stack
bool writeLockHolder = false;
if (fInit == B_OK) {
// determine which page in memory this stack represents
// this is managed by taking the address of the item on the
// stack and dividing it by the size of the memory pages
// if it is the same as the cached stack_page, there is a match
uint32 stackBase = (uint32)&writeLockHolder / B_PAGE_SIZE;
thread_id thread = 0;
if (fWriterStackBase == stackBase) {
writeLockHolder = true;
} else {
// as there was no stack page match we resort to the
// tried and true methods
thread = find_thread(NULL);
if (fWriterThread == thread)
writeLockHolder = true;
}
// if someone wants this information, give it to them
if (_stackBase != NULL)
*_stackBase = stackBase;
if (_thread != NULL)
*_thread = thread;
}
#if TIMING
bigtime_t end = system_time();
islock_time += (end - start);
islock_count++;
#endif
return writeLockHolder;
}
#if !DEBUG
// #pragma mark - Standard versions
bool
MultiLocker::ReadLock() MultiLocker::ReadLock()
{ {
#if TIMING #if TIMING
bigtime_t start = system_time(); bigtime_t start = system_time();
#endif #endif
bool locked = false; bool locked = false;
//the lock must be initialized // the lock must be initialized
if (fInit == B_OK) { if (fInit == B_OK) {
if (IsWriteLocked()) { if (IsWriteLocked()) {
//the writer simply increments the nesting // the writer simply increments the nesting
#if DEBUG
if (fWriterNest < 0)
debugger("ReadLock() - negative writer nest count\n");
#endif
fWriterNest++; fWriterNest++;
locked = true; locked = true;
} else { } else {
//increment and retrieve the current count of readers // increment and retrieve the current count of readers
int32 current_count = atomic_add(&fReadCount, 1); int32 currentCount = atomic_add(&fReadCount, 1);
if (current_count < 0) { if (currentCount < 0) {
//a writer holds the lock so wait for fReadSem to be released // a writer holds the lock so wait for fReadSem to be released
locked = (acquire_sem_etc(fReadSem, 1, B_DO_NOT_RESCHEDULE, status_t status;
B_INFINITE_TIMEOUT) == B_OK); do {
} else locked = true; status = acquire_sem(fReadSem);
#if DEBUG } while (status == B_INTERRUPTED);
//register if we acquired the lock
if (locked) register_thread(); locked = status == B_OK;
#endif } else
locked = true;
} }
} }
#if TIMING #if TIMING
bigtime_t end = system_time(); bigtime_t end = system_time();
rl_time += (end - start); rl_time += (end - start);
rl_count++; rl_count++;
#endif #endif
return locked; return locked;
} }
bool
bool
MultiLocker::WriteLock() MultiLocker::WriteLock()
{ {
#if TIMING #if TIMING
bigtime_t start = system_time(); bigtime_t start = system_time();
#endif #endif
bool locked = false; bool locked = false;
if (fInit == B_OK) { if (fInit == B_OK) {
uint32 stack_base = 0; uint32 stackBase = 0;
thread_id thread = -1; thread_id thread = -1;
if (IsWriteLocked(&stack_base, &thread)) { if (IsWriteLocked(&stackBase, &thread)) {
//already the writer - increment the nesting count // already the writer - increment the nesting count
#if DEBUG
if (fWriterNest < 0)
debugger("WriteLock() - negative nest count\n");
#endif
fWriterNest++; fWriterNest++;
locked = true; locked = true;
} else { } else {
//new writer acquiring the lock // new writer acquiring the lock
#if DEBUG
// NOTE: IsReadLocked() tells you
// if this thread really holds the
// "read lock" only in DEBUG mode!
if (IsReadLocked())
debugger("Reader wants to become writer!");
#endif
if (atomic_add(&fLockCount, 1) >= 1) { if (atomic_add(&fLockCount, 1) >= 1) {
//another writer in the lock - acquire the semaphore // another writer in the lock - acquire the semaphore
locked = (acquire_sem_etc(fWriterLock, 1, B_DO_NOT_RESCHEDULE, status_t status;
B_INFINITE_TIMEOUT) == B_OK); do {
} else locked = true; status = acquire_sem(fWriterLock);
} while (status == B_INTERRUPTED);
if (locked) {
//new holder of the lock
//decrement fReadCount by a very large number locked = status == B_OK;
//this will cause new readers to block on fReadSem } else
locked = true;
if (locked) {
// new holder of the lock
// decrement fReadCount by a very large number
// this will cause new readers to block on fReadSem
int32 readers = atomic_add(&fReadCount, -LARGE_NUMBER); int32 readers = atomic_add(&fReadCount, -LARGE_NUMBER);
if (readers > 0) {
if (readers > 0) { // readers hold the lock - acquire fWriteSem
//readers hold the lock - acquire fWriteSem status_t status;
locked = (acquire_sem_etc(fWriteSem, readers, do {
B_DO_NOT_RESCHEDULE, status = acquire_sem_etc(fWriteSem, readers, 0, 0);
B_INFINITE_TIMEOUT) == B_OK); } while (status == B_INTERRUPTED);
}
locked = status == B_OK;
}
if (locked) { if (locked) {
ASSERT(fWriterThread == -1); ASSERT(fWriterThread == -1);
//record thread information // record thread information
fWriterThread = thread; fWriterThread = thread;
fWriterStackBase = stack_base; fWriterStackBase = stackBase;
} }
} }
} }
} }
#if TIMING #if TIMING
bigtime_t end = system_time(); bigtime_t end = system_time();
wl_time += (end - start); wl_time += (end - start);
wl_count++; wl_count++;
#endif #endif
return locked; return locked;
} }
bool
bool
MultiLocker::ReadUnlock() MultiLocker::ReadUnlock()
{ {
#if TIMING #if TIMING
bigtime_t start = system_time(); bigtime_t start = system_time();
#endif #endif
bool unlocked = false; bool unlocked = false;
if (IsWriteLocked()) { if (IsWriteLocked()) {
//writers simply decrement the nesting count // writers simply decrement the nesting count
fWriterNest--; fWriterNest--;
#if DEBUG
if (fWriterNest < 0)
debugger("ReadUnlock() - negative writer nest count\n");
#endif
unlocked = true; unlocked = true;
} else { } else {
//decrement and retrieve the read counter // decrement and retrieve the read counter
int32 current_count = atomic_add(&fReadCount, -1); int32 current_count = atomic_add(&fReadCount, -1);
if (current_count < 0) { if (current_count < 0) {
//a writer is waiting for the lock so release fWriteSem // a writer is waiting for the lock so release fWriteSem
unlocked = (release_sem_etc(fWriteSem, 1, unlocked = release_sem_etc(fWriteSem, 1, B_DO_NOT_RESCHEDULE) == B_OK;
B_DO_NOT_RESCHEDULE) == B_OK); } else
} else unlocked = true; unlocked = true;
#if DEBUG
//unregister if we released the lock
if (unlocked) unregister_thread();
#endif
} }
#if TIMING #if TIMING
bigtime_t end = system_time(); bigtime_t end = system_time();
ru_time += (end - start); ru_time += (end - start);
ru_count++; ru_count++;
#endif #endif
return unlocked; return unlocked;
} }
bool
bool
MultiLocker::WriteUnlock() MultiLocker::WriteUnlock()
{ {
#if TIMING #if TIMING
bigtime_t start = system_time(); bigtime_t start = system_time();
#endif #endif
bool unlocked = false; bool unlocked = false;
if (IsWriteLocked()) { if (IsWriteLocked()) {
//if this is a nested lock simply decrement the nest count // if this is a nested lock simply decrement the nest count
if (fWriterNest > 0) { if (fWriterNest > 0) {
fWriterNest--; fWriterNest--;
#if DEBUG
if (fWriterNest < 0)
debugger("WriteUnlock(): nest count now negative\n");
#endif
unlocked = true; unlocked = true;
} else { } else {
//writer finally unlocking // writer finally unlocking
//increment fReadCount by a large number // increment fReadCount by a large number
//this will let new readers acquire the read lock // this will let new readers acquire the read lock
//retrieve the number of current waiters // retrieve the number of current waiters
int32 readersWaiting = atomic_add(&fReadCount, LARGE_NUMBER) int32 readersWaiting = atomic_add(&fReadCount, LARGE_NUMBER)
+ LARGE_NUMBER; + LARGE_NUMBER;
if (readersWaiting > 0) { if (readersWaiting > 0) {
//readers are waiting to acquire the lock // readers are waiting to acquire the lock
unlocked = (release_sem_etc(fReadSem, readersWaiting, unlocked = release_sem_etc(fReadSem, readersWaiting,
B_DO_NOT_RESCHEDULE) == B_OK); B_DO_NOT_RESCHEDULE) == B_OK;
} else unlocked = true; } else
unlocked = true;
if (unlocked) { if (unlocked) {
//clear the information // clear the information
fWriterThread = -1; fWriterThread = -1;
fWriterStackBase = 0; fWriterStackBase = 0;
//decrement and retrieve the lock count // decrement and retrieve the lock count
if (atomic_add(&fLockCount, -1) > 1) { if (atomic_add(&fLockCount, -1) > 1) {
//other writers are waiting so release fWriterLock // other writers are waiting so release fWriterLock
unlocked = (release_sem_etc(fWriterLock, 1, unlocked = release_sem_etc(fWriterLock, 1,
B_DO_NOT_RESCHEDULE) == B_OK); B_DO_NOT_RESCHEDULE) == B_OK;
} }
} }
} }
} else
} else debugger("Non-writer attempting to WriteUnlock()\n"); debugger("Non-writer attempting to WriteUnlock()");
#if TIMING #if TIMING
bigtime_t end = system_time(); bigtime_t end = system_time();
wu_time += (end - start); wu_time += (end - start);
wu_count++; wu_count++;
#endif #endif
return unlocked; return unlocked;
} }
/* this function demonstrates a nice method of determining if the current thread */
/* is the writer or not. The method involves caching the index of the page in memory */
/* where the thread's stack is located. Each time a new writer acquires the lock, */
/* its thread_id and stack_page are recorded. IsWriteLocked gets the stack_page of the */
/* current thread and sees if it is a match. If the stack_page matches you are guaranteed */
/* to have the matching thread. If the stack page doesn't match the more traditional */
/* find_thread(NULL) method of matching the thread_ids is used. */
/* This technique is very useful when dealing with a lock that is acquired in a nested fashion. */ #else // DEBUG
/* It could be expanded to cache the information of the last thread in the lock, and then if */ // #pragma mark - Debug versions
/* the same thread returns while there is no one in the lock, it could save some time, if the */
/* same thread is likely to acquire the lock again and again. */
/* I should note another shortcut that could be implemented here */
/* If fWriterThread is set to -1 then there is no writer in the lock, and we could */
/* return from this function much faster. However the function is currently set up */
/* so all of the stack_base and thread_id info is determined here. WriteLock passes */
/* in some variables so that if the lock is not held it does not have to get the thread_id */
/* and stack base again. Instead this function returns that information. So this shortcut */
/* would only move this information gathering outside of this function, and I like it all */
/* contained. */
bool
MultiLocker::IsWriteLocked(uint32 *the_stack_base, thread_id *the_thread) bool
MultiLocker::ReadLock()
{ {
#if TIMING bool locked = false;
bigtime_t start = system_time();
#endif
//get a variable on the stack if (fInit != B_OK)
bool write_lock_holder = false; debugger("lock not initialized");
if (fInit == B_OK) { if (IsWriteLocked()) {
uint32 stack_base; if (fWriterNest < 0)
thread_id thread = 0; debugger("ReadLock() - negative writer nest count");
//determine which page in memory this stack represents fWriterNest++;
//this is managed by taking the address of the item on the locked = true;
//stack and dividing it by the size of the memory pages } else {
//if it is the same as the cached stack_page, there is a match status_t status;
stack_base = (uint32)&write_lock_holder / B_PAGE_SIZE; do {
if (fWriterStackBase == stack_base) { status = acquire_sem(fLock);
write_lock_holder = true; } while (status == B_INTERRUPTED);
} else {
//as there was no stack_page match we resort to the locked = status == B_OK;
//tried and true methods
thread = find_thread(NULL); if (locked)
if (fWriterThread == thread) { _RegisterThread();
write_lock_holder = true; }
}
} return locked;
}
//if someone wants this information, give it to them
if (the_stack_base != NULL) {
*the_stack_base = stack_base; bool
} MultiLocker::WriteLock()
if (the_thread != NULL) { {
*the_thread = thread; bool locked = false;
if (fInit != B_OK)
debugger("lock not initialized");
uint32 stackBase = 0;
thread_id thread = -1;
if (IsWriteLocked(&stackBase, &thread)) {
if (fWriterNest < 0)
debugger("WriteLock() - negative writer nest count");
fWriterNest++;
locked = true;
} else {
// new writer acquiring the lock
if (IsReadLocked())
debugger("Reader wants to become writer!");
status_t status;
do {
status = acquire_sem_etc(fLock, LARGE_NUMBER, 0, 0);
} while (status == B_INTERRUPTED);
locked = status == B_OK;
if (locked) {
// record thread information
fWriterThread = thread;
fWriterStackBase = stackBase;
} }
} }
#if TIMING return locked;
bigtime_t end = system_time();
islock_time += (end - start);
islock_count++;
#endif
return write_lock_holder;
} }
bool
bool
MultiLocker::ReadUnlock()
{
bool unlocked = false;
if (IsWriteLocked()) {
// writers simply decrement the nesting count
fWriterNest--;
if (fWriterNest < 0)
debugger("ReadUnlock() - negative writer nest count");
unlocked = true;
} else {
// decrement and retrieve the read counter
unlocked = release_sem_etc(fLock, 1, B_DO_NOT_RESCHEDULE) == B_OK;
if (unlocked)
_UnregisterThread();
}
return unlocked;
}
bool
MultiLocker::WriteUnlock()
{
bool unlocked = false;
if (IsWriteLocked()) {
// if this is a nested lock simply decrement the nest count
if (fWriterNest > 0) {
fWriterNest--;
unlocked = true;
} else {
unlocked = release_sem_etc(fLock, LARGE_NUMBER, B_DO_NOT_RESCHEDULE) == B_OK;
if (unlocked) {
// clear the information
fWriterThread = -1;
fWriterStackBase = 0;
}
}
} else {
debug_printf("write holder %ld\n", fWriterThread);
debugger("Non-writer attempting to WriteUnlock()");
}
return unlocked;
}
bool
MultiLocker::IsReadLocked() MultiLocker::IsReadLocked()
{ {
//a properly initialized MultiLocker in non-debug always returns true if (fInit == B_NO_INIT)
bool locked = true; return false;
if (fInit == B_NO_INIT) locked = false;
// determine if the lock is actually held
#if DEBUG thread_id thread = find_thread(NULL);
//determine if the lock is actually held return fDebugArray[thread % fMaxThreads] > 0;
thread_id thread = find_thread(NULL);
if (fDebugArray[thread % fMaxThreads] > 0)
locked = true;
else
locked = false;
#endif
return locked;
} }
@@ -446,54 +546,25 @@ MultiLocker::IsReadLocked()
/* traversing a list of cached information. As this is only for a debug mode, the extra memory */ /* traversing a list of cached information. As this is only for a debug mode, the extra memory */
/* was not deemed to be a problem */ /* was not deemed to be a problem */
void void
MultiLocker::register_thread() MultiLocker::_RegisterThread()
{ {
#if DEBUG thread_id thread = find_thread(NULL);
#if TIMING
bigtime_t start = system_time();
#endif
thread_id thread = find_thread(NULL); if (fDebugArray[thread % fMaxThreads] != 0)
debugger("Nested ReadLock!");
if (fDebugArray[thread%fMaxThreads] != 0)
debugger("Nested ReadLock!\n");
fDebugArray[thread%fMaxThreads]++;
#if TIMING
bigtime_t end = system_time();
reg_time += (end - start);
reg_count++;
#endif
#else
debugger("register_thread should never be called unless in DEBUG mode!\n");
#endif
}
void
MultiLocker::unregister_thread()
{
#if DEBUG
#if TIMING
bigtime_t start = system_time();
#endif
thread_id thread = find_thread(NULL);
ASSERT(fDebugArray[thread%fMaxThreads] == 1);
fDebugArray[thread%fMaxThreads]--;
#if TIMING
bigtime_t end = system_time();
unreg_time += (end - start);
unreg_count++;
#endif
#else
debugger("unregister_thread should never be called unless in DEBUG mode!\n");
#endif
fDebugArray[thread % fMaxThreads]++;
} }
void
MultiLocker::_UnregisterThread()
{
thread_id thread = find_thread(NULL);
ASSERT(fDebugArray[thread % fMaxThreads] == 1);
fDebugArray[thread % fMaxThreads]--;
}
#endif // DEBUG
+126 -110
View File
@@ -1,13 +1,12 @@
/* MultiLocker.h */
/* /*
Copyright 2005-2006, Haiku. * Copyright 2005-2007, Haiku, Inc. All Rights Reserved.
Distributed under the terms of the MIT license. * Distributed under the terms of the MIT license.
*
* Copyright 1999, Be Incorporated. All Rights Reserved.
* This file may be used under the terms of the Be Sample Code License.
*/
Copyright 1999, Be Incorporated. All Rights Reserved. /*! multiple-reader single-writer locking class */
This file may be used under the terms of the Be Sample Code License.
*/
/** multiple-reader single-writer locking class */
// IMPORTANT: // IMPORTANT:
// * nested read locks are not supported // * nested read locks are not supported
@@ -19,125 +18,142 @@
#ifndef MULTI_LOCKER_H #ifndef MULTI_LOCKER_H
#define MULTI_LOCKER_H #define MULTI_LOCKER_H
//#define TIMING 1
#include <OS.h> #include <OS.h>
const int32 LARGE_NUMBER = 100000;
#define MULTI_LOCKER_TIMING 0
#if DEBUG
# define MULTI_LOCKER_DEBUG DEBUG
#else
# define MULTI_LOCKER_DEBUG 0
#endif
class MultiLocker { class MultiLocker {
public: public:
MultiLocker(const char* semaphoreBaseName); MultiLocker(const char* baseName);
virtual ~MultiLocker(); virtual ~MultiLocker();
status_t InitCheck();
//locking for reading or writing
bool ReadLock();
bool WriteLock();
//unlocking after reading or writing status_t InitCheck();
bool ReadUnlock();
bool WriteUnlock(); // locking for reading or writing
bool ReadLock();
bool WriteLock();
//does the current thread hold a write lock ? // unlocking after reading or writing
bool IsWriteLocked(uint32 *stack_base = NULL, bool ReadUnlock();
thread_id *thread = NULL); bool WriteUnlock();
//in DEBUG mode returns whether the lock is held
//in non-debug mode returns true // does the current thread hold a write lock ?
bool IsReadLocked(); bool IsWriteLocked(uint32 *stackBase = NULL,
thread_id *thread = NULL);
#if MULTI_LOCKER_DEBUG
// in DEBUG mode returns whether the lock is held
// in non-debug mode returns true
bool IsReadLocked();
#endif
private: private:
//functions for managing the DEBUG reader array #if MULTI_LOCKER_DEBUG
void register_thread(); // functions for managing the DEBUG reader array
void unregister_thread(); void _RegisterThread();
void _UnregisterThread();
status_t fInit;
//readers adjust count and block on fReadSem when a writer
//hold the lock
int32 fReadCount;
sem_id fReadSem;
//writers adjust the count and block on fWriteSem
//when readers hold the lock
int32 fWriteCount;
sem_id fWriteSem;
//writers must acquire fWriterLock when acquiring a write lock
int32 fLockCount;
sem_id fWriterLock;
int32 fWriterNest;
thread_id fWriterThread;
uint32 fWriterStackBase;
int32 * fDebugArray;
int32 fMaxThreads;
#if TIMING sem_id fLock;
uint32 rl_count; int32* fDebugArray;
bigtime_t rl_time; int32 fMaxThreads;
uint32 ru_count; #else
bigtime_t ru_time; // readers adjust count and block on fReadSem when a writer
uint32 wl_count; // hold the lock
bigtime_t wl_time; int32 fReadCount;
uint32 wu_count; sem_id fReadSem;
bigtime_t wu_time; // writers adjust the count and block on fWriteSem
uint32 islock_count; // when readers hold the lock
bigtime_t islock_time; int32 fWriteCount;
uint32 reg_count; sem_id fWriteSem;
bigtime_t reg_time; // writers must acquire fWriterLock when acquiring a write lock
uint32 unreg_count; int32 fLockCount;
bigtime_t unreg_time; sem_id fWriterLock;
#endif // MULTI_LOCKER_DEBUG
status_t fInit;
int32 fWriterNest;
thread_id fWriterThread;
uint32 fWriterStackBase;
#if MULTI_LOCKER_TIMING
uint32 rl_count;
bigtime_t rl_time;
uint32 ru_count;
bigtime_t ru_time;
uint32 wl_count;
bigtime_t wl_time;
uint32 wu_count;
bigtime_t wu_time;
uint32 islock_count;
bigtime_t islock_time;
#endif #endif
}; };
class AutoWriteLocker { class AutoWriteLocker {
public: public:
AutoWriteLocker(MultiLocker* lock) AutoWriteLocker(MultiLocker* lock)
: fLock(*lock) :
{ fLock(*lock)
fLock.WriteLock(); {
} fLock.WriteLock();
AutoWriteLocker(MultiLocker& lock) }
: fLock(lock)
{ AutoWriteLocker(MultiLocker& lock)
fLock.WriteLock(); :
} fLock(lock)
~AutoWriteLocker() {
{ fLock.WriteLock();
fLock.WriteUnlock(); }
}
private: ~AutoWriteLocker()
MultiLocker& fLock; {
fLock.WriteUnlock();
}
private:
MultiLocker& fLock;
}; };
class AutoReadLocker { class AutoReadLocker {
public: public:
AutoReadLocker(MultiLocker* lock) AutoReadLocker(MultiLocker* lock)
: fLock(*lock) :
{ fLock(*lock)
fLocked = fLock.ReadLock(); {
} fLocked = fLock.ReadLock();
AutoReadLocker(MultiLocker& lock) }
: fLock(lock)
{ AutoReadLocker(MultiLocker& lock)
fLocked = fLock.ReadLock(); :
} fLock(lock)
~AutoReadLocker() {
{ fLocked = fLock.ReadLock();
Unlock(); }
}
void Unlock() ~AutoReadLocker()
{ {
if (fLocked) { Unlock();
fLock.ReadUnlock(); }
fLocked = false;
} void
} Unlock()
private: {
MultiLocker& fLock; if (fLocked) {
bool fLocked; fLock.ReadUnlock();
fLocked = false;
}
}
private:
MultiLocker& fLock;
bool fLocked;
}; };
#endif // MULTI_LOCKER_H
#endif
+2
View File
@@ -50,7 +50,9 @@ class HWInterface : protected MultiLocker {
// locking // locking
bool LockParallelAccess() { return ReadLock(); } bool LockParallelAccess() { return ReadLock(); }
#if DEBUG
bool IsParallelAccessLocked() { return IsReadLocked(); } bool IsParallelAccessLocked() { return IsReadLocked(); }
#endif
void UnlockParallelAccess() { ReadUnlock(); } void UnlockParallelAccess() { ReadUnlock(); }
bool LockExclusiveAccess() { return WriteLock(); } bool LockExclusiveAccess() { return WriteLock(); }