kernel: Rewrite KDEBUG_RW_LOCK_DEBUG.
Previously this just turned the rw_lock into the equivalent of a recursive_lock, which meant that reader vs. writer assertions were of no use. Now, we have a per-thread static array which stores the held read locks, allowing ASSERT_READ_LOCKED_RW_LOCK to work properly, and allowing multiple readers to be active at a time. This probably should still remain disabled even on nightly builds, but at least it's much more useful as a debugging tool than it was beforehand. Change-Id: I386b2bc2ada8df42f4ab11a05563ef22af58e77f
This commit is contained in:
@@ -72,23 +72,27 @@ typedef struct rw_lock {
|
|||||||
#if KDEBUG
|
#if KDEBUG
|
||||||
# define KDEBUG_RW_LOCK_DEBUG 0
|
# define KDEBUG_RW_LOCK_DEBUG 0
|
||||||
// Define to 1 if you want to use ASSERT_READ_LOCKED_RW_LOCK().
|
// Define to 1 if you want to use ASSERT_READ_LOCKED_RW_LOCK().
|
||||||
// The rw_lock will just behave like a recursive locker then.
|
|
||||||
# define ASSERT_LOCKED_RECURSIVE(r) \
|
# define ASSERT_LOCKED_RECURSIVE(r) \
|
||||||
{ ASSERT(find_thread(NULL) == (r)->lock.holder); }
|
{ ASSERT(find_thread(NULL) == (r)->lock.holder); }
|
||||||
# define ASSERT_LOCKED_MUTEX(m) { ASSERT(find_thread(NULL) == (m)->holder); }
|
# define ASSERT_LOCKED_MUTEX(m) { ASSERT(find_thread(NULL) == (m)->holder); }
|
||||||
# define ASSERT_WRITE_LOCKED_RW_LOCK(l) \
|
# define ASSERT_WRITE_LOCKED_RW_LOCK(l) \
|
||||||
{ ASSERT(find_thread(NULL) == (l)->holder); }
|
{ ASSERT(find_thread(NULL) == (l)->holder); }
|
||||||
# if KDEBUG_RW_LOCK_DEBUG
|
# if KDEBUG_RW_LOCK_DEBUG
|
||||||
|
extern bool _rw_lock_is_read_locked(rw_lock* lock);
|
||||||
# define ASSERT_READ_LOCKED_RW_LOCK(l) \
|
# define ASSERT_READ_LOCKED_RW_LOCK(l) \
|
||||||
{ ASSERT(find_thread(NULL) == (l)->holder); }
|
{ ASSERT_PRINT(_rw_lock_is_read_locked(l), "rwlock %p", l); }
|
||||||
|
# define ASSERT_UNLOCKED_RW_LOCK(l) \
|
||||||
|
{ ASSERT_PRINT(!_rw_lock_is_read_locked(l), "rwlock %p", l); }
|
||||||
# else
|
# else
|
||||||
# define ASSERT_READ_LOCKED_RW_LOCK(l) do {} while (false)
|
# define ASSERT_READ_LOCKED_RW_LOCK(l) do {} while (false)
|
||||||
|
# define ASSERT_UNLOCKED_RW_LOCK(l) do {} while (false)
|
||||||
# endif
|
# endif
|
||||||
#else
|
#else
|
||||||
# define ASSERT_LOCKED_RECURSIVE(r) do {} while (false)
|
# define ASSERT_LOCKED_RECURSIVE(r) do {} while (false)
|
||||||
# define ASSERT_LOCKED_MUTEX(m) do {} while (false)
|
# define ASSERT_LOCKED_MUTEX(m) do {} while (false)
|
||||||
# define ASSERT_WRITE_LOCKED_RW_LOCK(m) do {} while (false)
|
# define ASSERT_WRITE_LOCKED_RW_LOCK(m) do {} while (false)
|
||||||
# define ASSERT_READ_LOCKED_RW_LOCK(l) do {} while (false)
|
# define ASSERT_READ_LOCKED_RW_LOCK(l) do {} while (false)
|
||||||
|
# define ASSERT_UNLOCKED_RW_LOCK(l) do {} while (false)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -180,7 +184,7 @@ static inline status_t
|
|||||||
rw_lock_read_lock(rw_lock* lock)
|
rw_lock_read_lock(rw_lock* lock)
|
||||||
{
|
{
|
||||||
#if KDEBUG_RW_LOCK_DEBUG
|
#if KDEBUG_RW_LOCK_DEBUG
|
||||||
return rw_lock_write_lock(lock);
|
return _rw_lock_read_lock(lock);
|
||||||
#else
|
#else
|
||||||
int32 oldCount = atomic_add(&lock->count, 1);
|
int32 oldCount = atomic_add(&lock->count, 1);
|
||||||
if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
|
if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
|
||||||
@@ -195,7 +199,7 @@ rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags,
|
|||||||
bigtime_t timeout)
|
bigtime_t timeout)
|
||||||
{
|
{
|
||||||
#if KDEBUG_RW_LOCK_DEBUG
|
#if KDEBUG_RW_LOCK_DEBUG
|
||||||
return mutex_lock_with_timeout(lock, timeoutFlags, timeout);
|
return _rw_lock_read_lock_with_timeout(lock, timeoutFlags, timeout);
|
||||||
#else
|
#else
|
||||||
int32 oldCount = atomic_add(&lock->count, 1);
|
int32 oldCount = atomic_add(&lock->count, 1);
|
||||||
if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
|
if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
|
||||||
@@ -209,7 +213,7 @@ static inline void
|
|||||||
rw_lock_read_unlock(rw_lock* lock)
|
rw_lock_read_unlock(rw_lock* lock)
|
||||||
{
|
{
|
||||||
#if KDEBUG_RW_LOCK_DEBUG
|
#if KDEBUG_RW_LOCK_DEBUG
|
||||||
rw_lock_write_unlock(lock);
|
_rw_lock_read_unlock(lock);
|
||||||
#else
|
#else
|
||||||
int32 oldCount = atomic_add(&lock->count, -1);
|
int32 oldCount = atomic_add(&lock->count, -1);
|
||||||
if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
|
if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
|
||||||
|
|||||||
@@ -547,6 +547,10 @@ struct Thread : TeamThreadIteratorEntry<thread_id>, KernelReferenceable {
|
|||||||
void (*post_interrupt_callback)(void*);
|
void (*post_interrupt_callback)(void*);
|
||||||
void* post_interrupt_data;
|
void* post_interrupt_data;
|
||||||
|
|
||||||
|
#if KDEBUG_RW_LOCK_DEBUG
|
||||||
|
rw_lock* held_read_locks[64] = {}; // only modified by this thread
|
||||||
|
#endif
|
||||||
|
|
||||||
// architecture dependent section
|
// architecture dependent section
|
||||||
struct arch_thread arch_info;
|
struct arch_thread arch_info;
|
||||||
|
|
||||||
|
|||||||
@@ -441,7 +441,53 @@ rw_lock_destroy(rw_lock* lock)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if !KDEBUG_RW_LOCK_DEBUG
|
#if KDEBUG_RW_LOCK_DEBUG
|
||||||
|
|
||||||
|
bool
|
||||||
|
_rw_lock_is_read_locked(rw_lock* lock)
|
||||||
|
{
|
||||||
|
if (lock->holder == thread_get_current_thread_id())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Thread* thread = thread_get_current_thread();
|
||||||
|
for (size_t i = 0; i < B_COUNT_OF(Thread::held_read_locks); i++) {
|
||||||
|
if (thread->held_read_locks[i] == lock)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
_rw_lock_set_read_locked(rw_lock* lock)
|
||||||
|
{
|
||||||
|
Thread* thread = thread_get_current_thread();
|
||||||
|
for (size_t i = 0; i < B_COUNT_OF(Thread::held_read_locks); i++) {
|
||||||
|
if (thread->held_read_locks[i] != NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
thread->held_read_locks[i] = lock;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
panic("too many read locks!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
_rw_lock_unset_read_locked(rw_lock* lock)
|
||||||
|
{
|
||||||
|
Thread* thread = thread_get_current_thread();
|
||||||
|
for (size_t i = 0; i < B_COUNT_OF(Thread::held_read_locks); i++) {
|
||||||
|
if (thread->held_read_locks[i] != lock)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
thread->held_read_locks[i] = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
_rw_lock_read_lock(rw_lock* lock)
|
_rw_lock_read_lock(rw_lock* lock)
|
||||||
@@ -452,6 +498,14 @@ _rw_lock_read_lock(rw_lock* lock)
|
|||||||
lock);
|
lock);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#if KDEBUG_RW_LOCK_DEBUG
|
||||||
|
int32 oldCount = atomic_add(&lock->count, 1);
|
||||||
|
if (oldCount < RW_LOCK_WRITER_COUNT_BASE) {
|
||||||
|
ASSERT_UNLOCKED_RW_LOCK(lock);
|
||||||
|
_rw_lock_set_read_locked(lock);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
InterruptsSpinLocker locker(lock->lock);
|
InterruptsSpinLocker locker(lock->lock);
|
||||||
|
|
||||||
@@ -461,6 +515,8 @@ _rw_lock_read_lock(rw_lock* lock)
|
|||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASSERT_UNLOCKED_RW_LOCK(lock);
|
||||||
|
|
||||||
// The writer that originally had the lock when we called atomic_add() might
|
// The writer that originally had the lock when we called atomic_add() might
|
||||||
// already have gone and another writer could have overtaken us. In this
|
// already have gone and another writer could have overtaken us. In this
|
||||||
// case the original writer set pending_readers, so we know that we don't
|
// case the original writer set pending_readers, so we know that we don't
|
||||||
@@ -471,13 +527,23 @@ _rw_lock_read_lock(rw_lock* lock)
|
|||||||
if (lock->count >= RW_LOCK_WRITER_COUNT_BASE)
|
if (lock->count >= RW_LOCK_WRITER_COUNT_BASE)
|
||||||
lock->active_readers++;
|
lock->active_readers++;
|
||||||
|
|
||||||
|
#if KDEBUG_RW_LOCK_DEBUG
|
||||||
|
_rw_lock_set_read_locked(lock);
|
||||||
|
#endif
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(lock->count >= RW_LOCK_WRITER_COUNT_BASE);
|
ASSERT(lock->count >= RW_LOCK_WRITER_COUNT_BASE);
|
||||||
|
|
||||||
// we need to wait
|
// we need to wait
|
||||||
return rw_lock_wait(lock, false, locker);
|
status_t status = rw_lock_wait(lock, false, locker);
|
||||||
|
|
||||||
|
#if KDEBUG_RW_LOCK_DEBUG
|
||||||
|
if (status == B_OK)
|
||||||
|
_rw_lock_set_read_locked(lock);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -491,6 +557,14 @@ _rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags,
|
|||||||
"disabled for lock %p", lock);
|
"disabled for lock %p", lock);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#if KDEBUG_RW_LOCK_DEBUG
|
||||||
|
int32 oldCount = atomic_add(&lock->count, 1);
|
||||||
|
if (oldCount < RW_LOCK_WRITER_COUNT_BASE) {
|
||||||
|
ASSERT_UNLOCKED_RW_LOCK(lock);
|
||||||
|
_rw_lock_set_read_locked(lock);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
InterruptsSpinLocker locker(lock->lock);
|
InterruptsSpinLocker locker(lock->lock);
|
||||||
|
|
||||||
@@ -500,6 +574,8 @@ _rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags,
|
|||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASSERT_UNLOCKED_RW_LOCK(lock);
|
||||||
|
|
||||||
// The writer that originally had the lock when we called atomic_add() might
|
// The writer that originally had the lock when we called atomic_add() might
|
||||||
// already have gone and another writer could have overtaken us. In this
|
// already have gone and another writer could have overtaken us. In this
|
||||||
// case the original writer set pending_readers, so we know that we don't
|
// case the original writer set pending_readers, so we know that we don't
|
||||||
@@ -510,6 +586,9 @@ _rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags,
|
|||||||
if (lock->count >= RW_LOCK_WRITER_COUNT_BASE)
|
if (lock->count >= RW_LOCK_WRITER_COUNT_BASE)
|
||||||
lock->active_readers++;
|
lock->active_readers++;
|
||||||
|
|
||||||
|
#if KDEBUG_RW_LOCK_DEBUG
|
||||||
|
_rw_lock_set_read_locked(lock);
|
||||||
|
#endif
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,6 +617,9 @@ _rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags,
|
|||||||
if (error == B_OK || waiter.thread == NULL) {
|
if (error == B_OK || waiter.thread == NULL) {
|
||||||
// We were unblocked successfully -- potentially our unblocker overtook
|
// We were unblocked successfully -- potentially our unblocker overtook
|
||||||
// us after we already failed. In either case, we've got the lock, now.
|
// us after we already failed. In either case, we've got the lock, now.
|
||||||
|
#if KDEBUG_RW_LOCK_DEBUG
|
||||||
|
_rw_lock_set_read_locked(lock);
|
||||||
|
#endif
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,6 +668,14 @@ _rw_lock_read_unlock(rw_lock* lock)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if KDEBUG_RW_LOCK_DEBUG
|
||||||
|
_rw_lock_unset_read_locked(lock);
|
||||||
|
|
||||||
|
int32 oldCount = atomic_add(&lock->count, -1);
|
||||||
|
if (oldCount < RW_LOCK_WRITER_COUNT_BASE)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (--lock->active_readers > 0)
|
if (--lock->active_readers > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -598,8 +688,6 @@ _rw_lock_read_unlock(rw_lock* lock)
|
|||||||
rw_lock_unblock(lock);
|
rw_lock_unblock(lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !KDEBUG_RW_LOCK_DEBUG
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
rw_lock_write_lock(rw_lock* lock)
|
rw_lock_write_lock(rw_lock* lock)
|
||||||
@@ -621,6 +709,8 @@ rw_lock_write_lock(rw_lock* lock)
|
|||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASSERT_UNLOCKED_RW_LOCK(lock);
|
||||||
|
|
||||||
// announce our claim
|
// announce our claim
|
||||||
int32 oldCount = atomic_add(&lock->count, RW_LOCK_WRITER_COUNT_BASE);
|
int32 oldCount = atomic_add(&lock->count, RW_LOCK_WRITER_COUNT_BASE);
|
||||||
|
|
||||||
@@ -668,6 +758,11 @@ _rw_lock_write_unlock(rw_lock* lock)
|
|||||||
lock->holder = -1;
|
lock->holder = -1;
|
||||||
lock->owner_count = 0;
|
lock->owner_count = 0;
|
||||||
|
|
||||||
|
#if KDEBUG_RW_LOCK_DEBUG
|
||||||
|
if (readerCount != 0)
|
||||||
|
_rw_lock_set_read_locked(lock);
|
||||||
|
#endif
|
||||||
|
|
||||||
int32 oldCount = atomic_add(&lock->count, -RW_LOCK_WRITER_COUNT_BASE);
|
int32 oldCount = atomic_add(&lock->count, -RW_LOCK_WRITER_COUNT_BASE);
|
||||||
oldCount -= RW_LOCK_WRITER_COUNT_BASE;
|
oldCount -= RW_LOCK_WRITER_COUNT_BASE;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user