diff options
| author | 2017-01-05 12:55:01 -0500 | |
|---|---|---|
| committer | 2017-01-05 12:55:01 -0500 | |
| commit | f20d872643654c574f73a263f032613046900f07 (patch) | |
| tree | 021284c18034d053c81928fa19d2efb6658451fb /src/core | |
| parent | Merge pull request #2407 from jroweboy/nightly-deploy (diff) | |
| parent | Kernel: Add some asserts to enforce the invariants in the scheduler. (diff) | |
| download | yuzu-f20d872643654c574f73a263f032613046900f07.tar.gz yuzu-f20d872643654c574f73a263f032613046900f07.tar.xz yuzu-f20d872643654c574f73a263f032613046900f07.zip | |
Merge pull request #2393 from Subv/synch
Kernel: Mutex priority inheritance and synchronization improvements.
Diffstat (limited to 'src/core')
| -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 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 69 |
17 files changed, 221 insertions, 159 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. |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 2ca270de3..855f3af82 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -248,6 +248,8 @@ static ResultCode SendSyncRequest(Kernel::Handle handle) { | |||
| 248 | 248 | ||
| 249 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); | 249 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); |
| 250 | 250 | ||
| 251 | Core::System::GetInstance().PrepareReschedule(); | ||
| 252 | |||
| 251 | // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server | 253 | // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server |
| 252 | // responds and cause a reschedule. | 254 | // responds and cause a reschedule. |
| 253 | return session->SendSyncRequest(); | 255 | return session->SendSyncRequest(); |
| @@ -270,27 +272,27 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) | |||
| 270 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, | 272 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, |
| 271 | object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); | 273 | object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); |
| 272 | 274 | ||
| 273 | if (object->ShouldWait()) { | 275 | if (object->ShouldWait(thread)) { |
| 274 | 276 | ||
| 275 | if (nano_seconds == 0) | 277 | if (nano_seconds == 0) |
| 276 | return ERR_SYNC_TIMEOUT; | 278 | return ERR_SYNC_TIMEOUT; |
| 277 | 279 | ||
| 280 | thread->wait_objects = {object}; | ||
| 278 | object->AddWaitingThread(thread); | 281 | object->AddWaitingThread(thread); |
| 279 | // TODO(Subv): Perform things like update the mutex lock owner's priority to | 282 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; |
| 280 | // prevent priority inversion. Currently this is done in Mutex::ShouldWait, | ||
| 281 | // but it should be moved to a function that is called from here. | ||
| 282 | thread->status = THREADSTATUS_WAIT_SYNCH; | ||
| 283 | 283 | ||
| 284 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 284 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 285 | thread->WakeAfterDelay(nano_seconds); | 285 | thread->WakeAfterDelay(nano_seconds); |
| 286 | 286 | ||
| 287 | Core::System::GetInstance().PrepareReschedule(); | ||
| 288 | |||
| 287 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread | 289 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread |
| 288 | // resumes due to a signal in its wait objects. | 290 | // resumes due to a signal in its wait objects. |
| 289 | // Otherwise we retain the default value of timeout. | 291 | // Otherwise we retain the default value of timeout. |
| 290 | return ERR_SYNC_TIMEOUT; | 292 | return ERR_SYNC_TIMEOUT; |
| 291 | } | 293 | } |
| 292 | 294 | ||
| 293 | object->Acquire(); | 295 | object->Acquire(thread); |
| 294 | 296 | ||
| 295 | return RESULT_SUCCESS; | 297 | return RESULT_SUCCESS; |
| 296 | } | 298 | } |
| @@ -324,19 +326,14 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 324 | objects[i] = object; | 326 | objects[i] = object; |
| 325 | } | 327 | } |
| 326 | 328 | ||
| 327 | // Clear the mapping of wait object indices. | ||
| 328 | // We don't want any lingering state in this map. | ||
| 329 | // It will be repopulated later in the wait_all = false case. | ||
| 330 | thread->wait_objects_index.clear(); | ||
| 331 | |||
| 332 | if (wait_all) { | 329 | if (wait_all) { |
| 333 | bool all_available = | 330 | bool all_available = |
| 334 | std::all_of(objects.begin(), objects.end(), | 331 | std::all_of(objects.begin(), objects.end(), |
| 335 | [](const ObjectPtr& object) { return !object->ShouldWait(); }); | 332 | [thread](const ObjectPtr& object) { return !object->ShouldWait(thread); }); |
| 336 | if (all_available) { | 333 | if (all_available) { |
| 337 | // We can acquire all objects right now, do so. | 334 | // We can acquire all objects right now, do so. |
| 338 | for (auto& object : objects) | 335 | for (auto& object : objects) |
| 339 | object->Acquire(); | 336 | object->Acquire(thread); |
| 340 | // Note: In this case, the `out` parameter is not set, | 337 | // Note: In this case, the `out` parameter is not set, |
| 341 | // and retains whatever value it had before. | 338 | // and retains whatever value it had before. |
| 342 | return RESULT_SUCCESS; | 339 | return RESULT_SUCCESS; |
| @@ -350,22 +347,20 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 350 | return ERR_SYNC_TIMEOUT; | 347 | return ERR_SYNC_TIMEOUT; |
| 351 | 348 | ||
| 352 | // Put the thread to sleep | 349 | // Put the thread to sleep |
| 353 | thread->status = THREADSTATUS_WAIT_SYNCH; | 350 | thread->status = THREADSTATUS_WAIT_SYNCH_ALL; |
| 354 | 351 | ||
| 355 | // Add the thread to each of the objects' waiting threads. | 352 | // Add the thread to each of the objects' waiting threads. |
| 356 | for (auto& object : objects) { | 353 | for (auto& object : objects) { |
| 357 | object->AddWaitingThread(thread); | 354 | object->AddWaitingThread(thread); |
| 358 | // TODO(Subv): Perform things like update the mutex lock owner's priority to | ||
| 359 | // prevent priority inversion. Currently this is done in Mutex::ShouldWait, | ||
| 360 | // but it should be moved to a function that is called from here. | ||
| 361 | } | 355 | } |
| 362 | 356 | ||
| 363 | // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN | ||
| 364 | thread->wait_objects = std::move(objects); | 357 | thread->wait_objects = std::move(objects); |
| 365 | 358 | ||
| 366 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 359 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 367 | thread->WakeAfterDelay(nano_seconds); | 360 | thread->WakeAfterDelay(nano_seconds); |
| 368 | 361 | ||
| 362 | Core::System::GetInstance().PrepareReschedule(); | ||
| 363 | |||
| 369 | // This value gets set to -1 by default in this case, it is not modified after this. | 364 | // This value gets set to -1 by default in this case, it is not modified after this. |
| 370 | *out = -1; | 365 | *out = -1; |
| 371 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to | 366 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to |
| @@ -373,13 +368,14 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 373 | return ERR_SYNC_TIMEOUT; | 368 | return ERR_SYNC_TIMEOUT; |
| 374 | } else { | 369 | } else { |
| 375 | // Find the first object that is acquirable in the provided list of objects | 370 | // Find the first object that is acquirable in the provided list of objects |
| 376 | auto itr = std::find_if(objects.begin(), objects.end(), | 371 | auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { |
| 377 | [](const ObjectPtr& object) { return !object->ShouldWait(); }); | 372 | return !object->ShouldWait(thread); |
| 373 | }); | ||
| 378 | 374 | ||
| 379 | if (itr != objects.end()) { | 375 | if (itr != objects.end()) { |
| 380 | // We found a ready object, acquire it and set the result value | 376 | // We found a ready object, acquire it and set the result value |
| 381 | Kernel::WaitObject* object = itr->get(); | 377 | Kernel::WaitObject* object = itr->get(); |
| 382 | object->Acquire(); | 378 | object->Acquire(thread); |
| 383 | *out = std::distance(objects.begin(), itr); | 379 | *out = std::distance(objects.begin(), itr); |
| 384 | return RESULT_SUCCESS; | 380 | return RESULT_SUCCESS; |
| 385 | } | 381 | } |
| @@ -392,28 +388,24 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 392 | return ERR_SYNC_TIMEOUT; | 388 | return ERR_SYNC_TIMEOUT; |
| 393 | 389 | ||
| 394 | // Put the thread to sleep | 390 | // Put the thread to sleep |
| 395 | thread->status = THREADSTATUS_WAIT_SYNCH; | 391 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; |
| 396 | |||
| 397 | // Clear the thread's waitlist, we won't use it for wait_all = false | ||
| 398 | thread->wait_objects.clear(); | ||
| 399 | 392 | ||
| 400 | // Add the thread to each of the objects' waiting threads. | 393 | // Add the thread to each of the objects' waiting threads. |
| 401 | for (size_t i = 0; i < objects.size(); ++i) { | 394 | for (size_t i = 0; i < objects.size(); ++i) { |
| 402 | Kernel::WaitObject* object = objects[i].get(); | 395 | Kernel::WaitObject* object = objects[i].get(); |
| 403 | // Set the index of this object in the mapping of Objects -> index for this thread. | ||
| 404 | thread->wait_objects_index[object->GetObjectId()] = static_cast<int>(i); | ||
| 405 | object->AddWaitingThread(thread); | 396 | object->AddWaitingThread(thread); |
| 406 | // TODO(Subv): Perform things like update the mutex lock owner's priority to | ||
| 407 | // prevent priority inversion. Currently this is done in Mutex::ShouldWait, | ||
| 408 | // but it should be moved to a function that is called from here. | ||
| 409 | } | 397 | } |
| 410 | 398 | ||
| 399 | thread->wait_objects = std::move(objects); | ||
| 400 | |||
| 411 | // Note: If no handles and no timeout were given, then the thread will deadlock, this is | 401 | // Note: If no handles and no timeout were given, then the thread will deadlock, this is |
| 412 | // consistent with hardware behavior. | 402 | // consistent with hardware behavior. |
| 413 | 403 | ||
| 414 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 404 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 415 | thread->WakeAfterDelay(nano_seconds); | 405 | thread->WakeAfterDelay(nano_seconds); |
| 416 | 406 | ||
| 407 | Core::System::GetInstance().PrepareReschedule(); | ||
| 408 | |||
| 417 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a | 409 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a |
| 418 | // signal in one of its wait objects. | 410 | // signal in one of its wait objects. |
| 419 | // Otherwise we retain the default value of timeout, and -1 in the out parameter | 411 | // Otherwise we retain the default value of timeout, and -1 in the out parameter |
| @@ -448,6 +440,9 @@ static ResultCode ArbitrateAddress(Kernel::Handle handle, u32 address, u32 type, | |||
| 448 | auto res = arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type), address, value, | 440 | auto res = arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type), address, value, |
| 449 | nanoseconds); | 441 | nanoseconds); |
| 450 | 442 | ||
| 443 | // TODO(Subv): Identify in which specific cases this call should cause a reschedule. | ||
| 444 | Core::System::GetInstance().PrepareReschedule(); | ||
| 445 | |||
| 451 | return res; | 446 | return res; |
| 452 | } | 447 | } |
| 453 | 448 | ||
| @@ -574,6 +569,8 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, s32 priority, u32 ent | |||
| 574 | 569 | ||
| 575 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); | 570 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); |
| 576 | 571 | ||
| 572 | Core::System::GetInstance().PrepareReschedule(); | ||
| 573 | |||
| 577 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " | 574 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " |
| 578 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", | 575 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", |
| 579 | entry_point, name.c_str(), arg, stack_top, priority, processor_id, *out_handle); | 576 | entry_point, name.c_str(), arg, stack_top, priority, processor_id, *out_handle); |
| @@ -586,6 +583,7 @@ static void ExitThread() { | |||
| 586 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::CPU().GetPC()); | 583 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::CPU().GetPC()); |
| 587 | 584 | ||
| 588 | Kernel::ExitCurrentThread(); | 585 | Kernel::ExitCurrentThread(); |
| 586 | Core::System::GetInstance().PrepareReschedule(); | ||
| 589 | } | 587 | } |
| 590 | 588 | ||
| 591 | /// Gets the priority for the specified thread | 589 | /// Gets the priority for the specified thread |
| @@ -605,6 +603,13 @@ static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { | |||
| 605 | return ERR_INVALID_HANDLE; | 603 | return ERR_INVALID_HANDLE; |
| 606 | 604 | ||
| 607 | thread->SetPriority(priority); | 605 | thread->SetPriority(priority); |
| 606 | thread->UpdatePriority(); | ||
| 607 | |||
| 608 | // Update the mutexes that this thread is waiting for | ||
| 609 | for (auto& mutex : thread->pending_mutexes) | ||
| 610 | mutex->UpdatePriority(); | ||
| 611 | |||
| 612 | Core::System::GetInstance().PrepareReschedule(); | ||
| 608 | return RESULT_SUCCESS; | 613 | return RESULT_SUCCESS; |
| 609 | } | 614 | } |
| 610 | 615 | ||
| @@ -849,6 +854,8 @@ static void SleepThread(s64 nanoseconds) { | |||
| 849 | 854 | ||
| 850 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 855 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 851 | Kernel::GetCurrentThread()->WakeAfterDelay(nanoseconds); | 856 | Kernel::GetCurrentThread()->WakeAfterDelay(nanoseconds); |
| 857 | |||
| 858 | Core::System::GetInstance().PrepareReschedule(); | ||
| 852 | } | 859 | } |
| 853 | 860 | ||
| 854 | /// This returns the total CPU ticks elapsed since the CPU was powered-on | 861 | /// This returns the total CPU ticks elapsed since the CPU was powered-on |
| @@ -1184,8 +1191,6 @@ void CallSVC(u32 immediate) { | |||
| 1184 | if (info) { | 1191 | if (info) { |
| 1185 | if (info->func) { | 1192 | if (info->func) { |
| 1186 | info->func(); | 1193 | info->func(); |
| 1187 | // TODO(Subv): Not all service functions should cause a reschedule in all cases. | ||
| 1188 | Core::System::GetInstance().PrepareReschedule(); | ||
| 1189 | } else { | 1194 | } else { |
| 1190 | LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); | 1195 | LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); |
| 1191 | } | 1196 | } |