diff options
| author | 2018-04-20 20:15:16 -0500 | |
|---|---|---|
| committer | 2018-04-23 11:23:44 -0500 | |
| commit | 46572d027dc9620ed2b2a50277e6afd2a115ab81 (patch) | |
| tree | 72562a37575252e8f4c0160a3067b415027fdf4b /src/core/hle/kernel/mutex.cpp | |
| parent | Kernel: Use 0x2C as default main thread priority for homebrew and lone NRO/NSOs (diff) | |
| download | yuzu-46572d027dc9620ed2b2a50277e6afd2a115ab81.tar.gz yuzu-46572d027dc9620ed2b2a50277e6afd2a115ab81.tar.xz yuzu-46572d027dc9620ed2b2a50277e6afd2a115ab81.zip | |
Kernel: Implemented mutex priority inheritance.
Verified with a hwtest and implemented based on reverse engineering.
Thread A's priority will get bumped to the highest priority among all the threads that are waiting for a mutex that A holds.
Once A releases the mutex and ownership is transferred to B, A's priority will return to normal and B's priority will be bumped.
Diffstat (limited to 'src/core/hle/kernel/mutex.cpp')
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 39 |
1 files changed, 31 insertions, 8 deletions
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 5cc0bd266..63733ad79 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -18,13 +18,13 @@ namespace Kernel { | |||
| 18 | 18 | ||
| 19 | /// Returns the number of threads that are waiting for a mutex, and the highest priority one among | 19 | /// Returns the number of threads that are waiting for a mutex, and the highest priority one among |
| 20 | /// those. | 20 | /// those. |
| 21 | static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(VAddr mutex_addr) { | 21 | static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread( |
| 22 | auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList(); | 22 | SharedPtr<Thread> current_thread, VAddr mutex_addr) { |
| 23 | 23 | ||
| 24 | SharedPtr<Thread> highest_priority_thread; | 24 | SharedPtr<Thread> highest_priority_thread; |
| 25 | u32 num_waiters = 0; | 25 | u32 num_waiters = 0; |
| 26 | 26 | ||
| 27 | for (auto& thread : thread_list) { | 27 | for (auto& thread : current_thread->wait_mutex_threads) { |
| 28 | if (thread->mutex_wait_address != mutex_addr) | 28 | if (thread->mutex_wait_address != mutex_addr) |
| 29 | continue; | 29 | continue; |
| 30 | 30 | ||
| @@ -40,6 +40,21 @@ static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(VA | |||
| 40 | return {highest_priority_thread, num_waiters}; | 40 | return {highest_priority_thread, num_waiters}; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. | ||
| 44 | static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread, | ||
| 45 | SharedPtr<Thread> new_owner) { | ||
| 46 | auto threads = current_thread->wait_mutex_threads; | ||
| 47 | for (auto& thread : threads) { | ||
| 48 | if (thread->mutex_wait_address != mutex_addr) | ||
| 49 | continue; | ||
| 50 | |||
| 51 | ASSERT(thread->lock_owner == current_thread); | ||
| 52 | current_thread->RemoveMutexWaiter(thread); | ||
| 53 | if (new_owner != thread) | ||
| 54 | new_owner->AddMutexWaiter(thread); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 43 | ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, | 58 | ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, |
| 44 | Handle requesting_thread_handle) { | 59 | Handle requesting_thread_handle) { |
| 45 | // The mutex address must be 4-byte aligned | 60 | // The mutex address must be 4-byte aligned |
| @@ -65,11 +80,14 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, | |||
| 65 | return ERR_INVALID_HANDLE; | 80 | return ERR_INVALID_HANDLE; |
| 66 | 81 | ||
| 67 | // Wait until the mutex is released | 82 | // Wait until the mutex is released |
| 68 | requesting_thread->mutex_wait_address = address; | 83 | GetCurrentThread()->mutex_wait_address = address; |
| 69 | requesting_thread->wait_handle = requesting_thread_handle; | 84 | GetCurrentThread()->wait_handle = requesting_thread_handle; |
| 70 | 85 | ||
| 71 | requesting_thread->status = THREADSTATUS_WAIT_MUTEX; | 86 | GetCurrentThread()->status = THREADSTATUS_WAIT_MUTEX; |
| 72 | requesting_thread->wakeup_callback = nullptr; | 87 | GetCurrentThread()->wakeup_callback = nullptr; |
| 88 | |||
| 89 | // Update the lock holder thread's priority to prevent priority inversion. | ||
| 90 | holding_thread->AddMutexWaiter(GetCurrentThread()); | ||
| 73 | 91 | ||
| 74 | Core::System::GetInstance().PrepareReschedule(); | 92 | Core::System::GetInstance().PrepareReschedule(); |
| 75 | 93 | ||
| @@ -82,14 +100,18 @@ ResultCode Mutex::Release(VAddr address) { | |||
| 82 | return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress); | 100 | return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress); |
| 83 | } | 101 | } |
| 84 | 102 | ||
| 85 | auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(address); | 103 | auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); |
| 86 | 104 | ||
| 87 | // There are no more threads waiting for the mutex, release it completely. | 105 | // There are no more threads waiting for the mutex, release it completely. |
| 88 | if (thread == nullptr) { | 106 | if (thread == nullptr) { |
| 107 | ASSERT(GetCurrentThread()->wait_mutex_threads.empty()); | ||
| 89 | Memory::Write32(address, 0); | 108 | Memory::Write32(address, 0); |
| 90 | return RESULT_SUCCESS; | 109 | return RESULT_SUCCESS; |
| 91 | } | 110 | } |
| 92 | 111 | ||
| 112 | // Transfer the ownership of the mutex from the previous owner to the new one. | ||
| 113 | TransferMutexOwnership(address, GetCurrentThread(), thread); | ||
| 114 | |||
| 93 | u32 mutex_value = thread->wait_handle; | 115 | u32 mutex_value = thread->wait_handle; |
| 94 | 116 | ||
| 95 | if (num_waiters >= 2) { | 117 | if (num_waiters >= 2) { |
| @@ -103,6 +125,7 @@ ResultCode Mutex::Release(VAddr address) { | |||
| 103 | ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX); | 125 | ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX); |
| 104 | thread->ResumeFromWait(); | 126 | thread->ResumeFromWait(); |
| 105 | 127 | ||
| 128 | thread->lock_owner = nullptr; | ||
| 106 | thread->condvar_wait_address = 0; | 129 | thread->condvar_wait_address = 0; |
| 107 | thread->mutex_wait_address = 0; | 130 | thread->mutex_wait_address = 0; |
| 108 | thread->wait_handle = 0; | 131 | thread->wait_handle = 0; |