diff options
Diffstat (limited to 'src/core/hle/kernel')
| -rw-r--r-- | src/core/hle/kernel/event.cpp | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/event.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 49 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 13 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 84 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.h | 17 | ||||
| -rw-r--r-- | src/core/hle/kernel/semaphore.cpp | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/semaphore.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/server_port.cpp | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/server_port.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/server_session.cpp | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/server_session.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 50 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 48 | ||||
| -rw-r--r-- | src/core/hle/kernel/timer.cpp | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/timer.h | 4 |
16 files changed, 184 insertions, 127 deletions
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 3e116e3df..e1f42af05 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp | |||
| @@ -30,12 +30,12 @@ SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) { | |||
| 30 | return evt; | 30 | return evt; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | bool Event::ShouldWait() { | 33 | bool Event::ShouldWait(Thread* thread) const { |
| 34 | return !signaled; | 34 | return !signaled; |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | void Event::Acquire() { | 37 | void Event::Acquire(Thread* thread) { |
| 38 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 38 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 39 | 39 | ||
| 40 | // Release the event if it's not sticky... | 40 | // Release the event if it's not sticky... |
| 41 | if (reset_type != ResetType::Sticky) | 41 | if (reset_type != ResetType::Sticky) |
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index 8dcd23edb..39452bf33 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h | |||
| @@ -35,8 +35,8 @@ public: | |||
| 35 | bool signaled; ///< Whether the event has already been signaled | 35 | bool signaled; ///< Whether the event has already been signaled |
| 36 | std::string name; ///< Name of event (optional) | 36 | std::string name; ///< Name of event (optional) |
| 37 | 37 | ||
| 38 | bool ShouldWait() override; | 38 | bool ShouldWait(Thread* thread) const override; |
| 39 | void Acquire() override; | 39 | void Acquire(Thread* thread) override; |
| 40 | 40 | ||
| 41 | void Signal(); | 41 | void Signal(); |
| 42 | void Clear(); | 42 | void Clear(); |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1db8e102f..f599916f0 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <boost/range/algorithm_ext/erase.hpp> | ||
| 7 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 8 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 9 | #include "core/hle/config_mem.h" | 8 | #include "core/hle/config_mem.h" |
| @@ -28,32 +27,39 @@ void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { | |||
| 28 | 27 | ||
| 29 | void WaitObject::RemoveWaitingThread(Thread* thread) { | 28 | void WaitObject::RemoveWaitingThread(Thread* thread) { |
| 30 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | 29 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); |
| 30 | // If a thread passed multiple handles to the same object, | ||
| 31 | // the kernel might attempt to remove the thread from the object's | ||
| 32 | // waiting threads list multiple times. | ||
| 31 | if (itr != waiting_threads.end()) | 33 | if (itr != waiting_threads.end()) |
| 32 | waiting_threads.erase(itr); | 34 | waiting_threads.erase(itr); |
| 33 | } | 35 | } |
| 34 | 36 | ||
| 35 | SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | 37 | SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { |
| 36 | // Remove the threads that are ready or already running from our waitlist | ||
| 37 | boost::range::remove_erase_if(waiting_threads, [](const SharedPtr<Thread>& thread) { | ||
| 38 | return thread->status == THREADSTATUS_RUNNING || thread->status == THREADSTATUS_READY || | ||
| 39 | thread->status == THREADSTATUS_DEAD; | ||
| 40 | }); | ||
| 41 | |||
| 42 | // TODO(Subv): This call should be performed inside the loop below to check if an object can be | ||
| 43 | // acquired by a particular thread. This is useful for things like recursive locking of Mutexes. | ||
| 44 | if (ShouldWait()) | ||
| 45 | return nullptr; | ||
| 46 | |||
| 47 | Thread* candidate = nullptr; | 38 | Thread* candidate = nullptr; |
| 48 | s32 candidate_priority = THREADPRIO_LOWEST + 1; | 39 | s32 candidate_priority = THREADPRIO_LOWEST + 1; |
| 49 | 40 | ||
| 50 | for (const auto& thread : waiting_threads) { | 41 | for (const auto& thread : waiting_threads) { |
| 42 | // The list of waiting threads must not contain threads that are not waiting to be awakened. | ||
| 43 | ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || | ||
| 44 | thread->status == THREADSTATUS_WAIT_SYNCH_ALL, | ||
| 45 | "Inconsistent thread statuses in waiting_threads"); | ||
| 46 | |||
| 51 | if (thread->current_priority >= candidate_priority) | 47 | if (thread->current_priority >= candidate_priority) |
| 52 | continue; | 48 | continue; |
| 53 | 49 | ||
| 54 | bool ready_to_run = | 50 | if (ShouldWait(thread.get())) |
| 55 | std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), | 51 | continue; |
| 56 | [](const SharedPtr<WaitObject>& object) { return object->ShouldWait(); }); | 52 | |
| 53 | // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or | ||
| 54 | // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready. | ||
| 55 | bool ready_to_run = true; | ||
| 56 | if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) { | ||
| 57 | ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), | ||
| 58 | [&thread](const SharedPtr<WaitObject>& object) { | ||
| 59 | return object->ShouldWait(thread.get()); | ||
| 60 | }); | ||
| 61 | } | ||
| 62 | |||
| 57 | if (ready_to_run) { | 63 | if (ready_to_run) { |
| 58 | candidate = thread.get(); | 64 | candidate = thread.get(); |
| 59 | candidate_priority = thread->current_priority; | 65 | candidate_priority = thread->current_priority; |
| @@ -66,7 +72,7 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | |||
| 66 | void WaitObject::WakeupAllWaitingThreads() { | 72 | void WaitObject::WakeupAllWaitingThreads() { |
| 67 | while (auto thread = GetHighestPriorityReadyThread()) { | 73 | while (auto thread = GetHighestPriorityReadyThread()) { |
| 68 | if (!thread->IsSleepingOnWaitAll()) { | 74 | if (!thread->IsSleepingOnWaitAll()) { |
| 69 | Acquire(); | 75 | Acquire(thread.get()); |
| 70 | // Set the output index of the WaitSynchronizationN call to the index of this object. | 76 | // Set the output index of the WaitSynchronizationN call to the index of this object. |
| 71 | if (thread->wait_set_output) { | 77 | if (thread->wait_set_output) { |
| 72 | thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); | 78 | thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); |
| @@ -74,18 +80,17 @@ void WaitObject::WakeupAllWaitingThreads() { | |||
| 74 | } | 80 | } |
| 75 | } else { | 81 | } else { |
| 76 | for (auto& object : thread->wait_objects) { | 82 | for (auto& object : thread->wait_objects) { |
| 77 | object->Acquire(); | 83 | object->Acquire(thread.get()); |
| 78 | object->RemoveWaitingThread(thread.get()); | ||
| 79 | } | 84 | } |
| 80 | // Note: This case doesn't update the output index of WaitSynchronizationN. | 85 | // Note: This case doesn't update the output index of WaitSynchronizationN. |
| 81 | // Clear the thread's waitlist | ||
| 82 | thread->wait_objects.clear(); | ||
| 83 | } | 86 | } |
| 84 | 87 | ||
| 88 | for (auto& object : thread->wait_objects) | ||
| 89 | object->RemoveWaitingThread(thread.get()); | ||
| 90 | thread->wait_objects.clear(); | ||
| 91 | |||
| 85 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | 92 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); |
| 86 | thread->ResumeFromWait(); | 93 | thread->ResumeFromWait(); |
| 87 | // Note: Removing the thread from the object's waitlist will be | ||
| 88 | // done by GetHighestPriorityReadyThread. | ||
| 89 | } | 94 | } |
| 90 | } | 95 | } |
| 91 | 96 | ||
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 9503e7d04..05097824b 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -132,25 +132,26 @@ using SharedPtr = boost::intrusive_ptr<T>; | |||
| 132 | class WaitObject : public Object { | 132 | class WaitObject : public Object { |
| 133 | public: | 133 | public: |
| 134 | /** | 134 | /** |
| 135 | * Check if the current thread should wait until the object is available | 135 | * Check if the specified thread should wait until the object is available |
| 136 | * @param thread The thread about which we're deciding. | ||
| 136 | * @return True if the current thread should wait due to this object being unavailable | 137 | * @return True if the current thread should wait due to this object being unavailable |
| 137 | */ | 138 | */ |
| 138 | virtual bool ShouldWait() = 0; | 139 | virtual bool ShouldWait(Thread* thread) const = 0; |
| 139 | 140 | ||
| 140 | /// Acquire/lock the object if it is available | 141 | /// Acquire/lock the object for the specified thread if it is available |
| 141 | virtual void Acquire() = 0; | 142 | virtual void Acquire(Thread* thread) = 0; |
| 142 | 143 | ||
| 143 | /** | 144 | /** |
| 144 | * Add a thread to wait on this object | 145 | * Add a thread to wait on this object |
| 145 | * @param thread Pointer to thread to add | 146 | * @param thread Pointer to thread to add |
| 146 | */ | 147 | */ |
| 147 | void AddWaitingThread(SharedPtr<Thread> thread); | 148 | virtual void AddWaitingThread(SharedPtr<Thread> thread); |
| 148 | 149 | ||
| 149 | /** | 150 | /** |
| 150 | * Removes a thread from waiting on this object (e.g. if it was resumed already) | 151 | * Removes a thread from waiting on this object (e.g. if it was resumed already) |
| 151 | * @param thread Pointer to thread to remove | 152 | * @param thread Pointer to thread to remove |
| 152 | */ | 153 | */ |
| 153 | void RemoveWaitingThread(Thread* thread); | 154 | virtual void RemoveWaitingThread(Thread* thread); |
| 154 | 155 | ||
| 155 | /** | 156 | /** |
| 156 | * Wake up all threads waiting on this object that can be awoken, in priority order, | 157 | * Wake up all threads waiting on this object that can be awoken, in priority order, |
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 736944bae..cef961289 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -6,26 +6,18 @@ | |||
| 6 | #include <vector> | 6 | #include <vector> |
| 7 | #include <boost/range/algorithm_ext/erase.hpp> | 7 | #include <boost/range/algorithm_ext/erase.hpp> |
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "core/core.h" | ||
| 9 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 10 | #include "core/hle/kernel/mutex.h" | 11 | #include "core/hle/kernel/mutex.h" |
| 11 | #include "core/hle/kernel/thread.h" | 12 | #include "core/hle/kernel/thread.h" |
| 12 | 13 | ||
| 13 | namespace Kernel { | 14 | namespace Kernel { |
| 14 | 15 | ||
| 15 | /** | ||
| 16 | * Resumes a thread waiting for the specified mutex | ||
| 17 | * @param mutex The mutex that some thread is waiting on | ||
| 18 | */ | ||
| 19 | static void ResumeWaitingThread(Mutex* mutex) { | ||
| 20 | // Reset mutex lock thread handle, nothing is waiting | ||
| 21 | mutex->lock_count = 0; | ||
| 22 | mutex->holding_thread = nullptr; | ||
| 23 | mutex->WakeupAllWaitingThreads(); | ||
| 24 | } | ||
| 25 | |||
| 26 | void ReleaseThreadMutexes(Thread* thread) { | 16 | void ReleaseThreadMutexes(Thread* thread) { |
| 27 | for (auto& mtx : thread->held_mutexes) { | 17 | for (auto& mtx : thread->held_mutexes) { |
| 28 | ResumeWaitingThread(mtx.get()); | 18 | mtx->lock_count = 0; |
| 19 | mtx->holding_thread = nullptr; | ||
| 20 | mtx->WakeupAllWaitingThreads(); | ||
| 29 | } | 21 | } |
| 30 | thread->held_mutexes.clear(); | 22 | thread->held_mutexes.clear(); |
| 31 | } | 23 | } |
| @@ -40,52 +32,74 @@ SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) { | |||
| 40 | mutex->name = std::move(name); | 32 | mutex->name = std::move(name); |
| 41 | mutex->holding_thread = nullptr; | 33 | mutex->holding_thread = nullptr; |
| 42 | 34 | ||
| 43 | // Acquire mutex with current thread if initialized as locked... | 35 | // Acquire mutex with current thread if initialized as locked |
| 44 | if (initial_locked) | 36 | if (initial_locked) |
| 45 | mutex->Acquire(); | 37 | mutex->Acquire(GetCurrentThread()); |
| 46 | 38 | ||
| 47 | return mutex; | 39 | return mutex; |
| 48 | } | 40 | } |
| 49 | 41 | ||
| 50 | bool Mutex::ShouldWait() { | 42 | bool Mutex::ShouldWait(Thread* thread) const { |
| 51 | auto thread = GetCurrentThread(); | 43 | return lock_count > 0 && thread != holding_thread; |
| 52 | bool wait = lock_count > 0 && holding_thread != thread; | ||
| 53 | |||
| 54 | // If the holding thread of the mutex is lower priority than this thread, that thread should | ||
| 55 | // temporarily inherit this thread's priority | ||
| 56 | if (wait && thread->current_priority < holding_thread->current_priority) | ||
| 57 | holding_thread->BoostPriority(thread->current_priority); | ||
| 58 | |||
| 59 | return wait; | ||
| 60 | } | ||
| 61 | |||
| 62 | void Mutex::Acquire() { | ||
| 63 | Acquire(GetCurrentThread()); | ||
| 64 | } | 44 | } |
| 65 | 45 | ||
| 66 | void Mutex::Acquire(SharedPtr<Thread> thread) { | 46 | void Mutex::Acquire(Thread* thread) { |
| 67 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 47 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 68 | 48 | ||
| 69 | // Actually "acquire" the mutex only if we don't already have it... | 49 | // Actually "acquire" the mutex only if we don't already have it |
| 70 | if (lock_count == 0) { | 50 | if (lock_count == 0) { |
| 51 | priority = thread->current_priority; | ||
| 71 | thread->held_mutexes.insert(this); | 52 | thread->held_mutexes.insert(this); |
| 72 | holding_thread = std::move(thread); | 53 | holding_thread = thread; |
| 54 | thread->UpdatePriority(); | ||
| 55 | Core::System::GetInstance().PrepareReschedule(); | ||
| 73 | } | 56 | } |
| 74 | 57 | ||
| 75 | lock_count++; | 58 | lock_count++; |
| 76 | } | 59 | } |
| 77 | 60 | ||
| 78 | void Mutex::Release() { | 61 | void Mutex::Release() { |
| 79 | // Only release if the mutex is held... | 62 | // Only release if the mutex is held |
| 80 | if (lock_count > 0) { | 63 | if (lock_count > 0) { |
| 81 | lock_count--; | 64 | lock_count--; |
| 82 | 65 | ||
| 83 | // Yield to the next thread only if we've fully released the mutex... | 66 | // Yield to the next thread only if we've fully released the mutex |
| 84 | if (lock_count == 0) { | 67 | if (lock_count == 0) { |
| 85 | holding_thread->held_mutexes.erase(this); | 68 | holding_thread->held_mutexes.erase(this); |
| 86 | ResumeWaitingThread(this); | 69 | holding_thread->UpdatePriority(); |
| 70 | holding_thread = nullptr; | ||
| 71 | WakeupAllWaitingThreads(); | ||
| 72 | Core::System::GetInstance().PrepareReschedule(); | ||
| 87 | } | 73 | } |
| 88 | } | 74 | } |
| 89 | } | 75 | } |
| 90 | 76 | ||
| 77 | void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { | ||
| 78 | WaitObject::AddWaitingThread(thread); | ||
| 79 | thread->pending_mutexes.insert(this); | ||
| 80 | UpdatePriority(); | ||
| 81 | } | ||
| 82 | |||
| 83 | void Mutex::RemoveWaitingThread(Thread* thread) { | ||
| 84 | WaitObject::RemoveWaitingThread(thread); | ||
| 85 | thread->pending_mutexes.erase(this); | ||
| 86 | UpdatePriority(); | ||
| 87 | } | ||
| 88 | |||
| 89 | void Mutex::UpdatePriority() { | ||
| 90 | if (!holding_thread) | ||
| 91 | return; | ||
| 92 | |||
| 93 | s32 best_priority = THREADPRIO_LOWEST; | ||
| 94 | for (auto& waiter : GetWaitingThreads()) { | ||
| 95 | if (waiter->current_priority < best_priority) | ||
| 96 | best_priority = waiter->current_priority; | ||
| 97 | } | ||
| 98 | |||
| 99 | if (best_priority != priority) { | ||
| 100 | priority = best_priority; | ||
| 101 | holding_thread->UpdatePriority(); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 91 | } // namespace | 105 | } // namespace |
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 53c3dc1f1..c57adf400 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h | |||
| @@ -35,17 +35,22 @@ public: | |||
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | int lock_count; ///< Number of times the mutex has been acquired | 37 | int lock_count; ///< Number of times the mutex has been acquired |
| 38 | u32 priority; ///< The priority of the mutex, used for priority inheritance. | ||
| 38 | std::string name; ///< Name of mutex (optional) | 39 | std::string name; ///< Name of mutex (optional) |
| 39 | SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex | 40 | SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex |
| 40 | 41 | ||
| 41 | bool ShouldWait() override; | ||
| 42 | void Acquire() override; | ||
| 43 | |||
| 44 | /** | 42 | /** |
| 45 | * Acquires the specified mutex for the specified thread | 43 | * Elevate the mutex priority to the best priority |
| 46 | * @param thread Thread that will acquire the mutex | 44 | * among the priorities of all its waiting threads. |
| 47 | */ | 45 | */ |
| 48 | void Acquire(SharedPtr<Thread> thread); | 46 | void UpdatePriority(); |
| 47 | |||
| 48 | bool ShouldWait(Thread* thread) const override; | ||
| 49 | void Acquire(Thread* thread) override; | ||
| 50 | |||
| 51 | void AddWaitingThread(SharedPtr<Thread> thread) override; | ||
| 52 | void RemoveWaitingThread(Thread* thread) override; | ||
| 53 | |||
| 49 | void Release(); | 54 | void Release(); |
| 50 | 55 | ||
| 51 | private: | 56 | private: |
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index bf7600780..5e6139265 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp | |||
| @@ -30,12 +30,12 @@ ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_cou | |||
| 30 | return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore)); | 30 | return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore)); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | bool Semaphore::ShouldWait() { | 33 | bool Semaphore::ShouldWait(Thread* thread) const { |
| 34 | return available_count <= 0; | 34 | return available_count <= 0; |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | void Semaphore::Acquire() { | 37 | void Semaphore::Acquire(Thread* thread) { |
| 38 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 38 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 39 | --available_count; | 39 | --available_count; |
| 40 | } | 40 | } |
| 41 | 41 | ||
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index e01908a25..cde94f7cc 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h | |||
| @@ -39,8 +39,8 @@ public: | |||
| 39 | s32 available_count; ///< Number of free slots left in the semaphore | 39 | s32 available_count; ///< Number of free slots left in the semaphore |
| 40 | std::string name; ///< Name of semaphore (optional) | 40 | std::string name; ///< Name of semaphore (optional) |
| 41 | 41 | ||
| 42 | bool ShouldWait() override; | 42 | bool ShouldWait(Thread* thread) const override; |
| 43 | void Acquire() override; | 43 | void Acquire(Thread* thread) override; |
| 44 | 44 | ||
| 45 | /** | 45 | /** |
| 46 | * Releases a certain number of slots from a semaphore. | 46 | * Releases a certain number of slots from a semaphore. |
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 6c19aa7c0..fd3bbbcad 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp | |||
| @@ -14,13 +14,13 @@ namespace Kernel { | |||
| 14 | ServerPort::ServerPort() {} | 14 | ServerPort::ServerPort() {} |
| 15 | ServerPort::~ServerPort() {} | 15 | ServerPort::~ServerPort() {} |
| 16 | 16 | ||
| 17 | bool ServerPort::ShouldWait() { | 17 | bool ServerPort::ShouldWait(Thread* thread) const { |
| 18 | // If there are no pending sessions, we wait until a new one is added. | 18 | // If there are no pending sessions, we wait until a new one is added. |
| 19 | return pending_sessions.size() == 0; | 19 | return pending_sessions.size() == 0; |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | void ServerPort::Acquire() { | 22 | void ServerPort::Acquire(Thread* thread) { |
| 23 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 23 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair( | 26 | std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair( |
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index b0f8df62c..6f8bdb6a9 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h | |||
| @@ -53,8 +53,8 @@ public: | |||
| 53 | /// ServerSessions created from this port inherit a reference to this handler. | 53 | /// ServerSessions created from this port inherit a reference to this handler. |
| 54 | std::shared_ptr<Service::SessionRequestHandler> hle_handler; | 54 | std::shared_ptr<Service::SessionRequestHandler> hle_handler; |
| 55 | 55 | ||
| 56 | bool ShouldWait() override; | 56 | bool ShouldWait(Thread* thread) const override; |
| 57 | void Acquire() override; | 57 | void Acquire(Thread* thread) override; |
| 58 | 58 | ||
| 59 | private: | 59 | private: |
| 60 | ServerPort(); | 60 | ServerPort(); |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 146458c1c..9447ff236 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -29,12 +29,12 @@ ResultVal<SharedPtr<ServerSession>> ServerSession::Create( | |||
| 29 | return MakeResult<SharedPtr<ServerSession>>(std::move(server_session)); | 29 | return MakeResult<SharedPtr<ServerSession>>(std::move(server_session)); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | bool ServerSession::ShouldWait() { | 32 | bool ServerSession::ShouldWait(Thread* thread) const { |
| 33 | return !signaled; | 33 | return !signaled; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | void ServerSession::Acquire() { | 36 | void ServerSession::Acquire(Thread* thread) { |
| 37 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 37 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 38 | signaled = false; | 38 | signaled = false; |
| 39 | } | 39 | } |
| 40 | 40 | ||
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 458284a5d..c088b9a19 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h | |||
| @@ -57,9 +57,9 @@ public: | |||
| 57 | */ | 57 | */ |
| 58 | ResultCode HandleSyncRequest(); | 58 | ResultCode HandleSyncRequest(); |
| 59 | 59 | ||
| 60 | bool ShouldWait() override; | 60 | bool ShouldWait(Thread* thread) const override; |
| 61 | 61 | ||
| 62 | void Acquire() override; | 62 | void Acquire(Thread* thread) override; |
| 63 | 63 | ||
| 64 | std::string name; ///< The name of this session (optional) | 64 | std::string name; ///< The name of this session (optional) |
| 65 | bool signaled; ///< Whether there's new data available to this ServerSession | 65 | bool signaled; ///< Whether there's new data available to this ServerSession |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 5fb95dada..9109bd10b 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -27,12 +27,12 @@ namespace Kernel { | |||
| 27 | /// Event type for the thread wake up event | 27 | /// Event type for the thread wake up event |
| 28 | static int ThreadWakeupEventType; | 28 | static int ThreadWakeupEventType; |
| 29 | 29 | ||
| 30 | bool Thread::ShouldWait() { | 30 | bool Thread::ShouldWait(Thread* thread) const { |
| 31 | return status != THREADSTATUS_DEAD; | 31 | return status != THREADSTATUS_DEAD; |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | void Thread::Acquire() { | 34 | void Thread::Acquire(Thread* thread) { |
| 35 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 35 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing | 38 | // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing |
| @@ -72,7 +72,8 @@ Thread* GetCurrentThread() { | |||
| 72 | * @return True if the thread is waiting, false otherwise | 72 | * @return True if the thread is waiting, false otherwise |
| 73 | */ | 73 | */ |
| 74 | static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { | 74 | static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { |
| 75 | if (thread->status != THREADSTATUS_WAIT_SYNCH) | 75 | if (thread->status != THREADSTATUS_WAIT_SYNCH_ALL && |
| 76 | thread->status != THREADSTATUS_WAIT_SYNCH_ANY) | ||
| 76 | return false; | 77 | return false; |
| 77 | 78 | ||
| 78 | auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); | 79 | auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); |
| @@ -90,9 +91,6 @@ static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { | |||
| 90 | } | 91 | } |
| 91 | 92 | ||
| 92 | void Thread::Stop() { | 93 | void Thread::Stop() { |
| 93 | // Release all the mutexes that this thread holds | ||
| 94 | ReleaseThreadMutexes(this); | ||
| 95 | |||
| 96 | // Cancel any outstanding wakeup events for this thread | 94 | // Cancel any outstanding wakeup events for this thread |
| 97 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); | 95 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); |
| 98 | wakeup_callback_handle_table.Close(callback_handle); | 96 | wakeup_callback_handle_table.Close(callback_handle); |
| @@ -114,6 +112,9 @@ void Thread::Stop() { | |||
| 114 | } | 112 | } |
| 115 | wait_objects.clear(); | 113 | wait_objects.clear(); |
| 116 | 114 | ||
| 115 | // Release all the mutexes that this thread holds | ||
| 116 | ReleaseThreadMutexes(this); | ||
| 117 | |||
| 117 | // Mark the TLS slot in the thread's page as free. | 118 | // Mark the TLS slot in the thread's page as free. |
| 118 | u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; | 119 | u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; |
| 119 | u32 tls_slot = | 120 | u32 tls_slot = |
| @@ -199,8 +200,8 @@ static void SwitchContext(Thread* new_thread) { | |||
| 199 | 200 | ||
| 200 | // Load context of new thread | 201 | // Load context of new thread |
| 201 | if (new_thread) { | 202 | if (new_thread) { |
| 202 | DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, | 203 | ASSERT_MSG(new_thread->status == THREADSTATUS_READY, |
| 203 | "Thread must be ready to become running."); | 204 | "Thread must be ready to become running."); |
| 204 | 205 | ||
| 205 | // Cancel any outstanding wakeup events for this thread | 206 | // Cancel any outstanding wakeup events for this thread |
| 206 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); | 207 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); |
| @@ -253,7 +254,7 @@ void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wa | |||
| 253 | Thread* thread = GetCurrentThread(); | 254 | Thread* thread = GetCurrentThread(); |
| 254 | thread->wait_set_output = wait_set_output; | 255 | thread->wait_set_output = wait_set_output; |
| 255 | thread->wait_objects = std::move(wait_objects); | 256 | thread->wait_objects = std::move(wait_objects); |
| 256 | thread->status = THREADSTATUS_WAIT_SYNCH; | 257 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; |
| 257 | } | 258 | } |
| 258 | 259 | ||
| 259 | void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { | 260 | void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { |
| @@ -281,7 +282,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { | |||
| 281 | return; | 282 | return; |
| 282 | } | 283 | } |
| 283 | 284 | ||
| 284 | if (thread->status == THREADSTATUS_WAIT_SYNCH || thread->status == THREADSTATUS_WAIT_ARB) { | 285 | if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || |
| 286 | thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { | ||
| 285 | thread->wait_set_output = false; | 287 | thread->wait_set_output = false; |
| 286 | // Remove the thread from each of its waiting objects' waitlists | 288 | // Remove the thread from each of its waiting objects' waitlists |
| 287 | for (auto& object : thread->wait_objects) | 289 | for (auto& object : thread->wait_objects) |
| @@ -305,8 +307,11 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { | |||
| 305 | } | 307 | } |
| 306 | 308 | ||
| 307 | void Thread::ResumeFromWait() { | 309 | void Thread::ResumeFromWait() { |
| 310 | ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); | ||
| 311 | |||
| 308 | switch (status) { | 312 | switch (status) { |
| 309 | case THREADSTATUS_WAIT_SYNCH: | 313 | case THREADSTATUS_WAIT_SYNCH_ALL: |
| 314 | case THREADSTATUS_WAIT_SYNCH_ANY: | ||
| 310 | case THREADSTATUS_WAIT_ARB: | 315 | case THREADSTATUS_WAIT_ARB: |
| 311 | case THREADSTATUS_WAIT_SLEEP: | 316 | case THREADSTATUS_WAIT_SLEEP: |
| 312 | break; | 317 | break; |
| @@ -515,8 +520,21 @@ void Thread::SetPriority(s32 priority) { | |||
| 515 | nominal_priority = current_priority = priority; | 520 | nominal_priority = current_priority = priority; |
| 516 | } | 521 | } |
| 517 | 522 | ||
| 523 | void Thread::UpdatePriority() { | ||
| 524 | s32 best_priority = nominal_priority; | ||
| 525 | for (auto& mutex : held_mutexes) { | ||
| 526 | if (mutex->priority < best_priority) | ||
| 527 | best_priority = mutex->priority; | ||
| 528 | } | ||
| 529 | BoostPriority(best_priority); | ||
| 530 | } | ||
| 531 | |||
| 518 | void Thread::BoostPriority(s32 priority) { | 532 | void Thread::BoostPriority(s32 priority) { |
| 519 | ready_queue.move(this, current_priority, priority); | 533 | // If thread was ready, adjust queues |
| 534 | if (status == THREADSTATUS_READY) | ||
| 535 | ready_queue.move(this, current_priority, priority); | ||
| 536 | else | ||
| 537 | ready_queue.prepare(priority); | ||
| 520 | current_priority = priority; | 538 | current_priority = priority; |
| 521 | } | 539 | } |
| 522 | 540 | ||
| @@ -563,6 +581,12 @@ void Thread::SetWaitSynchronizationOutput(s32 output) { | |||
| 563 | context.cpu_registers[1] = output; | 581 | context.cpu_registers[1] = output; |
| 564 | } | 582 | } |
| 565 | 583 | ||
| 584 | s32 Thread::GetWaitObjectIndex(WaitObject* object) const { | ||
| 585 | ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); | ||
| 586 | auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); | ||
| 587 | return std::distance(match, wait_objects.rend()) - 1; | ||
| 588 | } | ||
| 589 | |||
| 566 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 590 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 567 | 591 | ||
| 568 | void ThreadingInit() { | 592 | void ThreadingInit() { |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index c77ac644d..af72b76ea 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -31,13 +31,14 @@ enum ThreadProcessorId : s32 { | |||
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | enum ThreadStatus { | 33 | enum ThreadStatus { |
| 34 | THREADSTATUS_RUNNING, ///< Currently running | 34 | THREADSTATUS_RUNNING, ///< Currently running |
| 35 | THREADSTATUS_READY, ///< Ready to run | 35 | THREADSTATUS_READY, ///< Ready to run |
| 36 | THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter | 36 | THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter |
| 37 | THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC | 37 | THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC |
| 38 | THREADSTATUS_WAIT_SYNCH, ///< Waiting due to a WaitSynchronization SVC | 38 | THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false |
| 39 | THREADSTATUS_DORMANT, ///< Created but not yet made ready | 39 | THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true |
| 40 | THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated | 40 | THREADSTATUS_DORMANT, ///< Created but not yet made ready |
| 41 | THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated | ||
| 41 | }; | 42 | }; |
| 42 | 43 | ||
| 43 | namespace Kernel { | 44 | namespace Kernel { |
| @@ -72,8 +73,8 @@ public: | |||
| 72 | return HANDLE_TYPE; | 73 | return HANDLE_TYPE; |
| 73 | } | 74 | } |
| 74 | 75 | ||
| 75 | bool ShouldWait() override; | 76 | bool ShouldWait(Thread* thread) const override; |
| 76 | void Acquire() override; | 77 | void Acquire(Thread* thread) override; |
| 77 | 78 | ||
| 78 | /** | 79 | /** |
| 79 | * Gets the thread's current priority | 80 | * Gets the thread's current priority |
| @@ -90,6 +91,12 @@ public: | |||
| 90 | void SetPriority(s32 priority); | 91 | void SetPriority(s32 priority); |
| 91 | 92 | ||
| 92 | /** | 93 | /** |
| 94 | * Boost's a thread's priority to the best priority among the thread's held mutexes. | ||
| 95 | * This prevents priority inversion via priority inheritance. | ||
| 96 | */ | ||
| 97 | void UpdatePriority(); | ||
| 98 | |||
| 99 | /** | ||
| 93 | * Temporarily boosts the thread's priority until the next time it is scheduled | 100 | * Temporarily boosts the thread's priority until the next time it is scheduled |
| 94 | * @param priority The new priority | 101 | * @param priority The new priority |
| 95 | */ | 102 | */ |
| @@ -128,13 +135,14 @@ public: | |||
| 128 | 135 | ||
| 129 | /** | 136 | /** |
| 130 | * Retrieves the index that this particular object occupies in the list of objects | 137 | * Retrieves the index that this particular object occupies in the list of objects |
| 131 | * that the thread passed to WaitSynchronizationN. | 138 | * that the thread passed to WaitSynchronizationN, starting the search from the last element. |
| 132 | * It is used to set the output value of WaitSynchronizationN when the thread is awakened. | 139 | * It is used to set the output value of WaitSynchronizationN when the thread is awakened. |
| 140 | * When a thread wakes up due to an object signal, the kernel will use the index of the last | ||
| 141 | * matching object in the wait objects list in case of having multiple instances of the same | ||
| 142 | * object in the list. | ||
| 133 | * @param object Object to query the index of. | 143 | * @param object Object to query the index of. |
| 134 | */ | 144 | */ |
| 135 | s32 GetWaitObjectIndex(const WaitObject* object) const { | 145 | s32 GetWaitObjectIndex(WaitObject* object) const; |
| 136 | return wait_objects_index.at(object->GetObjectId()); | ||
| 137 | } | ||
| 138 | 146 | ||
| 139 | /** | 147 | /** |
| 140 | * Stops a thread, invalidating it from further use | 148 | * Stops a thread, invalidating it from further use |
| @@ -152,10 +160,10 @@ public: | |||
| 152 | /** | 160 | /** |
| 153 | * Returns whether this thread is waiting for all the objects in | 161 | * Returns whether this thread is waiting for all the objects in |
| 154 | * its wait list to become ready, as a result of a WaitSynchronizationN call | 162 | * its wait list to become ready, as a result of a WaitSynchronizationN call |
| 155 | * with wait_all = true, or a ReplyAndReceive call. | 163 | * with wait_all = true. |
| 156 | */ | 164 | */ |
| 157 | bool IsSleepingOnWaitAll() const { | 165 | bool IsSleepingOnWaitAll() const { |
| 158 | return !wait_objects.empty(); | 166 | return status == THREADSTATUS_WAIT_SYNCH_ALL; |
| 159 | } | 167 | } |
| 160 | 168 | ||
| 161 | ARM_Interface::ThreadContext context; | 169 | ARM_Interface::ThreadContext context; |
| @@ -178,15 +186,15 @@ public: | |||
| 178 | /// Mutexes currently held by this thread, which will be released when it exits. | 186 | /// Mutexes currently held by this thread, which will be released when it exits. |
| 179 | boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; | 187 | boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; |
| 180 | 188 | ||
| 189 | /// Mutexes that this thread is currently waiting for. | ||
| 190 | boost::container::flat_set<SharedPtr<Mutex>> pending_mutexes; | ||
| 191 | |||
| 181 | SharedPtr<Process> owner_process; ///< Process that owns this thread | 192 | SharedPtr<Process> owner_process; ///< Process that owns this thread |
| 182 | 193 | ||
| 183 | /// Objects that the thread is waiting on. | 194 | /// Objects that the thread is waiting on, in the same order as they were |
| 184 | /// This is only populated when the thread should wait for all the objects to become ready. | 195 | // passed to WaitSynchronization1/N. |
| 185 | std::vector<SharedPtr<WaitObject>> wait_objects; | 196 | std::vector<SharedPtr<WaitObject>> wait_objects; |
| 186 | 197 | ||
| 187 | /// Mapping of Object ids to their position in the last waitlist that this object waited on. | ||
| 188 | boost::container::flat_map<int, s32> wait_objects_index; | ||
| 189 | |||
| 190 | VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address | 198 | VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address |
| 191 | 199 | ||
| 192 | /// True if the WaitSynchronizationN output parameter should be set on thread wakeup. | 200 | /// True if the WaitSynchronizationN output parameter should be set on thread wakeup. |
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index b50cf520d..8f2bc4c7f 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp | |||
| @@ -39,12 +39,12 @@ SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) { | |||
| 39 | return timer; | 39 | return timer; |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | bool Timer::ShouldWait() { | 42 | bool Timer::ShouldWait(Thread* thread) const { |
| 43 | return !signaled; | 43 | return !signaled; |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | void Timer::Acquire() { | 46 | void Timer::Acquire(Thread* thread) { |
| 47 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 47 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 48 | 48 | ||
| 49 | if (reset_type == ResetType::OneShot) | 49 | if (reset_type == ResetType::OneShot) |
| 50 | signaled = false; | 50 | signaled = false; |
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index 18ea0236b..2e3b31b23 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h | |||
| @@ -39,8 +39,8 @@ public: | |||
| 39 | u64 initial_delay; ///< The delay until the timer fires for the first time | 39 | u64 initial_delay; ///< The delay until the timer fires for the first time |
| 40 | u64 interval_delay; ///< The delay until the timer fires after the first time | 40 | u64 interval_delay; ///< The delay until the timer fires after the first time |
| 41 | 41 | ||
| 42 | bool ShouldWait() override; | 42 | bool ShouldWait(Thread* thread) const override; |
| 43 | void Acquire() override; | 43 | void Acquire(Thread* thread) override; |
| 44 | 44 | ||
| 45 | /** | 45 | /** |
| 46 | * Starts the timer, with the specified initial delay and interval. | 46 | * Starts the timer, with the specified initial delay and interval. |