summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
authorGravatar bunnei2021-01-15 21:58:44 -0800
committerGravatar bunnei2021-01-28 21:42:26 -0800
commit5a4fc4a5299a3835a57ae8a35c6de51459df70c0 (patch)
treebe8fa37ff42d1928829e5e8769715d6a4d8cb0f5 /src/core/hle/kernel
parentcore: hle: kernel: Implement KThreadQueue. (diff)
downloadyuzu-5a4fc4a5299a3835a57ae8a35c6de51459df70c0.tar.gz
yuzu-5a4fc4a5299a3835a57ae8a35c6de51459df70c0.tar.xz
yuzu-5a4fc4a5299a3835a57ae8a35c6de51459df70c0.zip
core: hle: kernel: Implement KLightLock.
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/k_light_lock.cpp130
-rw-r--r--src/core/hle/kernel/k_light_lock.h41
2 files changed, 171 insertions, 0 deletions
diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp
new file mode 100644
index 000000000..08fa65fd5
--- /dev/null
+++ b/src/core/hle/kernel/k_light_lock.cpp
@@ -0,0 +1,130 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/kernel/k_light_lock.h"
6#include "core/hle/kernel/k_scheduler.h"
7#include "core/hle/kernel/k_thread.h"
8#include "core/hle/kernel/kernel.h"
9
10namespace Kernel {
11
12void KLightLock::Lock() {
13 const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
14 const uintptr_t cur_thread_tag = (cur_thread | 1);
15
16 while (true) {
17 uintptr_t old_tag = tag.load(std::memory_order_relaxed);
18
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
26 if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) {
27 break;
28 }
29
30 LockSlowPath(old_tag | 1, cur_thread);
31 }
32}
33
34void KLightLock::Unlock() {
35 const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel));
36 uintptr_t expected = cur_thread;
37 do {
38 if (expected != cur_thread) {
39 return UnlockSlowPath(cur_thread);
40 }
41 } while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release));
42}
43
44void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
45 KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread);
46
47 // Pend the current thread waiting on the owner thread.
48 {
49 KScopedSchedulerLock sl{kernel};
50
51 // Ensure we actually have locking to do.
52 if (tag.load(std::memory_order_relaxed) != _owner) {
53 return;
54 }
55
56 // Add the current thread as a waiter on the owner.
57 KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ul);
58 cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
59 owner_thread->AddWaiter(cur_thread);
60
61 // Set thread states.
62 if (cur_thread->GetState() == ThreadState::Runnable) {
63 cur_thread->SetState(ThreadState::Waiting);
64 } else {
65 KScheduler::SetSchedulerUpdateNeeded(kernel);
66 }
67
68 if (owner_thread->IsSuspended()) {
69 owner_thread->ContinueIfHasKernelWaiters();
70 KScheduler::SetSchedulerUpdateNeeded(kernel);
71 }
72 }
73
74 // We're no longer waiting on the lock owner.
75 {
76 KScopedSchedulerLock sl{kernel};
77 KThread* owner_thread = cur_thread->GetLockOwner();
78 if (owner_thread) {
79 owner_thread->RemoveWaiter(cur_thread);
80 }
81 }
82}
83
84void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) {
85 KThread* owner_thread = reinterpret_cast<KThread*>(_cur_thread);
86
87 // Unlock.
88 {
89 KScopedSchedulerLock sl{kernel};
90
91 // Get the next owner.
92 s32 num_waiters = 0;
93 KThread* next_owner = owner_thread->RemoveWaiterByKey(
94 std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag)));
95
96 // Pass the lock to the next owner.
97 uintptr_t next_tag = 0;
98 if (next_owner) {
99 next_tag = reinterpret_cast<uintptr_t>(next_owner);
100 if (num_waiters > 1) {
101 next_tag |= 0x1;
102 }
103
104 if (next_owner->GetState() == ThreadState::Waiting) {
105 next_owner->SetState(ThreadState::Runnable);
106 } else {
107 KScheduler::SetSchedulerUpdateNeeded(kernel);
108 }
109
110 if (next_owner->IsSuspended()) {
111 next_owner->ContinueIfHasKernelWaiters();
112 }
113 }
114
115 // We may have unsuspended in the process of acquiring the lock, so we'll re-suspend now if
116 // so.
117 if (owner_thread->IsSuspended()) {
118 owner_thread->TrySuspend();
119 }
120
121 // Write the new tag value.
122 tag.store(next_tag);
123 }
124}
125
126bool KLightLock::IsLockedByCurrentThread() const {
127 return (tag | 0x1ul) == (reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)) | 0x1ul);
128}
129
130} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_lock.h b/src/core/hle/kernel/k_light_lock.h
new file mode 100644
index 000000000..f4c45f76a
--- /dev/null
+++ b/src/core/hle/kernel/k_light_lock.h
@@ -0,0 +1,41 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8
9#include "common/common_types.h"
10#include "core/hle/kernel/k_scoped_lock.h"
11
12namespace Kernel {
13
14class KernelCore;
15
16class KLightLock {
17public:
18 explicit KLightLock(KernelCore& kernel_) : kernel{kernel_} {}
19
20 void Lock();
21
22 void Unlock();
23
24 void LockSlowPath(uintptr_t owner, uintptr_t cur_thread);
25
26 void UnlockSlowPath(uintptr_t cur_thread);
27
28 bool IsLocked() const {
29 return tag != 0;
30 }
31
32 bool IsLockedByCurrentThread() const;
33
34private:
35 std::atomic<uintptr_t> tag{};
36 KernelCore& kernel;
37};
38
39using KScopedLightLock = KScopedLock<KLightLock>;
40
41} // namespace Kernel