kernel/locks: Implement lock switching for recursive_lock.

This allows switching from another recursive_lock, mutex or read-locked
rw_lock analogous to the switching possibilities already in mutex.

With this, recursive_locks can be used in more complex situations where
previously only mutexes would work.

Also add debugger command to dump a recursive_lock.

Change-Id: Ibeeae1b42c543d925dec61a3b257e1f3df7f8934
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2834
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Michael Lotz
2020-05-30 01:47:40 +00:00
committed by waddlesplash
parent 38963e7596
commit 57656b93b6
2 changed files with 168 additions and 4 deletions
+13
View File
@@ -126,6 +126,19 @@ extern void recursive_lock_destroy(recursive_lock *lock);
extern status_t recursive_lock_lock(recursive_lock *lock); extern status_t recursive_lock_lock(recursive_lock *lock);
extern status_t recursive_lock_trylock(recursive_lock *lock); extern status_t recursive_lock_trylock(recursive_lock *lock);
extern void recursive_lock_unlock(recursive_lock *lock); extern void recursive_lock_unlock(recursive_lock *lock);
extern status_t recursive_lock_switch_lock(recursive_lock* from,
recursive_lock* to);
// Unlocks "from" and locks "to" such that unlocking and starting to wait
// for the lock is atomic. I.e. if "from" guards the object "to" belongs
// to, the operation is safe as long as "from" is held while destroying
// "to".
extern status_t recursive_lock_switch_from_mutex(mutex* from,
recursive_lock* to);
// Like recursive_lock_switch_lock(), just for switching from a mutex.
extern status_t recursive_lock_switch_from_read_lock(rw_lock* from,
recursive_lock* to);
// Like recursive_lock_switch_lock(), just for switching from a read-locked
// rw_lock.
extern int32 recursive_lock_get_recursion(recursive_lock *lock); extern int32 recursive_lock_get_recursion(recursive_lock *lock);
extern void rw_lock_init(rw_lock* lock, const char* name); extern void rw_lock_init(rw_lock* lock, const char* name);
+155 -4
View File
@@ -56,9 +56,7 @@ recursive_lock_get_recursion(recursive_lock *lock)
void void
recursive_lock_init(recursive_lock *lock, const char *name) recursive_lock_init(recursive_lock *lock, const char *name)
{ {
mutex_init(&lock->lock, name != NULL ? name : "recursive lock"); recursive_lock_init_etc(lock, name, 0);
RECURSIVE_LOCK_HOLDER(lock) = -1;
lock->recursion = 0;
} }
@@ -66,7 +64,9 @@ void
recursive_lock_init_etc(recursive_lock *lock, const char *name, uint32 flags) recursive_lock_init_etc(recursive_lock *lock, const char *name, uint32 flags)
{ {
mutex_init_etc(&lock->lock, name != NULL ? name : "recursive lock", flags); mutex_init_etc(&lock->lock, name != NULL ? name : "recursive lock", flags);
RECURSIVE_LOCK_HOLDER(lock) = -1; #if !KDEBUG
lock->holder = -1;
#endif
lock->recursion = 0; lock->recursion = 0;
} }
@@ -147,6 +147,151 @@ recursive_lock_unlock(recursive_lock *lock)
} }
status_t
recursive_lock_switch_lock(recursive_lock* from, recursive_lock* to)
{
#if KDEBUG
if (!gKernelStartup && !are_interrupts_enabled()) {
panic("recursive_lock_switch_lock(): called with interrupts "
"disabled for locks %p, %p", from, to);
}
#endif
if (--from->recursion > 0)
return recursive_lock_lock(to);
#if !KDEBUG
from->holder = -1;
#endif
thread_id thread = thread_get_current_thread_id();
if (thread == RECURSIVE_LOCK_HOLDER(to)) {
to->recursion++;
mutex_unlock(&from->lock);
return B_OK;
}
status_t status = mutex_switch_lock(&from->lock, &to->lock);
if (status != B_OK) {
from->recursion++;
#if !KDEBUG
from->holder = thread;
#endif
return status;
}
#if !KDEBUG
to->holder = thread;
#endif
to->recursion++;
return B_OK;
}
status_t
recursive_lock_switch_from_mutex(mutex* from, recursive_lock* to)
{
#if KDEBUG
if (!gKernelStartup && !are_interrupts_enabled()) {
panic("recursive_lock_switch_from_mutex(): called with interrupts "
"disabled for locks %p, %p", from, to);
}
#endif
thread_id thread = thread_get_current_thread_id();
if (thread == RECURSIVE_LOCK_HOLDER(to)) {
to->recursion++;
mutex_unlock(from);
return B_OK;
}
status_t status = mutex_switch_lock(from, &to->lock);
if (status != B_OK)
return status;
#if !KDEBUG
to->holder = thread;
#endif
to->recursion++;
return B_OK;
}
status_t
recursive_lock_switch_from_read_lock(rw_lock* from, recursive_lock* to)
{
#if KDEBUG
if (!gKernelStartup && !are_interrupts_enabled()) {
panic("recursive_lock_switch_from_read_lock(): called with interrupts "
"disabled for locks %p, %p", from, to);
}
#endif
thread_id thread = thread_get_current_thread_id();
if (thread != RECURSIVE_LOCK_HOLDER(to)) {
status_t status = mutex_switch_from_read_lock(from, &to->lock);
if (status != B_OK)
return status;
#if !KDEBUG
to->holder = thread;
#endif
} else {
#if KDEBUG_RW_LOCK_DEBUG
_rw_lock_write_unlock(from);
#else
int32 oldCount = atomic_add(&from->count, -1);
if (oldCount >= RW_LOCK_WRITER_COUNT_BASE)
_rw_lock_read_unlock(from);
#endif
}
to->recursion++;
return B_OK;
}
static int
dump_recursive_lock_info(int argc, char** argv)
{
if (argc < 2) {
print_debugger_command_usage(argv[0]);
return 0;
}
recursive_lock* lock = (recursive_lock*)parse_expression(argv[1]);
if (!IS_KERNEL_ADDRESS(lock)) {
kprintf("invalid address: %p\n", lock);
return 0;
}
kprintf("recrusive_lock %p:\n", lock);
kprintf(" mutex: %p\n", &lock->lock);
kprintf(" name: %s\n", lock->lock.name);
kprintf(" flags: 0x%x\n", lock->lock.flags);
#if KDEBUG
kprintf(" holder: %" B_PRId32 "\n", lock->lock.holder);
#else
kprintf(" holder: %" B_PRId32 "\n", lock->holder);
#endif
kprintf(" recursion: %d\n", lock->recursion);
kprintf(" waiting threads:");
mutex_waiter* waiter = lock->lock.waiters;
while (waiter != NULL) {
kprintf(" %" B_PRId32, waiter->thread->id);
waiter = waiter->next;
}
kputs("\n");
return 0;
}
// #pragma mark - // #pragma mark -
@@ -1002,4 +1147,10 @@ lock_debug_init()
"<lock>\n" "<lock>\n"
"Prints info about the specified rw lock.\n" "Prints info about the specified rw lock.\n"
" <lock> - pointer to the rw lock to print the info for.\n", 0); " <lock> - pointer to the rw lock to print the info for.\n", 0);
add_debugger_command_etc("recursivelock", &dump_recursive_lock_info,
"Dump info about a recursive lock",
"<lock>\n"
"Prints info about the specified recursive lock.\n"
" <lock> - pointer to the recursive lock to print the info for.\n",
0);
} }