summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2021-11-09 19:02:11 -0800
committerGravatar bunnei2021-12-06 16:39:17 -0800
commitbc1399204b914608715306a8a8dbe2f201dd4365 (patch)
tree7799195284bb20b15cd6be6addea57f45364136f /src
parentcore: hle: kernel: Disable dispatch count tracking on single core. (diff)
downloadyuzu-bc1399204b914608715306a8a8dbe2f201dd4365.tar.gz
yuzu-bc1399204b914608715306a8a8dbe2f201dd4365.tar.xz
yuzu-bc1399204b914608715306a8a8dbe2f201dd4365.zip
hle: kernel: Update KThreadQueue and migrate KSynchronizationObject.
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h1
-rw-r--r--src/core/hle/kernel/k_synchronization_object.cpp155
-rw-r--r--src/core/hle/kernel/k_synchronization_object.h32
-rw-r--r--src/core/hle/kernel/k_thread.cpp42
-rw-r--r--src/core/hle/kernel/k_thread.h27
-rw-r--r--src/core/hle/kernel/k_thread_queue.cpp51
-rw-r--r--src/core/hle/kernel/k_thread_queue.h17
8 files changed, 251 insertions, 75 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index eee8e2ccd..c42a29cc7 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -237,6 +237,7 @@ add_library(core STATIC
237 hle/kernel/k_system_control.h 237 hle/kernel/k_system_control.h
238 hle/kernel/k_thread.cpp 238 hle/kernel/k_thread.cpp
239 hle/kernel/k_thread.h 239 hle/kernel/k_thread.h
240 hle/kernel/k_thread_queue.cpp
240 hle/kernel/k_thread_queue.h 241 hle/kernel/k_thread_queue.h
241 hle/kernel/k_trace.h 242 hle/kernel/k_trace.h
242 hle/kernel/k_transfer_memory.cpp 243 hle/kernel/k_transfer_memory.cpp
diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
index 61dc2858f..2995c492d 100644
--- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
+++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
@@ -8,6 +8,7 @@
8#pragma once 8#pragma once
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/kernel/global_scheduler_context.h"
11#include "core/hle/kernel/k_thread.h" 12#include "core/hle/kernel/k_thread.h"
12#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/time_manager.h" 14#include "core/hle/kernel/time_manager.h"
diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp
index f168b4f21..ba8fc4010 100644
--- a/src/core/hle/kernel/k_synchronization_object.cpp
+++ b/src/core/hle/kernel/k_synchronization_object.cpp
@@ -8,11 +8,70 @@
8#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 8#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
9#include "core/hle/kernel/k_synchronization_object.h" 9#include "core/hle/kernel/k_synchronization_object.h"
10#include "core/hle/kernel/k_thread.h" 10#include "core/hle/kernel/k_thread.h"
11#include "core/hle/kernel/k_thread_queue.h"
11#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/svc_results.h" 13#include "core/hle/kernel/svc_results.h"
13 14
14namespace Kernel { 15namespace Kernel {
15 16
17namespace {
18
19class ThreadQueueImplForKSynchronizationObjectWait final : public KThreadQueueWithoutEndWait {
20private:
21 using ThreadListNode = KSynchronizationObject::ThreadListNode;
22
23private:
24 KSynchronizationObject** m_objects;
25 ThreadListNode* m_nodes;
26 s32 m_count;
27
28public:
29 ThreadQueueImplForKSynchronizationObjectWait(KernelCore& kernel_, KSynchronizationObject** o,
30 ThreadListNode* n, s32 c)
31 : KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) { // ...
32 }
33
34 virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
35 ResultCode wait_result) override {
36 // Determine the sync index, and unlink all nodes.
37 s32 sync_index = -1;
38 for (auto i = 0; i < m_count; ++i) {
39 // Check if this is the signaled object.
40 if (m_objects[i] == signaled_object && sync_index == -1) {
41 sync_index = i;
42 }
43
44 // Unlink the current node from the current object.
45 m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
46 }
47
48 // Set the waiting thread's sync index.
49 waiting_thread->SetSyncedIndex(sync_index);
50
51 // Set the waiting thread as not cancellable.
52 waiting_thread->ClearCancellable();
53
54 // Invoke the base end wait handler.
55 KThreadQueue::EndWait(waiting_thread, wait_result);
56 }
57
58 virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
59 bool cancel_timer_task) override {
60 // Remove all nodes from our list.
61 for (auto i = 0; i < m_count; ++i) {
62 m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
63 }
64
65 // Set the waiting thread as not cancellable.
66 waiting_thread->ClearCancellable();
67
68 // Invoke the base cancel wait handler.
69 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
70 }
71};
72
73} // namespace
74
16void KSynchronizationObject::Finalize() { 75void KSynchronizationObject::Finalize() {
17 this->OnFinalizeSynchronizationObject(); 76 this->OnFinalizeSynchronizationObject();
18 KAutoObject::Finalize(); 77 KAutoObject::Finalize();
@@ -25,11 +84,19 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
25 std::vector<ThreadListNode> thread_nodes(num_objects); 84 std::vector<ThreadListNode> thread_nodes(num_objects);
26 85
27 // Prepare for wait. 86 // Prepare for wait.
28 KThread* thread = kernel_ctx.CurrentScheduler()->GetCurrentThread(); 87 KThread* thread = GetCurrentThreadPointer(kernel_ctx);
88 ThreadQueueImplForKSynchronizationObjectWait wait_queue(kernel_ctx, objects,
89 thread_nodes.data(), num_objects);
29 90
30 { 91 {
31 // Setup the scheduling lock and sleep. 92 // Setup the scheduling lock and sleep.
32 KScopedSchedulerLockAndSleep slp{kernel_ctx, thread, timeout}; 93 KScopedSchedulerLockAndSleep slp(kernel_ctx, thread, timeout);
94
95 // Check if the thread should terminate.
96 if (thread->IsTerminationRequested()) {
97 slp.CancelSleep();
98 return ResultTerminationRequested;
99 }
33 100
34 // Check if any of the objects are already signaled. 101 // Check if any of the objects are already signaled.
35 for (auto i = 0; i < num_objects; ++i) { 102 for (auto i = 0; i < num_objects; ++i) {
@@ -48,12 +115,6 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
48 return ResultTimedOut; 115 return ResultTimedOut;
49 } 116 }
50 117
51 // Check if the thread should terminate.
52 if (thread->IsTerminationRequested()) {
53 slp.CancelSleep();
54 return ResultTerminationRequested;
55 }
56
57 // Check if waiting was canceled. 118 // Check if waiting was canceled.
58 if (thread->IsWaitCancelled()) { 119 if (thread->IsWaitCancelled()) {
59 slp.CancelSleep(); 120 slp.CancelSleep();
@@ -66,73 +127,25 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
66 thread_nodes[i].thread = thread; 127 thread_nodes[i].thread = thread;
67 thread_nodes[i].next = nullptr; 128 thread_nodes[i].next = nullptr;
68 129
69 if (objects[i]->thread_list_tail == nullptr) { 130 objects[i]->LinkNode(std::addressof(thread_nodes[i]));
70 objects[i]->thread_list_head = std::addressof(thread_nodes[i]);
71 } else {
72 objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]);
73 }
74
75 objects[i]->thread_list_tail = std::addressof(thread_nodes[i]);
76 } 131 }
77 132
78 // For debugging only 133 // Mark the thread as cancellable.
79 thread->SetWaitObjectsForDebugging({objects, static_cast<std::size_t>(num_objects)});
80
81 // Mark the thread as waiting.
82 thread->SetCancellable(); 134 thread->SetCancellable();
83 thread->SetSyncedObject(nullptr, ResultTimedOut);
84 thread->SetState(ThreadState::Waiting);
85 thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
86 }
87
88 // The lock/sleep is done, so we should be able to get our result.
89 135
90 // Thread is no longer cancellable. 136 // Clear the thread's synced index.
91 thread->ClearCancellable(); 137 thread->SetSyncedIndex(-1);
92 138
93 // For debugging only 139 // Wait for an object to be signaled.
94 thread->SetWaitObjectsForDebugging({}); 140 thread->BeginWait(std::addressof(wait_queue));
141 thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Synchronization);
142 }
95 143
96 // Cancel the timer as needed. 144 // Set the output index.
97 kernel_ctx.TimeManager().UnscheduleTimeEvent(thread); 145 *out_index = thread->GetSyncedIndex();
98 146
99 // Get the wait result. 147 // Get the wait result.
100 ResultCode wait_result{ResultSuccess}; 148 return thread->GetWaitResult();
101 s32 sync_index = -1;
102 {
103 KScopedSchedulerLock lock(kernel_ctx);
104 KSynchronizationObject* synced_obj;
105 wait_result = thread->GetWaitResult(std::addressof(synced_obj));
106
107 for (auto i = 0; i < num_objects; ++i) {
108 // Unlink the object from the list.
109 ThreadListNode* prev_ptr =
110 reinterpret_cast<ThreadListNode*>(std::addressof(objects[i]->thread_list_head));
111 ThreadListNode* prev_val = nullptr;
112 ThreadListNode *prev, *tail_prev;
113
114 do {
115 prev = prev_ptr;
116 prev_ptr = prev_ptr->next;
117 tail_prev = prev_val;
118 prev_val = prev_ptr;
119 } while (prev_ptr != std::addressof(thread_nodes[i]));
120
121 if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) {
122 objects[i]->thread_list_tail = tail_prev;
123 }
124
125 prev->next = thread_nodes[i].next;
126
127 if (objects[i] == synced_obj) {
128 sync_index = i;
129 }
130 }
131 }
132
133 // Set output.
134 *out_index = sync_index;
135 return wait_result;
136} 149}
137 150
138KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_) 151KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
@@ -141,7 +154,7 @@ KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
141KSynchronizationObject::~KSynchronizationObject() = default; 154KSynchronizationObject::~KSynchronizationObject() = default;
142 155
143void KSynchronizationObject::NotifyAvailable(ResultCode result) { 156void KSynchronizationObject::NotifyAvailable(ResultCode result) {
144 KScopedSchedulerLock lock(kernel); 157 KScopedSchedulerLock sl(kernel);
145 158
146 // If we're not signaled, we've nothing to notify. 159 // If we're not signaled, we've nothing to notify.
147 if (!this->IsSignaled()) { 160 if (!this->IsSignaled()) {
@@ -150,11 +163,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
150 163
151 // Iterate over each thread. 164 // Iterate over each thread.
152 for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { 165 for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
153 KThread* thread = cur_node->thread; 166 cur_node->thread->NotifyAvailable(this, result);
154 if (thread->GetState() == ThreadState::Waiting) {
155 thread->SetSyncedObject(this, result);
156 thread->SetState(ThreadState::Runnable);
157 }
158 } 167 }
159} 168}
160 169
diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h
index 898e58e16..789ff4780 100644
--- a/src/core/hle/kernel/k_synchronization_object.h
+++ b/src/core/hle/kernel/k_synchronization_object.h
@@ -35,6 +35,38 @@ public:
35 35
36 [[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const; 36 [[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
37 37
38 void LinkNode(ThreadListNode* node) {
39 // Link the node to the list.
40 if (thread_list_tail == nullptr) {
41 thread_list_head = node;
42 } else {
43 thread_list_tail->next = node;
44 }
45
46 thread_list_tail = node;
47 }
48
49 void UnlinkNode(ThreadListNode* node) {
50 // Unlink the node from the list.
51 ThreadListNode* prev_ptr =
52 reinterpret_cast<ThreadListNode*>(std::addressof(thread_list_head));
53 ThreadListNode* prev_val = nullptr;
54 ThreadListNode *prev, *tail_prev;
55
56 do {
57 prev = prev_ptr;
58 prev_ptr = prev_ptr->next;
59 tail_prev = prev_val;
60 prev_val = prev_ptr;
61 } while (prev_ptr != node);
62
63 if (thread_list_tail == node) {
64 thread_list_tail = tail_prev;
65 }
66
67 prev->next = node->next;
68 }
69
38protected: 70protected:
39 explicit KSynchronizationObject(KernelCore& kernel); 71 explicit KSynchronizationObject(KernelCore& kernel);
40 ~KSynchronizationObject() override; 72 ~KSynchronizationObject() override;
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 41bf9a6bb..3331b4845 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -303,7 +303,7 @@ void KThread::Wakeup() {
303 303
304 if (GetState() == ThreadState::Waiting) { 304 if (GetState() == ThreadState::Waiting) {
305 if (sleeping_queue != nullptr) { 305 if (sleeping_queue != nullptr) {
306 sleeping_queue->WakeupThread(this); 306 sleeping_queue->EndWait(this, ResultSuccess);
307 } else { 307 } else {
308 SetState(ThreadState::Runnable); 308 SetState(ThreadState::Runnable);
309 } 309 }
@@ -331,7 +331,7 @@ void KThread::StartTermination() {
331 331
332 // Signal. 332 // Signal.
333 signaled = true; 333 signaled = true;
334 NotifyAvailable(); 334 KSynchronizationObject::NotifyAvailable();
335 335
336 // Clear previous thread in KScheduler. 336 // Clear previous thread in KScheduler.
337 KScheduler::ClearPreviousThread(kernel, this); 337 KScheduler::ClearPreviousThread(kernel, this);
@@ -1026,6 +1026,44 @@ ResultCode KThread::Sleep(s64 timeout) {
1026 return ResultSuccess; 1026 return ResultSuccess;
1027} 1027}
1028 1028
1029void KThread::BeginWait(KThreadQueue* queue) {
1030 // Set our state as waiting.
1031 SetState(ThreadState::Waiting);
1032
1033 // Set our wait queue.
1034 sleeping_queue = queue;
1035}
1036
1037void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
1038 // Lock the scheduler.
1039 KScopedSchedulerLock sl(kernel);
1040
1041 // If we're waiting, notify our queue that we're available.
1042 if (GetState() == ThreadState::Waiting) {
1043 sleeping_queue->NotifyAvailable(this, signaled_object, wait_result_);
1044 }
1045}
1046
1047void KThread::EndWait(ResultCode wait_result_) {
1048 // Lock the scheduler.
1049 KScopedSchedulerLock sl(kernel);
1050
1051 // If we're waiting, notify our queue that we're available.
1052 if (GetState() == ThreadState::Waiting) {
1053 sleeping_queue->EndWait(this, wait_result_);
1054 }
1055}
1056
1057void KThread::CancelWait(ResultCode wait_result_, bool cancel_timer_task) {
1058 // Lock the scheduler.
1059 KScopedSchedulerLock sl(kernel);
1060
1061 // If we're waiting, notify our queue that we're available.
1062 if (GetState() == ThreadState::Waiting) {
1063 sleeping_queue->CancelWait(this, wait_result_, cancel_timer_task);
1064 }
1065}
1066
1029void KThread::SetState(ThreadState state) { 1067void KThread::SetState(ThreadState state) {
1030 KScopedSchedulerLock sl{kernel}; 1068 KScopedSchedulerLock sl{kernel};
1031 1069
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index e4c4c877d..bea5fd84d 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -202,6 +202,23 @@ public:
202 wait_result = wait_res; 202 wait_result = wait_res;
203 } 203 }
204 204
205 constexpr void SetSyncedIndex(s32 index) {
206 synced_index = index;
207 }
208
209 constexpr s32 GetSyncedIndex() const {
210 return synced_index;
211 }
212
213 constexpr void SetWaitResult(ResultCode wait_res) {
214 wait_result = wait_res;
215 synced_object = nullptr;
216 }
217
218 constexpr ResultCode GetWaitResult() const {
219 return wait_result;
220 }
221
205 [[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const { 222 [[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const {
206 *out = synced_object; 223 *out = synced_object;
207 return wait_result; 224 return wait_result;
@@ -596,6 +613,15 @@ public:
596 address_key_value = val; 613 address_key_value = val;
597 } 614 }
598 615
616 void ClearWaitQueue() {
617 sleeping_queue = nullptr;
618 }
619
620 void BeginWait(KThreadQueue* queue);
621 void NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_);
622 void EndWait(ResultCode wait_result_);
623 void CancelWait(ResultCode wait_result_, bool cancel_timer_task);
624
599 [[nodiscard]] bool HasWaiters() const { 625 [[nodiscard]] bool HasWaiters() const {
600 return !waiter_list.empty(); 626 return !waiter_list.empty();
601 } 627 }
@@ -707,6 +733,7 @@ private:
707 u32 address_key_value{}; 733 u32 address_key_value{};
708 u32 suspend_request_flags{}; 734 u32 suspend_request_flags{};
709 u32 suspend_allowed_flags{}; 735 u32 suspend_allowed_flags{};
736 s32 synced_index{};
710 ResultCode wait_result{ResultSuccess}; 737 ResultCode wait_result{ResultSuccess};
711 s32 base_priority{}; 738 s32 base_priority{};
712 s32 physical_ideal_core_id{}; 739 s32 physical_ideal_core_id{};
diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp
new file mode 100644
index 000000000..46f27172b
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_queue.cpp
@@ -0,0 +1,51 @@
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 "core/hle/kernel/k_thread_queue.h"
8#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/time_manager.h"
10
11namespace Kernel {
12
13void KThreadQueue::NotifyAvailable([[maybe_unused]] KThread* waiting_thread,
14 [[maybe_unused]] KSynchronizationObject* signaled_object,
15 [[maybe_unused]] ResultCode wait_result) {}
16
17void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) {
18 // Set the thread's wait result.
19 waiting_thread->SetWaitResult(wait_result);
20
21 // Set the thread as runnable.
22 waiting_thread->SetState(ThreadState::Runnable);
23
24 // Clear the thread's wait queue.
25 waiting_thread->ClearWaitQueue();
26
27 // Cancel the thread task.
28 kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
29}
30
31void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result,
32 bool cancel_timer_task) {
33 // Set the thread's wait result.
34 waiting_thread->SetWaitResult(wait_result);
35
36 // Set the thread as runnable.
37 waiting_thread->SetState(ThreadState::Runnable);
38
39 // Clear the thread's wait queue.
40 waiting_thread->ClearWaitQueue();
41
42 // Cancel the thread task.
43 if (cancel_timer_task) {
44 kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
45 }
46}
47
48void KThreadQueueWithoutEndWait::EndWait([[maybe_unused]] KThread* waiting_thread,
49 [[maybe_unused]] ResultCode wait_result) {}
50
51} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread_queue.h b/src/core/hle/kernel/k_thread_queue.h
index 35d471dc5..74e76e7cb 100644
--- a/src/core/hle/kernel/k_thread_queue.h
+++ b/src/core/hle/kernel/k_thread_queue.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/kernel/k_scheduler.h"
7#include "core/hle/kernel/k_thread.h" 8#include "core/hle/kernel/k_thread.h"
8 9
9namespace Kernel { 10namespace Kernel {
@@ -11,7 +12,16 @@ namespace Kernel {
11class KThreadQueue { 12class KThreadQueue {
12public: 13public:
13 explicit KThreadQueue(KernelCore& kernel_) : kernel{kernel_} {} 14 explicit KThreadQueue(KernelCore& kernel_) : kernel{kernel_} {}
15 virtual ~KThreadQueue(){};
14 16
17 virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
18 ResultCode wait_result);
19 virtual void EndWait(KThread* waiting_thread, ResultCode wait_result);
20 virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
21 bool cancel_timer_task);
22
23 // Deprecated, will be removed in subsequent commits.
24public:
15 bool IsEmpty() const { 25 bool IsEmpty() const {
16 return wait_list.empty(); 26 return wait_list.empty();
17 } 27 }
@@ -78,4 +88,11 @@ private:
78 KThread::WaiterList wait_list{}; 88 KThread::WaiterList wait_list{};
79}; 89};
80 90
91class KThreadQueueWithoutEndWait : public KThreadQueue {
92public:
93 explicit KThreadQueueWithoutEndWait(KernelCore& kernel_) : KThreadQueue(kernel_) {}
94
95 virtual void EndWait(KThread* waiting_thread, ResultCode wait_result) override final;
96};
97
81} // namespace Kernel 98} // namespace Kernel