address_arbiter.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <vector>
  6. #include "common/assert.h"
  7. #include "common/common_types.h"
  8. #include "core/core.h"
  9. #include "core/core_cpu.h"
  10. #include "core/hle/kernel/address_arbiter.h"
  11. #include "core/hle/kernel/errors.h"
  12. #include "core/hle/kernel/scheduler.h"
  13. #include "core/hle/kernel/thread.h"
  14. #include "core/hle/result.h"
  15. #include "core/memory.h"
  16. namespace Kernel {
  17. // Wake up num_to_wake (or all) threads in a vector.
  18. void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads,
  19. s32 num_to_wake) {
  20. // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
  21. // them all.
  22. std::size_t last = waiting_threads.size();
  23. if (num_to_wake > 0) {
  24. last = std::min(last, static_cast<std::size_t>(num_to_wake));
  25. }
  26. // Signal the waiting threads.
  27. for (std::size_t i = 0; i < last; i++) {
  28. ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb);
  29. waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
  30. RemoveThread(waiting_threads[i]);
  31. waiting_threads[i]->SetArbiterWaitAddress(0);
  32. waiting_threads[i]->ResumeFromWait();
  33. system.PrepareReschedule(waiting_threads[i]->GetProcessorID());
  34. }
  35. }
  36. AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
  37. AddressArbiter::~AddressArbiter() = default;
  38. ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value,
  39. s32 num_to_wake) {
  40. switch (type) {
  41. case SignalType::Signal:
  42. return SignalToAddressOnly(address, num_to_wake);
  43. case SignalType::IncrementAndSignalIfEqual:
  44. return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
  45. case SignalType::ModifyByWaitingCountAndSignalIfEqual:
  46. return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake);
  47. default:
  48. return ERR_INVALID_ENUM_VALUE;
  49. }
  50. }
  51. ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
  52. const std::vector<std::shared_ptr<Thread>> waiting_threads =
  53. GetThreadsWaitingOnAddress(address);
  54. WakeThreads(waiting_threads, num_to_wake);
  55. return RESULT_SUCCESS;
  56. }
  57. ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
  58. s32 num_to_wake) {
  59. auto& memory = system.Memory();
  60. // Ensure that we can write to the address.
  61. if (!memory.IsValidVirtualAddress(address)) {
  62. return ERR_INVALID_ADDRESS_STATE;
  63. }
  64. if (static_cast<s32>(memory.Read32(address)) != value) {
  65. return ERR_INVALID_STATE;
  66. }
  67. memory.Write32(address, static_cast<u32>(value + 1));
  68. return SignalToAddressOnly(address, num_to_wake);
  69. }
  70. ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
  71. s32 num_to_wake) {
  72. auto& memory = system.Memory();
  73. // Ensure that we can write to the address.
  74. if (!memory.IsValidVirtualAddress(address)) {
  75. return ERR_INVALID_ADDRESS_STATE;
  76. }
  77. // Get threads waiting on the address.
  78. const std::vector<std::shared_ptr<Thread>> waiting_threads =
  79. GetThreadsWaitingOnAddress(address);
  80. // Determine the modified value depending on the waiting count.
  81. s32 updated_value;
  82. if (num_to_wake <= 0) {
  83. if (waiting_threads.empty()) {
  84. updated_value = value + 1;
  85. } else {
  86. updated_value = value - 1;
  87. }
  88. } else {
  89. if (waiting_threads.empty()) {
  90. updated_value = value + 1;
  91. } else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
  92. updated_value = value - 1;
  93. } else {
  94. updated_value = value;
  95. }
  96. }
  97. if (static_cast<s32>(memory.Read32(address)) != value) {
  98. return ERR_INVALID_STATE;
  99. }
  100. memory.Write32(address, static_cast<u32>(updated_value));
  101. WakeThreads(waiting_threads, num_to_wake);
  102. return RESULT_SUCCESS;
  103. }
  104. ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value,
  105. s64 timeout_ns) {
  106. switch (type) {
  107. case ArbitrationType::WaitIfLessThan:
  108. return WaitForAddressIfLessThan(address, value, timeout_ns, false);
  109. case ArbitrationType::DecrementAndWaitIfLessThan:
  110. return WaitForAddressIfLessThan(address, value, timeout_ns, true);
  111. case ArbitrationType::WaitIfEqual:
  112. return WaitForAddressIfEqual(address, value, timeout_ns);
  113. default:
  114. return ERR_INVALID_ENUM_VALUE;
  115. }
  116. }
  117. ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
  118. bool should_decrement) {
  119. auto& memory = system.Memory();
  120. // Ensure that we can read the address.
  121. if (!memory.IsValidVirtualAddress(address)) {
  122. return ERR_INVALID_ADDRESS_STATE;
  123. }
  124. const s32 cur_value = static_cast<s32>(memory.Read32(address));
  125. if (cur_value >= value) {
  126. return ERR_INVALID_STATE;
  127. }
  128. if (should_decrement) {
  129. memory.Write32(address, static_cast<u32>(cur_value - 1));
  130. }
  131. // Short-circuit without rescheduling, if timeout is zero.
  132. if (timeout == 0) {
  133. return RESULT_TIMEOUT;
  134. }
  135. return WaitForAddressImpl(address, timeout);
  136. }
  137. ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
  138. auto& memory = system.Memory();
  139. // Ensure that we can read the address.
  140. if (!memory.IsValidVirtualAddress(address)) {
  141. return ERR_INVALID_ADDRESS_STATE;
  142. }
  143. // Only wait for the address if equal.
  144. if (static_cast<s32>(memory.Read32(address)) != value) {
  145. return ERR_INVALID_STATE;
  146. }
  147. // Short-circuit without rescheduling if timeout is zero.
  148. if (timeout == 0) {
  149. return RESULT_TIMEOUT;
  150. }
  151. return WaitForAddressImpl(address, timeout);
  152. }
  153. ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
  154. Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
  155. current_thread->SetArbiterWaitAddress(address);
  156. InsertThread(SharedFrom(current_thread));
  157. current_thread->SetStatus(ThreadStatus::WaitArb);
  158. current_thread->InvalidateWakeupCallback();
  159. current_thread->WakeAfterDelay(timeout);
  160. system.PrepareReschedule(current_thread->GetProcessorID());
  161. return RESULT_TIMEOUT;
  162. }
  163. void AddressArbiter::HandleWakeupThread(std::shared_ptr<Thread> thread) {
  164. ASSERT(thread->GetStatus() == ThreadStatus::WaitArb);
  165. RemoveThread(thread);
  166. thread->SetArbiterWaitAddress(0);
  167. }
  168. void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) {
  169. const VAddr arb_addr = thread->GetArbiterWaitAddress();
  170. std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
  171. auto it = thread_list.begin();
  172. while (it != thread_list.end()) {
  173. const std::shared_ptr<Thread>& current_thread = *it;
  174. if (current_thread->GetPriority() >= thread->GetPriority()) {
  175. thread_list.insert(it, thread);
  176. return;
  177. }
  178. ++it;
  179. }
  180. thread_list.push_back(std::move(thread));
  181. }
  182. void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) {
  183. const VAddr arb_addr = thread->GetArbiterWaitAddress();
  184. std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr];
  185. auto it = thread_list.begin();
  186. while (it != thread_list.end()) {
  187. const std::shared_ptr<Thread>& current_thread = *it;
  188. if (current_thread.get() == thread.get()) {
  189. thread_list.erase(it);
  190. return;
  191. }
  192. ++it;
  193. }
  194. UNREACHABLE();
  195. }
  196. std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) {
  197. std::vector<std::shared_ptr<Thread>> result;
  198. std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[address];
  199. auto it = thread_list.begin();
  200. while (it != thread_list.end()) {
  201. std::shared_ptr<Thread> current_thread = *it;
  202. result.push_back(std::move(current_thread));
  203. ++it;
  204. }
  205. return result;
  206. }
  207. } // namespace Kernel