From e6a7723f2f4b62279cd4f6d4b48eb02a9b60ffb6 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 1 Jan 2017 16:53:22 -0500 Subject: Kernel: Object ShouldWait and Acquire calls now take a thread as a parameter. This will be useful when implementing mutex priority inheritance. --- src/core/hle/kernel/kernel.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'src/core/hle/kernel/kernel.cpp') diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1db8e102f..ef9dbafa5 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -39,11 +39,6 @@ SharedPtr WaitObject::GetHighestPriorityReadyThread() { thread->status == THREADSTATUS_DEAD; }); - // TODO(Subv): This call should be performed inside the loop below to check if an object can be - // acquired by a particular thread. This is useful for things like recursive locking of Mutexes. - if (ShouldWait()) - return nullptr; - Thread* candidate = nullptr; s32 candidate_priority = THREADPRIO_LOWEST + 1; @@ -51,9 +46,12 @@ SharedPtr WaitObject::GetHighestPriorityReadyThread() { if (thread->current_priority >= candidate_priority) continue; + if (ShouldWait(thread.get())) + continue; + bool ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), - [](const SharedPtr& object) { return object->ShouldWait(); }); + [&thread](const SharedPtr& object) { return object->ShouldWait(thread.get()); }); if (ready_to_run) { candidate = thread.get(); candidate_priority = thread->current_priority; @@ -66,7 +64,7 @@ SharedPtr WaitObject::GetHighestPriorityReadyThread() { void WaitObject::WakeupAllWaitingThreads() { while (auto thread = GetHighestPriorityReadyThread()) { if (!thread->IsSleepingOnWaitAll()) { - Acquire(); + Acquire(thread.get()); // Set the output index of the WaitSynchronizationN call to the index of this object. if (thread->wait_set_output) { thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); @@ -74,7 +72,7 @@ void WaitObject::WakeupAllWaitingThreads() { } } else { for (auto& object : thread->wait_objects) { - object->Acquire(); + object->Acquire(thread.get()); object->RemoveWaitingThread(thread.get()); } // Note: This case doesn't update the output index of WaitSynchronizationN. -- cgit v1.2.3 From b6a0355568ee327bef8957b9a2498897b96e1278 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 2 Jan 2017 13:53:10 -0500 Subject: Kernel/Mutex: Update a mutex priority when a thread stops waiting on it. --- src/core/hle/kernel/kernel.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'src/core/hle/kernel/kernel.cpp') diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index ef9dbafa5..6f61d526a 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include -#include #include "common/assert.h" #include "common/logging/log.h" #include "core/hle/config_mem.h" @@ -34,10 +33,17 @@ void WaitObject::RemoveWaitingThread(Thread* thread) { SharedPtr WaitObject::GetHighestPriorityReadyThread() { // Remove the threads that are ready or already running from our waitlist - boost::range::remove_erase_if(waiting_threads, [](const SharedPtr& thread) { - return thread->status == THREADSTATUS_RUNNING || thread->status == THREADSTATUS_READY || - thread->status == THREADSTATUS_DEAD; - }); + auto to_remove = waiting_threads.end(); + do { + to_remove = std::find_if(waiting_threads.begin(), waiting_threads.end(), + [](const SharedPtr& thread) { + return thread->status == THREADSTATUS_RUNNING || + thread->status == THREADSTATUS_READY || + thread->status == THREADSTATUS_DEAD; + }); + // Call RemoveWaitingThread so that child classes can override the behavior. + RemoveWaitingThread(to_remove->get()); + } while (to_remove != waiting_threads.end()); Thread* candidate = nullptr; s32 candidate_priority = THREADPRIO_LOWEST + 1; @@ -49,9 +55,10 @@ SharedPtr WaitObject::GetHighestPriorityReadyThread() { if (ShouldWait(thread.get())) continue; - bool ready_to_run = - std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), - [&thread](const SharedPtr& object) { return object->ShouldWait(thread.get()); }); + bool ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), + [&thread](const SharedPtr& object) { + return object->ShouldWait(thread.get()); + }); if (ready_to_run) { candidate = thread.get(); candidate_priority = thread->current_priority; -- cgit v1.2.3 From fd95b6ee2606da4cd47c5f2916ad3b4f86c0e0f4 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 4 Jan 2017 10:53:01 -0500 Subject: Kernel: Remove Thread::wait_objects_index and use wait_objects to hold all the objects that a thread is waiting on. --- src/core/hle/kernel/kernel.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/core/hle/kernel/kernel.cpp') diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 6f61d526a..955f50a9b 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -55,10 +55,16 @@ SharedPtr WaitObject::GetHighestPriorityReadyThread() { if (ShouldWait(thread.get())) continue; - bool ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), + // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or + // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready. + bool ready_to_run = true; + if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) { + ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), [&thread](const SharedPtr& object) { return object->ShouldWait(thread.get()); }); + } + if (ready_to_run) { candidate = thread.get(); candidate_priority = thread->current_priority; -- cgit v1.2.3 From 7f1dca8cd26fe9be0080efee4e456630960af459 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 4 Jan 2017 11:37:19 -0500 Subject: Kernel: Remove a thread from all of its waiting objects' waiting_threads list when it is awoken. This fixes a potential bug where threads would not get removed from said list if they awoke after waiting with WaitSynchronizationN with wait_all = false --- src/core/hle/kernel/kernel.cpp | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) (limited to 'src/core/hle/kernel/kernel.cpp') diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 955f50a9b..47d4df69c 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -32,19 +32,6 @@ void WaitObject::RemoveWaitingThread(Thread* thread) { } SharedPtr WaitObject::GetHighestPriorityReadyThread() { - // Remove the threads that are ready or already running from our waitlist - auto to_remove = waiting_threads.end(); - do { - to_remove = std::find_if(waiting_threads.begin(), waiting_threads.end(), - [](const SharedPtr& thread) { - return thread->status == THREADSTATUS_RUNNING || - thread->status == THREADSTATUS_READY || - thread->status == THREADSTATUS_DEAD; - }); - // Call RemoveWaitingThread so that child classes can override the behavior. - RemoveWaitingThread(to_remove->get()); - } while (to_remove != waiting_threads.end()); - Thread* candidate = nullptr; s32 candidate_priority = THREADPRIO_LOWEST + 1; @@ -86,17 +73,16 @@ void WaitObject::WakeupAllWaitingThreads() { } else { for (auto& object : thread->wait_objects) { object->Acquire(thread.get()); - object->RemoveWaitingThread(thread.get()); } // Note: This case doesn't update the output index of WaitSynchronizationN. - // Clear the thread's waitlist - thread->wait_objects.clear(); } + for (auto& object : thread->wait_objects) + object->RemoveWaitingThread(thread.get()); + thread->wait_objects.clear(); + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); thread->ResumeFromWait(); - // Note: Removing the thread from the object's waitlist will be - // done by GetHighestPriorityReadyThread. } } -- cgit v1.2.3 From dda4ec93bea089e3286e9a965378d9411f480acd Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 4 Jan 2017 12:48:13 -0500 Subject: Kernel: Add some asserts to enforce the invariants in the scheduler. --- src/core/hle/kernel/kernel.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/core/hle/kernel/kernel.cpp') diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 47d4df69c..f599916f0 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -27,6 +27,9 @@ void WaitObject::AddWaitingThread(SharedPtr 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); } @@ -36,6 +39,11 @@ SharedPtr WaitObject::GetHighestPriorityReadyThread() { s32 candidate_priority = THREADPRIO_LOWEST + 1; for (const auto& thread : waiting_threads) { + // The list of waiting threads must not contain threads that are not waiting to be awakened. + ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || + thread->status == THREADSTATUS_WAIT_SYNCH_ALL, + "Inconsistent thread statuses in waiting_threads"); + if (thread->current_priority >= candidate_priority) continue; -- cgit v1.2.3