From 4d1fd46cdf49413c13bf55d392d5315457f3aa68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Fri, 2 Feb 2007 19:16:18 +0000 Subject: [PATCH] * 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 --- src/servers/app/DesktopSettings.cpp | 5 +- src/servers/app/MultiLocker.cpp | 733 ++++++++++++++------------ src/servers/app/MultiLocker.h | 236 +++++---- src/servers/app/drawing/HWInterface.h | 2 + 4 files changed, 534 insertions(+), 442 deletions(-) diff --git a/src/servers/app/DesktopSettings.cpp b/src/servers/app/DesktopSettings.cpp index d46d4df8a0..8304b93f7d 100644 --- a/src/servers/app/DesktopSettings.cpp +++ b/src/servers/app/DesktopSettings.cpp @@ -456,8 +456,11 @@ DesktopSettings::DesktopSettings(Desktop* desktop) : 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"); +#endif } diff --git a/src/servers/app/MultiLocker.cpp b/src/servers/app/MultiLocker.cpp index 10f017c375..c2ce9d72f4 100644 --- a/src/servers/app/MultiLocker.cpp +++ b/src/servers/app/MultiLocker.cpp @@ -1,8 +1,11 @@ -/* MultiLocker.cpp */ /* - Copyright 1999, Be Incorporated. All Rights Reserved. - This file may be used under the terms of the Be Sample Code License. -*/ + * Copyright 2005-2007, Haiku, Inc. All Rights Reserved. + * 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" @@ -10,32 +13,38 @@ #include #include -//#define TIMING 1 -//#define DEBUG 1 + +#define TIMING MULTI_LOCKER_TIMING +#define DEBUG MULTI_LOCKER_DEBUG -MultiLocker::MultiLocker(const char* semaphoreBaseName) - : fInit(B_NO_INIT), - fReadCount(0), - fReadSem(-1), - fWriteCount(0), - fWriteSem(-1), - fLockCount(0), - fWriterLock(-1), - fWriterNest(0), - fWriterThread(-1), - fWriterStackBase(0), - fDebugArray(NULL), - fMaxThreads(0) +const int32 LARGE_NUMBER = 100000; + + +MultiLocker::MultiLocker(const char* baseName) + : +#if DEBUG + fDebugArray(NULL), + fMaxThreads(0), +#else + fReadCount(0), + fWriteCount(0), + fLockCount(0), +#endif + fInit(B_NO_INIT), + fWriterNest(0), + fWriterThread(-1), + fWriterStackBase(0) { - //build the semaphores - if (semaphoreBaseName) { + // build the semaphores +#if !DEBUG + if (baseName) { char name[128]; - sprintf(name, "%s-%s", semaphoreBaseName, "ReadSem"); + sprintf(name, "%s-%s", baseName, "ReadSem"); fReadSem = create_sem(0, name); - sprintf(name, "%s-%s", semaphoreBaseName, "WriteSem"); + sprintf(name, "%s-%s", baseName, "WriteSem"); fWriteSem = create_sem(0, name); - sprintf(name, "%s-%s", semaphoreBaseName, "WriterLock"); + sprintf(name, "%s-%s", baseName, "WriterLock"); fWriterLock = create_sem(0, name); } else { fReadSem = create_sem(0, "MultiLocker_ReadSem"); @@ -45,383 +54,474 @@ MultiLocker::MultiLocker(const char* semaphoreBaseName) if (fReadSem >= 0 && fWriteSem >=0 && fWriterLock >= 0) 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 - //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 - reg_count = unreg_count = 0; - reg_time = unreg_time = 0; - #endif + reg_count = unreg_count = 0; + reg_time = unreg_time = 0; #endif +#endif } MultiLocker::~MultiLocker() { - //become the writer - if (!IsWriteLocked()) WriteLock(); - - //set locker to be uninitialized + // become the writer + if (!IsWriteLocked()) + WriteLock(); + + // set locker to be uninitialized fInit = B_NO_INIT; - //delete the semaphores +#if !DEBUG + // delete the semaphores delete_sem(fReadSem); delete_sem(fWriteSem); delete_sem(fWriterLock); - - #if DEBUG - //we are in debug mode! - //clear and delete the reader tracking list - free(fDebugArray); - #endif - #if TIMING - //let's produce some performance numbers - printf("MultiLocker Statistics:\n" - "Avg ReadLock: %lld\n" - "Avg ReadUnlock: %lld\n" - "Avg WriteLock: %lld\n" - "Avg WriteUnlock: %lld\n" - "Avg IsWriteLocked: %lld\n", - rl_count > 0 ? rl_time / rl_count : 0, - ru_count > 0 ? ru_time / ru_count : 0, - wl_count > 0 ? wl_time / wl_count : 0, - wu_count > 0 ? wu_time / wu_count : 0, - 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 +#else + delete_sem(fLock); + free(fDebugArray); +#endif +#if TIMING + // let's produce some performance numbers + printf("MultiLocker Statistics:\n" + "Avg ReadLock: %lld\n" + "Avg ReadUnlock: %lld\n" + "Avg WriteLock: %lld\n" + "Avg WriteUnlock: %lld\n" + "Avg IsWriteLocked: %lld\n", + rl_count > 0 ? rl_time / rl_count : 0, + ru_count > 0 ? ru_time / ru_count : 0, + wl_count > 0 ? wl_time / wl_count : 0, + wu_count > 0 ? wu_time / wu_count : 0, + islock_count > 0 ? islock_time / islock_count : 0); +#endif } -status_t + +status_t MultiLocker::InitCheck() { 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() { - #if TIMING - bigtime_t start = system_time(); - #endif +#if TIMING + bigtime_t start = system_time(); +#endif bool locked = false; - //the lock must be initialized + // the lock must be initialized if (fInit == B_OK) { if (IsWriteLocked()) { - //the writer simply increments the nesting - #if DEBUG - if (fWriterNest < 0) - debugger("ReadLock() - negative writer nest count\n"); - #endif - + // the writer simply increments the nesting fWriterNest++; locked = true; } else { - //increment and retrieve the current count of readers - int32 current_count = atomic_add(&fReadCount, 1); - if (current_count < 0) { - //a writer holds the lock so wait for fReadSem to be released - locked = (acquire_sem_etc(fReadSem, 1, B_DO_NOT_RESCHEDULE, - B_INFINITE_TIMEOUT) == B_OK); - } else locked = true; - #if DEBUG - //register if we acquired the lock - if (locked) register_thread(); - #endif + // increment and retrieve the current count of readers + int32 currentCount = atomic_add(&fReadCount, 1); + if (currentCount < 0) { + // a writer holds the lock so wait for fReadSem to be released + status_t status; + do { + status = acquire_sem(fReadSem); + } while (status == B_INTERRUPTED); + + locked = status == B_OK; + } else + locked = true; } - } - - #if TIMING - bigtime_t end = system_time(); - rl_time += (end - start); - rl_count++; - #endif + +#if TIMING + bigtime_t end = system_time(); + rl_time += (end - start); + rl_count++; +#endif return locked; } -bool + +bool MultiLocker::WriteLock() { - #if TIMING - bigtime_t start = system_time(); - #endif +#if TIMING + bigtime_t start = system_time(); +#endif bool locked = false; - + if (fInit == B_OK) { - uint32 stack_base = 0; + uint32 stackBase = 0; thread_id thread = -1; - if (IsWriteLocked(&stack_base, &thread)) { - //already the writer - increment the nesting count - #if DEBUG - if (fWriterNest < 0) - debugger("WriteLock() - negative nest count\n"); - #endif - + if (IsWriteLocked(&stackBase, &thread)) { + // already the writer - increment the nesting count fWriterNest++; locked = true; } else { - //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 + // new writer acquiring the lock if (atomic_add(&fLockCount, 1) >= 1) { - //another writer in the lock - acquire the semaphore - locked = (acquire_sem_etc(fWriterLock, 1, B_DO_NOT_RESCHEDULE, - B_INFINITE_TIMEOUT) == B_OK); - } else locked = true; - - if (locked) { - //new holder of the lock + // another writer in the lock - acquire the semaphore + status_t status; + do { + status = acquire_sem(fWriterLock); + } while (status == B_INTERRUPTED); - //decrement fReadCount by a very large number - //this will cause new readers to block on fReadSem + locked = status == B_OK; + } 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); - - if (readers > 0) { - //readers hold the lock - acquire fWriteSem - locked = (acquire_sem_etc(fWriteSem, readers, - B_DO_NOT_RESCHEDULE, - B_INFINITE_TIMEOUT) == B_OK); - } + if (readers > 0) { + // readers hold the lock - acquire fWriteSem + status_t status; + do { + status = acquire_sem_etc(fWriteSem, readers, 0, 0); + } while (status == B_INTERRUPTED); + + locked = status == B_OK; + } if (locked) { ASSERT(fWriterThread == -1); - //record thread information + // record thread information fWriterThread = thread; - fWriterStackBase = stack_base; + fWriterStackBase = stackBase; } } } } - #if TIMING - bigtime_t end = system_time(); - wl_time += (end - start); - wl_count++; - #endif +#if TIMING + bigtime_t end = system_time(); + wl_time += (end - start); + wl_count++; +#endif return locked; } -bool + +bool MultiLocker::ReadUnlock() { - #if TIMING - bigtime_t start = system_time(); - #endif +#if TIMING + bigtime_t start = system_time(); +#endif bool unlocked = false; if (IsWriteLocked()) { - //writers simply decrement the nesting count + // writers simply decrement the nesting count fWriterNest--; - - #if DEBUG - if (fWriterNest < 0) - debugger("ReadUnlock() - negative writer nest count\n"); - #endif - unlocked = true; } else { - //decrement and retrieve the read counter + // decrement and retrieve the read counter int32 current_count = atomic_add(&fReadCount, -1); if (current_count < 0) { - //a writer is waiting for the lock so release fWriteSem - unlocked = (release_sem_etc(fWriteSem, 1, - B_DO_NOT_RESCHEDULE) == B_OK); - } else unlocked = true; - - #if DEBUG - //unregister if we released the lock - if (unlocked) unregister_thread(); - #endif + // a writer is waiting for the lock so release fWriteSem + unlocked = release_sem_etc(fWriteSem, 1, B_DO_NOT_RESCHEDULE) == B_OK; + } else + unlocked = true; } - - #if TIMING - bigtime_t end = system_time(); - ru_time += (end - start); - ru_count++; - #endif + +#if TIMING + bigtime_t end = system_time(); + ru_time += (end - start); + ru_count++; +#endif return unlocked; } -bool + +bool MultiLocker::WriteUnlock() { - #if TIMING - bigtime_t start = system_time(); - #endif +#if TIMING + bigtime_t start = system_time(); +#endif bool unlocked = false; 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) { fWriterNest--; - - #if DEBUG - if (fWriterNest < 0) - debugger("WriteUnlock(): nest count now negative\n"); - #endif - unlocked = true; } else { - //writer finally unlocking + // writer finally unlocking - //increment fReadCount by a large number - //this will let new readers acquire the read lock - //retrieve the number of current waiters + // increment fReadCount by a large number + // this will let new readers acquire the read lock + // retrieve the number of current waiters int32 readersWaiting = atomic_add(&fReadCount, LARGE_NUMBER) + LARGE_NUMBER; - + if (readersWaiting > 0) { - //readers are waiting to acquire the lock - unlocked = (release_sem_etc(fReadSem, readersWaiting, - B_DO_NOT_RESCHEDULE) == B_OK); - } else unlocked = true; - + // readers are waiting to acquire the lock + unlocked = release_sem_etc(fReadSem, readersWaiting, + B_DO_NOT_RESCHEDULE) == B_OK; + } else + unlocked = true; + if (unlocked) { - //clear the information + // clear the information fWriterThread = -1; fWriterStackBase = 0; - - //decrement and retrieve the lock count + + // decrement and retrieve the lock count if (atomic_add(&fLockCount, -1) > 1) { - //other writers are waiting so release fWriterLock - unlocked = (release_sem_etc(fWriterLock, 1, - B_DO_NOT_RESCHEDULE) == B_OK); + // other writers are waiting so release fWriterLock + unlocked = release_sem_etc(fWriterLock, 1, + B_DO_NOT_RESCHEDULE) == B_OK; } } } - - } else debugger("Non-writer attempting to WriteUnlock()\n"); + } else + debugger("Non-writer attempting to WriteUnlock()"); - #if TIMING - bigtime_t end = system_time(); - wu_time += (end - start); - wu_count++; - #endif +#if TIMING + bigtime_t end = system_time(); + wu_time += (end - start); + wu_count++; +#endif 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. */ -/* 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. */ +#else // DEBUG +// #pragma mark - Debug versions -bool -MultiLocker::IsWriteLocked(uint32 *the_stack_base, thread_id *the_thread) + +bool +MultiLocker::ReadLock() { - #if TIMING - bigtime_t start = system_time(); - #endif + bool locked = false; - //get a variable on the stack - bool write_lock_holder = false; + if (fInit != B_OK) + debugger("lock not initialized"); - if (fInit == B_OK) { - uint32 stack_base; - thread_id thread = 0; - - //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 - stack_base = (uint32)&write_lock_holder / B_PAGE_SIZE; - if (fWriterStackBase == stack_base) { - write_lock_holder = true; - } else { - //as there was no stack_page match we resort to the - //tried and true methods - thread = find_thread(NULL); - if (fWriterThread == thread) { - write_lock_holder = true; - } - } - - //if someone wants this information, give it to them - if (the_stack_base != NULL) { - *the_stack_base = stack_base; - } - if (the_thread != NULL) { - *the_thread = thread; + if (IsWriteLocked()) { + if (fWriterNest < 0) + debugger("ReadLock() - negative writer nest count"); + + fWriterNest++; + locked = true; + } else { + status_t status; + do { + status = acquire_sem(fLock); + } while (status == B_INTERRUPTED); + + locked = status == B_OK; + + if (locked) + _RegisterThread(); + } + + return locked; +} + + +bool +MultiLocker::WriteLock() +{ + 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 - bigtime_t end = system_time(); - islock_time += (end - start); - islock_count++; - #endif - - return write_lock_holder; + return locked; } -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() { - //a properly initialized MultiLocker in non-debug always returns true - bool locked = true; - if (fInit == B_NO_INIT) locked = false; - - #if DEBUG - //determine if the lock is actually held - thread_id thread = find_thread(NULL); - if (fDebugArray[thread % fMaxThreads] > 0) - locked = true; - else - locked = false; - #endif - - return locked; + if (fInit == B_NO_INIT) + return false; + + // determine if the lock is actually held + thread_id thread = find_thread(NULL); + return fDebugArray[thread % fMaxThreads] > 0; } @@ -446,54 +546,25 @@ MultiLocker::IsReadLocked() /* traversing a list of cached information. As this is only for a debug mode, the extra memory */ /* was not deemed to be a problem */ -void -MultiLocker::register_thread() +void +MultiLocker::_RegisterThread() { - #if DEBUG - #if TIMING - bigtime_t start = system_time(); - #endif + thread_id thread = find_thread(NULL); - thread_id thread = find_thread(NULL); - - 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 + if (fDebugArray[thread % fMaxThreads] != 0) + debugger("Nested ReadLock!"); + fDebugArray[thread % fMaxThreads]++; } +void +MultiLocker::_UnregisterThread() +{ + thread_id thread = find_thread(NULL); + ASSERT(fDebugArray[thread % fMaxThreads] == 1); + fDebugArray[thread % fMaxThreads]--; +} + +#endif // DEBUG diff --git a/src/servers/app/MultiLocker.h b/src/servers/app/MultiLocker.h index 58530d7742..b300b1d338 100644 --- a/src/servers/app/MultiLocker.h +++ b/src/servers/app/MultiLocker.h @@ -1,13 +1,12 @@ -/* MultiLocker.h */ /* - Copyright 2005-2006, Haiku. - Distributed under the terms of the MIT license. + * Copyright 2005-2007, Haiku, Inc. All Rights Reserved. + * 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. - This file may be used under the terms of the Be Sample Code License. -*/ - -/** multiple-reader single-writer locking class */ +/*! multiple-reader single-writer locking class */ // IMPORTANT: // * nested read locks are not supported @@ -19,125 +18,142 @@ #ifndef MULTI_LOCKER_H #define MULTI_LOCKER_H -//#define TIMING 1 #include -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 { public: - MultiLocker(const char* semaphoreBaseName); - virtual ~MultiLocker(); - - status_t InitCheck(); - - //locking for reading or writing - bool ReadLock(); - bool WriteLock(); + MultiLocker(const char* baseName); + virtual ~MultiLocker(); - //unlocking after reading or writing - bool ReadUnlock(); - bool WriteUnlock(); + status_t InitCheck(); + + // locking for reading or writing + bool ReadLock(); + bool WriteLock(); - //does the current thread hold a write lock ? - bool IsWriteLocked(uint32 *stack_base = NULL, - thread_id *thread = NULL); - //in DEBUG mode returns whether the lock is held - //in non-debug mode returns true - bool IsReadLocked(); + // unlocking after reading or writing + bool ReadUnlock(); + bool WriteUnlock(); + + // does the current thread hold a write lock ? + 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: - //functions for managing the DEBUG reader array - void register_thread(); - void unregister_thread(); - - 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 MULTI_LOCKER_DEBUG + // functions for managing the DEBUG reader array + void _RegisterThread(); + void _UnregisterThread(); -#if 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; - uint32 reg_count; - bigtime_t reg_time; - uint32 unreg_count; - bigtime_t unreg_time; + sem_id fLock; + int32* fDebugArray; + int32 fMaxThreads; +#else + // 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; +#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 }; class AutoWriteLocker { - public: - AutoWriteLocker(MultiLocker* lock) - : fLock(*lock) - { - fLock.WriteLock(); - } - AutoWriteLocker(MultiLocker& lock) - : fLock(lock) - { - fLock.WriteLock(); - } - ~AutoWriteLocker() - { - fLock.WriteUnlock(); - } - private: - MultiLocker& fLock; + public: + AutoWriteLocker(MultiLocker* lock) + : + fLock(*lock) + { + fLock.WriteLock(); + } + + AutoWriteLocker(MultiLocker& lock) + : + fLock(lock) + { + fLock.WriteLock(); + } + + ~AutoWriteLocker() + { + fLock.WriteUnlock(); + } + + private: + MultiLocker& fLock; }; class AutoReadLocker { - public: - AutoReadLocker(MultiLocker* lock) - : fLock(*lock) - { - fLocked = fLock.ReadLock(); - } - AutoReadLocker(MultiLocker& lock) - : fLock(lock) - { - fLocked = fLock.ReadLock(); - } - ~AutoReadLocker() - { - Unlock(); - } - void Unlock() - { - if (fLocked) { - fLock.ReadUnlock(); - fLocked = false; - } - } - private: - MultiLocker& fLock; - bool fLocked; + public: + AutoReadLocker(MultiLocker* lock) + : + fLock(*lock) + { + fLocked = fLock.ReadLock(); + } + + AutoReadLocker(MultiLocker& lock) + : + fLock(lock) + { + fLocked = fLock.ReadLock(); + } + + ~AutoReadLocker() + { + Unlock(); + } + + void + Unlock() + { + if (fLocked) { + fLock.ReadUnlock(); + fLocked = false; + } + } + + private: + MultiLocker& fLock; + bool fLocked; }; - - -#endif +#endif // MULTI_LOCKER_H diff --git a/src/servers/app/drawing/HWInterface.h b/src/servers/app/drawing/HWInterface.h index 2f9bc43350..7eaf8caf28 100644 --- a/src/servers/app/drawing/HWInterface.h +++ b/src/servers/app/drawing/HWInterface.h @@ -50,7 +50,9 @@ class HWInterface : protected MultiLocker { // locking bool LockParallelAccess() { return ReadLock(); } +#if DEBUG bool IsParallelAccessLocked() { return IsReadLocked(); } +#endif void UnlockParallelAccess() { ReadUnlock(); } bool LockExclusiveAccess() { return WriteLock(); }