k_light_lock.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  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/k_thread_queue.h"
  8. #include "core/hle/kernel/kernel.h"
  9. namespace Kernel {
  10. namespace {
  11. class ThreadQueueImplForKLightLock final : public KThreadQueue {
  12. public:
  13. explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {}
  14. void CancelWait(KThread* waiting_thread, ResultCode wait_result,
  15. bool cancel_timer_task) override {
  16. // Remove the thread as a waiter from its owner.
  17. if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
  18. owner->RemoveWaiter(waiting_thread);
  19. }
  20. // Invoke the base cancel wait handler.
  21. KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
  22. }
  23. };
  24. } // namespace
  25. void KLightLock::Lock() {
  26. const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
  27. while (true) {
  28. uintptr_t old_tag = tag.load(std::memory_order_relaxed);
  29. while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : (old_tag | 1),
  30. std::memory_order_acquire)) {
  31. }
  32. if (old_tag == 0 || this->LockSlowPath(old_tag | 1, cur_thread)) {
  33. break;
  34. }
  35. }
  36. }
  37. void KLightLock::Unlock() {
  38. const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
  39. uintptr_t expected = cur_thread;
  40. if (!tag.compare_exchange_strong(expected, 0, std::memory_order_release)) {
  41. this->UnlockSlowPath(cur_thread);
  42. }
  43. }
  44. bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
  45. KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
  46. ThreadQueueImplForKLightLock wait_queue(kernel);
  47. // Pend the current thread waiting on the owner thread.
  48. {
  49. KScopedSchedulerLock sl{kernel};
  50. // Ensure we actually have locking to do.
  51. if (tag.load(std::memory_order_relaxed) != _owner) {
  52. return false;
  53. }
  54. // Add the current thread as a waiter on the owner.
  55. KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
  56. cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
  57. owner_thread->AddWaiter(cur_thread);
  58. // Begin waiting to hold the lock.
  59. cur_thread->BeginWait(std::addressof(wait_queue));
  60. if (owner_thread->IsSuspended()) {
  61. owner_thread->ContinueIfHasKernelWaiters();
  62. }
  63. }
  64. return true;
  65. }
  66. void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
  67. KThread* owner_thread = reinterpret_cast<KThread*>(_cur_thread);
  68. // Unlock.
  69. {
  70. KScopedSchedulerLock sl(kernel);
  71. // Get the next owner.
  72. s32 num_waiters;
  73. KThread* next_owner = owner_thread->RemoveWaiterByKey(
  74. std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
  75. // Pass the lock to the next owner.
  76. uintptr_t next_tag = 0;
  77. if (next_owner != nullptr) {
  78. next_tag =
  79. reinterpret_cast<uintptr_t>(next_owner) | static_cast<uintptr_t>(num_waiters > 1);
  80. next_owner->EndWait(ResultSuccess);
  81. if (next_owner->IsSuspended()) {
  82. next_owner->ContinueIfHasKernelWaiters();
  83. }
  84. }
  85. // We may have unsuspended in the process of acquiring the lock, so we'll re-suspend now if
  86. // so.
  87. if (owner_thread->IsSuspended()) {
  88. owner_thread->TrySuspend();
  89. }
  90. // Write the new tag value.
  91. tag.store(next_tag, std::memory_order_release);
  92. }
  93. }
  94. bool KLightLock::IsLockedByCurrentThread() const {
  95. return (tag | 1ULL) == (reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)) | 1ULL);
  96. }
  97. } // namespace Kernel