summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/thread.cpp
diff options
context:
space:
mode:
authorGravatar Lioncash2018-08-28 12:30:33 -0400
committerGravatar Lioncash2018-08-28 22:31:51 -0400
commit0cbcd6ec9aeeafc298fe2e6e4ac10d68bb7267c5 (patch)
tree2d7bb143d490c3984bff6deda426b818bf27d552 /src/core/hle/kernel/thread.cpp
parentMerge pull request #1193 from lioncash/priv (diff)
downloadyuzu-0cbcd6ec9aeeafc298fe2e6e4ac10d68bb7267c5.tar.gz
yuzu-0cbcd6ec9aeeafc298fe2e6e4ac10d68bb7267c5.tar.xz
yuzu-0cbcd6ec9aeeafc298fe2e6e4ac10d68bb7267c5.zip
kernel: Eliminate kernel global state
As means to pave the way for getting rid of global state within core, This eliminates kernel global state by removing all globals. Instead this introduces a KernelCore class which acts as a kernel instance. This instance lives in the System class, which keeps its lifetime contained to the lifetime of the System class. This also forces the kernel types to actually interact with the main kernel instance itself instead of having transient kernel state placed all over several translation units, keeping everything together. It also has a nice consequence of making dependencies much more explicit. This also makes our initialization a tad bit more correct. Previously we were creating a kernel process before the actual kernel was initialized, which doesn't really make much sense. The KernelCore class itself follows the PImpl idiom, which allows keeping all the implementation details sealed away from everything else, which forces the use of the exposed API and allows us to avoid any unnecessary inclusions within the main kernel header.
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
-rw-r--r--src/core/hle/kernel/thread.cpp119
1 files changed, 18 insertions, 101 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 4ffd8d5cc..520ea0853 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -20,6 +20,7 @@
20#include "core/core_timing_util.h" 20#include "core/core_timing_util.h"
21#include "core/hle/kernel/errors.h" 21#include "core/hle/kernel/errors.h"
22#include "core/hle/kernel/handle_table.h" 22#include "core/hle/kernel/handle_table.h"
23#include "core/hle/kernel/kernel.h"
23#include "core/hle/kernel/object.h" 24#include "core/hle/kernel/object.h"
24#include "core/hle/kernel/process.h" 25#include "core/hle/kernel/process.h"
25#include "core/hle/kernel/thread.h" 26#include "core/hle/kernel/thread.h"
@@ -29,9 +30,6 @@
29 30
30namespace Kernel { 31namespace Kernel {
31 32
32/// Event type for the thread wake up event
33static CoreTiming::EventType* ThreadWakeupEventType = nullptr;
34
35bool Thread::ShouldWait(Thread* thread) const { 33bool Thread::ShouldWait(Thread* thread) const {
36 return status != ThreadStatus::Dead; 34 return status != ThreadStatus::Dead;
37} 35}
@@ -40,32 +38,17 @@ void Thread::Acquire(Thread* thread) {
40 ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); 38 ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
41} 39}
42 40
43// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing 41Thread::Thread(KernelCore& kernel) : WaitObject{kernel} {}
44// us to simply use a pool index or similar. 42Thread::~Thread() = default;
45static Kernel::HandleTable wakeup_callback_handle_table;
46
47// The first available thread id at startup
48static u32 next_thread_id;
49
50/**
51 * Creates a new thread ID
52 * @return The new thread ID
53 */
54inline static u32 const NewThreadId() {
55 return next_thread_id++;
56}
57
58Thread::Thread() {}
59Thread::~Thread() {}
60 43
61void Thread::Stop() { 44void Thread::Stop() {
62 // Cancel any outstanding wakeup events for this thread 45 // Cancel any outstanding wakeup events for this thread
63 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); 46 CoreTiming::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle);
64 wakeup_callback_handle_table.Close(callback_handle); 47 kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
65 callback_handle = 0; 48 callback_handle = 0;
66 49
67 // Clean up thread from ready queue 50 // Clean up thread from ready queue
68 // This is only needed when the thread is termintated forcefully (SVC TerminateProcess) 51 // This is only needed when the thread is terminated forcefully (SVC TerminateProcess)
69 if (status == ThreadStatus::Ready) { 52 if (status == ThreadStatus::Ready) {
70 scheduler->UnscheduleThread(this, current_priority); 53 scheduler->UnscheduleThread(this, current_priority);
71 } 54 }
@@ -98,63 +81,6 @@ void ExitCurrentThread() {
98 Core::System::GetInstance().CurrentScheduler().RemoveThread(thread); 81 Core::System::GetInstance().CurrentScheduler().RemoveThread(thread);
99} 82}
100 83
101/**
102 * Callback that will wake up the thread it was scheduled for
103 * @param thread_handle The handle of the thread that's been awoken
104 * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time
105 */
106static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
107 const auto proper_handle = static_cast<Handle>(thread_handle);
108
109 // Lock the global kernel mutex when we enter the kernel HLE.
110 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
111
112 SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>(proper_handle);
113 if (thread == nullptr) {
114 LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle);
115 return;
116 }
117
118 bool resume = true;
119
120 if (thread->status == ThreadStatus::WaitSynchAny ||
121 thread->status == ThreadStatus::WaitSynchAll ||
122 thread->status == ThreadStatus::WaitHLEEvent) {
123 // Remove the thread from each of its waiting objects' waitlists
124 for (auto& object : thread->wait_objects)
125 object->RemoveWaitingThread(thread.get());
126 thread->wait_objects.clear();
127
128 // Invoke the wakeup callback before clearing the wait objects
129 if (thread->wakeup_callback)
130 resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
131 }
132
133 if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
134 thread->wait_handle) {
135 ASSERT(thread->status == ThreadStatus::WaitMutex);
136 thread->mutex_wait_address = 0;
137 thread->condvar_wait_address = 0;
138 thread->wait_handle = 0;
139
140 auto lock_owner = thread->lock_owner;
141 // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
142 // and don't have a lock owner unless SignalProcessWideKey was called first and the thread
143 // wasn't awakened due to the mutex already being acquired.
144 if (lock_owner) {
145 lock_owner->RemoveMutexWaiter(thread);
146 }
147 }
148
149 if (thread->arb_wait_address != 0) {
150 ASSERT(thread->status == ThreadStatus::WaitArb);
151 thread->arb_wait_address = 0;
152 }
153
154 if (resume)
155 thread->ResumeFromWait();
156}
157
158void Thread::WakeAfterDelay(s64 nanoseconds) { 84void Thread::WakeAfterDelay(s64 nanoseconds) {
159 // Don't schedule a wakeup if the thread wants to wait forever 85 // Don't schedule a wakeup if the thread wants to wait forever
160 if (nanoseconds == -1) 86 if (nanoseconds == -1)
@@ -162,12 +88,12 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
162 88
163 // This function might be called from any thread so we have to be cautious and use the 89 // This function might be called from any thread so we have to be cautious and use the
164 // thread-safe version of ScheduleEvent. 90 // thread-safe version of ScheduleEvent.
165 CoreTiming::ScheduleEventThreadsafe(CoreTiming::nsToCycles(nanoseconds), ThreadWakeupEventType, 91 CoreTiming::ScheduleEventThreadsafe(CoreTiming::nsToCycles(nanoseconds),
166 callback_handle); 92 kernel.ThreadWakeupCallbackEventType(), callback_handle);
167} 93}
168 94
169void Thread::CancelWakeupTimer() { 95void Thread::CancelWakeupTimer() {
170 CoreTiming::UnscheduleEventThreadsafe(ThreadWakeupEventType, callback_handle); 96 CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
171} 97}
172 98
173static boost::optional<s32> GetNextProcessorId(u64 mask) { 99static boost::optional<s32> GetNextProcessorId(u64 mask) {
@@ -294,9 +220,9 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAdd
294 context.fpscr = 0; 220 context.fpscr = 0;
295} 221}
296 222
297ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority, 223ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
298 u64 arg, s32 processor_id, VAddr stack_top, 224 u32 priority, u64 arg, s32 processor_id,
299 SharedPtr<Process> owner_process) { 225 VAddr stack_top, SharedPtr<Process> owner_process) {
300 // Check if priority is in ranged. Lowest priority -> highest priority id. 226 // Check if priority is in ranged. Lowest priority -> highest priority id.
301 if (priority > THREADPRIO_LOWEST) { 227 if (priority > THREADPRIO_LOWEST) {
302 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); 228 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
@@ -316,9 +242,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
316 return ResultCode(-1); 242 return ResultCode(-1);
317 } 243 }
318 244
319 SharedPtr<Thread> thread(new Thread); 245 SharedPtr<Thread> thread(new Thread(kernel));
320 246
321 thread->thread_id = NewThreadId(); 247 thread->thread_id = kernel.CreateNewThreadID();
322 thread->status = ThreadStatus::Dormant; 248 thread->status = ThreadStatus::Dormant;
323 thread->entry_point = entry_point; 249 thread->entry_point = entry_point;
324 thread->stack_top = stack_top; 250 thread->stack_top = stack_top;
@@ -333,7 +259,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
333 thread->condvar_wait_address = 0; 259 thread->condvar_wait_address = 0;
334 thread->wait_handle = 0; 260 thread->wait_handle = 0;
335 thread->name = std::move(name); 261 thread->name = std::move(name);
336 thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); 262 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
337 thread->owner_process = owner_process; 263 thread->owner_process = owner_process;
338 thread->scheduler = Core::System::GetInstance().Scheduler(processor_id); 264 thread->scheduler = Core::System::GetInstance().Scheduler(processor_id);
339 thread->scheduler->AddThread(thread, priority); 265 thread->scheduler->AddThread(thread, priority);
@@ -383,19 +309,19 @@ void Thread::BoostPriority(u32 priority) {
383 current_priority = priority; 309 current_priority = priority;
384} 310}
385 311
386SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, 312SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
387 SharedPtr<Process> owner_process) { 313 SharedPtr<Process> owner_process) {
388 // Setup page table so we can write to memory 314 // Setup page table so we can write to memory
389 SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); 315 SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
390 316
391 // Initialize new "main" thread 317 // Initialize new "main" thread
392 auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, 318 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
393 Memory::STACK_AREA_VADDR_END, std::move(owner_process)); 319 Memory::STACK_AREA_VADDR_END, std::move(owner_process));
394 320
395 SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); 321 SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
396 322
397 // Register 1 must be a handle to the main thread 323 // Register 1 must be a handle to the main thread
398 thread->guest_handle = Kernel::g_handle_table.Create(thread).Unwrap(); 324 thread->guest_handle = kernel.HandleTable().Create(thread).Unwrap();
399 325
400 thread->context.cpu_registers[1] = thread->guest_handle; 326 thread->context.cpu_registers[1] = thread->guest_handle;
401 327
@@ -528,13 +454,4 @@ Thread* GetCurrentThread() {
528 return Core::System::GetInstance().CurrentScheduler().GetCurrentThread(); 454 return Core::System::GetInstance().CurrentScheduler().GetCurrentThread();
529} 455}
530 456
531void ThreadingInit() {
532 ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
533 next_thread_id = 1;
534}
535
536void ThreadingShutdown() {
537 Kernel::ClearProcessList();
538}
539
540} // namespace Kernel 457} // namespace Kernel