| 
									
										
										
										
											2018-06-21 00:49:43 -06:00
										 |  |  | // Copyright 2018 yuzu emulator team
 | 
					
						
							|  |  |  | // Licensed under GPLv2 or any later version
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 08:06:09 -04:00
										 |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | #include <vector>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-21 00:49:43 -06:00
										 |  |  | #include "common/assert.h"
 | 
					
						
							|  |  |  | #include "common/common_types.h"
 | 
					
						
							| 
									
										
										
										
											2018-06-21 01:40:29 -06:00
										 |  |  | #include "core/core.h"
 | 
					
						
							| 
									
										
										
										
											2018-08-31 12:21:34 -04:00
										 |  |  | #include "core/core_cpu.h"
 | 
					
						
							| 
									
										
										
										
											2018-06-21 01:40:29 -06:00
										 |  |  | #include "core/hle/kernel/errors.h"
 | 
					
						
							| 
									
										
										
										
											2018-08-01 22:40:00 -04:00
										 |  |  | #include "core/hle/kernel/object.h"
 | 
					
						
							| 
									
										
										
										
											2018-06-21 00:49:43 -06:00
										 |  |  | #include "core/hle/kernel/process.h"
 | 
					
						
							| 
									
										
										
										
											2018-08-31 12:21:34 -04:00
										 |  |  | #include "core/hle/kernel/scheduler.h"
 | 
					
						
							| 
									
										
										
										
											2018-06-21 00:49:43 -06:00
										 |  |  | #include "core/hle/kernel/thread.h"
 | 
					
						
							| 
									
										
										
										
											2018-07-31 08:06:09 -04:00
										 |  |  | #include "core/hle/result.h"
 | 
					
						
							| 
									
										
										
										
											2018-06-21 00:49:43 -06:00
										 |  |  | #include "core/memory.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Kernel { | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  | namespace AddressArbiter { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Performs actual address waiting logic.
 | 
					
						
							| 
									
										
										
										
											2018-06-22 00:47:59 -06:00
										 |  |  | static ResultCode WaitForAddress(VAddr address, s64 timeout) { | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |     SharedPtr<Thread> current_thread = GetCurrentThread(); | 
					
						
							| 
									
										
										
										
											2018-10-03 18:47:57 -04:00
										 |  |  |     current_thread->SetArbiterWaitAddress(address); | 
					
						
							|  |  |  |     current_thread->SetStatus(ThreadStatus::WaitArb); | 
					
						
							|  |  |  |     current_thread->InvalidateWakeupCallback(); | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     current_thread->WakeAfterDelay(timeout); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 18:47:57 -04:00
										 |  |  |     Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); | 
					
						
							| 
									
										
										
										
											2018-06-22 00:47:59 -06:00
										 |  |  |     return RESULT_TIMEOUT; | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Gets the threads waiting on an address.
 | 
					
						
							| 
									
										
										
										
											2018-08-05 16:24:43 -04:00
										 |  |  | static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) { | 
					
						
							| 
									
										
										
										
											2018-09-15 15:21:06 +02:00
										 |  |  |     const auto RetrieveWaitingThreads = [](std::size_t core_index, | 
					
						
							|  |  |  |                                            std::vector<SharedPtr<Thread>>& waiting_threads, | 
					
						
							|  |  |  |                                            VAddr arb_addr) { | 
					
						
							|  |  |  |         const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); | 
					
						
							| 
									
										
										
										
											2018-10-15 09:25:11 -04:00
										 |  |  |         const auto& thread_list = scheduler.GetThreadList(); | 
					
						
							| 
									
										
										
										
											2018-09-15 15:21:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 18:47:57 -04:00
										 |  |  |         for (const auto& thread : thread_list) { | 
					
						
							|  |  |  |             if (thread->GetArbiterWaitAddress() == arb_addr) | 
					
						
							| 
									
										
										
										
											2018-09-15 15:21:06 +02:00
										 |  |  |                 waiting_threads.push_back(thread); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-05 16:24:43 -04:00
										 |  |  |     // Retrieve all threads that are waiting for this address.
 | 
					
						
							|  |  |  |     std::vector<SharedPtr<Thread>> threads; | 
					
						
							|  |  |  |     RetrieveWaitingThreads(0, threads, address); | 
					
						
							|  |  |  |     RetrieveWaitingThreads(1, threads, address); | 
					
						
							|  |  |  |     RetrieveWaitingThreads(2, threads, address); | 
					
						
							|  |  |  |     RetrieveWaitingThreads(3, threads, address); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |     // Sort them by priority, such that the highest priority ones come first.
 | 
					
						
							| 
									
										
										
										
											2018-08-05 16:24:43 -04:00
										 |  |  |     std::sort(threads.begin(), threads.end(), | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |               [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { | 
					
						
							| 
									
										
										
										
											2018-10-03 18:47:57 -04:00
										 |  |  |                   return lhs->GetPriority() < rhs->GetPriority(); | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |               }); | 
					
						
							| 
									
										
										
										
											2018-08-05 16:24:43 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return threads; | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Wake up num_to_wake (or all) threads in a vector.
 | 
					
						
							| 
									
										
										
										
											2018-06-22 00:47:59 -06:00
										 |  |  | static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) { | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |     // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
 | 
					
						
							|  |  |  |     // them all.
 | 
					
						
							| 
									
										
										
										
											2018-09-15 15:21:06 +02:00
										 |  |  |     std::size_t last = waiting_threads.size(); | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |     if (num_to_wake > 0) | 
					
						
							|  |  |  |         last = num_to_wake; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Signal the waiting threads.
 | 
					
						
							| 
									
										
										
										
											2018-09-15 15:21:06 +02:00
										 |  |  |     for (std::size_t i = 0; i < last; i++) { | 
					
						
							| 
									
										
										
										
											2018-10-03 18:47:57 -04:00
										 |  |  |         ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb); | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |         waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); | 
					
						
							| 
									
										
										
										
											2018-10-03 18:47:57 -04:00
										 |  |  |         waiting_threads[i]->SetArbiterWaitAddress(0); | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |         waiting_threads[i]->ResumeFromWait(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Signals an address being waited on.
 | 
					
						
							|  |  |  | ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { | 
					
						
							| 
									
										
										
										
											2018-08-05 16:24:43 -04:00
										 |  |  |     std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     WakeThreads(waiting_threads, num_to_wake); | 
					
						
							|  |  |  |     return RESULT_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Signals an address being waited on and increments its value if equal to the value argument.
 | 
					
						
							|  |  |  | ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { | 
					
						
							|  |  |  |     // Ensure that we can write to the address.
 | 
					
						
							|  |  |  |     if (!Memory::IsValidVirtualAddress(address)) { | 
					
						
							|  |  |  |         return ERR_INVALID_ADDRESS_STATE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-22 00:47:59 -06:00
										 |  |  |     if (static_cast<s32>(Memory::Read32(address)) == value) { | 
					
						
							|  |  |  |         Memory::Write32(address, static_cast<u32>(value + 1)); | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         return ERR_INVALID_STATE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return SignalToAddress(address, num_to_wake); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Signals an address being waited on and modifies its value based on waiting thread count if equal
 | 
					
						
							|  |  |  | // to the value argument.
 | 
					
						
							|  |  |  | ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, | 
					
						
							|  |  |  |                                                          s32 num_to_wake) { | 
					
						
							|  |  |  |     // Ensure that we can write to the address.
 | 
					
						
							|  |  |  |     if (!Memory::IsValidVirtualAddress(address)) { | 
					
						
							|  |  |  |         return ERR_INVALID_ADDRESS_STATE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Get threads waiting on the address.
 | 
					
						
							| 
									
										
										
										
											2018-08-05 16:24:43 -04:00
										 |  |  |     std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Determine the modified value depending on the waiting count.
 | 
					
						
							|  |  |  |     s32 updated_value; | 
					
						
							| 
									
										
										
										
											2018-08-05 16:24:43 -04:00
										 |  |  |     if (waiting_threads.empty()) { | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |         updated_value = value - 1; | 
					
						
							| 
									
										
										
										
											2018-07-16 20:55:53 -06:00
										 |  |  |     } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) { | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |         updated_value = value + 1; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         updated_value = value; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-22 00:47:59 -06:00
										 |  |  |     if (static_cast<s32>(Memory::Read32(address)) == value) { | 
					
						
							|  |  |  |         Memory::Write32(address, static_cast<u32>(updated_value)); | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         return ERR_INVALID_STATE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     WakeThreads(waiting_threads, num_to_wake); | 
					
						
							|  |  |  |     return RESULT_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Waits on an address if the value passed is less than the argument value, optionally decrementing.
 | 
					
						
							|  |  |  | ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) { | 
					
						
							|  |  |  |     // Ensure that we can read the address.
 | 
					
						
							|  |  |  |     if (!Memory::IsValidVirtualAddress(address)) { | 
					
						
							|  |  |  |         return ERR_INVALID_ADDRESS_STATE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-22 00:47:59 -06:00
										 |  |  |     s32 cur_value = static_cast<s32>(Memory::Read32(address)); | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |     if (cur_value < value) { | 
					
						
							| 
									
										
										
										
											2018-07-16 20:55:53 -06:00
										 |  |  |         if (should_decrement) { | 
					
						
							|  |  |  |             Memory::Write32(address, static_cast<u32>(cur_value - 1)); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         return ERR_INVALID_STATE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // Short-circuit without rescheduling, if timeout is zero.
 | 
					
						
							|  |  |  |     if (timeout == 0) { | 
					
						
							|  |  |  |         return RESULT_TIMEOUT; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return WaitForAddress(address, timeout); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Waits on an address if the value passed is equal to the argument value.
 | 
					
						
							|  |  |  | ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { | 
					
						
							|  |  |  |     // Ensure that we can read the address.
 | 
					
						
							|  |  |  |     if (!Memory::IsValidVirtualAddress(address)) { | 
					
						
							|  |  |  |         return ERR_INVALID_ADDRESS_STATE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // Only wait for the address if equal.
 | 
					
						
							| 
									
										
										
										
											2018-06-22 00:47:59 -06:00
										 |  |  |     if (static_cast<s32>(Memory::Read32(address)) != value) { | 
					
						
							| 
									
										
										
										
											2018-06-21 21:05:34 -06:00
										 |  |  |         return ERR_INVALID_STATE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // Short-circuit without rescheduling, if timeout is zero.
 | 
					
						
							|  |  |  |     if (timeout == 0) { | 
					
						
							|  |  |  |         return RESULT_TIMEOUT; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return WaitForAddress(address, timeout); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | } // namespace AddressArbiter
 | 
					
						
							|  |  |  | } // namespace Kernel
 |