skyline/app/src/main/cpp/skyline/kernel/types/KThread.cpp

342 lines
12 KiB
C++
Raw Normal View History

// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <cxxabi.h>
#include <unistd.h>
#include <common/signal.h>
#include <common/trace.h>
#include <nce.h>
#include <os.h>
#include "KProcess.h"
2020-12-08 11:43:54 +01:00
#include "KThread.h"
namespace skyline::kernel::type {
KThread::KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, void *stackTop, i8 priority, u8 idealCore)
: handle(handle),
parent(parent),
id(id),
entry(entry),
entryArgument(argument),
stackTop(stackTop),
priority(priority),
basePriority(priority),
idealCore(idealCore),
coreId(idealCore),
KSyncObject(state, KType::KThread) {
affinityMask.set(coreId);
}
KThread::~KThread() {
Kill(true);
if (thread.joinable())
thread.join();
2020-12-08 11:43:54 +01:00
if (preemptionTimer)
timer_delete(preemptionTimer);
}
void KThread::StartThread() {
pthread = pthread_self();
std::array<char, 16> threadName{};
if (int result{pthread_getname_np(pthread, threadName.data(), threadName.size())})
Logger::Warn("Failed to get the thread name: {}", strerror(result));
if (int result{pthread_setname_np(pthread, fmt::format("HOS-{}", id).c_str())})
Logger::Warn("Failed to set the thread name: {}", strerror(result));
2021-11-10 23:21:43 +01:00
Logger::UpdateTag();
if (!ctx.tpidrroEl0)
ctx.tpidrroEl0 = parent->AllocateTlsSlot();
2020-10-10 17:53:14 +02:00
ctx.state = &state;
state.ctx = &ctx;
2020-10-10 17:53:14 +02:00
state.thread = shared_from_this();
if (setjmp(originalCtx)) { // Returns 1 if it's returning from guest, 0 otherwise
state.scheduler->RemoveThread();
{
std::scoped_lock lock{statusMutex};
running = false;
ready = false;
statusCondition.notify_all();
}
Signal();
if (threadName[0] != 'H' || threadName[1] != 'O' || threadName[2] != 'S' || threadName[3] != '-') {
if (int result{pthread_setname_np(pthread, threadName.data())})
Logger::Warn("Failed to set the thread name: {}", strerror(result));
2021-11-10 23:21:43 +01:00
Logger::UpdateTag();
}
return;
}
struct sigevent event{
Address CR Comments 2 (#132) + Fix Several Scheduler Bugs The following scheduler bugs were fixed: * It was assumed that all non-cooperative `Rotate` calls were from a preemptive yield and changed the state of `KThread::isPreempted` incorrectly which could lead to UB, an example of a scenario with it would be: * * Preemptive thread A gets a signal to yield from cooperative thread B due to it being ready to schedule and higher priority * * A complies with this request but there's an assumption that the signal was actually from it's preemption timer therefore it doesn't reset it (As it isn't required if the timer was responsible for the signal) * * A receives the actual preemption signal a while later, causing UB as the signal handler is invoked twice * `Scheduler::UpdatePriority` * * A check for `currentIt == core->queue.begin()` existed which caused an incorrect early return * * The preemption timer was armed correctly when a priority transition from cooperative priority -> preemption priority occurred but not disarmed when a transition from preemption priority -> cooperative priority occurred * * The timer was unnecessarily disarmed in the case of updating the priority of a non-running thread, this isn't as much a bug as it is just pointless * Priority inheritance in `KProcess::MutexLock` is fundamentally broken as it performs UB with `waitThread` being accessed prior to being assigned * When a thread sets its own priority using `SvcSetThreadCoreMask` and its current core is no longer in the affinity mask, it wouldn't actually move to the new thread until the next time the thread is load balanced
2021-03-05 10:06:41 +01:00
.sigev_signo = Scheduler::PreemptionSignal,
.sigev_notify = SIGEV_THREAD_ID,
.sigev_notify_thread_id = gettid(),
};
if (timer_create(CLOCK_THREAD_CPUTIME_ID, &event, &preemptionTimer))
throw exception("timer_create has failed with '{}'", strerror(errno));
signal::SetSignalHandler({SIGINT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}, nce::NCE::SignalHandler);
Address CR Comments 2 (#132) + Fix Several Scheduler Bugs The following scheduler bugs were fixed: * It was assumed that all non-cooperative `Rotate` calls were from a preemptive yield and changed the state of `KThread::isPreempted` incorrectly which could lead to UB, an example of a scenario with it would be: * * Preemptive thread A gets a signal to yield from cooperative thread B due to it being ready to schedule and higher priority * * A complies with this request but there's an assumption that the signal was actually from it's preemption timer therefore it doesn't reset it (As it isn't required if the timer was responsible for the signal) * * A receives the actual preemption signal a while later, causing UB as the signal handler is invoked twice * `Scheduler::UpdatePriority` * * A check for `currentIt == core->queue.begin()` existed which caused an incorrect early return * * The preemption timer was armed correctly when a priority transition from cooperative priority -> preemption priority occurred but not disarmed when a transition from preemption priority -> cooperative priority occurred * * The timer was unnecessarily disarmed in the case of updating the priority of a non-running thread, this isn't as much a bug as it is just pointless * Priority inheritance in `KProcess::MutexLock` is fundamentally broken as it performs UB with `waitThread` being accessed prior to being assigned * When a thread sets its own priority using `SvcSetThreadCoreMask` and its current core is no longer in the affinity mask, it wouldn't actually move to the new thread until the next time the thread is load balanced
2021-03-05 10:06:41 +01:00
signal::SetSignalHandler({Scheduler::YieldSignal, Scheduler::PreemptionSignal}, Scheduler::SignalHandler, false); // We want futexes to fail and their predicates rechecked
{
std::scoped_lock lock{statusMutex};
ready = true;
statusCondition.notify_all();
}
try {
if (!Scheduler::YieldPending)
state.scheduler->WaitSchedule();
while (Scheduler::YieldPending) {
// If there is a yield pending on us after thread creation
state.scheduler->Rotate();
Scheduler::YieldPending = false;
state.scheduler->WaitSchedule();
}
TRACE_EVENT_BEGIN("guest", "Guest");
asm volatile(
"MRS X0, TPIDR_EL0\n\t"
"MSR TPIDR_EL0, %x0\n\t" // Set TLS to ThreadContext
"STR X0, [%x0, #0x2A0]\n\t" // Write ThreadContext::hostTpidrEl0
"MOV X0, SP\n\t"
"STR X0, [%x0, #0x2A8]\n\t" // Write ThreadContext::hostSp
"MOV SP, %x1\n\t" // Replace SP with guest stack
"MOV LR, %x2\n\t" // Store entry in Link Register so it's jumped to on return
"MOV X0, %x3\n\t" // Store the argument in X0
"MOV X1, %x4\n\t" // Store the thread handle in X1, NCA applications require this
"MOV X2, XZR\n\t" // Zero out other GP and SIMD registers, not doing this will break applications
"MOV X3, XZR\n\t"
"MOV X4, XZR\n\t"
"MOV X5, XZR\n\t"
"MOV X6, XZR\n\t"
"MOV X7, XZR\n\t"
"MOV X8, XZR\n\t"
"MOV X9, XZR\n\t"
"MOV X10, XZR\n\t"
"MOV X11, XZR\n\t"
"MOV X12, XZR\n\t"
"MOV X13, XZR\n\t"
"MOV X14, XZR\n\t"
"MOV X15, XZR\n\t"
"MOV X16, XZR\n\t"
"MOV X17, XZR\n\t"
"MOV X18, XZR\n\t"
"MOV X19, XZR\n\t"
"MOV X20, XZR\n\t"
"MOV X21, XZR\n\t"
"MOV X22, XZR\n\t"
"MOV X23, XZR\n\t"
"MOV X24, XZR\n\t"
"MOV X25, XZR\n\t"
"MOV X26, XZR\n\t"
"MOV X27, XZR\n\t"
"MOV X28, XZR\n\t"
"MOV X29, XZR\n\t"
"MSR FPSR, XZR\n\t"
"MSR FPCR, XZR\n\t"
"MSR NZCV, XZR\n\t"
"DUP V0.16B, WZR\n\t"
"DUP V1.16B, WZR\n\t"
"DUP V2.16B, WZR\n\t"
"DUP V3.16B, WZR\n\t"
"DUP V4.16B, WZR\n\t"
"DUP V5.16B, WZR\n\t"
"DUP V6.16B, WZR\n\t"
"DUP V7.16B, WZR\n\t"
"DUP V8.16B, WZR\n\t"
"DUP V9.16B, WZR\n\t"
"DUP V10.16B, WZR\n\t"
"DUP V11.16B, WZR\n\t"
"DUP V12.16B, WZR\n\t"
"DUP V13.16B, WZR\n\t"
"DUP V14.16B, WZR\n\t"
"DUP V15.16B, WZR\n\t"
"DUP V16.16B, WZR\n\t"
"DUP V17.16B, WZR\n\t"
"DUP V18.16B, WZR\n\t"
"DUP V19.16B, WZR\n\t"
"DUP V20.16B, WZR\n\t"
"DUP V21.16B, WZR\n\t"
"DUP V22.16B, WZR\n\t"
"DUP V23.16B, WZR\n\t"
"DUP V24.16B, WZR\n\t"
"DUP V25.16B, WZR\n\t"
"DUP V26.16B, WZR\n\t"
"DUP V27.16B, WZR\n\t"
"DUP V28.16B, WZR\n\t"
"DUP V29.16B, WZR\n\t"
"DUP V30.16B, WZR\n\t"
"DUP V31.16B, WZR\n\t"
"RET"
:
: "r"(&ctx), "r"(stackTop), "r"(entry), "r"(entryArgument), "r"(handle)
: "x0", "x1", "lr"
);
__builtin_unreachable();
} catch (const std::exception &e) {
2021-11-10 23:21:43 +01:00
Logger::Error(e.what());
Logger::EmulationContext.Flush();
if (id) {
signal::BlockSignal({SIGINT});
state.process->Kill(false);
}
abi::__cxa_end_catch();
std::longjmp(originalCtx, true);
} catch (const signal::SignalException &e) {
if (e.signal != SIGINT) {
2021-11-10 23:21:43 +01:00
Logger::Error(e.what());
Logger::EmulationContext.Flush();
if (id) {
signal::BlockSignal({SIGINT});
state.process->Kill(false);
}
}
abi::__cxa_end_catch();
std::longjmp(originalCtx, true);
}
}
void KThread::Start(bool self) {
std::unique_lock lock(statusMutex);
if (!running) {
{
std::scoped_lock migrationLock{coreMigrationMutex};
auto thisShared{shared_from_this()};
coreId = state.scheduler->GetOptimalCoreForThread(thisShared).id;
state.scheduler->InsertThread(thisShared);
}
running = true;
killed = false;
statusCondition.notify_all();
if (self) {
lock.unlock();
StartThread();
} else {
thread = std::thread(&KThread::StartThread, this);
}
}
}
void KThread::Kill(bool join) {
std::unique_lock lock(statusMutex);
if (!killed && running) {
statusCondition.wait(lock, [this]() { return ready || killed; });
if (!killed) {
pthread_kill(pthread, SIGINT);
killed = true;
statusCondition.notify_all();
}
}
if (join)
statusCondition.wait(lock, [this]() { return !running; });
}
2020-12-08 11:43:54 +01:00
void KThread::SendSignal(int signal) {
std::unique_lock lock(statusMutex);
statusCondition.wait(lock, [this]() { return ready || killed; });
if (!killed && running)
2020-12-08 11:43:54 +01:00
pthread_kill(pthread, signal);
}
Address CR Comments 2 (#132) + Fix Several Scheduler Bugs The following scheduler bugs were fixed: * It was assumed that all non-cooperative `Rotate` calls were from a preemptive yield and changed the state of `KThread::isPreempted` incorrectly which could lead to UB, an example of a scenario with it would be: * * Preemptive thread A gets a signal to yield from cooperative thread B due to it being ready to schedule and higher priority * * A complies with this request but there's an assumption that the signal was actually from it's preemption timer therefore it doesn't reset it (As it isn't required if the timer was responsible for the signal) * * A receives the actual preemption signal a while later, causing UB as the signal handler is invoked twice * `Scheduler::UpdatePriority` * * A check for `currentIt == core->queue.begin()` existed which caused an incorrect early return * * The preemption timer was armed correctly when a priority transition from cooperative priority -> preemption priority occurred but not disarmed when a transition from preemption priority -> cooperative priority occurred * * The timer was unnecessarily disarmed in the case of updating the priority of a non-running thread, this isn't as much a bug as it is just pointless * Priority inheritance in `KProcess::MutexLock` is fundamentally broken as it performs UB with `waitThread` being accessed prior to being assigned * When a thread sets its own priority using `SvcSetThreadCoreMask` and its current core is no longer in the affinity mask, it wouldn't actually move to the new thread until the next time the thread is load balanced
2021-03-05 10:06:41 +01:00
void KThread::ArmPreemptionTimer(std::chrono::nanoseconds timeToFire) {
std::unique_lock lock(statusMutex);
statusCondition.wait(lock, [this]() { return ready || killed; });
if (!killed && running) {
struct itimerspec spec{.it_value = {
.tv_nsec = std::min(static_cast<i64>(timeToFire.count()), constant::NsInSecond),
.tv_sec = std::max(std::chrono::duration_cast<std::chrono::seconds>(timeToFire).count() - 1, 0LL),
}};
timer_settime(preemptionTimer, 0, &spec, nullptr);
isPreempted = true;
}
Address CR Comments 2 (#132) + Fix Several Scheduler Bugs The following scheduler bugs were fixed: * It was assumed that all non-cooperative `Rotate` calls were from a preemptive yield and changed the state of `KThread::isPreempted` incorrectly which could lead to UB, an example of a scenario with it would be: * * Preemptive thread A gets a signal to yield from cooperative thread B due to it being ready to schedule and higher priority * * A complies with this request but there's an assumption that the signal was actually from it's preemption timer therefore it doesn't reset it (As it isn't required if the timer was responsible for the signal) * * A receives the actual preemption signal a while later, causing UB as the signal handler is invoked twice * `Scheduler::UpdatePriority` * * A check for `currentIt == core->queue.begin()` existed which caused an incorrect early return * * The preemption timer was armed correctly when a priority transition from cooperative priority -> preemption priority occurred but not disarmed when a transition from preemption priority -> cooperative priority occurred * * The timer was unnecessarily disarmed in the case of updating the priority of a non-running thread, this isn't as much a bug as it is just pointless * Priority inheritance in `KProcess::MutexLock` is fundamentally broken as it performs UB with `waitThread` being accessed prior to being assigned * When a thread sets its own priority using `SvcSetThreadCoreMask` and its current core is no longer in the affinity mask, it wouldn't actually move to the new thread until the next time the thread is load balanced
2021-03-05 10:06:41 +01:00
}
void KThread::DisarmPreemptionTimer() {
2021-06-18 18:24:31 +02:00
if (!isPreempted) [[unlikely]]
return;
std::unique_lock lock(statusMutex);
statusCondition.wait(lock, [this]() { return ready || killed; });
if (!killed && running) {
Address CR Comments 2 (#132) + Fix Several Scheduler Bugs The following scheduler bugs were fixed: * It was assumed that all non-cooperative `Rotate` calls were from a preemptive yield and changed the state of `KThread::isPreempted` incorrectly which could lead to UB, an example of a scenario with it would be: * * Preemptive thread A gets a signal to yield from cooperative thread B due to it being ready to schedule and higher priority * * A complies with this request but there's an assumption that the signal was actually from it's preemption timer therefore it doesn't reset it (As it isn't required if the timer was responsible for the signal) * * A receives the actual preemption signal a while later, causing UB as the signal handler is invoked twice * `Scheduler::UpdatePriority` * * A check for `currentIt == core->queue.begin()` existed which caused an incorrect early return * * The preemption timer was armed correctly when a priority transition from cooperative priority -> preemption priority occurred but not disarmed when a transition from preemption priority -> cooperative priority occurred * * The timer was unnecessarily disarmed in the case of updating the priority of a non-running thread, this isn't as much a bug as it is just pointless * Priority inheritance in `KProcess::MutexLock` is fundamentally broken as it performs UB with `waitThread` being accessed prior to being assigned * When a thread sets its own priority using `SvcSetThreadCoreMask` and its current core is no longer in the affinity mask, it wouldn't actually move to the new thread until the next time the thread is load balanced
2021-03-05 10:06:41 +01:00
struct itimerspec spec{};
timer_settime(preemptionTimer, 0, &spec, nullptr);
isPreempted = false;
}
}
void KThread::UpdatePriorityInheritance() {
std::unique_lock lock{waiterMutex};
std::shared_ptr<KThread> waitingOn{waitThread};
i8 currentPriority{priority.load()};
Address CR Comments 2 (#132) + Fix Several Scheduler Bugs The following scheduler bugs were fixed: * It was assumed that all non-cooperative `Rotate` calls were from a preemptive yield and changed the state of `KThread::isPreempted` incorrectly which could lead to UB, an example of a scenario with it would be: * * Preemptive thread A gets a signal to yield from cooperative thread B due to it being ready to schedule and higher priority * * A complies with this request but there's an assumption that the signal was actually from it's preemption timer therefore it doesn't reset it (As it isn't required if the timer was responsible for the signal) * * A receives the actual preemption signal a while later, causing UB as the signal handler is invoked twice * `Scheduler::UpdatePriority` * * A check for `currentIt == core->queue.begin()` existed which caused an incorrect early return * * The preemption timer was armed correctly when a priority transition from cooperative priority -> preemption priority occurred but not disarmed when a transition from preemption priority -> cooperative priority occurred * * The timer was unnecessarily disarmed in the case of updating the priority of a non-running thread, this isn't as much a bug as it is just pointless * Priority inheritance in `KProcess::MutexLock` is fundamentally broken as it performs UB with `waitThread` being accessed prior to being assigned * When a thread sets its own priority using `SvcSetThreadCoreMask` and its current core is no longer in the affinity mask, it wouldn't actually move to the new thread until the next time the thread is load balanced
2021-03-05 10:06:41 +01:00
while (waitingOn) {
i8 ownerPriority;
Address CR Comments 2 (#132) + Fix Several Scheduler Bugs The following scheduler bugs were fixed: * It was assumed that all non-cooperative `Rotate` calls were from a preemptive yield and changed the state of `KThread::isPreempted` incorrectly which could lead to UB, an example of a scenario with it would be: * * Preemptive thread A gets a signal to yield from cooperative thread B due to it being ready to schedule and higher priority * * A complies with this request but there's an assumption that the signal was actually from it's preemption timer therefore it doesn't reset it (As it isn't required if the timer was responsible for the signal) * * A receives the actual preemption signal a while later, causing UB as the signal handler is invoked twice * `Scheduler::UpdatePriority` * * A check for `currentIt == core->queue.begin()` existed which caused an incorrect early return * * The preemption timer was armed correctly when a priority transition from cooperative priority -> preemption priority occurred but not disarmed when a transition from preemption priority -> cooperative priority occurred * * The timer was unnecessarily disarmed in the case of updating the priority of a non-running thread, this isn't as much a bug as it is just pointless * Priority inheritance in `KProcess::MutexLock` is fundamentally broken as it performs UB with `waitThread` being accessed prior to being assigned * When a thread sets its own priority using `SvcSetThreadCoreMask` and its current core is no longer in the affinity mask, it wouldn't actually move to the new thread until the next time the thread is load balanced
2021-03-05 10:06:41 +01:00
do {
// Try to CAS the priority of the owner with the current thread
// If the new priority is equivalent to the current priority then we don't need to CAS
ownerPriority = waitingOn->priority.load();
if (ownerPriority <= currentPriority)
return;
} while (!waitingOn->priority.compare_exchange_strong(ownerPriority, currentPriority));
Address CR Comments 2 (#132) + Fix Several Scheduler Bugs The following scheduler bugs were fixed: * It was assumed that all non-cooperative `Rotate` calls were from a preemptive yield and changed the state of `KThread::isPreempted` incorrectly which could lead to UB, an example of a scenario with it would be: * * Preemptive thread A gets a signal to yield from cooperative thread B due to it being ready to schedule and higher priority * * A complies with this request but there's an assumption that the signal was actually from it's preemption timer therefore it doesn't reset it (As it isn't required if the timer was responsible for the signal) * * A receives the actual preemption signal a while later, causing UB as the signal handler is invoked twice * `Scheduler::UpdatePriority` * * A check for `currentIt == core->queue.begin()` existed which caused an incorrect early return * * The preemption timer was armed correctly when a priority transition from cooperative priority -> preemption priority occurred but not disarmed when a transition from preemption priority -> cooperative priority occurred * * The timer was unnecessarily disarmed in the case of updating the priority of a non-running thread, this isn't as much a bug as it is just pointless * Priority inheritance in `KProcess::MutexLock` is fundamentally broken as it performs UB with `waitThread` being accessed prior to being assigned * When a thread sets its own priority using `SvcSetThreadCoreMask` and its current core is no longer in the affinity mask, it wouldn't actually move to the new thread until the next time the thread is load balanced
2021-03-05 10:06:41 +01:00
if (ownerPriority != currentPriority) {
std::unique_lock waiterLock{waitingOn->waiterMutex, std::try_to_lock};
if (!waiterLock) {
// We want to avoid a deadlock here from the thread holding waitingOn->waiterMutex waiting for waiterMutex
// We use a fallback mechanism to avoid this, resetting the state and trying again after being able to successfully acquire waitingOn->waiterMutex once
waitingOn->priority = ownerPriority;
lock.unlock();
waiterLock.lock();
waiterLock.unlock();
lock.lock();
waitingOn = waitThread;
continue;
}
Address CR Comments 2 (#132) + Fix Several Scheduler Bugs The following scheduler bugs were fixed: * It was assumed that all non-cooperative `Rotate` calls were from a preemptive yield and changed the state of `KThread::isPreempted` incorrectly which could lead to UB, an example of a scenario with it would be: * * Preemptive thread A gets a signal to yield from cooperative thread B due to it being ready to schedule and higher priority * * A complies with this request but there's an assumption that the signal was actually from it's preemption timer therefore it doesn't reset it (As it isn't required if the timer was responsible for the signal) * * A receives the actual preemption signal a while later, causing UB as the signal handler is invoked twice * `Scheduler::UpdatePriority` * * A check for `currentIt == core->queue.begin()` existed which caused an incorrect early return * * The preemption timer was armed correctly when a priority transition from cooperative priority -> preemption priority occurred but not disarmed when a transition from preemption priority -> cooperative priority occurred * * The timer was unnecessarily disarmed in the case of updating the priority of a non-running thread, this isn't as much a bug as it is just pointless * Priority inheritance in `KProcess::MutexLock` is fundamentally broken as it performs UB with `waitThread` being accessed prior to being assigned * When a thread sets its own priority using `SvcSetThreadCoreMask` and its current core is no longer in the affinity mask, it wouldn't actually move to the new thread until the next time the thread is load balanced
2021-03-05 10:06:41 +01:00
auto nextThread{waitingOn->waitThread};
if (nextThread) {
Address CR Comments 2 (#132) + Fix Several Scheduler Bugs The following scheduler bugs were fixed: * It was assumed that all non-cooperative `Rotate` calls were from a preemptive yield and changed the state of `KThread::isPreempted` incorrectly which could lead to UB, an example of a scenario with it would be: * * Preemptive thread A gets a signal to yield from cooperative thread B due to it being ready to schedule and higher priority * * A complies with this request but there's an assumption that the signal was actually from it's preemption timer therefore it doesn't reset it (As it isn't required if the timer was responsible for the signal) * * A receives the actual preemption signal a while later, causing UB as the signal handler is invoked twice * `Scheduler::UpdatePriority` * * A check for `currentIt == core->queue.begin()` existed which caused an incorrect early return * * The preemption timer was armed correctly when a priority transition from cooperative priority -> preemption priority occurred but not disarmed when a transition from preemption priority -> cooperative priority occurred * * The timer was unnecessarily disarmed in the case of updating the priority of a non-running thread, this isn't as much a bug as it is just pointless * Priority inheritance in `KProcess::MutexLock` is fundamentally broken as it performs UB with `waitThread` being accessed prior to being assigned * When a thread sets its own priority using `SvcSetThreadCoreMask` and its current core is no longer in the affinity mask, it wouldn't actually move to the new thread until the next time the thread is load balanced
2021-03-05 10:06:41 +01:00
// We need to update the location of the owner thread in the waiter queue of the thread it's waiting on
std::unique_lock nextWaiterLock{nextThread->waiterMutex, std::try_to_lock};
if (!nextWaiterLock) {
// We want to avoid a deadlock here from the thread holding nextThread->waiterMutex waiting for waiterMutex or waitingOn->waiterMutex
waitingOn->priority = ownerPriority;
lock.unlock();
waiterLock.unlock();
nextWaiterLock.lock();
nextWaiterLock.unlock();
lock.lock();
waitingOn = waitThread;
continue;
}
Address CR Comments 2 (#132) + Fix Several Scheduler Bugs The following scheduler bugs were fixed: * It was assumed that all non-cooperative `Rotate` calls were from a preemptive yield and changed the state of `KThread::isPreempted` incorrectly which could lead to UB, an example of a scenario with it would be: * * Preemptive thread A gets a signal to yield from cooperative thread B due to it being ready to schedule and higher priority * * A complies with this request but there's an assumption that the signal was actually from it's preemption timer therefore it doesn't reset it (As it isn't required if the timer was responsible for the signal) * * A receives the actual preemption signal a while later, causing UB as the signal handler is invoked twice * `Scheduler::UpdatePriority` * * A check for `currentIt == core->queue.begin()` existed which caused an incorrect early return * * The preemption timer was armed correctly when a priority transition from cooperative priority -> preemption priority occurred but not disarmed when a transition from preemption priority -> cooperative priority occurred * * The timer was unnecessarily disarmed in the case of updating the priority of a non-running thread, this isn't as much a bug as it is just pointless * Priority inheritance in `KProcess::MutexLock` is fundamentally broken as it performs UB with `waitThread` being accessed prior to being assigned * When a thread sets its own priority using `SvcSetThreadCoreMask` and its current core is no longer in the affinity mask, it wouldn't actually move to the new thread until the next time the thread is load balanced
2021-03-05 10:06:41 +01:00
auto &piWaiters{nextThread->waiters};
piWaiters.erase(std::find(piWaiters.begin(), piWaiters.end(), waitingOn));
piWaiters.insert(std::upper_bound(piWaiters.begin(), piWaiters.end(), currentPriority, KThread::IsHigherPriority), waitingOn);
break;
}
state.scheduler->UpdatePriority(waitingOn);
waitingOn = nextThread;
} else {
break;
}
}
}
}