summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/thread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
-rw-r--r--src/core/hle/kernel/thread.cpp203
1 files changed, 14 insertions, 189 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 130b669a0..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,112 +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
112Thread* ArbitrateHighestPriorityThread(u32 address) {
113 Thread* highest_priority_thread = nullptr;
114 u32 priority = THREADPRIO_LOWEST;
115
116 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
117 for (auto& thread : thread_list) {
118 if (!CheckWait_AddressArbiter(thread.get(), address))
119 continue;
120
121 if (thread == nullptr)
122 continue;
123
124 if (thread->current_priority <= priority) {
125 highest_priority_thread = thread.get();
126 priority = thread->current_priority;
127 }
128 }
129
130 // If a thread was arbitrated, resume it
131 if (nullptr != highest_priority_thread) {
132 highest_priority_thread->ResumeFromWait();
133 }
134
135 return highest_priority_thread;
136}
137
138void ArbitrateAllThreads(u32 address) {
139 // Resume all threads found to be waiting on the address
140 for (auto& thread : thread_list) {
141 if (CheckWait_AddressArbiter(thread.get(), address))
142 thread->ResumeFromWait();
143 }
144}
145
146/**
147 * Switches the CPU's active thread context to that of the specified thread
148 * @param new_thread The thread to switch to
149 */
150static void SwitchContext(Thread* new_thread) {
151 Thread* previous_thread = GetCurrentThread();
152
153 // Save context for previous thread
154 if (previous_thread) {
155 previous_thread->last_running_ticks = CoreTiming::GetTicks();
156 Core::CPU().SaveContext(previous_thread->context);
157
158 if (previous_thread->status == THREADSTATUS_RUNNING) {
159 // This is only the case when a reschedule is triggered without the current thread
160 // yielding execution (i.e. an event triggered, system core time-sliced, etc)
161 ready_queue.push_front(previous_thread->current_priority, previous_thread);
162 previous_thread->status = THREADSTATUS_READY;
163 }
164 }
165
166 // Load context of new thread
167 if (new_thread) {
168 ASSERT_MSG(new_thread->status == THREADSTATUS_READY,
169 "Thread must be ready to become running.");
170
171 // Cancel any outstanding wakeup events for this thread
172 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle);
173
174 auto previous_process = Kernel::g_current_process;
175
176 current_thread = new_thread;
177
178 ready_queue.remove(new_thread->current_priority, new_thread);
179 new_thread->status = THREADSTATUS_RUNNING;
180
181 if (previous_process != current_thread->owner_process) {
182 Kernel::g_current_process = current_thread->owner_process;
183 SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
184 }
185
186 Core::CPU().LoadContext(new_thread->context);
187 Core::CPU().SetTlsAddress(new_thread->GetTLSAddress());
188 } else {
189 current_thread = nullptr;
190 // Note: We do not reset the current process and current page table when idling because
191 // technically we haven't changed processes, our threads are just paused.
192 }
193}
194
195/**
196 * Pops and returns the next thread from the thread queue
197 * @return A pointer to the next ready thread
198 */
199static Thread* PopNextReadyThread() {
200 Thread* next;
201 Thread* thread = GetCurrentThread();
202
203 if (thread && thread->status == THREADSTATUS_RUNNING) {
204 // We have to do better than the current thread.
205 // This call returns null when that's not possible.
206 next = ready_queue.pop_first_better(thread->current_priority);
207 if (!next) {
208 // Otherwise just keep going with the current thread
209 next = thread;
210 }
211 } else {
212 next = ready_queue.pop_first();
213 }
214
215 return next;
216}
217
218void WaitCurrentThread_Sleep() { 100void WaitCurrentThread_Sleep() {
219 Thread* thread = GetCurrentThread(); 101 Thread* thread = GetCurrentThread();
220 thread->status = THREADSTATUS_WAIT_SLEEP; 102 thread->status = THREADSTATUS_WAIT_SLEEP;
@@ -229,8 +111,7 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
229void ExitCurrentThread() { 111void ExitCurrentThread() {
230 Thread* thread = GetCurrentThread(); 112 Thread* thread = GetCurrentThread();
231 thread->Stop(); 113 thread->Stop();
232 thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), 114 Core::System::GetInstance().Scheduler().RemoveThread(thread);
233 thread_list.end());
234} 115}
235 116
236/** 117/**
@@ -308,32 +189,12 @@ void Thread::ResumeFromWait() {
308 189
309 wakeup_callback = nullptr; 190 wakeup_callback = nullptr;
310 191
311 ready_queue.push_back(current_priority, this);
312 status = THREADSTATUS_READY; 192 status = THREADSTATUS_READY;
193 Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority);
313 Core::System::GetInstance().PrepareReschedule(); 194 Core::System::GetInstance().PrepareReschedule();
314} 195}
315 196
316/** 197/**
317 * Prints the thread queue for debugging purposes
318 */
319static void DebugThreadQueue() {
320 Thread* thread = GetCurrentThread();
321 if (!thread) {
322 LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD");
323 } else {
324 LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority,
325 GetCurrentThread()->GetObjectId());
326 }
327
328 for (auto& t : thread_list) {
329 u32 priority = ready_queue.contains(t.get());
330 if (priority != -1) {
331 LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId());
332 }
333 }
334}
335
336/**
337 * Finds a free location for the TLS section of a thread. 198 * Finds a free location for the TLS section of a thread.
338 * @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.
339 * Returns a tuple of (page, slot, alloc_needed) where: 200 * Returns a tuple of (page, slot, alloc_needed) where:
@@ -400,8 +261,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
400 261
401 SharedPtr<Thread> thread(new Thread); 262 SharedPtr<Thread> thread(new Thread);
402 263
403 thread_list.push_back(thread); 264 Core::System::GetInstance().Scheduler().AddThread(thread, priority);
404 ready_queue.prepare(priority);
405 265
406 thread->thread_id = NewThreadId(); 266 thread->thread_id = NewThreadId();
407 thread->status = THREADSTATUS_DORMANT; 267 thread->status = THREADSTATUS_DORMANT;
@@ -472,12 +332,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
472void Thread::SetPriority(u32 priority) { 332void Thread::SetPriority(u32 priority) {
473 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, 333 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
474 "Invalid priority value."); 334 "Invalid priority value.");
475 // If thread was ready, adjust queues 335 Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
476 if (status == THREADSTATUS_READY)
477 ready_queue.move(this, current_priority, priority);
478 else
479 ready_queue.prepare(priority);
480
481 nominal_priority = current_priority = priority; 336 nominal_priority = current_priority = priority;
482} 337}
483 338
@@ -491,11 +346,7 @@ void Thread::UpdatePriority() {
491} 346}
492 347
493void Thread::BoostPriority(u32 priority) { 348void Thread::BoostPriority(u32 priority) {
494 // If thread was ready, adjust queues 349 Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
495 if (status == THREADSTATUS_READY)
496 ready_queue.move(this, current_priority, priority);
497 else
498 ready_queue.prepare(priority);
499 current_priority = priority; 350 current_priority = priority;
500} 351}
501 352
@@ -521,25 +372,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
521 return thread; 372 return thread;
522} 373}
523 374
524bool HaveReadyThreads() {
525 return ready_queue.get_first() != nullptr;
526}
527
528void Reschedule() {
529 Thread* cur = GetCurrentThread();
530 Thread* next = PopNextReadyThread();
531
532 if (cur && next) {
533 LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId());
534 } else if (cur) {
535 LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId());
536 } else if (next) {
537 LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId());
538 }
539
540 SwitchContext(next);
541}
542
543void Thread::SetWaitSynchronizationResult(ResultCode result) { 375void Thread::SetWaitSynchronizationResult(ResultCode result) {
544 context.cpu_registers[0] = result.raw; 376 context.cpu_registers[0] = result.raw;
545} 377}
@@ -562,25 +394,18 @@ VAddr Thread::GetCommandBufferAddress() const {
562 394
563//////////////////////////////////////////////////////////////////////////////////////////////////// 395////////////////////////////////////////////////////////////////////////////////////////////////////
564 396
397/**
398 * Gets the current thread
399 */
400Thread* GetCurrentThread() {
401 return Core::System::GetInstance().Scheduler().GetCurrentThread();
402}
403
565void ThreadingInit() { 404void ThreadingInit() {
566 ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); 405 ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
567
568 current_thread = nullptr;
569 next_thread_id = 1; 406 next_thread_id = 1;
570} 407}
571 408
572void ThreadingShutdown() { 409void ThreadingShutdown() {}
573 current_thread = nullptr;
574
575 for (auto& t : thread_list) {
576 t->Stop();
577 }
578 thread_list.clear();
579 ready_queue.clear();
580}
581
582const std::vector<SharedPtr<Thread>>& GetThreadList() {
583 return thread_list;
584}
585 410
586} // namespace Kernel 411} // namespace Kernel