From 1eb4eec1037b25bd4899cc0d7d54d0c32e1482bd Mon Sep 17 00:00:00 2001 From: PixelyIon Date: Sat, 26 Nov 2022 00:58:45 +0530 Subject: [PATCH] Allow locking external thread in `MutexLock` We want the ability to lock mutexes on behalf of other threads to refactor condition variables to match HOS on waking behavior. --- app/src/main/cpp/skyline/kernel/svc.cpp | 2 +- .../cpp/skyline/kernel/types/KProcess.cpp | 24 ++++++++++--------- .../main/cpp/skyline/kernel/types/KProcess.h | 3 ++- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index dfe8ecde..27ddc514 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -751,7 +751,7 @@ namespace skyline::kernel::svc { KHandle ownerHandle{state.ctx->gpr.w0}; KHandle requesterHandle{state.ctx->gpr.w2}; - auto result{state.process->MutexLock(mutex, ownerHandle, requesterHandle)}; + auto result{state.process->MutexLock(state.thread, mutex, ownerHandle, requesterHandle)}; if (result == Result{}) Logger::Debug("Locked 0x{:X}", mutex); else if (result == result::InvalidCurrentMemory) diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp index 3a428470..cb90d89f 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp @@ -111,8 +111,8 @@ namespace skyline::kernel::type { constexpr u32 HandleWaitersBit{1UL << 30}; //!< A bit which denotes if a mutex psuedo-handle has waiters or not - Result KProcess::MutexLock(u32 *mutex, KHandle ownerHandle, KHandle tag, bool failOnOutdated) { - TRACE_EVENT_FMT("kernel", "MutexLock 0x{:X}", mutex); + Result KProcess::MutexLock(const std::shared_ptr &thread, u32 *mutex, KHandle ownerHandle, KHandle tag, bool failOnOutdated) { + TRACE_EVENT_FMT("kernel", "MutexLock 0x{:X} @ 0x{:X}", mutex, thread->id); std::shared_ptr owner; try { @@ -126,7 +126,7 @@ namespace skyline::kernel::type { bool isHighestPriority; { - std::scoped_lock lock{owner->waiterMutex, state.thread->waiterMutex}; // We need to lock both mutexes at the same time as we mutate the owner and the current thread, the ordering of locks **must** match MutexUnlock to avoid deadlocks + std::scoped_lock lock{owner->waiterMutex, thread->waiterMutex}; // We need to lock both mutexes at the same time as we mutate the owner and the current thread, the ordering of locks **must** match MutexUnlock to avoid deadlocks u32 value{__atomic_load_n(mutex, __ATOMIC_SEQ_CST)}; if (value != (ownerHandle | HandleWaitersBit)) @@ -134,19 +134,21 @@ namespace skyline::kernel::type { return failOnOutdated ? result::InvalidCurrentMemory : Result{}; auto &waiters{owner->waiters}; - isHighestPriority = waiters.insert(std::upper_bound(waiters.begin(), waiters.end(), state.thread->priority.load(), KThread::IsHigherPriority), state.thread) == waiters.begin(); - state.scheduler->RemoveThread(); + isHighestPriority = waiters.insert(std::upper_bound(waiters.begin(), waiters.end(), thread->priority.load(), KThread::IsHigherPriority), thread) == waiters.begin(); + if (thread == state.thread) + state.scheduler->RemoveThread(); - state.thread->waitThread = owner; - state.thread->waitKey = mutex; - state.thread->waitTag = tag; + thread->waitThread = owner; + thread->waitKey = mutex; + thread->waitTag = tag; } if (isHighestPriority) // If we were the highest priority thread then we need to inherit priorities for all threads we're waiting on recursively - state.thread->UpdatePriorityInheritance(); + thread->UpdatePriorityInheritance(); - state.scheduler->WaitSchedule(); + if (thread == state.thread) + state.scheduler->WaitSchedule(); return {}; } @@ -247,7 +249,7 @@ namespace skyline::kernel::type { KHandle value{}; if (!__atomic_compare_exchange_n(mutex, &value, tag, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) - while (MutexLock(mutex, value & ~HandleWaitersBit, tag, true) != Result{}) + while (MutexLock(state.thread, mutex, value & ~HandleWaitersBit, tag, true) != Result{}) if ((value = __atomic_or_fetch(mutex, HandleWaitersBit, __ATOMIC_SEQ_CST)) == HandleWaitersBit) if (__atomic_compare_exchange_n(mutex, &value, tag, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) break; diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.h b/app/src/main/cpp/skyline/kernel/types/KProcess.h index 08158785..6f5d457f 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.h +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.h @@ -210,11 +210,12 @@ namespace skyline { /** * @brief Locks the mutex at the specified address + * @param thread The thread that is locking the mutex * @param ownerHandle The psuedo-handle of the current mutex owner * @param tag The handle of the thread which is requesting this lock * @param failOnOutdated If true, the function will return InvalidCurrentMemory if the supplied ownerHandle is outdated */ - Result MutexLock(u32 *mutex, KHandle ownerHandle, KHandle tag, bool failOnOutdated = false); + Result MutexLock(const std::shared_ptr &thread, u32 *mutex, KHandle ownerHandle, KHandle tag, bool failOnOutdated = false); /** * @brief Unlocks the mutex at the specified address