k_light_lock.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "core/hle/kernel/k_light_lock.h"
  5. #include "core/hle/kernel/k_scheduler.h"
  6. #include "core/hle/kernel/k_thread.h"
  7. #include "core/hle/kernel/kernel.h"
  8. namespace Kernel {
  9. static KThread* ToThread(uintptr_t thread_) {
  10. ASSERT((thread_ & EmuThreadHandleReserved) == 0);
  11. ASSERT((thread_ & 1) == 0);
  12. return reinterpret_cast<KThread*>(thread_);
  13. }
  14. void KLightLock::Lock() {
  15. const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
  16. const uintptr_t cur_thread_tag = (cur_thread | 1);
  17. while (true) {
  18. uintptr_t old_tag = tag.load(std::memory_order_relaxed);
  19. while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1,
  20. std::memory_order_acquire)) {
  21. if ((old_tag | 1) == cur_thread_tag) {
  22. return;
  23. }
  24. }
  25. if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) {
  26. break;
  27. }
  28. LockSlowPath(old_tag | 1, cur_thread);
  29. }
  30. }
  31. void KLightLock::Unlock() {
  32. const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
  33. uintptr_t expected = cur_thread;
  34. do {
  35. if (expected != cur_thread) {
  36. return UnlockSlowPath(cur_thread);
  37. }
  38. } while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release));
  39. }
  40. void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
  41. KThread* cur_thread = ToThread(_cur_thread);
  42. // Pend the current thread waiting on the owner thread.
  43. {
  44. KScopedSchedulerLock sl{kernel};
  45. // Ensure we actually have locking to do.
  46. if (tag.load(std::memory_order_relaxed) != _owner) {
  47. return;
  48. }
  49. // Add the current thread as a waiter on the owner.
  50. KThread* owner_thread = ToThread(_owner & ~1ul);
  51. cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
  52. owner_thread->AddWaiter(cur_thread);
  53. // Set thread states.
  54. if (cur_thread->GetState() == ThreadState::Runnable) {
  55. cur_thread->SetState(ThreadState::Waiting);
  56. } else {
  57. KScheduler::SetSchedulerUpdateNeeded(kernel);
  58. }
  59. if (owner_thread->IsSuspended()) {
  60. owner_thread->ContinueIfHasKernelWaiters();
  61. KScheduler::SetSchedulerUpdateNeeded(kernel);
  62. }
  63. }
  64. // We're no longer waiting on the lock owner.
  65. {
  66. KScopedSchedulerLock sl{kernel};
  67. KThread* owner_thread = cur_thread->GetLockOwner();
  68. if (owner_thread) {
  69. owner_thread->RemoveWaiter(cur_thread);
  70. }
  71. }
  72. }
  73. void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
  74. KThread* owner_thread = ToThread(_cur_thread);
  75. // Unlock.
  76. {
  77. KScopedSchedulerLock sl{kernel};
  78. // Get the next owner.
  79. s32 num_waiters = 0;
  80. KThread* next_owner = owner_thread->RemoveWaiterByKey(
  81. std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
  82. // Pass the lock to the next owner.
  83. uintptr_t next_tag = 0;
  84. if (next_owner) {
  85. next_tag = reinterpret_cast<uintptr_t>(next_owner);
  86. if (num_waiters > 1) {
  87. next_tag |= 0x1;
  88. }
  89. if (next_owner->GetState() == ThreadState::Waiting) {
  90. next_owner->SetState(ThreadState::Runnable);
  91. } else {
  92. KScheduler::SetSchedulerUpdateNeeded(kernel);
  93. }
  94. if (next_owner->IsSuspended()) {
  95. next_owner->ContinueIfHasKernelWaiters();
  96. }
  97. }
  98. // We may have unsuspended in the process of acquiring the lock, so we'll re-suspend now if
  99. // so.
  100. if (owner_thread->IsSuspended()) {
  101. owner_thread->TrySuspend();
  102. }
  103. // Write the new tag value.
  104. tag.store(next_tag);
  105. }
  106. }
  107. bool KLightLock::IsLockedByCurrentThread() const {
  108. return (tag | 0x1ul) == (reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)) | 0x1ul);
  109. }
  110. } // namespace Kernel