Optimize Scheduler/IPC/HID + Fix Various Bugs

* Optimize Scheduler With Per-Thread Scheduler Conditions
* Optimize IPC by yielding
* Optimize HID Vibration
* Fix Priority Inheritance
* Fix `KThread` Start/Kill/Signal Races 
* Fix `YieldPending` Races in `StartThread` & `SvcHandler` 
* Fix POSIX Time -> NN CalendarTime Conversion 
* Fix HID `TouchScreen`/`NPad` Activation
This commit is contained in:
◱ PixelyIon 2021-01-12 01:11:21 +05:30 committed by ◱ Mark
parent ef52e22cef
commit 98b1fd9056
14 changed files with 181 additions and 126 deletions

View File

@ -11,7 +11,7 @@ namespace skyline::input {
{*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::Handheld}, {*this, hid->npad[9], NpadId::Unknown},
} {}
} { Activate(); /* NPads are activated by default, certain homebrew is reliant on this behavior */ }
void NpadManager::Update() {
std::lock_guard guard(mutex);
@ -83,25 +83,27 @@ namespace skyline::input {
void NpadManager::Activate() {
std::lock_guard guard(mutex);
if (!activated) {
supportedIds = {NpadId::Handheld, NpadId::Player1, NpadId::Player2, NpadId::Player3, NpadId::Player4, NpadId::Player5, NpadId::Player6, NpadId::Player7, NpadId::Player8};
styles = {.proController = true, .joyconHandheld = true, .joyconDual = true, .joyconLeft = true, .joyconRight = true};
activated = true;
supportedIds = {NpadId::Handheld, NpadId::Player1, NpadId::Player2, NpadId::Player3, NpadId::Player4, NpadId::Player5, NpadId::Player6, NpadId::Player7, NpadId::Player8};
styles = {.proController = true, .joyconHandheld = true, .joyconDual = true, .joyconLeft = true, .joyconRight = true};
activated = true;
Update();
Update();
}
}
void NpadManager::Deactivate() {
std::lock_guard guard(mutex);
if (activated) {
supportedIds = {};
styles = {};
activated = false;
supportedIds = {};
styles = {};
activated = false;
for (auto &npad : npads)
npad.Disconnect();
for (auto &npad : npads)
npad.Disconnect();
for (auto &controller : controllers)
controller.device = nullptr;
for (auto &controller : controllers)
controller.device = nullptr;
}
}
}

View File

@ -21,7 +21,7 @@ namespace skyline::input {
class NpadManager {
private:
const DeviceState &state;
bool activated{false}; //!< If this NpadManager is activated or not
bool activated{};
friend NpadDevice;

View File

@ -425,10 +425,15 @@ namespace skyline::input {
}
void NpadDevice::Vibrate(bool isRight, const NpadVibrationValue &value) {
if (isRight)
if (isRight) {
if (vibrationRight && (*vibrationRight) == value)
return;
vibrationRight = value;
else
} else {
if (vibrationLeft == value)
return;
vibrationLeft = value;
}
if (vibrationRight)
Vibrate(vibrationLeft, *vibrationRight);
@ -437,6 +442,12 @@ namespace skyline::input {
}
void NpadDevice::Vibrate(const NpadVibrationValue &left, const NpadVibrationValue &right) {
if (vibrationLeft == left && vibrationRight && (*vibrationRight) == right)
return;
vibrationLeft = left;
vibrationRight = right;
if (partnerIndex == constant::NullIndex) {
std::array<VibrationInfo, 4> vibrations{
VibrationInfo{left.frequencyLow, left.amplitudeLow * (constant::AmplitudeMax / 4)},

View File

@ -105,6 +105,13 @@ namespace skyline::input {
float frequencyLow;
float amplitudeHigh;
float frequencyHigh;
bool operator==(const NpadVibrationValue &other) const {
return (amplitudeLow == other.amplitudeLow) &&
(frequencyLow == other.frequencyLow) &&
(amplitudeHigh == other.amplitudeHigh) &&
(frequencyHigh == other.frequencyHigh);
}
};
static_assert(sizeof(NpadVibrationValue) == 0x10);

View File

@ -5,12 +5,14 @@
namespace skyline::input {
TouchManager::TouchManager(const DeviceState &state, input::HidSharedMemory *hid) : state(state), section(hid->touchScreen) {
Activate();
Activate(); // The touch screen is expected to be activated by default, commercial games are reliant on this behavior
}
void TouchManager::Activate() {
activated = true;
SetState({});
if (!activated) {
activated = true;
SetState({});
}
}
void TouchManager::SetState(const span<TouchScreenPoint> &points) {
@ -37,7 +39,7 @@ namespace skyline::input {
section.header.timestamp = util::GetTimeTicks();
section.header.entryCount = std::min(static_cast<u8>(section.header.entryCount + 1), constant::HidEntryCount);
section.header.maxEntry = constant::HidEntryCount - 1;
section.header.maxEntry = section.header.entryCount;
section.header.currentEntry = entryIndex;
}
}

View File

@ -15,8 +15,8 @@ namespace skyline::kernel {
if (*tls) {
const auto &state{*reinterpret_cast<nce::ThreadContext *>(*tls)->state};
state.scheduler->Rotate(false);
state.scheduler->WaitSchedule();
YieldPending = false;
state.scheduler->WaitSchedule();
} else {
YieldPending = true;
}
@ -59,7 +59,10 @@ namespace skyline::kernel {
}
if (optimalCore != currentCore) {
RemoveThread();
if (!alwaysInsert && thread == state.thread)
RemoveThread();
else if (!alwaysInsert && thread != state.thread)
throw exception("Migrating an external thread (T{}) without 'alwaysInsert' isn't supported", thread->id);
thread->coreId = optimalCore->id;
InsertThread(thread);
state.logger->Debug("Load Balancing T{}: C{} -> C{}", thread->id, currentCore->id, optimalCore->id);
@ -81,27 +84,6 @@ namespace skyline::kernel {
void Scheduler::InsertThread(const std::shared_ptr<type::KThread> &thread) {
auto &core{cores.at(thread->coreId)};
thread_local bool signalHandlerSetup{};
if (!signalHandlerSetup) {
signal::SetSignalHandler({YieldSignal}, SignalHandler);
signalHandlerSetup = true;
}
if (!thread->preemptionTimer) {
struct sigevent event{
.sigev_signo = YieldSignal,
.sigev_notify = SIGEV_THREAD_ID,
._sigev_un = {
._tid = gettid(),
},
};
timer_t timer;
if (timer_create(CLOCK_THREAD_CPUTIME_ID, &event, &timer))
throw exception("timer_create has failed with '{}'", strerror(errno));
thread->preemptionTimer.emplace(timer);
}
std::unique_lock lock(core.mutex);
auto nextThread{std::upper_bound(core.queue.begin(), core.queue.end(), thread->priority.load(), type::KThread::IsHigherPriority)};
if (nextThread == core.queue.begin()) {
@ -116,10 +98,8 @@ namespace skyline::kernel {
} else {
core.queue.push_front(thread);
}
if (thread != state.thread) {
lock.unlock(); // We should unlock this prior to waking all threads to not cause contention on the core lock
core.frontCondition.notify_all(); // We only want to trigger the conditional variable if the current thread isn't inserting itself
}
if (thread != state.thread)
thread->wakeCondition.notify_one(); // We only want to trigger the conditional variable if the current thread isn't inserting itself
} else {
core.queue.insert(nextThread, thread);
}
@ -132,7 +112,7 @@ namespace skyline::kernel {
std::shared_lock lock(core->mutex);
if (loadBalance && thread->affinityMask.count() > 1) {
std::chrono::milliseconds loadBalanceThreshold{PreemptiveTimeslice * 2}; //!< The amount of time that needs to pass unscheduled for a thread to attempt load balancing
while (!core->frontCondition.wait_for(lock, loadBalanceThreshold, [&]() { return !core->queue.empty() && core->queue.front() == thread; })) {
while (!thread->wakeCondition.wait_for(lock, loadBalanceThreshold, [&]() { return !core->queue.empty() && core->queue.front() == thread; })) {
lock.unlock();
LoadBalance(state.thread);
if (thread->coreId == core->id) {
@ -145,12 +125,12 @@ namespace skyline::kernel {
loadBalanceThreshold *= 2; // We double the duration required for future load balancing for this invocation to minimize pointless load balancing
}
} else {
core->frontCondition.wait(lock, [&]() { return !core->queue.empty() && core->queue.front() == thread; });
thread->wakeCondition.wait(lock, [&]() { return !core->queue.empty() && core->queue.front() == thread; });
}
if (thread->priority == core->preemptionPriority) {
struct itimerspec spec{.it_value = {.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(PreemptiveTimeslice).count()}};
timer_settime(*thread->preemptionTimer, 0, &spec, nullptr);
timer_settime(thread->preemptionTimer, 0, &spec, nullptr);
thread->isPreempted = true;
}
@ -162,10 +142,10 @@ namespace skyline::kernel {
auto *core{&cores.at(thread->coreId)};
std::shared_lock lock(core->mutex);
if (core->frontCondition.wait_for(lock, timeout, [&]() { return !core->queue.empty() && core->queue.front() == thread; })) {
if (thread->wakeCondition.wait_for(lock, timeout, [&]() { return !core->queue.empty() && core->queue.front() == thread; })) {
if (thread->priority == core->preemptionPriority) {
struct itimerspec spec{.it_value = {.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(PreemptiveTimeslice).count()}};
timer_settime(*thread->preemptionTimer, 0, &spec, nullptr);
timer_settime(thread->preemptionTimer, 0, &spec, nullptr);
thread->isPreempted = true;
}
@ -188,18 +168,18 @@ namespace skyline::kernel {
// 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());
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();
}
auto front{core.queue.front()};
if (front != thread)
front->wakeCondition.notify_one(); // If we aren't at the front of the queue, only then should we wake the thread at the front up
if (cooperative && thread->isPreempted) {
// If a preemptive thread did a cooperative yield then we need to disarm the preemptive timer
struct itimerspec spec{};
timer_settime(*thread->preemptionTimer, 0, &spec, nullptr);
timer_settime(thread->preemptionTimer, 0, &spec, nullptr);
}
thread->isPreempted = false;
} else {
throw exception("T{} called Rotate while not being at the front of C{}'s queue", thread->id, thread->coreId);
}
}
@ -213,11 +193,14 @@ namespace skyline::kernel {
// If the thread isn't in the queue then the new priority will be handled automatically on insertion
return;
if (currentIt == core->queue.begin()) {
// Alternatively, if it's currently running then we'd just want to reorder after it rotates
if (!thread->isPreempted && thread->priority == core->preemptionPriority) {
// Alternatively, if it's currently running then we'd just want to cause it to yield if it's priority is lower than the the thread behind it
auto nextIt{std::next(currentIt)};
if (nextIt != core->queue.end() && (*nextIt)->priority < thread->priority) {
thread->SendSignal(YieldSignal);
} else if (!thread->isPreempted && thread->priority == core->preemptionPriority) {
// If the thread needs to be preempted due to the new priority then arm it's preemption timer
struct itimerspec spec{.it_value = {.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(PreemptiveTimeslice).count()}};
timer_settime(*thread->preemptionTimer, 0, &spec, nullptr);
timer_settime(thread->preemptionTimer, 0, &spec, nullptr);
thread->isPreempted = true;
}
return;
@ -232,7 +215,7 @@ namespace skyline::kernel {
if (thread->isPreempted && thread->priority != core->preemptionPriority) {
struct itimerspec spec{};
timer_settime(*thread->preemptionTimer, 0, &spec, nullptr);
timer_settime(thread->preemptionTimer, 0, &spec, nullptr);
thread->isPreempted = false;
}
@ -247,13 +230,21 @@ namespace skyline::kernel {
void Scheduler::ParkThread() {
auto &thread{state.thread};
std::lock_guard migrationLock(thread->coreMigrationMutex);
RemoveThread();
{
auto originalCoreId{thread->coreId};
thread->coreId = constant::ParkedCoreId;
for (auto &core : cores)
if (originalCoreId != core.id && thread->affinityMask.test(core.id) && (core.queue.empty() || core.queue.front()->priority > thread->priority))
thread->coreId = core.id;
if (thread->coreId == constant::ParkedCoreId) {
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; });
thread->wakeCondition.wait(lock, [&]() { return parkedQueue.front() == thread && thread->coreId != constant::ParkedCoreId; });
}
InsertThread(thread);
}
@ -272,7 +263,7 @@ namespace skyline::kernel {
if (parkedThread->priority < thread->priority || (parkedThread->priority == thread->priority && (!nextThread || parkedThread->timesliceStart < nextThread->timesliceStart))) {
parkedThread->coreId = thread->coreId;
parkedLock.unlock();
parkedFrontCondition.notify_all();
thread->wakeCondition.notify_one();
}
}
}
@ -290,16 +281,15 @@ namespace skyline::kernel {
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();
if (it != core.queue.end())
(*it)->wakeCondition.notify_one(); // We need to wake the thread at the front of the queue, if we were at the front previously
}
}
}
if (thread->isPreempted) {
struct itimerspec spec{};
timer_settime(*thread->preemptionTimer, 0, &spec, nullptr);
timer_settime(thread->preemptionTimer, 0, &spec, nullptr);
thread->isPreempted = false;
}

View File

@ -30,7 +30,7 @@ namespace skyline {
return (std::numeric_limits<u64>::max() >> ((std::numeric_limits<u64>::digits - 1 + min) - max)) << min;
}
constexpr bool Valid(i8 value) const {
constexpr bool Valid(u8 value) const {
return (value >= min) && (value <= max);
}
};
@ -47,7 +47,6 @@ namespace skyline {
u8 id;
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::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);
@ -59,8 +58,6 @@ namespace skyline {
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
@ -78,6 +75,7 @@ namespace skyline {
* @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 inserts the thread into the migrated process's queue after load balancing, there is no need to call it redundantly
* @note alwaysInsert makes the assumption that the thread isn't inserted in any core's queue currently
*/
CoreContext& LoadBalance(const std::shared_ptr<type::KThread> &thread, bool alwaysInsert = false);

View File

@ -347,7 +347,14 @@ namespace skyline::kernel::svc {
auto thread{state.process->GetHandle<type::KThread>(handle)};
state.logger->Debug("svcSetThreadPriority: Setting thread #{}'s priority to {}", thread->id, priority);
if (thread->priority != priority) {
thread->priority = priority;
thread->basePriority = priority;
u8 newPriority{};
do {
// Try to CAS the priority of the thread with it's new base priority
// If the new priority is equivalent to the current priority then we don't need to CAS
newPriority = thread->priority.load();
newPriority = std::min(newPriority, priority);
} while (newPriority != priority && thread->priority.compare_exchange_strong(newPriority, priority));
state.scheduler->UpdatePriority(thread);
}
state.ctx->gpr.w0 = Result{};
@ -773,6 +780,7 @@ namespace skyline::kernel::svc {
}
void SendSyncRequest(const DeviceState &state) {
SchedulerScopedLock schedulerLock(state);
state.os->serviceManager.SyncRequestHandler(static_cast<KHandle>(state.ctx->gpr.x0));
state.ctx->gpr.w0 = Result{};
}

View File

@ -137,7 +137,7 @@ namespace skyline::kernel::type {
u8 priority, ownerPriority;
do {
// Try to CAS the priority of the owner with the current thread
// If they're equivalent then we don't need to CAS as the priority won't be inherited
// If the new priority is equivalent to the current priority then we don't need to CAS
ownerPriority = owner->priority.load();
priority = std::min(ownerPriority, state.thread->priority.load());
} while (ownerPriority != priority && owner->priority.compare_exchange_strong(ownerPriority, priority));
@ -198,13 +198,21 @@ namespace skyline::kernel::type {
if (!waiters.empty()) {
// If there are threads still waiting on us then try to inherit their priority
auto highestPriority{waiters.front()};
u8 priority, ownerPriority;
auto highestPriorityThread{waiters.front()};
u8 newPriority, basePriority;
do {
ownerPriority = state.thread->priority.load();
priority = std::min(ownerPriority, highestPriority->priority.load());
} while (ownerPriority != priority && nextOwner->priority.compare_exchange_strong(ownerPriority, priority));
basePriority = state.thread->basePriority.load();
newPriority = std::min(basePriority, highestPriorityThread->priority.load());
} while (basePriority != newPriority && state.thread->priority.compare_exchange_strong(basePriority, newPriority));
state.scheduler->UpdatePriority(state.thread);
} else {
u8 priority, basePriority;
do {
basePriority = state.thread->basePriority.load();
priority = state.thread->priority.load();
} while (priority != basePriority && !state.thread->priority.compare_exchange_strong(priority, basePriority));
if (priority != basePriority)
state.scheduler->UpdatePriority(state.thread);
}
if (nextWaiter) {

View File

@ -10,22 +10,16 @@
#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, u8 priority, i8 idealCore) : handle(handle), parent(parent), id(id), entry(entry), entryArgument(argument), stackTop(stackTop), priority(priority), idealCore(idealCore), coreId(idealCore), KSyncObject(state, KType::KThread) {
KThread::KThread(const DeviceState &state, KHandle handle, KProcess *parent, size_t id, void *entry, u64 argument, void *stackTop, u8 priority, i8 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() {
std::unique_lock lock(mutex);
if (running && pthread != pthread_self()) {
pthread_kill(pthread, SIGINT);
if (!thread.joinable())
pthread_join(pthread, nullptr);
}
Kill(true);
if (thread.joinable())
thread.join();
if (preemptionTimer)
timer_delete(*preemptionTimer);
timer_delete(preemptionTimer);
}
void KThread::StartThread() {
@ -44,7 +38,13 @@ namespace skyline::kernel::type {
if (setjmp(originalCtx)) { // Returns 1 if it's returning from guest, 0 otherwise
state.scheduler->RemoveThread();
running = false;
{
std::unique_lock lock(statusMutex);
running = false;
ready = false;
statusCondition.notify_all();
}
Signal();
if (threadName[0] != 'H' || threadName[1] != 'O' || threadName[2] != 'S' || threadName[3] != '-') {
@ -55,10 +55,32 @@ namespace skyline::kernel::type {
return;
}
struct sigevent event{
.sigev_signo = Scheduler::YieldSignal,
.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);
signal::SetSignalHandler({Scheduler::YieldSignal}, Scheduler::SignalHandler);
{
std::lock_guard lock(statusMutex);
ready = true;
statusCondition.notify_all();
}
try {
state.scheduler->WaitSchedule();
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();
}
asm volatile(
"MRS X0, TPIDR_EL0\n\t"
@ -161,10 +183,13 @@ namespace skyline::kernel::type {
}
void KThread::Start(bool self) {
std::unique_lock lock(mutex);
std::unique_lock lock(statusMutex);
if (!running) {
running = true;
state.scheduler->LoadBalance(shared_from_this(), true); // This will automatically insert the thread into the core queue after load balancing
running = true;
killed = false;
statusCondition.notify_all();
if (self) {
pthread = pthread_self();
lock.unlock();
@ -177,22 +202,23 @@ namespace skyline::kernel::type {
}
void KThread::Kill(bool join) {
std::lock_guard lock(mutex);
if (running) {
pthread_kill(pthread, SIGINT);
if (join) {
if (thread.joinable())
thread.join();
else
pthread_join(pthread, nullptr);
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();
}
running = false;
}
if (join)
statusCondition.wait(lock, [this]() { return !running; });
}
void KThread::SendSignal(int signal) {
std::lock_guard lock(mutex);
if (running)
std::unique_lock lock(statusMutex);
statusCondition.wait(lock, [this]() { return ready || killed; });
if (!killed && running)
pthread_kill(pthread, signal);
}
}

View File

@ -24,9 +24,11 @@ namespace skyline {
void StartThread();
public:
std::mutex mutex; //!< Synchronizes all thread state changes
std::mutex statusMutex; //!< Synchronizes all thread state changes, running/ready
std::condition_variable statusCondition; //!< A conditional variable signalled on the status of the thread changing
bool running{false}; //!< If the host thread that corresponds to this thread is running, this doesn't reflect guest scheduling changes
bool killed{false}; //!< If this thread was previously running and has been killed
bool ready{false}; //!< If this thread is ready to recieve signals or not
KHandle handle;
size_t id; //!< Index of thread in parent process's KThread vector
@ -38,6 +40,7 @@ namespace skyline {
u64 entryArgument;
void *stackTop;
std::condition_variable_any wakeCondition; //!< A conditional variable which is signalled to wake the current thread while it's sleeping
std::atomic<u8> basePriority; //!< The priority of the thread for the scheduler without any priority-inheritance
std::atomic<u8> priority; //!< The priority of the thread for the scheduler
i8 idealCore; //!< The ideal CPU core for this thread to run on
@ -46,7 +49,7 @@ namespace skyline {
std::mutex coreMigrationMutex; //!< Synchronizes operations which depend on which core the thread is running on
u64 timesliceStart{}; //!< Start of the scheduler timeslice
u64 averageTimeslice{}; //!< A weighted average of the timeslice duration for this thread
std::optional<timer_t> preemptionTimer{}; //!< A kernel timer used for preemption interrupts
timer_t preemptionTimer{}; //!< A kernel timer used for preemption interrupts
bool isPreempted{}; //!< If the preemption timer has been armed and will fire
std::mutex waiterMutex; //!< Synchronizes operations on mutation of the waiter members
u32* waitKey; //!< The key of the mutex which this thread is waiting on

View File

@ -25,10 +25,10 @@ namespace skyline::nce {
throw exception("Unimplemented SVC 0x{:X}", svc);
}
if (kernel::Scheduler::YieldPending) {
while (kernel::Scheduler::YieldPending) {
state.scheduler->Rotate(false);
state.scheduler->WaitSchedule();
kernel::Scheduler::YieldPending = false;
state.scheduler->WaitSchedule();
}
} catch (const signal::SignalException &e) {
if (e.signal != SIGINT) {

View File

@ -7,12 +7,12 @@ namespace skyline::service::timesrv {
ITimeZoneService::ITimeZoneService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
Result ITimeZoneService::ToCalendarTimeWithMyRule(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto curTime{std::time(nullptr)};
auto calender{*std::gmtime(&curTime)};
auto& time{request.Pop<u64>()};
auto calender{*std::gmtime(reinterpret_cast<const time_t *>(&time))};
CalendarTime calendarTime{
.year = static_cast<u16>(calender.tm_year),
.month = static_cast<u8>(calender.tm_mon),
.month = static_cast<u8>(calender.tm_mon + 1),
.day = static_cast<u8>(calender.tm_hour),
.minute = static_cast<u8>(calender.tm_min),
.second = static_cast<u8>(calender.tm_sec),
@ -22,7 +22,7 @@ namespace skyline::service::timesrv {
CalendarAdditionalInfo calendarInfo{
.dayWeek = static_cast<u32>(calender.tm_wday),
.dayMonth = static_cast<u32>(calender.tm_mday),
.name = *reinterpret_cast<const u64 *>(calender.tm_zone),
.tzName = *reinterpret_cast<const u64 *>(calender.tm_zone),
.dst = static_cast<i32>(calender.tm_isdst),
.utcRel = static_cast<u32>(calender.tm_gmtoff),
};

View File

@ -13,28 +13,28 @@ namespace skyline::service::timesrv {
class ITimeZoneService : public BaseService {
private:
/**
* @brief A particular time point in calendar format
* @brief A particular time point in Nintendo's calendar format
*/
struct CalendarTime {
u16 year; //!< The Year component of the date
u8 month; //!< The Month component of the date
u8 day; //!< The Day component of the date
u8 hour; //!< The Hour component of the date
u8 minute; //!< The Minute component of the date
u8 second; //!< The Second component of the date
u16 year; //!< Amount of years that have passed since 1900
u8 month; //!< Month of the year (1-12) [POSIX time uses 0-11]
u8 day; //!< Day of the month (1-31)
u8 hour; //!< Hour of the day (0-23)
u8 minute; //!< Minute of the hour (0-59)
u8 second; //!< Second of the minute (0-60)
u8 _pad_;
};
static_assert(sizeof(CalendarTime) == 0x8);
/**
* @brief Information that is packaged along with CalendarTime
* @brief Additional metadata about the time alongside CalendarTime
*/
struct CalendarAdditionalInfo {
u32 dayWeek; //!< The amount of days since Sunday
u32 dayMonth; //!< The amount of days since the start of the month
u64 name; //!< The name of the time zone
u32 dayWeek; //!< Amount of days since Sunday
u32 dayMonth; //!< Amount of days since the start of the month
u64 tzName; //!< The name of the time zone
i32 dst; //!< If DST is in effect or not
u32 utcRel; //!< The offset of the time from GMT in seconds
u32 utcRel; //!< Offset of the time from GMT in seconds
};
static_assert(sizeof(CalendarAdditionalInfo) == 0x18);