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.cpp360
1 files changed, 175 insertions, 185 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 3987f9608..7f629c20e 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -21,8 +21,11 @@
21 21
22namespace Kernel { 22namespace Kernel {
23 23
24/// Event type for the thread wake up event
25static int ThreadWakeupEventType = -1;
26
24bool Thread::ShouldWait() { 27bool Thread::ShouldWait() {
25 return status != THREADSTATUS_DORMANT; 28 return status != THREADSTATUS_DEAD;
26} 29}
27 30
28void Thread::Acquire() { 31void Thread::Acquire() {
@@ -33,12 +36,20 @@ void Thread::Acquire() {
33static std::vector<SharedPtr<Thread>> thread_list; 36static std::vector<SharedPtr<Thread>> thread_list;
34 37
35// Lists only ready thread ids. 38// Lists only ready thread ids.
36static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> thread_ready_queue; 39static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> ready_queue;
37 40
38static Thread* current_thread; 41static Thread* current_thread;
39 42
40static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup 43// The first available thread id at startup
41static u32 next_thread_id; ///< The next available thread id 44static u32 next_thread_id = 1;
45
46/**
47 * Creates a new thread ID
48 * @return The new thread ID
49 */
50inline static u32 const NewThreadId() {
51 return next_thread_id++;
52}
42 53
43Thread::Thread() {} 54Thread::Thread() {}
44Thread::~Thread() {} 55Thread::~Thread() {}
@@ -47,86 +58,53 @@ Thread* GetCurrentThread() {
47 return current_thread; 58 return current_thread;
48} 59}
49 60
50/// Resets a thread 61/**
51static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { 62 * Check if a thread is waiting on the specified wait object
52 memset(&t->context, 0, sizeof(Core::ThreadContext)); 63 * @param thread The thread to test
53 64 * @param wait_object The object to test against
54 t->context.cpu_registers[0] = arg; 65 * @return True if the thread is waiting, false otherwise
55 t->context.pc = t->entry_point; 66 */
56 t->context.sp = t->stack_top;
57 t->context.cpsr = 0x1F; // Usermode
58
59 // TODO(bunnei): This instructs the CPU core to start the execution as if it is "resuming" a
60 // thread. This is somewhat Sky-Eye specific, and should be re-architected in the future to be
61 // agnostic of the CPU core.
62 t->context.mode = 8;
63
64 if (t->current_priority < lowest_priority) {
65 t->current_priority = t->initial_priority;
66 }
67
68 t->wait_objects.clear();
69 t->wait_address = 0;
70}
71
72/// Change a thread to "ready" state
73static void ChangeReadyState(Thread* t, bool ready) {
74 if (t->IsReady()) {
75 if (!ready) {
76 thread_ready_queue.remove(t->current_priority, t);
77 }
78 } else if (ready) {
79 if (t->IsRunning()) {
80 thread_ready_queue.push_front(t->current_priority, t);
81 } else {
82 thread_ready_queue.push_back(t->current_priority, t);
83 }
84 t->status = THREADSTATUS_READY;
85 }
86}
87
88/// Check if a thread is waiting on a the specified wait object
89static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { 67static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) {
90 auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); 68 if (thread->status != THREADSTATUS_WAIT_SYNCH)
69 return false;
91 70
92 if (itr != thread->wait_objects.end()) 71 auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object);
93 return thread->IsWaiting(); 72 return itr != thread->wait_objects.end();
94
95 return false;
96} 73}
97 74
98/// Check if the specified thread is waiting on the specified address to be arbitrated 75/**
76 * Check if the specified thread is waiting on the specified address to be arbitrated
77 * @param thread The thread to test
78 * @param wait_address The address to test against
79 * @return True if the thread is waiting, false otherwise
80 */
99static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { 81static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) {
100 return thread->IsWaiting() && thread->wait_objects.empty() && wait_address == thread->wait_address; 82 return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address;
101} 83}
102 84
103/// Stops the current thread 85void Thread::Stop() {
104void Thread::Stop(const char* reason) {
105 // Release all the mutexes that this thread holds 86 // Release all the mutexes that this thread holds
106 ReleaseThreadMutexes(this); 87 ReleaseThreadMutexes(this);
107 88
108 ChangeReadyState(this, false); 89 // Cancel any outstanding wakeup events for this thread
109 status = THREADSTATUS_DORMANT; 90 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
91
92 // Clean up thread from ready queue
93 // This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
94 if (status == THREADSTATUS_READY){
95 ready_queue.remove(current_priority, this);
96 }
97
98 status = THREADSTATUS_DEAD;
99
110 WakeupAllWaitingThreads(); 100 WakeupAllWaitingThreads();
111 101
112 // Stopped threads are never waiting. 102 // Clean up any dangling references in objects that this thread was waiting for
113 for (auto& wait_object : wait_objects) { 103 for (auto& wait_object : wait_objects) {
114 wait_object->RemoveWaitingThread(this); 104 wait_object->RemoveWaitingThread(this);
115 } 105 }
116 wait_objects.clear();
117 wait_address = 0;
118}
119
120/// Changes a threads state
121static void ChangeThreadState(Thread* t, ThreadStatus new_status) {
122 if (!t || t->status == new_status) {
123 return;
124 }
125 ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0);
126 t->status = new_status;
127} 106}
128 107
129/// Arbitrate the highest priority thread that is waiting
130Thread* ArbitrateHighestPriorityThread(u32 address) { 108Thread* ArbitrateHighestPriorityThread(u32 address) {
131 Thread* highest_priority_thread = nullptr; 109 Thread* highest_priority_thread = nullptr;
132 s32 priority = THREADPRIO_LOWEST; 110 s32 priority = THREADPRIO_LOWEST;
@@ -153,108 +131,113 @@ Thread* ArbitrateHighestPriorityThread(u32 address) {
153 return highest_priority_thread; 131 return highest_priority_thread;
154} 132}
155 133
156/// Arbitrate all threads currently waiting
157void ArbitrateAllThreads(u32 address) { 134void ArbitrateAllThreads(u32 address) {
158 135 // Resume all threads found to be waiting on the address
159 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
160 for (auto& thread : thread_list) { 136 for (auto& thread : thread_list) {
161 if (CheckWait_AddressArbiter(thread.get(), address)) 137 if (CheckWait_AddressArbiter(thread.get(), address))
162 thread->ResumeFromWait(); 138 thread->ResumeFromWait();
163 } 139 }
164} 140}
165 141
166/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) 142/**
167static void CallThread(Thread* t) { 143 * Switches the CPU's active thread context to that of the specified thread
168 // Stop waiting 144 * @param new_thread The thread to switch to
169 ChangeThreadState(t, THREADSTATUS_READY); 145 */
170} 146static void SwitchContext(Thread* new_thread) {
147 _dbg_assert_msg_(Kernel, new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running.");
171 148
172/// Switches CPU context to that of the specified thread 149 Thread* previous_thread = GetCurrentThread();
173static void SwitchContext(Thread* t) {
174 Thread* cur = GetCurrentThread();
175 150
176 // Save context for current thread 151 // Save context for previous thread
177 if (cur) { 152 if (previous_thread) {
178 Core::g_app_core->SaveContext(cur->context); 153 Core::g_app_core->SaveContext(previous_thread->context);
179 154
180 if (cur->IsRunning()) { 155 if (previous_thread->status == THREADSTATUS_RUNNING) {
181 ChangeReadyState(cur, true); 156 // This is only the case when a reschedule is triggered without the current thread
157 // yielding execution (i.e. an event triggered, system core time-sliced, etc)
158 ready_queue.push_front(previous_thread->current_priority, previous_thread);
159 previous_thread->status = THREADSTATUS_READY;
182 } 160 }
183 } 161 }
162
184 // Load context of new thread 163 // Load context of new thread
185 if (t) { 164 if (new_thread) {
186 current_thread = t; 165 current_thread = new_thread;
187 ChangeReadyState(t, false); 166
188 t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; 167 ready_queue.remove(new_thread->current_priority, new_thread);
189 Core::g_app_core->LoadContext(t->context); 168 new_thread->status = THREADSTATUS_RUNNING;
169
170 Core::g_app_core->LoadContext(new_thread->context);
190 } else { 171 } else {
191 current_thread = nullptr; 172 current_thread = nullptr;
192 } 173 }
193} 174}
194 175
195/// Gets the next thread that is ready to be run by priority 176/**
196static Thread* NextThread() { 177 * Pops and returns the next thread from the thread queue
178 * @return A pointer to the next ready thread
179 */
180static Thread* PopNextReadyThread() {
197 Thread* next; 181 Thread* next;
198 Thread* cur = GetCurrentThread(); 182 Thread* thread = GetCurrentThread();
199 183
200 if (cur && cur->IsRunning()) { 184 if (thread && thread->status == THREADSTATUS_RUNNING) {
201 next = thread_ready_queue.pop_first_better(cur->current_priority); 185 // We have to do better than the current thread.
186 // This call returns null when that's not possible.
187 next = ready_queue.pop_first_better(thread->current_priority);
202 } else { 188 } else {
203 next = thread_ready_queue.pop_first(); 189 next = ready_queue.pop_first();
204 }
205 if (next == 0) {
206 return nullptr;
207 } 190 }
191
208 return next; 192 return next;
209} 193}
210 194
211void WaitCurrentThread_Sleep() { 195void WaitCurrentThread_Sleep() {
212 Thread* thread = GetCurrentThread(); 196 Thread* thread = GetCurrentThread();
213 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); 197 thread->status = THREADSTATUS_WAIT_SLEEP;
214} 198}
215 199
216void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all) { 200void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, bool wait_set_output, bool wait_all) {
217 Thread* thread = GetCurrentThread(); 201 Thread* thread = GetCurrentThread();
218 thread->wait_set_output = wait_set_output; 202 thread->wait_set_output = wait_set_output;
219 thread->wait_all = wait_all; 203 thread->wait_all = wait_all;
220 204 thread->wait_objects = std::move(wait_objects);
221 // It's possible to call WaitSynchronizationN without any objects passed in... 205 thread->status = THREADSTATUS_WAIT_SYNCH;
222 if (wait_object != nullptr)
223 thread->wait_objects.push_back(wait_object);
224
225 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
226} 206}
227 207
228void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { 208void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
229 Thread* thread = GetCurrentThread(); 209 Thread* thread = GetCurrentThread();
230 thread->wait_address = wait_address; 210 thread->wait_address = wait_address;
231 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); 211 thread->status = THREADSTATUS_WAIT_ARB;
232} 212}
233 213
234/// Event type for the thread wake up event
235static int ThreadWakeupEventType = -1;
236// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing 214// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing
237// us to simply use a pool index or similar. 215// us to simply use a pool index or similar.
238static Kernel::HandleTable wakeup_callback_handle_table; 216static Kernel::HandleTable wakeup_callback_handle_table;
239 217
240/// Callback that will wake up the thread it was scheduled for 218/**
219 * Callback that will wake up the thread it was scheduled for
220 * @param thread_handle The handle of the thread that's been awoken
221 * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time
222 */
241static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { 223static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
242 SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>((Handle)thread_handle); 224 SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>((Handle)thread_handle);
243 if (thread == nullptr) { 225 if (thread == nullptr) {
244 LOG_CRITICAL(Kernel, "Callback fired for invalid thread %08X", thread_handle); 226 LOG_CRITICAL(Kernel, "Callback fired for invalid thread %08X", (Handle)thread_handle);
245 return; 227 return;
246 } 228 }
247 229
248 thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, 230 if (thread->status == THREADSTATUS_WAIT_SYNCH) {
249 ErrorSummary::StatusChanged, ErrorLevel::Info)); 231 thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
232 ErrorSummary::StatusChanged, ErrorLevel::Info));
250 233
251 if (thread->wait_set_output) 234 if (thread->wait_set_output)
252 thread->SetWaitSynchronizationOutput(-1); 235 thread->SetWaitSynchronizationOutput(-1);
236 }
253 237
254 thread->ResumeFromWait(); 238 thread->ResumeFromWait();
255} 239}
256 240
257
258void Thread::WakeAfterDelay(s64 nanoseconds) { 241void Thread::WakeAfterDelay(s64 nanoseconds) {
259 // Don't schedule a wakeup if the thread wants to wait forever 242 // Don't schedule a wakeup if the thread wants to wait forever
260 if (nanoseconds == -1) 243 if (nanoseconds == -1)
@@ -265,7 +248,7 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
265} 248}
266 249
267void Thread::ReleaseWaitObject(WaitObject* wait_object) { 250void Thread::ReleaseWaitObject(WaitObject* wait_object) {
268 if (wait_objects.empty()) { 251 if (status != THREADSTATUS_WAIT_SYNCH || wait_objects.empty()) {
269 LOG_CRITICAL(Kernel, "thread is not waiting on any objects!"); 252 LOG_CRITICAL(Kernel, "thread is not waiting on any objects!");
270 return; 253 return;
271 } 254 }
@@ -307,34 +290,48 @@ void Thread::ReleaseWaitObject(WaitObject* wait_object) {
307} 290}
308 291
309void Thread::ResumeFromWait() { 292void Thread::ResumeFromWait() {
310 // Cancel any outstanding wakeup events 293 // Cancel any outstanding wakeup events for this thread
311 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); 294 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
312 295
313 status &= ~THREADSTATUS_WAIT; 296 switch (status) {
314 297 case THREADSTATUS_WAIT_SYNCH:
315 // Remove this thread from all other WaitObjects 298 // Remove this thread from all other WaitObjects
316 for (auto wait_object : wait_objects) 299 for (auto wait_object : wait_objects)
317 wait_object->RemoveWaitingThread(this); 300 wait_object->RemoveWaitingThread(this);
318 301 break;
319 wait_objects.clear(); 302 case THREADSTATUS_WAIT_ARB:
320 wait_set_output = false; 303 case THREADSTATUS_WAIT_SLEEP:
321 wait_all = false; 304 break;
322 wait_address = 0; 305 case THREADSTATUS_RUNNING:
323 306 case THREADSTATUS_READY:
324 if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { 307 LOG_ERROR(Kernel, "Thread with object id %u has already resumed.", GetObjectId());
325 ChangeReadyState(this, true); 308 _dbg_assert_(Kernel, false);
309 return;
310 case THREADSTATUS_DEAD:
311 // This should never happen, as threads must complete before being stopped.
312 LOG_CRITICAL(Kernel, "Thread with object id %u cannot be resumed because it's DEAD.",
313 GetObjectId());
314 _dbg_assert_(Kernel, false);
315 return;
326 } 316 }
317
318 ready_queue.push_back(current_priority, this);
319 status = THREADSTATUS_READY;
327} 320}
328 321
329/// Prints the thread queue for debugging purposes 322/**
323 * Prints the thread queue for debugging purposes
324 */
330static void DebugThreadQueue() { 325static void DebugThreadQueue() {
331 Thread* thread = GetCurrentThread(); 326 Thread* thread = GetCurrentThread();
332 if (!thread) { 327 if (!thread) {
333 return; 328 LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD");
329 } else {
330 LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, GetCurrentThread()->GetObjectId());
334 } 331 }
335 LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, GetCurrentThread()->GetObjectId()); 332
336 for (auto& t : thread_list) { 333 for (auto& t : thread_list) {
337 s32 priority = thread_ready_queue.contains(t.get()); 334 s32 priority = ready_queue.contains(t.get());
338 if (priority != -1) { 335 if (priority != -1) {
339 LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); 336 LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId());
340 } 337 }
@@ -342,14 +339,7 @@ static void DebugThreadQueue() {
342} 339}
343 340
344ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority, 341ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority,
345 u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size) { 342 u32 arg, s32 processor_id, VAddr stack_top) {
346 if (stack_size < 0x200) {
347 LOG_ERROR(Kernel, "(name=%s): invalid stack_size=0x%08X", name.c_str(), stack_size);
348 // TODO: Verify error
349 return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Kernel,
350 ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
351 }
352
353 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { 343 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
354 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); 344 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
355 LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", 345 LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
@@ -369,13 +359,12 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
369 SharedPtr<Thread> thread(new Thread); 359 SharedPtr<Thread> thread(new Thread);
370 360
371 thread_list.push_back(thread); 361 thread_list.push_back(thread);
372 thread_ready_queue.prepare(priority); 362 ready_queue.prepare(priority);
373 363
374 thread->thread_id = next_thread_id++; 364 thread->thread_id = NewThreadId();
375 thread->status = THREADSTATUS_DORMANT; 365 thread->status = THREADSTATUS_DORMANT;
376 thread->entry_point = entry_point; 366 thread->entry_point = entry_point;
377 thread->stack_top = stack_top; 367 thread->stack_top = stack_top;
378 thread->stack_size = stack_size;
379 thread->initial_priority = thread->current_priority = priority; 368 thread->initial_priority = thread->current_priority = priority;
380 thread->processor_id = processor_id; 369 thread->processor_id = processor_id;
381 thread->wait_set_output = false; 370 thread->wait_set_output = false;
@@ -385,75 +374,74 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
385 thread->name = std::move(name); 374 thread->name = std::move(name);
386 thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); 375 thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom();
387 376
388 ResetThread(thread.get(), arg, 0); 377 // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
389 CallThread(thread.get()); 378 // to initialize the context
379 Core::g_app_core->ResetContext(thread->context, stack_top, entry_point, arg);
380
381 ready_queue.push_back(thread->current_priority, thread.get());
382 thread->status = THREADSTATUS_READY;
390 383
391 return MakeResult<SharedPtr<Thread>>(std::move(thread)); 384 return MakeResult<SharedPtr<Thread>>(std::move(thread));
392} 385}
393 386
394/// Set the priority of the thread specified by handle 387// TODO(peachum): Remove this. Range checking should be done, and an appropriate error should be returned.
395void Thread::SetPriority(s32 priority) { 388static void ClampPriority(const Thread* thread, s32* priority) {
396 // If priority is invalid, clamp to valid range 389 if (*priority < THREADPRIO_HIGHEST || *priority > THREADPRIO_LOWEST) {
397 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { 390 _dbg_assert_msg_(Kernel, false, "Application passed an out of range priority. An error should be returned.");
398 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); 391
399 LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority); 392 s32 new_priority = CLAMP(*priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
393 LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
394 thread->name.c_str(), *priority, new_priority);
400 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm 395 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
401 // validity of this 396 // validity of this
402 priority = new_priority; 397 *priority = new_priority;
403 } 398 }
399}
404 400
405 // Change thread priority 401void Thread::SetPriority(s32 priority) {
406 s32 old = current_priority; 402 ClampPriority(this, &priority);
407 thread_ready_queue.remove(old, this);
408 current_priority = priority;
409 thread_ready_queue.prepare(current_priority);
410 403
411 // Change thread status to "ready" and push to ready queue 404 if (current_priority == priority) {
412 if (IsRunning()) { 405 return;
413 status = (status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
414 } 406 }
415 if (IsReady()) { 407
416 thread_ready_queue.push_back(current_priority, this); 408 if (status == THREADSTATUS_READY) {
409 // If thread was ready, adjust queues
410 ready_queue.remove(current_priority, this);
411 ready_queue.prepare(priority);
412 ready_queue.push_back(priority, this);
417 } 413 }
414
415 current_priority = priority;
418} 416}
419 417
420SharedPtr<Thread> SetupIdleThread() { 418SharedPtr<Thread> SetupIdleThread() {
421 // We need to pass a few valid values to get around parameter checking in Thread::Create. 419 // We need to pass a few valid values to get around parameter checking in Thread::Create.
422 auto thread = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, 420 auto thread = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0,
423 THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE).MoveFrom(); 421 THREADPROCESSORID_0, 0).MoveFrom();
424 422
425 thread->idle = true; 423 thread->idle = true;
426 CallThread(thread.get());
427 return thread; 424 return thread;
428} 425}
429 426
430SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size) { 427SharedPtr<Thread> SetupMainThread(u32 stack_size, u32 entry_point, s32 priority) {
428 _dbg_assert_(Kernel, !GetCurrentThread());
429
431 // Initialize new "main" thread 430 // Initialize new "main" thread
432 auto thread_res = Thread::Create("main", Core::g_app_core->GetPC(), priority, 0, 431 auto thread_res = Thread::Create("main", entry_point, priority, 0,
433 THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); 432 THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END);
434 // TODO(yuriks): Propagate error 433
435 _dbg_assert_(Kernel, thread_res.Succeeded()); 434 SharedPtr<Thread> thread = thread_res.MoveFrom();
436 SharedPtr<Thread> thread = std::move(*thread_res);
437
438 // If running another thread already, set it to "ready" state
439 Thread* cur = GetCurrentThread();
440 if (cur && cur->IsRunning()) {
441 ChangeReadyState(cur, true);
442 }
443 435
444 // Run new "main" thread 436 // Run new "main" thread
445 current_thread = thread.get(); 437 SwitchContext(thread.get());
446 thread->status = THREADSTATUS_RUNNING;
447 Core::g_app_core->LoadContext(thread->context);
448 438
449 return thread; 439 return thread;
450} 440}
451 441
452
453/// Reschedules to the next available thread (call after current thread is suspended)
454void Reschedule() { 442void Reschedule() {
455 Thread* prev = GetCurrentThread(); 443 Thread* prev = GetCurrentThread();
456 Thread* next = NextThread(); 444 Thread* next = PopNextReadyThread();
457 HLE::g_reschedule = false; 445 HLE::g_reschedule = false;
458 446
459 if (next != nullptr) { 447 if (next != nullptr) {
@@ -480,8 +468,10 @@ void Thread::SetWaitSynchronizationOutput(s32 output) {
480//////////////////////////////////////////////////////////////////////////////////////////////////// 468////////////////////////////////////////////////////////////////////////////////////////////////////
481 469
482void ThreadingInit() { 470void ThreadingInit() {
483 next_thread_id = INITIAL_THREAD_ID;
484 ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); 471 ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
472
473 // Setup the idle thread
474 SetupIdleThread();
485} 475}
486 476
487void ThreadingShutdown() { 477void ThreadingShutdown() {