address_arbiter.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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. namespace {
  18. // Wake up num_to_wake (or all) threads in a vector.
  19. void WakeThreads(const std::vector<std::shared_ptr<Thread>>& waiting_threads, s32 num_to_wake) {
  20. auto& system = Core::System::GetInstance();
  21. // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
  22. // them all.
  23. std::size_t last = waiting_threads.size();
  24. if (num_to_wake > 0) {
  25. last = std::min(last, static_cast<std::size_t>(num_to_wake));
  26. }
  27. // Signal the waiting threads.
  28. for (std::size_t i = 0; i < last; i++) {
  29. ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb);
  30. waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
  31. waiting_threads[i]->SetArbiterWaitAddress(0);
  32. waiting_threads[i]->ResumeFromWait();
  33. system.PrepareReschedule(waiting_threads[i]->GetProcessorID());
  34. }
  35. }
  36. } // Anonymous namespace
  37. AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
  38. AddressArbiter::~AddressArbiter() = default;
  39. ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value,
  40. s32 num_to_wake) {
  41. switch (type) {
  42. case SignalType::Signal:
  43. return SignalToAddressOnly(address, num_to_wake);
  44. case SignalType::IncrementAndSignalIfEqual:
  45. return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
  46. case SignalType::ModifyByWaitingCountAndSignalIfEqual:
  47. return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake);
  48. default:
  49. return ERR_INVALID_ENUM_VALUE;
  50. }
  51. }
  52. ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
  53. const std::vector<std::shared_ptr<Thread>> waiting_threads =
  54. GetThreadsWaitingOnAddress(address);
  55. WakeThreads(waiting_threads, num_to_wake);
  56. return RESULT_SUCCESS;
  57. }
  58. ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
  59. s32 num_to_wake) {
  60. auto& memory = system.Memory();
  61. // Ensure that we can write to the address.
  62. if (!memory.IsValidVirtualAddress(address)) {
  63. return ERR_INVALID_ADDRESS_STATE;
  64. }
  65. if (static_cast<s32>(memory.Read32(address)) != value) {
  66. return ERR_INVALID_STATE;
  67. }
  68. memory.Write32(address, static_cast<u32>(value + 1));
  69. return SignalToAddressOnly(address, num_to_wake);
  70. }
  71. ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
  72. s32 num_to_wake) {
  73. auto& memory = system.Memory();
  74. // Ensure that we can write to the address.
  75. if (!memory.IsValidVirtualAddress(address)) {
  76. return ERR_INVALID_ADDRESS_STATE;
  77. }
  78. // Get threads waiting on the address.
  79. const std::vector<std::shared_ptr<Thread>> waiting_threads =
  80. GetThreadsWaitingOnAddress(address);
  81. // Determine the modified value depending on the waiting count.
  82. s32 updated_value;
  83. if (num_to_wake <= 0) {
  84. if (waiting_threads.empty()) {
  85. updated_value = value + 1;
  86. } else {
  87. updated_value = value - 1;
  88. }
  89. } else {
  90. if (waiting_threads.empty()) {
  91. updated_value = value + 1;
  92. } else if (waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
  93. updated_value = value - 1;
  94. } else {
  95. updated_value = value;
  96. }
  97. }
  98. if (static_cast<s32>(memory.Read32(address)) != value) {
  99. return ERR_INVALID_STATE;
  100. }
  101. memory.Write32(address, static_cast<u32>(updated_value));
  102. WakeThreads(waiting_threads, num_to_wake);
  103. return RESULT_SUCCESS;
  104. }
  105. ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value,
  106. s64 timeout_ns) {
  107. switch (type) {
  108. case ArbitrationType::WaitIfLessThan:
  109. return WaitForAddressIfLessThan(address, value, timeout_ns, false);
  110. case ArbitrationType::DecrementAndWaitIfLessThan:
  111. return WaitForAddressIfLessThan(address, value, timeout_ns, true);
  112. case ArbitrationType::WaitIfEqual:
  113. return WaitForAddressIfEqual(address, value, timeout_ns);
  114. default:
  115. return ERR_INVALID_ENUM_VALUE;
  116. }
  117. }
  118. ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
  119. bool should_decrement) {
  120. auto& memory = system.Memory();
  121. // Ensure that we can read the address.
  122. if (!memory.IsValidVirtualAddress(address)) {
  123. return ERR_INVALID_ADDRESS_STATE;
  124. }
  125. const s32 cur_value = static_cast<s32>(memory.Read32(address));
  126. if (cur_value >= value) {
  127. return ERR_INVALID_STATE;
  128. }
  129. if (should_decrement) {
  130. memory.Write32(address, static_cast<u32>(cur_value - 1));
  131. }
  132. // Short-circuit without rescheduling, if timeout is zero.
  133. if (timeout == 0) {
  134. return RESULT_TIMEOUT;
  135. }
  136. return WaitForAddressImpl(address, timeout);
  137. }
  138. ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
  139. auto& memory = system.Memory();
  140. // Ensure that we can read the address.
  141. if (!memory.IsValidVirtualAddress(address)) {
  142. return ERR_INVALID_ADDRESS_STATE;
  143. }
  144. // Only wait for the address if equal.
  145. if (static_cast<s32>(memory.Read32(address)) != value) {
  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. return WaitForAddressImpl(address, timeout);
  153. }
  154. ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
  155. Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
  156. current_thread->SetArbiterWaitAddress(address);
  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. std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(
  164. VAddr address) const {
  165. // Retrieve all threads that are waiting for this address.
  166. std::vector<std::shared_ptr<Thread>> threads;
  167. const auto& scheduler = system.GlobalScheduler();
  168. const auto& thread_list = scheduler.GetThreadList();
  169. for (const auto& thread : thread_list) {
  170. if (thread->GetArbiterWaitAddress() == address) {
  171. threads.push_back(thread);
  172. }
  173. }
  174. // Sort them by priority, such that the highest priority ones come first.
  175. std::sort(threads.begin(), threads.end(),
  176. [](const std::shared_ptr<Thread>& lhs, const std::shared_ptr<Thread>& rhs) {
  177. return lhs->GetPriority() < rhs->GetPriority();
  178. });
  179. return threads;
  180. }
  181. } // namespace Kernel