* 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:
@@ -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
@@ -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
@@ -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
|
|
||||||
|
|||||||
@@ -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(); }
|
||||||
|
|||||||
Reference in New Issue
Block a user