summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2018-02-18 15:17:16 -0500
committerGravatar bunnei2018-02-18 15:17:16 -0500
commitac81c02ed9401b137e83bece2edd6dee8d0a0af2 (patch)
treef849c805fdb9ac756ece2e2f72d6009c58676bd6
parentkernel: Add Scheduler, which encapsulates the scheduling loading from Thread ... (diff)
downloadyuzu-ac81c02ed9401b137e83bece2edd6dee8d0a0af2.tar.gz
yuzu-ac81c02ed9401b137e83bece2edd6dee8d0a0af2.tar.xz
yuzu-ac81c02ed9401b137e83bece2edd6dee8d0a0af2.zip
kernel: Use Scheduler class for threading.
Diffstat (limited to '')
-rw-r--r--src/core/core.cpp3
-rw-r--r--src/core/core.h6
-rw-r--r--src/core/hle/kernel/svc.cpp4
-rw-r--r--src/core/hle/kernel/thread.cpp169
-rw-r--r--src/core/hle/kernel/thread.h15
-rw-r--r--src/yuzu/debugger/wait_tree.cpp3
6 files changed, 26 insertions, 174 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 89bb2887e..cef697dfe 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -133,7 +133,7 @@ void System::Reschedule() {
133 } 133 }
134 134
135 reschedule_pending = false; 135 reschedule_pending = false;
136 Kernel::Reschedule(); 136 Core::System::GetInstance().Scheduler().Reschedule();
137} 137}
138 138
139System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { 139System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
@@ -154,6 +154,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
154 break; 154 break;
155 } 155 }
156 156
157 scheduler = std::make_unique<Kernel::Scheduler>(cpu_core);
157 gpu_core = std::make_unique<Tegra::GPU>(); 158 gpu_core = std::make_unique<Tegra::GPU>();
158 159
159 telemetry_session = std::make_unique<Core::TelemetrySession>(); 160 telemetry_session = std::make_unique<Core::TelemetrySession>();
diff --git a/src/core/core.h b/src/core/core.h
index e7599e18c..ada23b347 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/kernel/scheduler.h"
10#include "core/loader/loader.h" 11#include "core/loader/loader.h"
11#include "core/memory.h" 12#include "core/memory.h"
12#include "core/perf_stats.h" 13#include "core/perf_stats.h"
@@ -107,6 +108,10 @@ public:
107 return *gpu_core; 108 return *gpu_core;
108 } 109 }
109 110
111 Kernel::Scheduler& Scheduler() {
112 return *scheduler;
113 }
114
110 PerfStats perf_stats; 115 PerfStats perf_stats;
111 FrameLimiter frame_limiter; 116 FrameLimiter frame_limiter;
112 117
@@ -141,6 +146,7 @@ private:
141 std::unique_ptr<Loader::AppLoader> app_loader; 146 std::unique_ptr<Loader::AppLoader> app_loader;
142 147
143 std::shared_ptr<ARM_Interface> cpu_core; 148 std::shared_ptr<ARM_Interface> cpu_core;
149 std::unique_ptr<Kernel::Scheduler> scheduler;
144 std::unique_ptr<Tegra::GPU> gpu_core; 150 std::unique_ptr<Tegra::GPU> gpu_core;
145 151
146 /// When true, signals that a reschedule should happen 152 /// When true, signals that a reschedule should happen
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 4d20ef134..cbd5b69aa 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -483,7 +483,7 @@ static void ExitProcess() {
483 g_current_process->status = ProcessStatus::Exited; 483 g_current_process->status = ProcessStatus::Exited;
484 484
485 // Stop all the process threads that are currently waiting for objects. 485 // Stop all the process threads that are currently waiting for objects.
486 auto& thread_list = GetThreadList(); 486 auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
487 for (auto& thread : thread_list) { 487 for (auto& thread : thread_list) {
488 if (thread->owner_process != g_current_process) 488 if (thread->owner_process != g_current_process)
489 continue; 489 continue;
@@ -585,7 +585,7 @@ static void SleepThread(s64 nanoseconds) {
585 585
586 // Don't attempt to yield execution if there are no available threads to run, 586 // Don't attempt to yield execution if there are no available threads to run,
587 // this way we avoid a useless reschedule to the idle thread. 587 // this way we avoid a useless reschedule to the idle thread.
588 if (nanoseconds == 0 && !HaveReadyThreads()) 588 if (nanoseconds == 0 && !Core::System::GetInstance().Scheduler().HaveReadyThreads())
589 return; 589 return;
590 590
591 // Sleep current thread and check for next thread to schedule 591 // Sleep current thread and check for next thread to schedule
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 0fcc65cbf..dd0a8ae48 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -41,14 +41,6 @@ void Thread::Acquire(Thread* thread) {
41// us to simply use a pool index or similar. 41// us to simply use a pool index or similar.
42static Kernel::HandleTable wakeup_callback_handle_table; 42static Kernel::HandleTable wakeup_callback_handle_table;
43 43
44// Lists all thread ids that aren't deleted/etc.
45static std::vector<SharedPtr<Thread>> thread_list;
46
47// Lists only ready thread ids.
48static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
49
50static SharedPtr<Thread> current_thread;
51
52// The first available thread id at startup 44// The first available thread id at startup
53static u32 next_thread_id; 45static u32 next_thread_id;
54 46
@@ -63,10 +55,6 @@ inline static u32 const NewThreadId() {
63Thread::Thread() {} 55Thread::Thread() {}
64Thread::~Thread() {} 56Thread::~Thread() {}
65 57
66Thread* GetCurrentThread() {
67 return current_thread.get();
68}
69
70/** 58/**
71 * Check if the specified thread is waiting on the specified address to be arbitrated 59 * Check if the specified thread is waiting on the specified address to be arbitrated
72 * @param thread The thread to test 60 * @param thread The thread to test
@@ -86,7 +74,7 @@ void Thread::Stop() {
86 // Clean up thread from ready queue 74 // Clean up thread from ready queue
87 // This is only needed when the thread is termintated forcefully (SVC TerminateProcess) 75 // This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
88 if (status == THREADSTATUS_READY) { 76 if (status == THREADSTATUS_READY) {
89 ready_queue.remove(current_priority, this); 77 Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority);
90 } 78 }
91 79
92 status = THREADSTATUS_DEAD; 80 status = THREADSTATUS_DEAD;
@@ -109,78 +97,6 @@ void Thread::Stop() {
109 Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot); 97 Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot);
110} 98}
111 99
112/**
113 * Switches the CPU's active thread context to that of the specified thread
114 * @param new_thread The thread to switch to
115 */
116static void SwitchContext(Thread* new_thread) {
117 Thread* previous_thread = GetCurrentThread();
118
119 // Save context for previous thread
120 if (previous_thread) {
121 previous_thread->last_running_ticks = CoreTiming::GetTicks();
122 Core::CPU().SaveContext(previous_thread->context);
123
124 if (previous_thread->status == THREADSTATUS_RUNNING) {
125 // This is only the case when a reschedule is triggered without the current thread
126 // yielding execution (i.e. an event triggered, system core time-sliced, etc)
127 ready_queue.push_front(previous_thread->current_priority, previous_thread);
128 previous_thread->status = THREADSTATUS_READY;
129 }
130 }
131
132 // Load context of new thread
133 if (new_thread) {
134 ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
135 "Thread must be ready to become running.");
136
137 // Cancel any outstanding wakeup events for this thread
138 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
139
140 auto previous_process = Kernel::g_current_process;
141
142 current_thread = new_thread;
143
144 ready_queue.remove(new_thread->current_priority, new_thread);
145 new_thread->status = THREADSTATUS_RUNNING;
146
147 if (previous_process != current_thread->owner_process) {
148 Kernel::g_current_process = current_thread->owner_process;
149 SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
150 }
151
152 Core::CPU().LoadContext(new_thread->context);
153 Core::CPU().SetTlsAddress(new_thread->GetTLSAddress());
154 } else {
155 current_thread = nullptr;
156 // Note: We do not reset the current process and current page table when idling because
157 // technically we haven't changed processes, our threads are just paused.
158 }
159}
160
161/**
162 * Pops and returns the next thread from the thread queue
163 * @return A pointer to the next ready thread
164 */
165static Thread* PopNextReadyThread() {
166 Thread* next;
167 Thread* thread = GetCurrentThread();
168
169 if (thread && thread->status == THREADSTATUS_RUNNING) {
170 // We have to do better than the current thread.
171 // This call returns null when that's not possible.
172 next = ready_queue.pop_first_better(thread->current_priority);
173 if (!next) {
174 // Otherwise just keep going with the current thread
175 next = thread;
176 }
177 } else {
178 next = ready_queue.pop_first();
179 }
180
181 return next;
182}
183
184void WaitCurrentThread_Sleep() { 100void WaitCurrentThread_Sleep() {
185 Thread* thread = GetCurrentThread(); 101 Thread* thread = GetCurrentThread();
186 thread->status = THREADSTATUS_WAIT_SLEEP; 102 thread->status = THREADSTATUS_WAIT_SLEEP;
@@ -195,8 +111,7 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
195void ExitCurrentThread() { 111void ExitCurrentThread() {
196 Thread* thread = GetCurrentThread(); 112 Thread* thread = GetCurrentThread();
197 thread->Stop(); 113 thread->Stop();
198 thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), 114 Core::System::GetInstance().Scheduler().RemoveThread(thread);
199 thread_list.end());
200} 115}
201 116
202/** 117/**
@@ -274,32 +189,12 @@ void Thread::ResumeFromWait() {
274 189
275 wakeup_callback = nullptr; 190 wakeup_callback = nullptr;
276 191
277 ready_queue.push_back(current_priority, this);
278 status = THREADSTATUS_READY; 192 status = THREADSTATUS_READY;
193 Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority);
279 Core::System::GetInstance().PrepareReschedule(); 194 Core::System::GetInstance().PrepareReschedule();
280} 195}
281 196
282/** 197/**
283 * Prints the thread queue for debugging purposes
284 */
285static void DebugThreadQueue() {
286 Thread* thread = GetCurrentThread();
287 if (!thread) {
288 LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD");
289 } else {
290 LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority,
291 GetCurrentThread()->GetObjectId());
292 }
293
294 for (auto& t : thread_list) {
295 u32 priority = ready_queue.contains(t.get());
296 if (priority != -1) {
297 LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId());
298 }
299 }
300}
301
302/**
303 * Finds a free location for the TLS section of a thread. 198 * Finds a free location for the TLS section of a thread.
304 * @param tls_slots The TLS page array of the thread's owner process. 199 * @param tls_slots The TLS page array of the thread's owner process.
305 * Returns a tuple of (page, slot, alloc_needed) where: 200 * Returns a tuple of (page, slot, alloc_needed) where:
@@ -366,8 +261,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
366 261
367 SharedPtr<Thread> thread(new Thread); 262 SharedPtr<Thread> thread(new Thread);
368 263
369 thread_list.push_back(thread); 264 Core::System::GetInstance().Scheduler().AddThread(thread, priority);
370 ready_queue.prepare(priority);
371 265
372 thread->thread_id = NewThreadId(); 266 thread->thread_id = NewThreadId();
373 thread->status = THREADSTATUS_DORMANT; 267 thread->status = THREADSTATUS_DORMANT;
@@ -438,12 +332,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
438void Thread::SetPriority(u32 priority) { 332void Thread::SetPriority(u32 priority) {
439 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, 333 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
440 "Invalid priority value."); 334 "Invalid priority value.");
441 // If thread was ready, adjust queues 335 Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
442 if (status == THREADSTATUS_READY)
443 ready_queue.move(this, current_priority, priority);
444 else
445 ready_queue.prepare(priority);
446
447 nominal_priority = current_priority = priority; 336 nominal_priority = current_priority = priority;
448} 337}
449 338
@@ -457,11 +346,7 @@ void Thread::UpdatePriority() {
457} 346}
458 347
459void Thread::BoostPriority(u32 priority) { 348void Thread::BoostPriority(u32 priority) {
460 // If thread was ready, adjust queues 349 Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
461 if (status == THREADSTATUS_READY)
462 ready_queue.move(this, current_priority, priority);
463 else
464 ready_queue.prepare(priority);
465 current_priority = priority; 350 current_priority = priority;
466} 351}
467 352
@@ -487,25 +372,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
487 return thread; 372 return thread;
488} 373}
489 374
490bool HaveReadyThreads() {
491 return ready_queue.get_first() != nullptr;
492}
493
494void Reschedule() {
495 Thread* cur = GetCurrentThread();
496 Thread* next = PopNextReadyThread();
497
498 if (cur && next) {
499 LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId());
500 } else if (cur) {
501 LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId());
502 } else if (next) {
503 LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId());
504 }
505
506 SwitchContext(next);
507}
508
509void Thread::SetWaitSynchronizationResult(ResultCode result) { 375void Thread::SetWaitSynchronizationResult(ResultCode result) {
510 context.cpu_registers[0] = result.raw; 376 context.cpu_registers[0] = result.raw;
511} 377}
@@ -528,25 +394,18 @@ VAddr Thread::GetCommandBufferAddress() const {
528 394
529//////////////////////////////////////////////////////////////////////////////////////////////////// 395////////////////////////////////////////////////////////////////////////////////////////////////////
530 396
397/**
398 * Gets the current thread
399 */
400Thread* GetCurrentThread() {
401 return Core::System::GetInstance().Scheduler().GetCurrentThread();
402}
403
531void ThreadingInit() { 404void ThreadingInit() {
532 ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); 405 ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
533
534 current_thread = nullptr;
535 next_thread_id = 1; 406 next_thread_id = 1;
536} 407}
537 408
538void ThreadingShutdown() { 409void ThreadingShutdown() {}
539 current_thread = nullptr;
540
541 for (auto& t : thread_list) {
542 t->Stop();
543 }
544 thread_list.clear();
545 ready_queue.clear();
546}
547
548const std::vector<SharedPtr<Thread>>& GetThreadList() {
549 return thread_list;
550}
551 410
552} // namespace Kernel 411} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index aa80a51a9..4fd2fc2f8 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -250,16 +250,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
250 SharedPtr<Process> owner_process); 250 SharedPtr<Process> owner_process);
251 251
252/** 252/**
253 * Returns whether there are any threads that are ready to run.
254 */
255bool HaveReadyThreads();
256
257/**
258 * Reschedules to the next available thread (call after current thread is suspended)
259 */
260void Reschedule();
261
262/**
263 * Gets the current thread 253 * Gets the current thread
264 */ 254 */
265Thread* GetCurrentThread(); 255Thread* GetCurrentThread();
@@ -290,9 +280,4 @@ void ThreadingInit();
290 */ 280 */
291void ThreadingShutdown(); 281void ThreadingShutdown();
292 282
293/**
294 * Get a const reference to the thread list for debug use
295 */
296const std::vector<SharedPtr<Thread>>& GetThreadList();
297
298} // namespace Kernel 283} // namespace Kernel
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index e4a6d16ae..7a62f57b5 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -5,6 +5,7 @@
5#include "yuzu/debugger/wait_tree.h" 5#include "yuzu/debugger/wait_tree.h"
6#include "yuzu/util/util.h" 6#include "yuzu/util/util.h"
7 7
8#include "core/core.h"
8#include "core/hle/kernel/condition_variable.h" 9#include "core/hle/kernel/condition_variable.h"
9#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/mutex.h" 11#include "core/hle/kernel/mutex.h"
@@ -50,7 +51,7 @@ std::size_t WaitTreeItem::Row() const {
50} 51}
51 52
52std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() { 53std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() {
53 const auto& threads = Kernel::GetThreadList(); 54 const auto& threads = Core::System::GetInstance().Scheduler().GetThreadList();
54 std::vector<std::unique_ptr<WaitTreeThread>> item_list; 55 std::vector<std::unique_ptr<WaitTreeThread>> item_list;
55 item_list.reserve(threads.size()); 56 item_list.reserve(threads.size());
56 for (std::size_t i = 0; i < threads.size(); ++i) { 57 for (std::size_t i = 0; i < threads.size(); ++i) {