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.
This commit is contained in:
PixelyIon 2022-11-26 00:58:45 +05:30 committed by Mark Collins
parent 6bbe9de881
commit 1eb4eec103
3 changed files with 16 additions and 13 deletions

View File

@ -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)

View File

@ -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<KThread> &thread, u32 *mutex, KHandle ownerHandle, KHandle tag, bool failOnOutdated) {
TRACE_EVENT_FMT("kernel", "MutexLock 0x{:X} @ 0x{:X}", mutex, thread->id);
std::shared_ptr<KThread> 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;

View File

@ -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<KThread> &thread, u32 *mutex, KHandle ownerHandle, KHandle tag, bool failOnOutdated = false);
/**
* @brief Unlocks the mutex at the specified address