From 57656b93b602d41246039ff396febfd16071fa6b Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Thu, 14 May 2020 08:38:17 +0200 Subject: [PATCH] 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 --- headers/private/kernel/lock.h | 13 +++ src/system/kernel/locks/lock.cpp | 159 ++++++++++++++++++++++++++++++- 2 files changed, 168 insertions(+), 4 deletions(-) diff --git a/headers/private/kernel/lock.h b/headers/private/kernel/lock.h index 770c3cb895..92d0dda24a 100644 --- a/headers/private/kernel/lock.h +++ b/headers/private/kernel/lock.h @@ -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_trylock(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 void rw_lock_init(rw_lock* lock, const char* name); diff --git a/src/system/kernel/locks/lock.cpp b/src/system/kernel/locks/lock.cpp index 2176b3d290..c6d5bcdba6 100644 --- a/src/system/kernel/locks/lock.cpp +++ b/src/system/kernel/locks/lock.cpp @@ -56,9 +56,7 @@ recursive_lock_get_recursion(recursive_lock *lock) void recursive_lock_init(recursive_lock *lock, const char *name) { - mutex_init(&lock->lock, name != NULL ? name : "recursive lock"); - RECURSIVE_LOCK_HOLDER(lock) = -1; - lock->recursion = 0; + recursive_lock_init_etc(lock, name, 0); } @@ -66,7 +64,9 @@ void recursive_lock_init_etc(recursive_lock *lock, const char *name, uint32 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; } @@ -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 - @@ -1002,4 +1147,10 @@ lock_debug_init() "\n" "Prints info about the specified rw lock.\n" " - 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", + "\n" + "Prints info about the specified recursive lock.\n" + " - pointer to the recursive lock to print the info for.\n", + 0); }