Atmosphere/mesosphere/source/threading/KThread.cpp
2019-06-03 17:41:03 -07:00

314 lines
8.3 KiB
C++

#include <mutex>
#include <atomic>
#include <algorithm>
#include <mesosphere/threading/KThread.hpp>
#include <mesosphere/threading/KScheduler.hpp>
#include <mesosphere/core/KCoreContext.hpp>
namespace mesosphere
{
bool KThread::IsAlive() const
{
return true;
}
void KThread::OnAlarm()
{
CancelKernelSync();
}
void KThread::AdjustScheduling(ushort oldMaskFull)
{
if (currentSchedMaskFull == oldMaskFull) {
return;
} else if (CompareSchedulingStatusFull(oldMaskFull, SchedulingStatus::Running)) {
KScheduler::Global::SetThreadPaused(*this);
} else if (CompareSchedulingStatusFull(SchedulingStatus::Running)) {
KScheduler::Global::SetThreadRunning(*this);
}
}
void KThread::Reschedule(KThread::SchedulingStatus newStatus)
{
//std::lock_guard criticalSection{KScheduler::GetCriticalSection()};
// TODO check the above ^
AdjustScheduling(SetSchedulingStatusField(newStatus));
}
void KThread::RescheduleIfStatusEquals(SchedulingStatus expectedStatus, SchedulingStatus newStatus)
{
if(GetSchedulingStatus() == expectedStatus) {
Reschedule(newStatus);
}
}
void KThread::AddForcePauseReason(KThread::ForcePauseReason reason)
{
KScopedCriticalSection critical_section;
if (!IsDying()) {
AddForcePauseReasonToField(reason);
if (numKernelMutexWaiters == 0) {
AdjustScheduling(CommitForcePauseToField());
}
}
}
void KThread::RemoveForcePauseReason(KThread::ForcePauseReason reason)
{
KScopedCriticalSection critical_section;
if (!IsDying()) {
RemoveForcePauseReasonToField(reason);
if (!IsForcePaused() && numKernelMutexWaiters == 0) {
AdjustScheduling(CommitForcePauseToField());
}
}
}
bool KThread::WaitForKernelSync(KThread::WaitList &waitList)
{
// Has to be called from critical section
currentWaitList = &waitList;
Reschedule(SchedulingStatus::Paused);
waitList.push_back(*this);
if (IsDying()) {
// Whoops
ResumeFromKernelSync();
return false;
}
return true;
}
void KThread::ResumeFromKernelSync()
{
// Has to be called from critical section
currentWaitList->erase(currentWaitList->iterator_to(*this));
currentWaitList = nullptr;
Reschedule(SchedulingStatus::Running);
}
void KThread::ResumeAllFromKernelSync(KThread::WaitList &waitList)
{
// Has to be called from critical section
waitList.clear_and_dispose(
[](KThread *t) {
t->currentWaitList = nullptr;
t->Reschedule(SchedulingStatus::Running);
}
);
}
void KThread::CancelKernelSync()
{
KScopedCriticalSection critical_section;
if (GetSchedulingStatus() == SchedulingStatus::Paused) {
// Note: transparent to force-pause
if (currentWaitList != nullptr) {
ResumeFromKernelSync();
} else {
Reschedule(SchedulingStatus::Running);
}
}
}
void KThread::CancelKernelSync(Result res)
{
syncResult = res;
CancelKernelSync();
}
void KThread::HandleSyncObjectSignaled(KSynchronizationObject *syncObj)
{
if (GetSchedulingStatus() == SchedulingStatus::Paused) {
signaledSyncObject = syncObj;
syncResult = ResultSuccess{};
Reschedule(SchedulingStatus::Running);
}
}
Result KThread::WaitSynchronizationImpl(int &outId, KSynchronizationObject **syncObjs, int numSyncObjs, const KSystemClock::time_point &timeoutTime)
{
KLinkedList<KThread *>::const_iterator nodes[numSyncObjs];
outId = -1;
{
KScopedCriticalSection critical_section;
// Try to find an already signaled object.
if (numSyncObjs >= 1) {
KSynchronizationObject **readyFound = std::find_if(
syncObjs,
syncObjs + numSyncObjs,
[](KSynchronizationObject *obj) {
return obj->IsSignaled();
}
);
outId = readyFound - syncObjs >= numSyncObjs ? -1 : readyFound - syncObjs;
}
if (timeoutTime == KSystemClock::time_point{} && outId == -1) {
return ResultKernelTimedOut{};
}
if (IsDying()) {
return ResultKernelThreadTerminating{};
}
if (cancelled) {
return ResultKernelCancelled{};
}
for (int i = 0; i < numSyncObjs; i++) {
nodes[i] = syncObjs[i]->AddWaiter(*this);
}
isWaitingSync = true;
signaledSyncObject = nullptr;
syncResult = ResultKernelTimedOut{};
Reschedule(SchedulingStatus::Paused);
if (timeoutTime > KSystemClock::time_point{}) {
SetAlarmTime(timeoutTime);
}
}
// Now waiting...
{
KScopedCriticalSection critical_section;
isWaitingSync = false;
if (timeoutTime > KSystemClock::time_point{}) {
ClearAlarm();
}
for (int i = 0; i < numSyncObjs; i++) {
syncObjs[i]->RemoveWaiter(nodes[i]);
if (syncObjs[i] == signaledSyncObject) {
outId = i;
}
}
}
return syncResult;
}
void KThread::AddToMutexWaitList(KThread &thread)
{
// TODO: check&increment numKernelMutexWaiters
// Ordered list insertion
auto it = std::find_if(
mutexWaitList.begin(),
mutexWaitList.end(),
[&thread](const KThread &t) {
return t.GetPriority() > thread.GetPriority();
}
);
if (it != mutexWaitList.end()) {
mutexWaitList.insert(it, thread);
} else {
mutexWaitList.push_back(thread);
}
}
KThread::MutexWaitList::iterator KThread::RemoveFromMutexWaitList(KThread::MutexWaitList::const_iterator it)
{
// TODO: check&decrement numKernelMutexWaiters
return mutexWaitList.erase(it);
}
void KThread::RemoveFromMutexWaitList(const KThread &t)
{
RemoveFromMutexWaitList(mutexWaitList.iterator_to(t));
}
void KThread::InheritDynamicPriority()
{
/*
Do priority inheritance
Since we're maybe changing the priority of the thread,
we must go through the entire mutex owner chain.
The invariant must be preserved:
A thread holding a mutex must have a higher-or-same priority than
all threads waiting for it to release the mutex.
*/
for (KThread *t = this; t != nullptr; t = t->wantedMutexOwner) {
uint newPrio, oldPrio = priority;
if (!mutexWaitList.empty() && mutexWaitList.front().priority < basePriority) {
newPrio = mutexWaitList.front().priority;
} else {
newPrio = basePriority;
}
if (newPrio == oldPrio) {
break;
} else {
// Update everything that depends on dynamic priority:
// TODO update condvar
// TODO update ctr arbiter
priority = newPrio;
// TODO update condvar
// TODO update ctr arbiter
if (CompareSchedulingStatusFull(SchedulingStatus::Running)) {
KScheduler::Global::AdjustThreadPriorityChanged(*this, oldPrio, this == KCoreContext::GetCurrentInstance().GetCurrentThread());
}
if (wantedMutexOwner != nullptr) {
wantedMutexOwner->RemoveFromMutexWaitList(*this);
wantedMutexOwner->AddToMutexWaitList(*this);
}
}
}
}
void KThread::AddMutexWaiter(KThread &waiter)
{
AddToMutexWaitList(waiter);
InheritDynamicPriority();
}
void KThread::RemoveMutexWaiter(KThread &waiter)
{
RemoveFromMutexWaitList(waiter);
InheritDynamicPriority();
}
KThread *KThread::RelinquishMutex(size_t *count, uiptr mutexAddr)
{
KThread *newOwner = nullptr;
*count = 0;
// First in list wanting mutexAddr becomes owner, the rest is transferred
for (auto it = mutexWaitList.begin(); it != mutexWaitList.end(); ) {
if (it->wantedMutex != mutexAddr) {
++it;
continue;
} else {
KThread &t = *it;
++(*count);
it = RemoveFromMutexWaitList(it);
if (newOwner == nullptr) {
newOwner = &t;
} else {
newOwner->AddToMutexWaitList(t);
}
}
}
// Mutex waiters list have changed
InheritDynamicPriority();
if (newOwner != nullptr) {
newOwner->InheritDynamicPriority();
}
return newOwner;
}
}