mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 15:54:18 +01:00
Fix Priority Queue + Cooperative Yielding + Conditional Variable Timeouts
This commit is contained in:
parent
33bbfb9fb7
commit
14dbb5305a
@ -9,7 +9,9 @@ extern skyline::u16 Fps;
|
||||
extern skyline::u32 FrameTime;
|
||||
|
||||
namespace skyline::gpu {
|
||||
PresentationEngine::PresentationEngine(const DeviceState &state) : state(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state)), bufferEvent(std::make_shared<kernel::type::KEvent>(state)) {}
|
||||
PresentationEngine::PresentationEngine(const DeviceState &state) : state(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state)), bufferEvent(std::make_shared<kernel::type::KEvent>(state)) {
|
||||
vsyncEvent->Signal();
|
||||
}
|
||||
|
||||
PresentationEngine::~PresentationEngine() {
|
||||
if (window)
|
||||
|
@ -10,7 +10,7 @@ namespace skyline::input {
|
||||
{*this, hid->npad[2], NpadId::Player3}, {*this, hid->npad[3], NpadId::Player4},
|
||||
{*this, hid->npad[4], NpadId::Player5}, {*this, hid->npad[5], NpadId::Player6},
|
||||
{*this, hid->npad[6], NpadId::Player7}, {*this, hid->npad[7], NpadId::Player8},
|
||||
{*this, hid->npad[8], NpadId::Unknown}, {*this, hid->npad[9], NpadId::Handheld},
|
||||
{*this, hid->npad[8], NpadId::Handheld}, {*this, hid->npad[9], NpadId::Unknown},
|
||||
} {}
|
||||
|
||||
void NpadManager::Update() {
|
||||
|
@ -187,7 +187,7 @@ namespace skyline::input {
|
||||
|
||||
info.header.timestamp = util::GetTimeTicks();
|
||||
info.header.entryCount = std::min(static_cast<u8>(info.header.entryCount + 1), constant::HidEntryCount);
|
||||
info.header.maxEntry = constant::HidEntryCount - 1;
|
||||
info.header.maxEntry = info.header.entryCount;
|
||||
info.header.currentEntry = (info.header.currentEntry != constant::HidEntryCount - 1) ? info.header.currentEntry + 1 : 0;
|
||||
|
||||
auto &entry{info.state.at(info.header.currentEntry)};
|
||||
|
@ -22,7 +22,7 @@ namespace skyline::kernel {
|
||||
}
|
||||
}
|
||||
|
||||
Scheduler::CoreContext &Scheduler::LoadBalance(const std::shared_ptr<type::KThread> &thread) {
|
||||
Scheduler::CoreContext &Scheduler::LoadBalance(const std::shared_ptr<type::KThread> &thread, bool alwaysInsert) {
|
||||
std::lock_guard migrationLock(thread->coreMigrationMutex);
|
||||
auto *currentCore{&cores.at(thread->coreId)};
|
||||
|
||||
@ -59,20 +59,21 @@ namespace skyline::kernel {
|
||||
}
|
||||
|
||||
if (optimalCore != currentCore) {
|
||||
std::unique_lock coreLock(currentCore->mutex);
|
||||
currentCore->queue.erase(std::remove(currentCore->queue.begin(), currentCore->queue.end(), thread), currentCore->queue.end());
|
||||
currentCore->frontCondition.notify_all();
|
||||
|
||||
RemoveThread();
|
||||
thread->coreId = optimalCore->id;
|
||||
|
||||
InsertThread(thread);
|
||||
state.logger->Debug("Load Balancing T{}: C{} -> C{}", thread->id, currentCore->id, optimalCore->id);
|
||||
} else {
|
||||
if (alwaysInsert)
|
||||
InsertThread(thread);
|
||||
state.logger->Debug("Load Balancing T{}: C{} (Late)", thread->id, currentCore->id);
|
||||
}
|
||||
|
||||
return *optimalCore;
|
||||
}
|
||||
|
||||
if (alwaysInsert)
|
||||
InsertThread(thread);
|
||||
state.logger->Debug("Load Balancing T{}: C{} (Early)", thread->id, currentCore->id);
|
||||
|
||||
return *currentCore;
|
||||
@ -91,7 +92,9 @@ namespace skyline::kernel {
|
||||
struct sigevent event{
|
||||
.sigev_signo = YieldSignal,
|
||||
.sigev_notify = SIGEV_THREAD_ID,
|
||||
.sigev_notify_thread_id = gettid(),
|
||||
._sigev_un = {
|
||||
._tid = gettid(),
|
||||
},
|
||||
};
|
||||
timer_t timer;
|
||||
if (timer_create(CLOCK_THREAD_CPUTIME_ID, &event, &timer))
|
||||
@ -120,8 +123,6 @@ namespace skyline::kernel {
|
||||
} else {
|
||||
core.queue.insert(nextThread, thread);
|
||||
}
|
||||
|
||||
thread->needsReorder = true; // We need to reorder the thread from back to align it with other threads of it's priority and ensure strict ordering amongst priorities
|
||||
}
|
||||
|
||||
void Scheduler::WaitSchedule(bool loadBalance) {
|
||||
@ -137,7 +138,6 @@ namespace skyline::kernel {
|
||||
if (thread->coreId == core->id) {
|
||||
lock.lock();
|
||||
} else {
|
||||
InsertThread(state.thread);
|
||||
core = &cores.at(thread->coreId);
|
||||
lock = std::shared_lock(core->mutex);
|
||||
}
|
||||
@ -185,26 +185,14 @@ namespace skyline::kernel {
|
||||
if (core.queue.front() == thread) {
|
||||
thread->averageTimeslice = (thread->averageTimeslice / 4) + (3 * (util::GetTimeTicks() - thread->timesliceStart / 4)); // 0.25 * old timeslice duration + 0.75 * current timeslice duration
|
||||
|
||||
core.queue.pop_front();
|
||||
if (!thread->needsReorder) {
|
||||
core.queue.push_back(thread);
|
||||
} else if (core.queue.size() > 1 && core.queue.back()->priority > thread->priority) {
|
||||
// If 'needsReorder' is set, the core queue isn't empty nor has only one member and the thread at the back of the queue has a lower priority than the current one
|
||||
// We can attempt to reorder this thread, this is done by doing a priority-aware insert with the search starting at the "folding point"
|
||||
// The folding point is where a thread has a lower priority than the one succeeding it in the queue, this is where a new "sequence" starts from highest to lowest priorities
|
||||
u8 lastPriority{core.queue.front()->priority};
|
||||
auto foldingPoint{std::find_if(std::next(core.queue.begin()), core.queue.end(), [&](const std::shared_ptr<type::KThread> &it) {
|
||||
return lastPriority > it->priority ? true : lastPriority = it->priority, false;
|
||||
})};
|
||||
core.queue.insert(std::upper_bound(foldingPoint, core.queue.end(), thread->priority.load(), type::KThread::IsHigherPriority), thread);
|
||||
thread->needsReorder = false;
|
||||
} else {
|
||||
core.queue.push_back(thread);
|
||||
thread->needsReorder = false;
|
||||
}
|
||||
// Splice the linked element from the beginning of the queue to where it's priority is present
|
||||
core.queue.splice(std::upper_bound(core.queue.begin(), core.queue.end(), thread->priority.load(), type::KThread::IsHigherPriority), core.queue, core.queue.begin());
|
||||
|
||||
lock.unlock();
|
||||
core.frontCondition.notify_all();
|
||||
if (core.queue.front() != thread) {
|
||||
// If we aren't at the front of the queue, only then should we notify other threads that the front of the queue has changed
|
||||
lock.unlock();
|
||||
core.frontCondition.notify_all();
|
||||
}
|
||||
|
||||
if (cooperative && thread->isPreempted) {
|
||||
// If a preemptive thread did a cooperative yield then we need to disarm the preemptive timer
|
||||
@ -232,7 +220,6 @@ namespace skyline::kernel {
|
||||
timer_settime(*thread->preemptionTimer, 0, &spec, nullptr);
|
||||
thread->isPreempted = true;
|
||||
}
|
||||
thread->needsReorder = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -256,18 +243,59 @@ namespace skyline::kernel {
|
||||
} else {
|
||||
core->queue.insert(targetIt, thread);
|
||||
}
|
||||
thread->needsReorder = true;
|
||||
}
|
||||
|
||||
void Scheduler::ParkThread() {
|
||||
auto &thread{state.thread};
|
||||
RemoveThread();
|
||||
{
|
||||
std::unique_lock lock(parkedMutex);
|
||||
parkedQueue.insert(std::upper_bound(parkedQueue.begin(), parkedQueue.end(), thread->priority.load(), type::KThread::IsHigherPriority), thread);
|
||||
thread->coreId = constant::ParkedCoreId;
|
||||
parkedFrontCondition.wait(lock, [&]() { return parkedCore.queue.front() == thread && thread->coreId != constant::ParkedCoreId; });
|
||||
}
|
||||
InsertThread(thread);
|
||||
}
|
||||
|
||||
void Scheduler::WakeParkedThread() {
|
||||
std::unique_lock parkedLock(parkedMutex);
|
||||
if (!parkedQueue.empty()) {
|
||||
auto &thread{state.thread};
|
||||
auto &core{cores.at(thread->coreId)};
|
||||
std::unique_lock coreLock(core.mutex);
|
||||
auto nextThread{core.queue.size() > 1 ? *std::next(core.queue.begin()) : nullptr};
|
||||
nextThread = nextThread->priority == thread->priority ? nextThread : nullptr; // If the next thread doesn't have the same priority then it won't be scheduled next
|
||||
auto parkedThread{parkedQueue.front()};
|
||||
|
||||
// We need to be conservative about waking up a parked thread, it should only be done if it's priority is higher than the current thread
|
||||
// Alternatively, it should be done if it's priority is equivalent to the current thread's priority but the next thread had been scheduled prior or if there is no next thread (Current thread would be rescheduled)
|
||||
if (parkedThread->priority < thread->priority || (parkedThread->priority == thread->priority && (!nextThread || parkedThread->timesliceStart < nextThread->timesliceStart))) {
|
||||
parkedThread->coreId = thread->coreId;
|
||||
parkedLock.unlock();
|
||||
parkedFrontCondition.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::RemoveThread() {
|
||||
auto &thread{state.thread};
|
||||
auto &core{cores.at(thread->coreId)};
|
||||
|
||||
{
|
||||
std::unique_lock lock(core.mutex);
|
||||
core.queue.erase(std::remove(core.queue.begin(), core.queue.end(), thread), core.queue.end());
|
||||
auto it{std::find(core.queue.begin(), core.queue.end(), thread)};
|
||||
if (it != core.queue.end()) {
|
||||
it = core.queue.erase(it);
|
||||
if (it == core.queue.begin()) {
|
||||
// We need to update the averageTimeslice accordingly, if we've been unscheduled by this
|
||||
if (thread->timesliceStart)
|
||||
thread->averageTimeslice = (thread->averageTimeslice / 4) + (3 * (util::GetTimeTicks() - thread->timesliceStart / 4));
|
||||
|
||||
// We need to notify that the front was changed, if we were at the front previously
|
||||
lock.unlock();
|
||||
core.frontCondition.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
core.frontCondition.notify_all(); // We need to notify any threads in line to be scheduled
|
||||
|
||||
if (thread->isPreempted) {
|
||||
struct itimerspec spec{};
|
||||
|
@ -9,6 +9,7 @@
|
||||
namespace skyline {
|
||||
namespace constant {
|
||||
constexpr u8 CoreCount{4}; //!< The amount of cores an HOS process can be scheduled onto (User applications can only be on the first 3 cores, the last one is reserved for the system)
|
||||
constexpr u8 ParkedCoreId{CoreCount}; //!< // An invalid core ID, representing that a thread has been parked
|
||||
}
|
||||
|
||||
namespace kernel {
|
||||
@ -47,13 +48,19 @@ namespace skyline {
|
||||
u8 preemptionPriority; //!< The priority at which this core becomes preemptive as opposed to cooperative
|
||||
std::shared_mutex mutex; //!< Synchronizes all operations on the queue
|
||||
std::condition_variable_any frontCondition; //!< A conditional variable which is signalled when the front of the queue has changed
|
||||
std::deque<std::shared_ptr<type::KThread>> queue; //!< A queue of threads which are running or to be run on this core
|
||||
std::list<std::shared_ptr<type::KThread>> queue; //!< A queue of threads which are running or to be run on this core
|
||||
|
||||
CoreContext(u8 id, u8 preemptionPriority);
|
||||
};
|
||||
|
||||
std::array<CoreContext, constant::CoreCount> cores{CoreContext(0, 59), CoreContext(1, 59), CoreContext(2, 59), CoreContext(3, 63)};
|
||||
|
||||
std::mutex parkedMutex; //!< Synchronizes all operations on the queue of parked threads
|
||||
std::condition_variable parkedFrontCondition; //!< A conditional variable which is signalled when the front of the parked queue has changed
|
||||
std::list<std::shared_ptr<type::KThread>> parkedQueue; //!< A queue of threads which are parked and waiting on core migration
|
||||
|
||||
CoreContext parkedCore{constant::CoreCount, 64}; //!< A psuedo-core which all parked threads are moved onto
|
||||
|
||||
public:
|
||||
static constexpr std::chrono::milliseconds PreemptiveTimeslice{10}; //!< The duration of time a preemptive thread can run before yielding
|
||||
inline static int YieldSignal{SIGRTMIN}; //!< The signal used to cause a yield in running threads
|
||||
@ -68,10 +75,11 @@ namespace skyline {
|
||||
|
||||
/**
|
||||
* @brief Checks all cores and migrates the specified thread to the core where the calling thread should be scheduled the earliest
|
||||
* @param alwaysInsert If to insert the thread even if it hasn't migrated cores, this is used during thread creation
|
||||
* @return A reference to the CoreContext of the core which the calling thread is running on after load balancing
|
||||
* @note This doesn't insert the thread into the migrated process's queue after load balancing
|
||||
* @note This inserts the thread into the migrated process's queue after load balancing, there is no need to call it redundantly
|
||||
*/
|
||||
CoreContext& LoadBalance(const std::shared_ptr<type::KThread> &thread);
|
||||
CoreContext& LoadBalance(const std::shared_ptr<type::KThread> &thread, bool alwaysInsert = false);
|
||||
|
||||
/**
|
||||
* @brief Inserts the specified thread into the scheduler queue at the appropriate location based on it's priority
|
||||
@ -103,10 +111,42 @@ namespace skyline {
|
||||
*/
|
||||
void UpdatePriority(const std::shared_ptr<type::KThread>& thread);
|
||||
|
||||
/**
|
||||
* @brief Parks the calling thread after removing it from it's resident core's queue and inserts it on the core it's been awoken on
|
||||
* @note This will not handle waiting for the thread to be scheduled, this should be followed with a call to WaitSchedule/TimedWaitSchedule
|
||||
*/
|
||||
void ParkThread();
|
||||
|
||||
/**
|
||||
* @brief Wakes a single parked thread which may be appropriate for running next on this core
|
||||
* @note We will only wake a thread if it is determined to be a better pick than the thread which would be run on this core next
|
||||
*/
|
||||
void WakeParkedThread();
|
||||
|
||||
/**
|
||||
* @brief Removes the calling thread from it's resident core queue
|
||||
*/
|
||||
void RemoveThread();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A lock which removes the calling thread from it's resident core's scheduler queue and adds it back when being destroyed
|
||||
* @note It also blocks till the thread has been rescheduled in it's destructor, this behavior might not be preferable in some cases
|
||||
* @note This is not an analogue to KScopedSchedulerLock on HOS, it is for handling thread state changes which we handle with Scheduler::YieldPending
|
||||
*/
|
||||
struct SchedulerScopedLock {
|
||||
private:
|
||||
const DeviceState& state;
|
||||
|
||||
public:
|
||||
inline SchedulerScopedLock(const DeviceState& state) : state(state) {
|
||||
state.scheduler->RemoveThread();
|
||||
}
|
||||
|
||||
inline ~SchedulerScopedLock() {
|
||||
state.scheduler->InsertThread(state.thread);
|
||||
state.scheduler->WaitSchedule();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ namespace skyline::kernel::svc {
|
||||
newChunk.attributes.isUncached = value.isUncached;
|
||||
state.process->memory.InsertChunk(newChunk);
|
||||
|
||||
state.logger->Debug("svcSetMemoryAttribute: Set caching to {} at 0x{:X} - 0x{:X} (0x{:X} bytes)", static_cast<bool>(value.isUncached), pointer, pointer + size, size);
|
||||
state.logger->Debug("svcSetMemoryAttribute: Set CPU caching to {} at 0x{:X} - 0x{:X} (0x{:X} bytes)", !static_cast<bool>(value.isUncached), pointer, pointer + size, size);
|
||||
state.ctx->gpr.w0 = Result{};
|
||||
}
|
||||
|
||||
@ -266,7 +266,7 @@ namespace skyline::kernel::svc {
|
||||
KHandle handle{state.ctx->gpr.w0};
|
||||
try {
|
||||
auto thread{state.process->GetHandle<type::KThread>(handle)};
|
||||
state.logger->Debug("svcStartThread: Starting thread: 0x{:X}, PID: {}", handle, thread->id);
|
||||
state.logger->Debug("svcStartThread: Starting thread #{}: 0x{:X}", thread->id, handle);
|
||||
thread->Start();
|
||||
state.ctx->gpr.w0 = Result{};
|
||||
} catch (const std::out_of_range &) {
|
||||
@ -294,14 +294,28 @@ namespace skyline::kernel::svc {
|
||||
.tv_nsec = static_cast<long>(in % 1000000000),
|
||||
};
|
||||
|
||||
state.scheduler->Rotate();
|
||||
SchedulerScopedLock schedulerLock(state);
|
||||
nanosleep(&spec, nullptr);
|
||||
state.scheduler->WaitSchedule();
|
||||
} else if (in == yieldWithoutCoreMigration || in == yieldWithCoreMigration || in == yieldToAnyThread) {
|
||||
// Core Migration doesn't affect us as threads schedule and load balance themselves
|
||||
state.logger->Debug("svcSleepThread: Cooperative Yield");
|
||||
state.scheduler->Rotate();
|
||||
state.scheduler->WaitSchedule();
|
||||
} else {
|
||||
switch (in) {
|
||||
case yieldWithCoreMigration:
|
||||
state.logger->Debug("svcSleepThread: Waking any appropriate parked threads");
|
||||
state.scheduler->WakeParkedThread();
|
||||
case yieldWithoutCoreMigration:
|
||||
state.logger->Debug("svcSleepThread: Cooperative Yield");
|
||||
state.scheduler->Rotate();
|
||||
state.scheduler->WaitSchedule();
|
||||
break;
|
||||
|
||||
case yieldToAnyThread:
|
||||
state.logger->Debug("svcSleepThread: Parking current thread");
|
||||
state.scheduler->ParkThread();
|
||||
state.scheduler->WaitSchedule(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,7 +344,7 @@ namespace skyline::kernel::svc {
|
||||
}
|
||||
try {
|
||||
auto thread{state.process->GetHandle<type::KThread>(handle)};
|
||||
state.logger->Debug("svcSetThreadPriority: Setting thread priority to {}", priority);
|
||||
state.logger->Debug("svcSetThreadPriority: Setting thread #{}'s priority to {}", thread->id, priority);
|
||||
if (thread->priority != priority) {
|
||||
thread->priority = priority;
|
||||
state.scheduler->UpdatePriority(thread);
|
||||
@ -348,7 +362,7 @@ namespace skyline::kernel::svc {
|
||||
auto thread{state.process->GetHandle<type::KThread>(handle)};
|
||||
auto idealCore{thread->idealCore};
|
||||
auto affinityMask{thread->affinityMask};
|
||||
state.logger->Debug("svcGetThreadCoreMask: Setting Ideal Core ({}) + Affinity Mask ({})", idealCore, affinityMask);
|
||||
state.logger->Debug("svcGetThreadCoreMask: Getting thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, idealCore, affinityMask);
|
||||
|
||||
state.ctx->gpr.x2 = affinityMask.to_ullong();
|
||||
state.ctx->gpr.w1 = idealCore;
|
||||
@ -388,18 +402,23 @@ namespace skyline::kernel::svc {
|
||||
return;
|
||||
}
|
||||
|
||||
state.logger->Debug("svcSetThreadCoreMask: Setting Ideal Core ({}) + Affinity Mask ({})", idealCore, affinityMask);
|
||||
state.logger->Debug("svcSetThreadCoreMask: Setting thread #{}'s Ideal Core ({}) + Affinity Mask ({})", thread->id, idealCore, affinityMask);
|
||||
|
||||
thread->idealCore = idealCore;
|
||||
thread->affinityMask = affinityMask;
|
||||
|
||||
if (!affinityMask.test(thread->coreId)) {
|
||||
state.logger->Debug("svcSetThreadCoreMask: Migrating to Ideal Core C{} -> C{}", thread->coreId, idealCore);
|
||||
state.logger->Debug("svcSetThreadCoreMask: Migrating thread #{} to Ideal Core C{} -> C{}", thread->id, thread->coreId, idealCore);
|
||||
|
||||
state.scheduler->RemoveThread();
|
||||
thread->coreId = idealCore;
|
||||
state.scheduler->InsertThread(state.thread);
|
||||
state.scheduler->WaitSchedule();
|
||||
if (thread == state.thread) {
|
||||
state.scheduler->RemoveThread();
|
||||
state.scheduler->InsertThread(state.thread);
|
||||
state.scheduler->WaitSchedule();
|
||||
} else if (!thread->running) {
|
||||
thread->coreId = idealCore;
|
||||
} else {
|
||||
throw exception("svcSetThreadCoreMask: Migrating a running thread due to a new core mask is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
state.ctx->gpr.w0 = Result{};
|
||||
@ -411,7 +430,7 @@ namespace skyline::kernel::svc {
|
||||
|
||||
void GetCurrentProcessorNumber(const DeviceState &state) {
|
||||
auto coreId{state.thread->coreId};
|
||||
state.logger->Debug("svcGetCurrentProcessorNumber: Writing current core: {}", coreId);
|
||||
state.logger->Debug("svcGetCurrentProcessorNumber: C{}", coreId);
|
||||
state.ctx->gpr.w0 = coreId;
|
||||
}
|
||||
|
||||
@ -520,10 +539,6 @@ namespace skyline::kernel::svc {
|
||||
|
||||
state.logger->Debug("svcResetSignal: Resetting signal: 0x{:X}", handle);
|
||||
state.ctx->gpr.w0 = Result{};
|
||||
|
||||
// There is an implicit yield while resetting a signal
|
||||
state.scheduler->Rotate();
|
||||
state.scheduler->WaitSchedule();
|
||||
} catch (const std::out_of_range &) {
|
||||
state.logger->Warn("svcResetSignal: 'handle' invalid: 0x{:X}", handle);
|
||||
state.ctx->gpr.w0 = result::InvalidHandle;
|
||||
@ -567,36 +582,32 @@ namespace skyline::kernel::svc {
|
||||
u64 timeout{state.ctx->gpr.x3};
|
||||
state.logger->Debug("svcWaitSynchronization: Waiting on handles:\n{}Timeout: 0x{:X} ns", handleStr, timeout);
|
||||
|
||||
// The thread shouldn't be occupying the core while it's waiting on objects
|
||||
state.scheduler->Rotate();
|
||||
SchedulerScopedLock schedulerLock(state);
|
||||
auto start{util::GetTimeNs()};
|
||||
[&] () {
|
||||
while (true) {
|
||||
if (state.thread->cancelSync) {
|
||||
state.thread->cancelSync = false;
|
||||
state.ctx->gpr.w0 = result::Cancelled;
|
||||
return;
|
||||
}
|
||||
|
||||
u32 index{};
|
||||
for (const auto &object : objectTable) {
|
||||
if (object->signalled) {
|
||||
state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", waitHandles[index]);
|
||||
state.ctx->gpr.w0 = Result{};
|
||||
state.ctx->gpr.w1 = index;
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if ((util::GetTimeNs() - start) >= timeout) {
|
||||
state.logger->Debug("svcWaitSynchronization: Wait has timed out");
|
||||
state.ctx->gpr.w0 = result::TimedOut;
|
||||
return;
|
||||
}
|
||||
while (true) {
|
||||
if (state.thread->cancelSync) {
|
||||
state.thread->cancelSync = false;
|
||||
state.ctx->gpr.w0 = result::Cancelled;
|
||||
return;
|
||||
}
|
||||
}();
|
||||
state.scheduler->WaitSchedule();
|
||||
|
||||
u32 index{};
|
||||
for (const auto &object : objectTable) {
|
||||
if (object->signalled) {
|
||||
state.logger->Debug("svcWaitSynchronization: Signalled handle: 0x{:X}", waitHandles[index]);
|
||||
state.ctx->gpr.w0 = Result{};
|
||||
state.ctx->gpr.w1 = index;
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if ((util::GetTimeNs() - start) >= timeout) {
|
||||
state.logger->Debug("svcWaitSynchronization: Wait has timed out");
|
||||
state.ctx->gpr.w0 = result::TimedOut;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CancelSynchronization(const DeviceState &state) {
|
||||
@ -658,13 +669,13 @@ namespace skyline::kernel::svc {
|
||||
KHandle requesterHandle{state.ctx->gpr.w2};
|
||||
|
||||
i64 timeout{static_cast<i64>(state.ctx->gpr.x3)};
|
||||
state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x{:X}, Timeout: {} ns", mutex, conditional, timeout);
|
||||
state.logger->Debug("svcWaitProcessWideKeyAtomic: Mutex: 0x{:X}, Conditional-Variable: 0x{:X}, Timeout: {}ns", mutex, conditional, timeout);
|
||||
|
||||
auto result{state.process->ConditionalVariableWait(conditional, mutex, requesterHandle, timeout)};
|
||||
if (result == Result{})
|
||||
state.logger->Debug("svcWaitProcessWideKeyAtomic: Waited for conditional variable and reacquired mutex");
|
||||
state.logger->Debug("svcWaitProcessWideKeyAtomic: Waited for conditional variable (0x{:X}) and reacquired mutex", conditional);
|
||||
else if (result == result::TimedOut)
|
||||
state.logger->Debug("svcWaitProcessWideKeyAtomic: Wait has timed out");
|
||||
state.logger->Debug("svcWaitProcessWideKeyAtomic: Wait has timed out ({}ns) for 0x{:X}", timeout, conditional);
|
||||
state.ctx->gpr.w0 = result;
|
||||
}
|
||||
|
||||
@ -716,11 +727,11 @@ namespace skyline::kernel::svc {
|
||||
|
||||
void GetThreadId(const DeviceState &state) {
|
||||
KHandle handle{state.ctx->gpr.w1};
|
||||
size_t pid{state.process->GetHandle<type::KThread>(handle)->id};
|
||||
size_t tid{state.process->GetHandle<type::KThread>(handle)->id};
|
||||
|
||||
state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, PID: {}", handle, pid);
|
||||
state.logger->Debug("svcGetThreadId: Handle: 0x{:X}, TID: {}", handle, tid);
|
||||
|
||||
state.ctx->gpr.x1 = pid;
|
||||
state.ctx->gpr.x1 = tid;
|
||||
state.ctx->gpr.w0 = Result{};
|
||||
}
|
||||
|
||||
|
@ -15,10 +15,10 @@ namespace skyline::kernel::type {
|
||||
return Get(index++);
|
||||
}
|
||||
|
||||
u8 *KProcess::TlsPage::Get(u8 index) {
|
||||
if (index >= constant::TlsSlots)
|
||||
u8 *KProcess::TlsPage::Get(u8 slot) {
|
||||
if (slot >= constant::TlsSlots)
|
||||
throw exception("TLS slot is out of range");
|
||||
return memory->ptr + (constant::TlsSlotSize * index);
|
||||
return memory->ptr + (constant::TlsSlotSize * slot);
|
||||
}
|
||||
|
||||
bool KProcess::TlsPage::Full() {
|
||||
@ -234,41 +234,34 @@ namespace skyline::kernel::type {
|
||||
} else {
|
||||
__atomic_store_n(mutex, 0, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
state.scheduler->Rotate();
|
||||
state.scheduler->WaitSchedule();
|
||||
}
|
||||
|
||||
Result KProcess::ConditionalVariableWait(u32 *key, u32 *mutex, KHandle tag, i64 timeout) {
|
||||
{
|
||||
std::lock_guard lock(syncWaiterMutex);
|
||||
auto queue{syncWaiters.equal_range(key)};
|
||||
auto it{syncWaiters.insert(std::upper_bound(queue.first, queue.second, state.thread->priority.load(), [](const i8 priority, const SyncWaiters::value_type &it) { return it.second->priority > priority; }), {key, state.thread})};
|
||||
|
||||
// TODO: REMOVE THIS AFTER TESTING
|
||||
auto prevIt{std::prev(it)}, nextIt{std::next(it)};
|
||||
if ((prevIt != syncWaiters.begin() && prevIt->first == key && prevIt->second->priority > state.thread->priority.load()))
|
||||
throw exception("Previous node incorrect");
|
||||
if ((nextIt != syncWaiters.end() && nextIt->first == key && nextIt->second->priority < state.thread->priority.load()))
|
||||
throw exception("Next node incorrect");
|
||||
syncWaiters.insert(std::upper_bound(queue.first, queue.second, state.thread->priority.load(), [](const i8 priority, const SyncWaiters::value_type &it) { return it.second->priority > priority; }), {key, state.thread});
|
||||
|
||||
__atomic_store_n(key, true, __ATOMIC_SEQ_CST); // We need to notify any userspace threads that there are waiters on this conditional variable by writing back a boolean flag denoting it
|
||||
|
||||
MutexUnlock(mutex);
|
||||
state.scheduler->RemoveThread();
|
||||
MutexUnlock(mutex);
|
||||
__sync_synchronize();
|
||||
}
|
||||
|
||||
bool hasTimedOut{};
|
||||
if (timeout > 0)
|
||||
hasTimedOut = !state.scheduler->TimedWaitSchedule(std::chrono::nanoseconds(timeout));
|
||||
else
|
||||
state.scheduler->WaitSchedule(false);
|
||||
|
||||
if (hasTimedOut) {
|
||||
std::lock_guard lock(syncWaiterMutex);
|
||||
if (timeout > 0 && !state.scheduler->TimedWaitSchedule(std::chrono::nanoseconds(timeout))) {
|
||||
std::unique_lock lock(syncWaiterMutex);
|
||||
auto queue{syncWaiters.equal_range(key)};
|
||||
syncWaiters.erase(std::find(queue.first, queue.second, SyncWaiters::value_type{key, state.thread}));
|
||||
auto iterator{std::find(queue.first, queue.second, SyncWaiters::value_type{key, state.thread})};
|
||||
if (iterator != queue.second)
|
||||
if (syncWaiters.erase(iterator) == queue.second)
|
||||
__atomic_store_n(key, false, __ATOMIC_SEQ_CST);
|
||||
lock.unlock();
|
||||
state.scheduler->InsertThread(state.thread);
|
||||
state.scheduler->WaitSchedule();
|
||||
return result::TimedOut;
|
||||
} else {
|
||||
state.scheduler->WaitSchedule(false);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
@ -284,13 +277,13 @@ namespace skyline::kernel::type {
|
||||
}
|
||||
|
||||
void KProcess::ConditionalVariableSignal(u32 *key, u64 amount) {
|
||||
std::unique_lock lock(syncWaiterMutex);
|
||||
std::lock_guard lock(syncWaiterMutex);
|
||||
auto queue{syncWaiters.equal_range(key)};
|
||||
auto it{queue.first};
|
||||
if (queue.first != queue.second)
|
||||
for (; it != queue.second && amount; it = syncWaiters.erase(it), amount--)
|
||||
state.scheduler->InsertThread(it->second);
|
||||
if (it == queue.second)
|
||||
__atomic_store_n(key, 0, __ATOMIC_SEQ_CST); // We need to update the boolean flag denoting that there are no more threads waiting on this conditional variable
|
||||
__atomic_store_n(key, false, __ATOMIC_SEQ_CST); // We need to update the boolean flag denoting that there are no more threads waiting on this conditional variable
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ namespace skyline {
|
||||
|
||||
u8 *ReserveSlot();
|
||||
|
||||
u8 *Get(u8 index);
|
||||
u8 *Get(u8 slot);
|
||||
|
||||
bool Full();
|
||||
};
|
||||
|
@ -164,9 +164,7 @@ namespace skyline::kernel::type {
|
||||
std::unique_lock lock(mutex);
|
||||
if (!running) {
|
||||
running = true;
|
||||
auto sharedThis{shared_from_this()};
|
||||
state.scheduler->LoadBalance(sharedThis);
|
||||
state.scheduler->InsertThread(sharedThis);
|
||||
state.scheduler->LoadBalance(shared_from_this(), true); // This will automatically insert the thread into the core queue after load balancing
|
||||
if (self) {
|
||||
pthread = pthread_self();
|
||||
lock.unlock();
|
||||
|
@ -49,7 +49,6 @@ namespace skyline {
|
||||
u64 averageTimeslice{}; //!< A weighted average of the timeslice duration for this thread
|
||||
std::optional<timer_t> preemptionTimer{}; //!< A kernel timer used for preemption interrupts
|
||||
bool isPreempted{}; //!< If the preemption timer has been armed and will fire
|
||||
bool needsReorder{}; //!< If the thread needs to reorder itself during scheduler rotation
|
||||
std::mutex waiterMutex; //!< Synchronizes operations on mutation of the waiter members
|
||||
u32* waitKey; //!< The key of the mutex which this thread is waiting on
|
||||
KHandle waitTag; //!< The handle of the thread which requested the mutex lock
|
||||
|
@ -10,12 +10,14 @@ namespace skyline::service::audio {
|
||||
|
||||
Result IAudioDevice::ListAudioDeviceName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
span buffer{request.outputBuf.at(0)};
|
||||
for (std::string_view deviceName : {"AudioTvOutput\0", "AudioStereoJackOutput\0", "AudioBuiltInSpeakerOutput\0"}) {
|
||||
std::array<std::string_view, 3> devices{"AudioTvOutput\0", "AudioStereoJackOutput\0", "AudioBuiltInSpeakerOutput\0"};
|
||||
for (std::string_view deviceName : devices) {
|
||||
if (deviceName.size() > buffer.size())
|
||||
throw exception("The buffer supplied to ListAudioDeviceName is too small");
|
||||
buffer.copy_from(deviceName);
|
||||
buffer = buffer.subspan(deviceName.size());
|
||||
}
|
||||
response.Push<u32>(devices.size());
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ namespace skyline::service::nvdrv::device {
|
||||
std::pair<std::function<NvStatus(IoctlType, span<u8>, span<u8>)>, std::string_view> function;
|
||||
try {
|
||||
function = GetIoctlFunction(cmd);
|
||||
state.logger->Debug("{} @ {}: {}", typeString, GetName(), function.second);
|
||||
state.logger->Debug("{}: {} @ {}", typeString, GetName(), function.second);
|
||||
} catch (std::out_of_range &) {
|
||||
state.logger->Warn("Cannot find IOCTL for device '{}': 0x{:X}", GetName(), cmd);
|
||||
return NvStatus::NotImplemented;
|
||||
|
Loading…
Reference in New Issue
Block a user