diff options
Diffstat (limited to 'src/core/hle/kernel')
| -rw-r--r-- | src/core/hle/kernel/address_arbiter.cpp | 313 | ||||
| -rw-r--r-- | src/core/hle/kernel/address_arbiter.h | 44 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 4 |
3 files changed, 181 insertions, 180 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 01c5bf61b..972911e42 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -14,160 +14,161 @@ | |||
| 14 | #include "core/memory.h" | 14 | #include "core/memory.h" |
| 15 | 15 | ||
| 16 | namespace Kernel { | 16 | namespace Kernel { |
| 17 | namespace AddressArbiter { | 17 | namespace AddressArbiter { |
| 18 | 18 | ||
| 19 | // Performs actual address waiting logic. | 19 | // Performs actual address waiting logic. |
| 20 | ResultCode WaitForAddress(VAddr address, s64 timeout) { | 20 | ResultCode WaitForAddress(VAddr address, s64 timeout) { |
| 21 | SharedPtr<Thread> current_thread = GetCurrentThread(); | 21 | SharedPtr<Thread> current_thread = GetCurrentThread(); |
| 22 | current_thread->arb_wait_address = address; | 22 | current_thread->arb_wait_address = address; |
| 23 | current_thread->status = THREADSTATUS_WAIT_ARB; | 23 | current_thread->status = THREADSTATUS_WAIT_ARB; |
| 24 | current_thread->wakeup_callback = nullptr; | 24 | current_thread->wakeup_callback = nullptr; |
| 25 | 25 | ||
| 26 | current_thread->WakeAfterDelay(timeout); | 26 | current_thread->WakeAfterDelay(timeout); |
| 27 | 27 | ||
| 28 | Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); | 28 | Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); |
| 29 | // This should never actually execute. | 29 | // This should never actually execute. |
| 30 | return RESULT_SUCCESS; | 30 | return RESULT_SUCCESS; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | // Gets the threads waiting on an address. | 33 | // Gets the threads waiting on an address. |
| 34 | void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>> &waiting_threads, VAddr address) { | 34 | void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads, VAddr address) { |
| 35 | auto RetrieveWaitingThreads = | 35 | auto RetrieveWaitingThreads = |
| 36 | [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { | 36 | [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { |
| 37 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); | 37 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); |
| 38 | auto& thread_list = scheduler->GetThreadList(); | 38 | auto& thread_list = scheduler->GetThreadList(); |
| 39 | 39 | ||
| 40 | for (auto& thread : thread_list) { | 40 | for (auto& thread : thread_list) { |
| 41 | if (thread->arb_wait_address == arb_addr) | 41 | if (thread->arb_wait_address == arb_addr) |
| 42 | waiting_threads.push_back(thread); | 42 | waiting_threads.push_back(thread); |
| 43 | } | ||
| 44 | }; | ||
| 45 | |||
| 46 | // Retrieve a list of all threads that are waiting for this address. | ||
| 47 | RetrieveWaitingThreads(0, waiting_threads, address); | ||
| 48 | RetrieveWaitingThreads(1, waiting_threads, address); | ||
| 49 | RetrieveWaitingThreads(2, waiting_threads, address); | ||
| 50 | RetrieveWaitingThreads(3, waiting_threads, address); | ||
| 51 | // Sort them by priority, such that the highest priority ones come first. | ||
| 52 | std::sort(waiting_threads.begin(), waiting_threads.end(), | ||
| 53 | [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { | ||
| 54 | return lhs->current_priority < rhs->current_priority; | ||
| 55 | }); | ||
| 56 | } | ||
| 57 | |||
| 58 | // Wake up num_to_wake (or all) threads in a vector. | ||
| 59 | void WakeThreads(std::vector<SharedPtr<Thread>> &waiting_threads, s32 num_to_wake) { | ||
| 60 | // Only process up to 'target' threads, unless 'target' is <= 0, in which case process | ||
| 61 | // them all. | ||
| 62 | size_t last = waiting_threads.size(); | ||
| 63 | if (num_to_wake > 0) | ||
| 64 | last = num_to_wake; | ||
| 65 | |||
| 66 | // Signal the waiting threads. | ||
| 67 | // TODO: Rescheduling should not occur while waking threads. How can it be prevented? | ||
| 68 | for (size_t i = 0; i < last; i++) { | ||
| 69 | ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB); | ||
| 70 | waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 71 | waiting_threads[i]->arb_wait_address = 0; | ||
| 72 | waiting_threads[i]->ResumeFromWait(); | ||
| 73 | } | 43 | } |
| 74 | 44 | }; | |
| 75 | } | 45 | |
| 76 | 46 | // Retrieve a list of all threads that are waiting for this address. | |
| 77 | // Signals an address being waited on. | 47 | RetrieveWaitingThreads(0, waiting_threads, address); |
| 78 | ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { | 48 | RetrieveWaitingThreads(1, waiting_threads, address); |
| 79 | // Get threads waiting on the address. | 49 | RetrieveWaitingThreads(2, waiting_threads, address); |
| 80 | std::vector<SharedPtr<Thread>> waiting_threads; | 50 | RetrieveWaitingThreads(3, waiting_threads, address); |
| 81 | GetThreadsWaitingOnAddress(waiting_threads, address); | 51 | // Sort them by priority, such that the highest priority ones come first. |
| 82 | 52 | std::sort(waiting_threads.begin(), waiting_threads.end(), | |
| 83 | WakeThreads(waiting_threads, num_to_wake); | 53 | [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { |
| 84 | return RESULT_SUCCESS; | 54 | return lhs->current_priority < rhs->current_priority; |
| 85 | } | 55 | }); |
| 86 | 56 | } | |
| 87 | // Signals an address being waited on and increments its value if equal to the value argument. | 57 | |
| 88 | ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { | 58 | // Wake up num_to_wake (or all) threads in a vector. |
| 89 | // Ensure that we can write to the address. | 59 | void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) { |
| 90 | if (!Memory::IsValidVirtualAddress(address)) { | 60 | // Only process up to 'target' threads, unless 'target' is <= 0, in which case process |
| 91 | return ERR_INVALID_ADDRESS_STATE; | 61 | // them all. |
| 92 | } | 62 | size_t last = waiting_threads.size(); |
| 93 | 63 | if (num_to_wake > 0) | |
| 94 | if ((s32)Memory::Read32(address) == value) { | 64 | last = num_to_wake; |
| 95 | Memory::Write32(address, (u32)(value + 1)); | 65 | |
| 96 | } else { | 66 | // Signal the waiting threads. |
| 97 | return ERR_INVALID_STATE; | 67 | // TODO: Rescheduling should not occur while waking threads. How can it be prevented? |
| 98 | } | 68 | for (size_t i = 0; i < last; i++) { |
| 99 | 69 | ASSERT(waiting_threads[i]->status = THREADSTATUS_WAIT_ARB); | |
| 100 | return SignalToAddress(address, num_to_wake); | 70 | waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); |
| 101 | } | 71 | waiting_threads[i]->arb_wait_address = 0; |
| 102 | 72 | waiting_threads[i]->ResumeFromWait(); | |
| 103 | // Signals an address being waited on and modifies its value based on waiting thread count if equal to the value argument. | 73 | } |
| 104 | ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { | 74 | } |
| 105 | // Ensure that we can write to the address. | 75 | |
| 106 | if (!Memory::IsValidVirtualAddress(address)) { | 76 | // Signals an address being waited on. |
| 107 | return ERR_INVALID_ADDRESS_STATE; | 77 | ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { |
| 108 | } | 78 | // Get threads waiting on the address. |
| 109 | 79 | std::vector<SharedPtr<Thread>> waiting_threads; | |
| 110 | // Get threads waiting on the address. | 80 | GetThreadsWaitingOnAddress(waiting_threads, address); |
| 111 | std::vector<SharedPtr<Thread>> waiting_threads; | 81 | |
| 112 | GetThreadsWaitingOnAddress(waiting_threads, address); | 82 | WakeThreads(waiting_threads, num_to_wake); |
| 113 | 83 | return RESULT_SUCCESS; | |
| 114 | // Determine the modified value depending on the waiting count. | 84 | } |
| 115 | s32 updated_value; | 85 | |
| 116 | if (waiting_threads.size() == 0) { | 86 | // Signals an address being waited on and increments its value if equal to the value argument. |
| 117 | updated_value = value - 1; | 87 | ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { |
| 118 | } else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) { | 88 | // Ensure that we can write to the address. |
| 119 | updated_value = value + 1; | 89 | if (!Memory::IsValidVirtualAddress(address)) { |
| 120 | } else { | 90 | return ERR_INVALID_ADDRESS_STATE; |
| 121 | updated_value = value; | 91 | } |
| 122 | } | 92 | |
| 123 | 93 | if ((s32)Memory::Read32(address) == value) { | |
| 124 | if ((s32)Memory::Read32(address) == value) { | 94 | Memory::Write32(address, (u32)(value + 1)); |
| 125 | Memory::Write32(address, (u32)(updated_value)); | 95 | } else { |
| 126 | } else { | 96 | return ERR_INVALID_STATE; |
| 127 | return ERR_INVALID_STATE; | 97 | } |
| 128 | } | 98 | |
| 129 | 99 | return SignalToAddress(address, num_to_wake); | |
| 130 | WakeThreads(waiting_threads, num_to_wake); | 100 | } |
| 131 | return RESULT_SUCCESS; | 101 | |
| 132 | } | 102 | // Signals an address being waited on and modifies its value based on waiting thread count if equal |
| 133 | 103 | // to the value argument. | |
| 134 | // Waits on an address if the value passed is less than the argument value, optionally decrementing. | 104 | ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, |
| 135 | ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) { | 105 | s32 num_to_wake) { |
| 136 | // Ensure that we can read the address. | 106 | // Ensure that we can write to the address. |
| 137 | if (!Memory::IsValidVirtualAddress(address)) { | 107 | if (!Memory::IsValidVirtualAddress(address)) { |
| 138 | return ERR_INVALID_ADDRESS_STATE; | 108 | return ERR_INVALID_ADDRESS_STATE; |
| 139 | } | 109 | } |
| 140 | 110 | ||
| 141 | s32 cur_value = (s32)Memory::Read32(address); | 111 | // Get threads waiting on the address. |
| 142 | if (cur_value < value) { | 112 | std::vector<SharedPtr<Thread>> waiting_threads; |
| 143 | Memory::Write32(address, (u32)(cur_value - 1)); | 113 | GetThreadsWaitingOnAddress(waiting_threads, address); |
| 144 | } else { | 114 | |
| 145 | return ERR_INVALID_STATE; | 115 | // Determine the modified value depending on the waiting count. |
| 146 | } | 116 | s32 updated_value; |
| 147 | // Short-circuit without rescheduling, if timeout is zero. | 117 | if (waiting_threads.size() == 0) { |
| 148 | if (timeout == 0) { | 118 | updated_value = value - 1; |
| 149 | return RESULT_TIMEOUT; | 119 | } else if (num_to_wake <= 0 || waiting_threads.size() <= num_to_wake) { |
| 150 | } | 120 | updated_value = value + 1; |
| 151 | 121 | } else { | |
| 152 | return WaitForAddress(address, timeout); | 122 | updated_value = value; |
| 153 | } | 123 | } |
| 154 | 124 | ||
| 155 | // Waits on an address if the value passed is equal to the argument value. | 125 | if ((s32)Memory::Read32(address) == value) { |
| 156 | ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { | 126 | Memory::Write32(address, (u32)(updated_value)); |
| 157 | // Ensure that we can read the address. | 127 | } else { |
| 158 | if (!Memory::IsValidVirtualAddress(address)) { | 128 | return ERR_INVALID_STATE; |
| 159 | return ERR_INVALID_ADDRESS_STATE; | 129 | } |
| 160 | } | 130 | |
| 161 | // Only wait for the address if equal. | 131 | WakeThreads(waiting_threads, num_to_wake); |
| 162 | if ((s32)Memory::Read32(address) != value) { | 132 | return RESULT_SUCCESS; |
| 163 | return ERR_INVALID_STATE; | 133 | } |
| 164 | } | 134 | |
| 165 | // Short-circuit without rescheduling, if timeout is zero. | 135 | // Waits on an address if the value passed is less than the argument value, optionally decrementing. |
| 166 | if (timeout == 0) { | 136 | ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) { |
| 167 | return RESULT_TIMEOUT; | 137 | // Ensure that we can read the address. |
| 168 | } | 138 | if (!Memory::IsValidVirtualAddress(address)) { |
| 169 | 139 | return ERR_INVALID_ADDRESS_STATE; | |
| 170 | return WaitForAddress(address, timeout); | 140 | } |
| 171 | } | 141 | |
| 172 | } // namespace AddressArbiter | 142 | s32 cur_value = (s32)Memory::Read32(address); |
| 173 | } // namespace Kernel \ No newline at end of file | 143 | if (cur_value < value) { |
| 144 | Memory::Write32(address, (u32)(cur_value - 1)); | ||
| 145 | } else { | ||
| 146 | return ERR_INVALID_STATE; | ||
| 147 | } | ||
| 148 | // Short-circuit without rescheduling, if timeout is zero. | ||
| 149 | if (timeout == 0) { | ||
| 150 | return RESULT_TIMEOUT; | ||
| 151 | } | ||
| 152 | |||
| 153 | return WaitForAddress(address, timeout); | ||
| 154 | } | ||
| 155 | |||
| 156 | // Waits on an address if the value passed is equal to the argument value. | ||
| 157 | ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { | ||
| 158 | // Ensure that we can read the address. | ||
| 159 | if (!Memory::IsValidVirtualAddress(address)) { | ||
| 160 | return ERR_INVALID_ADDRESS_STATE; | ||
| 161 | } | ||
| 162 | // Only wait for the address if equal. | ||
| 163 | if ((s32)Memory::Read32(address) != value) { | ||
| 164 | return ERR_INVALID_STATE; | ||
| 165 | } | ||
| 166 | // Short-circuit without rescheduling, if timeout is zero. | ||
| 167 | if (timeout == 0) { | ||
| 168 | return RESULT_TIMEOUT; | ||
| 169 | } | ||
| 170 | |||
| 171 | return WaitForAddress(address, timeout); | ||
| 172 | } | ||
| 173 | } // namespace AddressArbiter | ||
| 174 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 32d4a77a9..f20f3dbc0 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h | |||
| @@ -8,25 +8,25 @@ | |||
| 8 | 8 | ||
| 9 | namespace Kernel { | 9 | namespace Kernel { |
| 10 | 10 | ||
| 11 | namespace AddressArbiter { | 11 | namespace AddressArbiter { |
| 12 | enum class ArbitrationType { | 12 | enum class ArbitrationType { |
| 13 | WaitIfLessThan = 0, | 13 | WaitIfLessThan = 0, |
| 14 | DecrementAndWaitIfLessThan = 1, | 14 | DecrementAndWaitIfLessThan = 1, |
| 15 | WaitIfEqual = 2, | 15 | WaitIfEqual = 2, |
| 16 | }; | 16 | }; |
| 17 | 17 | ||
| 18 | enum class SignalType { | 18 | enum class SignalType { |
| 19 | Signal = 0, | 19 | Signal = 0, |
| 20 | IncrementAndSignalIfEqual = 1, | 20 | IncrementAndSignalIfEqual = 1, |
| 21 | ModifyByWaitingCountAndSignalIfEqual = 2, | 21 | ModifyByWaitingCountAndSignalIfEqual = 2, |
| 22 | }; | 22 | }; |
| 23 | 23 | ||
| 24 | ResultCode SignalToAddress(VAddr address, s32 num_to_wake); | 24 | ResultCode SignalToAddress(VAddr address, s32 num_to_wake); |
| 25 | ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); | 25 | ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); |
| 26 | ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); | 26 | ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); |
| 27 | 27 | ||
| 28 | ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement); | 28 | ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement); |
| 29 | ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); | 29 | ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); |
| 30 | } // namespace AddressArbiter | 30 | } // namespace AddressArbiter |
| 31 | 31 | ||
| 32 | } // namespace Kernel \ No newline at end of file | 32 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 3851d1085..f1e759802 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -228,8 +228,8 @@ public: | |||
| 228 | 228 | ||
| 229 | // If waiting on a ConditionVariable, this is the ConditionVariable address | 229 | // If waiting on a ConditionVariable, this is the ConditionVariable address |
| 230 | VAddr condvar_wait_address; | 230 | VAddr condvar_wait_address; |
| 231 | VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address | 231 | VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address |
| 232 | Handle wait_handle; ///< The handle used to wait for the mutex. | 232 | Handle wait_handle; ///< The handle used to wait for the mutex. |
| 233 | 233 | ||
| 234 | // If waiting for an AddressArbiter, this is the address being waited on. | 234 | // If waiting for an AddressArbiter, this is the address being waited on. |
| 235 | VAddr arb_wait_address{0}; | 235 | VAddr arb_wait_address{0}; |