yuzu/src/core/hle/kernel/wait_object.cpp
Lioncash c268ffd831 kernel/thread: Unify wait synchronization types
This is a holdover from Citra, where the 3DS has both
WaitSynchronization1 and WaitSynchronizationN. The switch only has one
form of wait synchronizing (literally WaitSynchonization). This allows
us to throw out code that doesn't apply at all to the Switch kernel.

Because of this unnecessary dichotomy within the wait synchronization
utilities, we were also neglecting to properly handle waiting on
multiple objects.

While we're at it, we can also scrub out any lingering references to
WaitSynchronization1/WaitSynchronizationN in comments, and change them
to WaitSynchronization (or remove them if the mention no longer
applies).
2019-04-17 09:30:56 -04:00

111 lines
3.4 KiB
C++

// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
WaitObject::WaitObject(KernelCore& kernel) : Object{kernel} {}
WaitObject::~WaitObject() = default;
void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) {
auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
if (itr == waiting_threads.end())
waiting_threads.push_back(std::move(thread));
}
void WaitObject::RemoveWaitingThread(Thread* thread) {
auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
// If a thread passed multiple handles to the same object,
// the kernel might attempt to remove the thread from the object's
// waiting threads list multiple times.
if (itr != waiting_threads.end())
waiting_threads.erase(itr);
}
SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
Thread* candidate = nullptr;
u32 candidate_priority = THREADPRIO_LOWEST + 1;
for (const auto& thread : waiting_threads) {
const ThreadStatus thread_status = thread->GetStatus();
// The list of waiting threads must not contain threads that are not waiting to be awakened.
ASSERT_MSG(thread_status == ThreadStatus::WaitSynch ||
thread_status == ThreadStatus::WaitHLEEvent,
"Inconsistent thread statuses in waiting_threads");
if (thread->GetPriority() >= candidate_priority)
continue;
if (ShouldWait(thread.get()))
continue;
// A thread is ready to run if it's either in ThreadStatus::WaitSynch
// and the rest of the objects it is waiting on are ready.
bool ready_to_run = true;
if (thread_status == ThreadStatus::WaitSynch) {
ready_to_run = thread->AllWaitObjectsReady();
}
if (ready_to_run) {
candidate = thread.get();
candidate_priority = thread->GetPriority();
}
}
return candidate;
}
void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) {
ASSERT(!ShouldWait(thread.get()));
if (!thread) {
return;
}
if (thread->IsSleepingOnWait()) {
for (const auto& object : thread->GetWaitObjects()) {
ASSERT(!object->ShouldWait(thread.get()));
object->Acquire(thread.get());
}
} else {
Acquire(thread.get());
}
const std::size_t index = thread->GetWaitObjectIndex(this);
for (const auto& object : thread->GetWaitObjects()) {
object->RemoveWaitingThread(thread.get());
}
thread->ClearWaitObjects();
thread->CancelWakeupTimer();
bool resume = true;
if (thread->HasWakeupCallback()) {
resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index);
}
if (resume) {
thread->ResumeFromWait();
}
}
void WaitObject::WakeupAllWaitingThreads() {
while (auto thread = GetHighestPriorityReadyThread()) {
WakeupWaitingThread(thread);
}
}
const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const {
return waiting_threads;
}
} // namespace Kernel