Merge pull request #2416 from lioncash/wait

kernel/svc: Clean up wait synchronization related functionality
This commit is contained in:
bunnei 2019-04-24 22:56:08 -04:00 committed by GitHub
commit 78574e7a47
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 54 additions and 51 deletions

View file

@ -46,8 +46,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
bool resume = true; bool resume = true;
if (thread->GetStatus() == ThreadStatus::WaitSynchAny || if (thread->GetStatus() == ThreadStatus::WaitSynch ||
thread->GetStatus() == ThreadStatus::WaitSynchAll ||
thread->GetStatus() == ThreadStatus::WaitHLEEvent) { thread->GetStatus() == ThreadStatus::WaitHLEEvent) {
// Remove the thread from each of its waiting objects' waitlists // Remove the thread from each of its waiting objects' waitlists
for (const auto& object : thread->GetWaitObjects()) { for (const auto& object : thread->GetWaitObjects()) {

View file

@ -147,8 +147,7 @@ void Process::PrepareForTermination() {
continue; continue;
// TODO(Subv): When are the other running/ready threads terminated? // TODO(Subv): When are the other running/ready threads terminated?
ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynchAny || ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch,
thread->GetStatus() == ThreadStatus::WaitSynchAll,
"Exiting processes with non-waiting threads is currently unimplemented"); "Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop(); thread->Stop();

View file

@ -424,7 +424,7 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
/// Default thread wakeup callback for WaitSynchronization /// Default thread wakeup callback for WaitSynchronization
static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index) { SharedPtr<WaitObject> object, std::size_t index) {
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch);
if (reason == ThreadWakeupReason::Timeout) { if (reason == ThreadWakeupReason::Timeout) {
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
@ -502,7 +502,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
} }
thread->SetWaitObjects(std::move(objects)); thread->SetWaitObjects(std::move(objects));
thread->SetStatus(ThreadStatus::WaitSynchAny); thread->SetStatus(ThreadStatus::WaitSynch);
// Create an event to wake the thread up after the specified nanosecond delay has passed // Create an event to wake the thread up after the specified nanosecond delay has passed
thread->WakeAfterDelay(nano_seconds); thread->WakeAfterDelay(nano_seconds);
@ -518,16 +518,14 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) { if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
thread_handle); thread_handle);
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
} }
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); thread->CancelWait();
thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
thread->ResumeFromWait();
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }

View file

@ -101,8 +101,7 @@ void Thread::ResumeFromWait() {
ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects");
switch (status) { switch (status) {
case ThreadStatus::WaitSynchAll: case ThreadStatus::WaitSynch:
case ThreadStatus::WaitSynchAny:
case ThreadStatus::WaitHLEEvent: case ThreadStatus::WaitHLEEvent:
case ThreadStatus::WaitSleep: case ThreadStatus::WaitSleep:
case ThreadStatus::WaitIPC: case ThreadStatus::WaitIPC:
@ -142,6 +141,12 @@ void Thread::ResumeFromWait() {
ChangeScheduler(); ChangeScheduler();
} }
void Thread::CancelWait() {
ASSERT(GetStatus() == ThreadStatus::WaitSynch);
SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
ResumeFromWait();
}
/** /**
* Resets a thread context, making it ready to be scheduled and run by the CPU * Resets a thread context, making it ready to be scheduled and run by the CPU
* @param context Thread context to reset * @param context Thread context to reset

View file

@ -49,8 +49,7 @@ enum class ThreadStatus {
WaitHLEEvent, ///< Waiting for hle event to finish WaitHLEEvent, ///< Waiting for hle event to finish
WaitSleep, ///< Waiting due to a SleepThread SVC WaitSleep, ///< Waiting due to a SleepThread SVC
WaitIPC, ///< Waiting for the reply from an IPC request WaitIPC, ///< Waiting for the reply from an IPC request
WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false WaitSynch, ///< Waiting due to WaitSynchronization
WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true
WaitMutex, ///< Waiting due to an ArbitrateLock svc WaitMutex, ///< Waiting due to an ArbitrateLock svc
WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc
WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc
@ -169,11 +168,17 @@ public:
return tls_memory; return tls_memory;
} }
/** /// Resumes a thread from waiting
* Resumes a thread from waiting
*/
void ResumeFromWait(); void ResumeFromWait();
/// Cancels a waiting operation that this thread may or may not be within.
///
/// When the thread is within a waiting state, this will set the thread's
/// waiting result to signal a canceled wait. The function will then resume
/// this thread.
///
void CancelWait();
/** /**
* Schedules an event to wake up the specified thread after the specified delay * Schedules an event to wake up the specified thread after the specified delay
* @param nanoseconds The time this thread will be allowed to sleep for * @param nanoseconds The time this thread will be allowed to sleep for
@ -184,24 +189,27 @@ public:
void CancelWakeupTimer(); void CancelWakeupTimer();
/** /**
* Sets the result after the thread awakens (from either WaitSynchronization SVC) * Sets the result after the thread awakens (from svcWaitSynchronization)
* @param result Value to set to the returned result * @param result Value to set to the returned result
*/ */
void SetWaitSynchronizationResult(ResultCode result); void SetWaitSynchronizationResult(ResultCode result);
/** /**
* Sets the output parameter value after the thread awakens (from WaitSynchronizationN SVC only) * Sets the output parameter value after the thread awakens (from svcWaitSynchronization)
* @param output Value to set to the output parameter * @param output Value to set to the output parameter
*/ */
void SetWaitSynchronizationOutput(s32 output); void SetWaitSynchronizationOutput(s32 output);
/** /**
* Retrieves the index that this particular object occupies in the list of objects * Retrieves the index that this particular object occupies in the list of objects
* that the thread passed to WaitSynchronizationN, starting the search from the last element. * that the thread passed to WaitSynchronization, starting the search from the last element.
* It is used to set the output value of WaitSynchronizationN when the thread is awakened. *
* It is used to set the output index of WaitSynchronization when the thread is awakened.
*
* When a thread wakes up due to an object signal, the kernel will use the index of the last * When a thread wakes up due to an object signal, the kernel will use the index of the last
* matching object in the wait objects list in case of having multiple instances of the same * matching object in the wait objects list in case of having multiple instances of the same
* object in the list. * object in the list.
*
* @param object Object to query the index of. * @param object Object to query the index of.
*/ */
s32 GetWaitObjectIndex(const WaitObject* object) const; s32 GetWaitObjectIndex(const WaitObject* object) const;
@ -238,13 +246,9 @@ public:
*/ */
VAddr GetCommandBufferAddress() const; VAddr GetCommandBufferAddress() const;
/** /// Returns whether this thread is waiting on objects from a WaitSynchronization call.
* Returns whether this thread is waiting for all the objects in bool IsSleepingOnWait() const {
* its wait list to become ready, as a result of a WaitSynchronizationN call return status == ThreadStatus::WaitSynch;
* with wait_all = true.
*/
bool IsSleepingOnWaitAll() const {
return status == ThreadStatus::WaitSynchAll;
} }
ThreadContext& GetContext() { ThreadContext& GetContext() {
@ -418,7 +422,7 @@ private:
Process* owner_process; Process* owner_process;
/// Objects that the thread is waiting on, in the same order as they were /// Objects that the thread is waiting on, in the same order as they were
/// passed to WaitSynchronization1/N. /// passed to WaitSynchronization.
ThreadWaitObjects wait_objects; ThreadWaitObjects wait_objects;
/// List of threads that are waiting for a mutex that is held by this thread. /// List of threads that are waiting for a mutex that is held by this thread.
@ -441,7 +445,7 @@ private:
Handle callback_handle = 0; Handle callback_handle = 0;
/// Callback that will be invoked when the thread is resumed from a waiting state. If the thread /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
/// was waiting via WaitSynchronizationN then the object will be the last object that became /// was waiting via WaitSynchronization then the object will be the last object that became
/// available. In case of a timeout, the object will be nullptr. /// available. In case of a timeout, the object will be nullptr.
WakeupCallback wakeup_callback; WakeupCallback wakeup_callback;

View file

@ -38,8 +38,7 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
const ThreadStatus thread_status = thread->GetStatus(); const ThreadStatus thread_status = thread->GetStatus();
// The list of waiting threads must not contain threads that are not waiting to be awakened. // The list of waiting threads must not contain threads that are not waiting to be awakened.
ASSERT_MSG(thread_status == ThreadStatus::WaitSynchAny || ASSERT_MSG(thread_status == ThreadStatus::WaitSynch ||
thread_status == ThreadStatus::WaitSynchAll ||
thread_status == ThreadStatus::WaitHLEEvent, thread_status == ThreadStatus::WaitHLEEvent,
"Inconsistent thread statuses in waiting_threads"); "Inconsistent thread statuses in waiting_threads");
@ -49,10 +48,10 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
if (ShouldWait(thread.get())) if (ShouldWait(thread.get()))
continue; continue;
// A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or // A thread is ready to run if it's either in ThreadStatus::WaitSynch
// in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready. // and the rest of the objects it is waiting on are ready.
bool ready_to_run = true; bool ready_to_run = true;
if (thread_status == ThreadStatus::WaitSynchAll) { if (thread_status == ThreadStatus::WaitSynch) {
ready_to_run = thread->AllWaitObjectsReady(); ready_to_run = thread->AllWaitObjectsReady();
} }
@ -68,33 +67,35 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) { void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) {
ASSERT(!ShouldWait(thread.get())); ASSERT(!ShouldWait(thread.get()));
if (!thread) if (!thread) {
return; return;
}
if (!thread->IsSleepingOnWaitAll()) { if (thread->IsSleepingOnWait()) {
Acquire(thread.get());
} else {
for (const auto& object : thread->GetWaitObjects()) { for (const auto& object : thread->GetWaitObjects()) {
ASSERT(!object->ShouldWait(thread.get())); ASSERT(!object->ShouldWait(thread.get()));
object->Acquire(thread.get()); object->Acquire(thread.get());
} }
} else {
Acquire(thread.get());
} }
const std::size_t index = thread->GetWaitObjectIndex(this); const std::size_t index = thread->GetWaitObjectIndex(this);
for (const auto& object : thread->GetWaitObjects()) for (const auto& object : thread->GetWaitObjects()) {
object->RemoveWaitingThread(thread.get()); object->RemoveWaitingThread(thread.get());
}
thread->ClearWaitObjects(); thread->ClearWaitObjects();
thread->CancelWakeupTimer(); thread->CancelWakeupTimer();
bool resume = true; bool resume = true;
if (thread->HasWakeupCallback()) {
if (thread->HasWakeupCallback())
resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index); resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index);
}
if (resume) if (resume) {
thread->ResumeFromWait(); thread->ResumeFromWait();
}
} }
void WaitObject::WakeupAllWaitingThreads() { void WaitObject::WakeupAllWaitingThreads() {

View file

@ -227,8 +227,7 @@ QString WaitTreeThread::GetText() const {
case Kernel::ThreadStatus::WaitIPC: case Kernel::ThreadStatus::WaitIPC:
status = tr("waiting for IPC reply"); status = tr("waiting for IPC reply");
break; break;
case Kernel::ThreadStatus::WaitSynchAll: case Kernel::ThreadStatus::WaitSynch:
case Kernel::ThreadStatus::WaitSynchAny:
status = tr("waiting for objects"); status = tr("waiting for objects");
break; break;
case Kernel::ThreadStatus::WaitMutex: case Kernel::ThreadStatus::WaitMutex:
@ -269,8 +268,7 @@ QColor WaitTreeThread::GetColor() const {
return QColor(Qt::GlobalColor::darkRed); return QColor(Qt::GlobalColor::darkRed);
case Kernel::ThreadStatus::WaitSleep: case Kernel::ThreadStatus::WaitSleep:
return QColor(Qt::GlobalColor::darkYellow); return QColor(Qt::GlobalColor::darkYellow);
case Kernel::ThreadStatus::WaitSynchAll: case Kernel::ThreadStatus::WaitSynch:
case Kernel::ThreadStatus::WaitSynchAny:
case Kernel::ThreadStatus::WaitMutex: case Kernel::ThreadStatus::WaitMutex:
case Kernel::ThreadStatus::WaitCondVar: case Kernel::ThreadStatus::WaitCondVar:
case Kernel::ThreadStatus::WaitArb: case Kernel::ThreadStatus::WaitArb:
@ -325,10 +323,9 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex")));
} }
if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAny || if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) {
thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAll) {
list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjects(), list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjects(),
thread.IsSleepingOnWaitAll())); thread.IsSleepingOnWait()));
} }
list.push_back(std::make_unique<WaitTreeCallstack>(thread)); list.push_back(std::make_unique<WaitTreeCallstack>(thread));