| 
									
										
										
										
											2014-05-09 22:11:18 -04:00
										 |  |  | // Copyright 2014 Citra Emulator Project / PPSSPP Project
 | 
					
						
							| 
									
										
										
										
											2014-12-16 21:38:14 -08:00
										 |  |  | // Licensed under GPLv2 or any later version
 | 
					
						
							| 
									
										
										
										
											2014-11-19 08:49:13 +00:00
										 |  |  | // Refer to the license.txt file included.
 | 
					
						
							| 
									
										
										
										
											2014-05-09 22:11:18 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-05 22:35:36 -04:00
										 |  |  | #include <algorithm>
 | 
					
						
							| 
									
										
										
										
											2014-08-17 23:03:22 -04:00
										 |  |  | #include <list>
 | 
					
						
							|  |  |  | #include <vector>
 | 
					
						
							| 
									
										
										
										
											2014-05-09 22:11:18 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-06 04:06:12 -03:00
										 |  |  | #include "common/assert.h"
 | 
					
						
							|  |  |  | #include "common/common_types.h"
 | 
					
						
							|  |  |  | #include "common/logging/log.h"
 | 
					
						
							| 
									
										
										
										
											2015-02-18 22:46:21 -08:00
										 |  |  | #include "common/math_util.h"
 | 
					
						
							| 
									
										
										
										
											2014-05-15 18:27:08 -04:00
										 |  |  | #include "common/thread_queue_list.h"
 | 
					
						
							| 
									
										
										
										
											2014-05-09 22:11:18 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-22 04:30:09 -02:00
										 |  |  | #include "core/arm/arm_interface.h"
 | 
					
						
							| 
									
										
										
										
											2015-07-25 21:10:41 -04:00
										 |  |  | #include "core/arm/skyeye_common/armstate.h"
 | 
					
						
							| 
									
										
										
										
											2014-05-13 22:00:11 -04:00
										 |  |  | #include "core/core.h"
 | 
					
						
							| 
									
										
										
										
											2015-01-07 10:10:58 -05:00
										 |  |  | #include "core/core_timing.h"
 | 
					
						
							| 
									
										
										
										
											2014-05-14 20:50:30 -04:00
										 |  |  | #include "core/hle/hle.h"
 | 
					
						
							| 
									
										
										
										
											2014-05-09 22:11:18 -04:00
										 |  |  | #include "core/hle/kernel/kernel.h"
 | 
					
						
							| 
									
										
										
										
											2015-05-11 09:15:10 -05:00
										 |  |  | #include "core/hle/kernel/process.h"
 | 
					
						
							| 
									
										
										
										
											2014-05-09 22:11:18 -04:00
										 |  |  | #include "core/hle/kernel/thread.h"
 | 
					
						
							| 
									
										
										
										
											2015-11-26 19:00:16 -08:00
										 |  |  | #include "core/hle/kernel/memory.h"
 | 
					
						
							| 
									
										
										
										
											2014-12-07 15:44:21 -05:00
										 |  |  | #include "core/hle/kernel/mutex.h"
 | 
					
						
							| 
									
										
										
										
											2014-10-23 01:20:01 -02:00
										 |  |  | #include "core/hle/result.h"
 | 
					
						
							| 
									
										
										
										
											2015-05-12 22:38:29 -03:00
										 |  |  | #include "core/memory.h"
 | 
					
						
							| 
									
										
										
										
											2014-05-09 22:11:18 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-20 19:37:46 -04:00
										 |  |  | namespace Kernel { | 
					
						
							| 
									
										
										
										
											2014-05-13 22:00:11 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | /// Event type for the thread wake up event
 | 
					
						
							| 
									
										
										
										
											2015-04-27 22:12:35 -04:00
										 |  |  | static int ThreadWakeupEventType; | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-20 18:16:45 -05:00
										 |  |  | bool Thread::ShouldWait() { | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     return status != THREADSTATUS_DEAD; | 
					
						
							| 
									
										
										
										
											2014-12-22 04:32:03 -02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-05-09 22:11:18 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-20 18:16:45 -05:00
										 |  |  | void Thread::Acquire() { | 
					
						
							| 
									
										
										
										
											2015-01-20 17:16:47 -08:00
										 |  |  |     ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 
					
						
							| 
									
										
										
										
											2015-01-17 22:23:49 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-17 02:24:13 -03:00
										 |  |  | // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing
 | 
					
						
							|  |  |  | //               us to simply use a pool index or similar.
 | 
					
						
							|  |  |  | static Kernel::HandleTable wakeup_callback_handle_table; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-13 22:00:11 -04:00
										 |  |  | // Lists all thread ids that aren't deleted/etc.
 | 
					
						
							| 
									
										
										
										
											2014-12-29 10:55:30 -02:00
										 |  |  | static std::vector<SharedPtr<Thread>> thread_list; | 
					
						
							| 
									
										
										
										
											2014-05-13 22:00:11 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-15 18:27:08 -04:00
										 |  |  | // Lists only ready thread ids.
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> ready_queue; | 
					
						
							| 
									
										
										
										
											2014-05-13 22:00:11 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-18 08:48:11 -05:00
										 |  |  | static Thread* current_thread; | 
					
						
							| 
									
										
										
										
											2014-05-13 22:00:11 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | // The first available thread id at startup
 | 
					
						
							| 
									
										
										
										
											2015-04-27 22:12:35 -04:00
										 |  |  | static u32 next_thread_id; | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Creates a new thread ID | 
					
						
							|  |  |  |  * @return The new thread ID | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | inline static u32 const NewThreadId() { | 
					
						
							|  |  |  |     return next_thread_id++; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2014-12-04 08:13:53 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-31 22:56:59 -02:00
										 |  |  | Thread::Thread() {} | 
					
						
							|  |  |  | Thread::~Thread() {} | 
					
						
							| 
									
										
										
										
											2015-01-31 19:22:40 -02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-20 03:04:36 -02:00
										 |  |  | Thread* GetCurrentThread() { | 
					
						
							| 
									
										
										
										
											2014-11-18 08:48:11 -05:00
										 |  |  |     return current_thread; | 
					
						
							| 
									
										
										
										
											2014-05-15 18:27:08 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-05-13 22:00:11 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Check if a thread is waiting on the specified wait object | 
					
						
							|  |  |  |  * @param thread The thread to test | 
					
						
							|  |  |  |  * @param wait_object The object to test against | 
					
						
							|  |  |  |  * @return True if the thread is waiting, false otherwise | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2015-01-18 13:56:40 -05:00
										 |  |  | static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     if (thread->status != THREADSTATUS_WAIT_SYNCH) | 
					
						
							|  |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2015-01-20 18:40:01 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); | 
					
						
							|  |  |  |     return itr != thread->wait_objects.end(); | 
					
						
							| 
									
										
										
										
											2014-12-20 02:32:19 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Check if the specified thread is waiting on the specified address to be arbitrated | 
					
						
							|  |  |  |  * @param thread The thread to test | 
					
						
							|  |  |  |  * @param wait_address The address to test against | 
					
						
							|  |  |  |  * @return True if the thread is waiting, false otherwise | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2015-01-18 13:56:40 -05:00
										 |  |  | static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address; | 
					
						
							| 
									
										
										
										
											2014-12-03 00:46:34 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | void Thread::Stop() { | 
					
						
							| 
									
										
										
										
											2014-12-07 15:44:21 -05:00
										 |  |  |     // Release all the mutexes that this thread holds
 | 
					
						
							| 
									
										
										
										
											2015-01-20 18:33:23 -05:00
										 |  |  |     ReleaseThreadMutexes(this); | 
					
						
							| 
									
										
										
										
											2014-12-20 02:32:19 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     // Cancel any outstanding wakeup events for this thread
 | 
					
						
							|  |  |  |     CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); | 
					
						
							| 
									
										
										
										
											2015-07-17 02:24:13 -03:00
										 |  |  |     wakeup_callback_handle_table.Close(callback_handle); | 
					
						
							|  |  |  |     callback_handle = 0; | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Clean up thread from ready queue
 | 
					
						
							|  |  |  |     // This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
 | 
					
						
							|  |  |  |     if (status == THREADSTATUS_READY){ | 
					
						
							|  |  |  |         ready_queue.remove(current_priority, this); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     status = THREADSTATUS_DEAD; | 
					
						
							| 
									
										
										
										
											2015-05-25 20:34:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-20 18:20:47 -05:00
										 |  |  |     WakeupAllWaitingThreads(); | 
					
						
							| 
									
										
										
										
											2014-06-09 22:14:03 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     // Clean up any dangling references in objects that this thread was waiting for
 | 
					
						
							| 
									
										
										
										
											2015-01-31 23:26:16 -02:00
										 |  |  |     for (auto& wait_object : wait_objects) { | 
					
						
							|  |  |  |         wait_object->RemoveWaitingThread(this); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-07-17 02:24:13 -03:00
										 |  |  |     wait_objects.clear(); | 
					
						
							| 
									
										
										
										
											2015-05-10 23:19:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-19 17:12:48 -05:00
										 |  |  |     // Mark the TLS slot in the thread's page as free.
 | 
					
						
							|  |  |  |     u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; | 
					
						
							|  |  |  |     u32 tls_slot = ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; | 
					
						
							|  |  |  |     Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot); | 
					
						
							| 
									
										
										
										
											2015-05-19 20:24:30 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     HLE::Reschedule(__func__); | 
					
						
							| 
									
										
										
										
											2014-05-13 22:00:11 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-18 13:56:40 -05:00
										 |  |  | Thread* ArbitrateHighestPriorityThread(u32 address) { | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  |     Thread* highest_priority_thread = nullptr; | 
					
						
							| 
									
										
										
										
											2014-07-06 22:48:19 -04:00
										 |  |  |     s32 priority = THREADPRIO_LOWEST; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
 | 
					
						
							| 
									
										
										
										
											2014-12-29 10:55:30 -02:00
										 |  |  |     for (auto& thread : thread_list) { | 
					
						
							| 
									
										
										
										
											2015-01-18 13:56:40 -05:00
										 |  |  |         if (!CheckWait_AddressArbiter(thread.get(), address)) | 
					
						
							| 
									
										
										
										
											2014-07-06 22:48:19 -04:00
										 |  |  |             continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-23 01:20:01 -02:00
										 |  |  |         if (thread == nullptr) | 
					
						
							| 
									
										
										
										
											2015-01-18 13:56:40 -05:00
										 |  |  |             continue; | 
					
						
							| 
									
										
										
										
											2014-12-03 00:46:34 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-06 22:48:19 -04:00
										 |  |  |         if(thread->current_priority <= priority) { | 
					
						
							| 
									
										
										
										
											2014-12-29 10:55:30 -02:00
										 |  |  |             highest_priority_thread = thread.get(); | 
					
						
							| 
									
										
										
										
											2014-07-06 22:48:19 -04:00
										 |  |  |             priority = thread->current_priority; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-06 22:48:19 -04:00
										 |  |  |     // If a thread was arbitrated, resume it
 | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  |     if (nullptr != highest_priority_thread) { | 
					
						
							| 
									
										
										
										
											2015-01-18 13:56:40 -05:00
										 |  |  |         highest_priority_thread->ResumeFromWait(); | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-07-06 22:48:19 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return highest_priority_thread; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-18 13:56:40 -05:00
										 |  |  | void ArbitrateAllThreads(u32 address) { | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     // Resume all threads found to be waiting on the address
 | 
					
						
							| 
									
										
										
										
											2014-12-29 10:55:30 -02:00
										 |  |  |     for (auto& thread : thread_list) { | 
					
						
							| 
									
										
										
										
											2015-01-18 13:56:40 -05:00
										 |  |  |         if (CheckWait_AddressArbiter(thread.get(), address)) | 
					
						
							|  |  |  |             thread->ResumeFromWait(); | 
					
						
							| 
									
										
										
										
											2014-07-06 22:48:19 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-23 23:55:21 -04:00
										 |  |  | /// Boost low priority threads (temporarily) that have been starved
 | 
					
						
							|  |  |  | static void PriorityBoostStarvedThreads() { | 
					
						
							|  |  |  |     u64 current_ticks = CoreTiming::GetTicks(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (auto& thread : thread_list) { | 
					
						
							|  |  |  |         // TODO(bunnei): Threads that have been waiting to be scheduled for `boost_ticks` (or
 | 
					
						
							|  |  |  |         // longer) will have their priority temporarily adjusted to 1 higher than the highest
 | 
					
						
							|  |  |  |         // priority thread to prevent thread starvation. This general behavior has been verified
 | 
					
						
							|  |  |  |         // on hardware. However, this is almost certainly not perfect, and the real CTR OS scheduler
 | 
					
						
							|  |  |  |         // should probably be reversed to verify this.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const u64 boost_timeout = 2000000;  // Boost threads that have been ready for > this long
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         u64 delta = current_ticks - thread->last_running_ticks; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-11 16:09:10 -03:00
										 |  |  |         if (thread->status == THREADSTATUS_READY && delta > boost_timeout) { | 
					
						
							| 
									
										
										
										
											2015-04-03 18:40:16 -04:00
										 |  |  |             const s32 priority = std::max(ready_queue.get_first()->current_priority - 1, 0); | 
					
						
							|  |  |  |             thread->BoostPriority(priority); | 
					
						
							| 
									
										
										
										
											2015-03-23 23:55:21 -04:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-03 22:09:09 +03:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Gets the registers for timeout parameter of the next WaitSynchronization call. | 
					
						
							|  |  |  |  * @param thread a pointer to the thread that is ready to call WaitSynchronization | 
					
						
							|  |  |  |  * @returns a tuple of two register pointers to low and high part of the timeout parameter | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static std::tuple<u32*, u32*> GetWaitSynchTimeoutParameterRegister(Thread* thread) { | 
					
						
							|  |  |  |     bool thumb_mode = (thread->context.cpsr & TBIT) != 0; | 
					
						
							|  |  |  |     u16 thumb_inst = Memory::Read16(thread->context.pc & 0xFFFFFFFE); | 
					
						
							|  |  |  |     u32 inst = Memory::Read32(thread->context.pc & 0xFFFFFFFC) & 0x0FFFFFFF; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((thumb_mode && thumb_inst == 0xDF24) || (!thumb_mode && inst == 0x0F000024)) { | 
					
						
							|  |  |  |         // svc #0x24 (WaitSynchronization1)
 | 
					
						
							|  |  |  |         return std::make_tuple(&thread->context.cpu_registers[2], &thread->context.cpu_registers[3]); | 
					
						
							|  |  |  |     } else if ((thumb_mode && thumb_inst == 0xDF25) || (!thumb_mode && inst == 0x0F000025)) { | 
					
						
							|  |  |  |         // svc #0x25 (WaitSynchronizationN)
 | 
					
						
							|  |  |  |         return std::make_tuple(&thread->context.cpu_registers[0], &thread->context.cpu_registers[4]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     UNREACHABLE(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Updates the WaitSynchronization timeout paramter according to the difference | 
					
						
							|  |  |  |  * between ticks of the last WaitSynchronization call and the incoming one. | 
					
						
							|  |  |  |  * @param timeout_low a pointer to the register for the low part of the timeout parameter | 
					
						
							|  |  |  |  * @param timeout_high a pointer to the register for the high part of the timeout parameter | 
					
						
							|  |  |  |  * @param last_tick tick of the last WaitSynchronization call | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void UpdateTimeoutParameter(u32* timeout_low, u32* timeout_high, u64 last_tick) { | 
					
						
							|  |  |  |     s64 timeout = ((s64)*timeout_high << 32) | *timeout_low; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (timeout != -1) { | 
					
						
							|  |  |  |         timeout -= cyclesToUs(CoreTiming::GetTicks() - last_tick) * 1000; // in nanoseconds
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (timeout < 0) | 
					
						
							|  |  |  |             timeout = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         *timeout_low = timeout & 0xFFFFFFFF; | 
					
						
							|  |  |  |         *timeout_high = timeout >> 32; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-25 20:34:09 +02:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |  * Switches the CPU's active thread context to that of the specified thread | 
					
						
							|  |  |  |  * @param new_thread The thread to switch to | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void SwitchContext(Thread* new_thread) { | 
					
						
							|  |  |  |     Thread* previous_thread = GetCurrentThread(); | 
					
						
							| 
									
										
										
										
											2014-11-19 08:49:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     // Save context for previous thread
 | 
					
						
							|  |  |  |     if (previous_thread) { | 
					
						
							| 
									
										
										
										
											2015-03-23 23:55:21 -04:00
										 |  |  |         previous_thread->last_running_ticks = CoreTiming::GetTicks(); | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         Core::g_app_core->SaveContext(previous_thread->context); | 
					
						
							| 
									
										
										
										
											2014-11-19 08:49:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         if (previous_thread->status == THREADSTATUS_RUNNING) { | 
					
						
							|  |  |  |             // This is only the case when a reschedule is triggered without the current thread
 | 
					
						
							|  |  |  |             // yielding execution (i.e. an event triggered, system core time-sliced, etc)
 | 
					
						
							|  |  |  |             ready_queue.push_front(previous_thread->current_priority, previous_thread); | 
					
						
							|  |  |  |             previous_thread->status = THREADSTATUS_READY; | 
					
						
							| 
									
										
										
										
											2014-05-20 19:37:46 -04:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-20 19:37:46 -04:00
										 |  |  |     // Load context of new thread
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     if (new_thread) { | 
					
						
							| 
									
										
										
										
											2015-05-11 16:09:10 -03:00
										 |  |  |         DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running."); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-07 23:39:37 -04:00
										 |  |  |         // Cancel any outstanding wakeup events for this thread
 | 
					
						
							|  |  |  |         CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         current_thread = new_thread; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-07 23:39:37 -04:00
										 |  |  |         // If the thread was waited by a svcWaitSynch call, step back PC by one instruction to rerun
 | 
					
						
							|  |  |  |         // the SVC when the thread wakes up. This is necessary to ensure that the thread can acquire
 | 
					
						
							|  |  |  |         // the requested wait object(s) before continuing.
 | 
					
						
							|  |  |  |         if (new_thread->waitsynch_waited) { | 
					
						
							|  |  |  |             // CPSR flag indicates CPU mode
 | 
					
						
							|  |  |  |             bool thumb_mode = (new_thread->context.cpsr & TBIT) != 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // SVC instruction is 2 bytes for THUMB, 4 bytes for ARM
 | 
					
						
							|  |  |  |             new_thread->context.pc -= thumb_mode ? 2 : 4; | 
					
						
							| 
									
										
										
										
											2016-06-03 22:09:09 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // Get the register for timeout parameter
 | 
					
						
							|  |  |  |             u32* timeout_low, *timeout_high; | 
					
						
							|  |  |  |             std::tie(timeout_low, timeout_high) = GetWaitSynchTimeoutParameterRegister(new_thread); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Update the timeout parameter
 | 
					
						
							|  |  |  |             UpdateTimeoutParameter(timeout_low, timeout_high, new_thread->last_running_ticks); | 
					
						
							| 
									
										
										
										
											2015-06-07 23:39:37 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-20 17:15:54 -05:00
										 |  |  |         // Clean up the thread's wait_objects, they'll be restored if needed during
 | 
					
						
							|  |  |  |         // the svcWaitSynchronization call
 | 
					
						
							| 
									
										
										
										
											2015-10-06 21:28:19 -05:00
										 |  |  |         for (size_t i = 0; i < new_thread->wait_objects.size(); ++i) { | 
					
						
							| 
									
										
										
										
											2015-07-20 17:15:54 -05:00
										 |  |  |             SharedPtr<WaitObject> object = new_thread->wait_objects[i]; | 
					
						
							|  |  |  |             object->RemoveWaitingThread(new_thread); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         new_thread->wait_objects.clear(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         ready_queue.remove(new_thread->current_priority, new_thread); | 
					
						
							|  |  |  |         new_thread->status = THREADSTATUS_RUNNING; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-23 23:55:21 -04:00
										 |  |  |         // Restores thread to its nominal priority if it has been temporarily changed
 | 
					
						
							|  |  |  |         new_thread->current_priority = new_thread->nominal_priority; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         Core::g_app_core->LoadContext(new_thread->context); | 
					
						
							| 
									
										
										
										
											2015-05-10 18:43:59 -05:00
										 |  |  |         Core::g_app_core->SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); | 
					
						
							| 
									
										
										
										
											2014-05-20 19:37:46 -04:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  |         current_thread = nullptr; | 
					
						
							| 
									
										
										
										
											2014-05-20 19:37:46 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Pops and returns the next thread from the thread queue | 
					
						
							|  |  |  |  * @return A pointer to the next ready thread | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static Thread* PopNextReadyThread() { | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  |     Thread* next; | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     Thread* thread = GetCurrentThread(); | 
					
						
							| 
									
										
										
										
											2014-11-19 08:49:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     if (thread && thread->status == THREADSTATUS_RUNNING) { | 
					
						
							|  |  |  |         // We have to do better than the current thread.
 | 
					
						
							|  |  |  |         // This call returns null when that's not possible.
 | 
					
						
							|  |  |  |         next = ready_queue.pop_first_better(thread->current_priority); | 
					
						
							| 
									
										
										
										
											2015-05-11 16:09:10 -03:00
										 |  |  |         if (!next) { | 
					
						
							|  |  |  |             // Otherwise just keep going with the current thread
 | 
					
						
							|  |  |  |             next = thread; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2014-05-20 19:37:46 -04:00
										 |  |  |     } else  { | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         next = ready_queue.pop_first(); | 
					
						
							| 
									
										
										
										
											2014-05-20 19:37:46 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  |     return next; | 
					
						
							| 
									
										
										
										
											2014-05-20 19:37:46 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-18 13:56:40 -05:00
										 |  |  | void WaitCurrentThread_Sleep() { | 
					
						
							| 
									
										
										
										
											2015-01-17 02:03:44 -05:00
										 |  |  |     Thread* thread = GetCurrentThread(); | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     thread->status = THREADSTATUS_WAIT_SLEEP; | 
					
						
							| 
									
										
										
										
											2015-05-19 20:24:30 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     HLE::Reschedule(__func__); | 
					
						
							| 
									
										
										
										
											2015-01-17 02:03:44 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, bool wait_set_output, bool wait_all) { | 
					
						
							| 
									
										
										
										
											2014-06-09 22:14:03 -04:00
										 |  |  |     Thread* thread = GetCurrentThread(); | 
					
						
							| 
									
										
										
										
											2015-01-20 20:53:52 -05:00
										 |  |  |     thread->wait_set_output = wait_set_output; | 
					
						
							| 
									
										
										
										
											2015-01-18 13:25:51 -05:00
										 |  |  |     thread->wait_all = wait_all; | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     thread->wait_objects = std::move(wait_objects); | 
					
						
							| 
									
										
										
										
											2015-06-07 23:39:37 -04:00
										 |  |  |     thread->waitsynch_waited = true; | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     thread->status = THREADSTATUS_WAIT_SYNCH; | 
					
						
							| 
									
										
										
										
											2014-05-20 21:00:10 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-18 13:56:40 -05:00
										 |  |  | void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { | 
					
						
							|  |  |  |     Thread* thread = GetCurrentThread(); | 
					
						
							|  |  |  |     thread->wait_address = wait_address; | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     thread->status = THREADSTATUS_WAIT_ARB; | 
					
						
							| 
									
										
										
										
											2014-12-03 00:46:34 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Callback that will wake up the thread it was scheduled for | 
					
						
							|  |  |  |  * @param thread_handle The handle of the thread that's been awoken | 
					
						
							|  |  |  |  * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2015-01-31 14:23:09 -02:00
										 |  |  | static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { | 
					
						
							|  |  |  |     SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>((Handle)thread_handle); | 
					
						
							| 
									
										
										
										
											2015-01-07 16:40:08 -05:00
										 |  |  |     if (thread == nullptr) { | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         LOG_CRITICAL(Kernel, "Callback fired for invalid thread %08X", (Handle)thread_handle); | 
					
						
							| 
									
										
										
										
											2015-01-07 16:40:08 -05:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-07 23:39:37 -04:00
										 |  |  |     thread->waitsynch_waited = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-27 18:44:42 -05:00
										 |  |  |     if (thread->status == THREADSTATUS_WAIT_SYNCH || thread->status == THREADSTATUS_WAIT_ARB) { | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | 
					
						
							|  |  |  |                                                         ErrorSummary::StatusChanged, ErrorLevel::Info)); | 
					
						
							| 
									
										
										
										
											2015-01-20 20:53:52 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         if (thread->wait_set_output) | 
					
						
							|  |  |  |             thread->SetWaitSynchronizationOutput(-1); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-01-17 02:03:44 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  |     thread->ResumeFromWait(); | 
					
						
							| 
									
										
										
										
											2015-01-07 16:40:08 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-30 23:07:54 -02:00
										 |  |  | void Thread::WakeAfterDelay(s64 nanoseconds) { | 
					
						
							| 
									
										
										
										
											2015-01-07 16:40:08 -05:00
										 |  |  |     // Don't schedule a wakeup if the thread wants to wait forever
 | 
					
						
							|  |  |  |     if (nanoseconds == -1) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     u64 microseconds = nanoseconds / 1000; | 
					
						
							| 
									
										
										
										
											2015-01-31 14:23:09 -02:00
										 |  |  |     CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle); | 
					
						
							| 
									
										
										
										
											2015-01-07 16:40:08 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  | void Thread::ResumeFromWait() { | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     switch (status) { | 
					
						
							|  |  |  |         case THREADSTATUS_WAIT_SYNCH: | 
					
						
							|  |  |  |         case THREADSTATUS_WAIT_ARB: | 
					
						
							|  |  |  |         case THREADSTATUS_WAIT_SLEEP: | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2015-06-07 23:39:37 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         case THREADSTATUS_READY: | 
					
						
							| 
									
										
										
										
											2015-06-07 23:39:37 -04:00
										 |  |  |             // If the thread is waiting on multiple wait objects, it might be awoken more than once
 | 
					
						
							|  |  |  |             // before actually resuming. We can ignore subsequent wakeups if the thread status has
 | 
					
						
							|  |  |  |             // already been set to THREADSTATUS_READY.
 | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         case THREADSTATUS_RUNNING: | 
					
						
							| 
									
										
										
										
											2015-01-20 17:16:47 -08:00
										 |  |  |             DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId()); | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |             return; | 
					
						
							|  |  |  |         case THREADSTATUS_DEAD: | 
					
						
							|  |  |  |             // This should never happen, as threads must complete before being stopped.
 | 
					
						
							| 
									
										
										
										
											2015-01-20 17:16:47 -08:00
										 |  |  |             DEBUG_ASSERT_MSG(false, "Thread with object id %u cannot be resumed because it's DEAD.", | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |                 GetObjectId()); | 
					
						
							|  |  |  |             return; | 
					
						
							| 
									
										
										
										
											2014-05-20 19:37:46 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-05-25 20:34:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     ready_queue.push_back(current_priority, this); | 
					
						
							|  |  |  |     status = THREADSTATUS_READY; | 
					
						
							| 
									
										
										
										
											2014-05-20 19:37:46 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Prints the thread queue for debugging purposes | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  | static void DebugThreadQueue() { | 
					
						
							| 
									
										
										
										
											2014-06-05 22:35:36 -04:00
										 |  |  |     Thread* thread = GetCurrentThread(); | 
					
						
							|  |  |  |     if (!thread) { | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD"); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, GetCurrentThread()->GetObjectId()); | 
					
						
							| 
									
										
										
										
											2014-06-05 22:35:36 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-29 10:55:30 -02:00
										 |  |  |     for (auto& t : thread_list) { | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         s32 priority = ready_queue.contains(t.get()); | 
					
						
							| 
									
										
										
										
											2014-06-05 22:35:36 -04:00
										 |  |  |         if (priority != -1) { | 
					
						
							| 
									
										
										
										
											2015-01-31 14:55:40 -02:00
										 |  |  |             LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); | 
					
						
							| 
									
										
										
										
											2014-06-05 22:35:36 -04:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-19 17:12:48 -05:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Finds a free location for the TLS section of a thread. | 
					
						
							|  |  |  |  * @param tls_slots The TLS page array of the thread's owner process. | 
					
						
							|  |  |  |  * Returns a tuple of (page, slot, alloc_needed) where: | 
					
						
							|  |  |  |  * page: The index of the first allocated TLS page that has free slots. | 
					
						
							|  |  |  |  * slot: The index of the first free slot in the indicated page. | 
					
						
							|  |  |  |  * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | std::tuple<u32, u32, bool> GetFreeThreadLocalSlot(std::vector<std::bitset<8>>& tls_slots) { | 
					
						
							|  |  |  |     // Iterate over all the allocated pages, and try to find one where not all slots are used.
 | 
					
						
							|  |  |  |     for (unsigned page = 0; page < tls_slots.size(); ++page) { | 
					
						
							|  |  |  |         const auto& page_tls_slots = tls_slots[page]; | 
					
						
							|  |  |  |         if (!page_tls_slots.all()) { | 
					
						
							|  |  |  |             // We found a page with at least one free slot, find which slot it is
 | 
					
						
							|  |  |  |             for (unsigned slot = 0; slot < page_tls_slots.size(); ++slot) { | 
					
						
							|  |  |  |                 if (!page_tls_slots.test(slot)) { | 
					
						
							|  |  |  |                     return std::make_tuple(page, slot, false); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return std::make_tuple(0, 0, true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-02 08:53:42 -04:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Resets a thread context, making it ready to be scheduled and run by the CPU | 
					
						
							|  |  |  |  * @param context Thread context to reset | 
					
						
							|  |  |  |  * @param stack_top Address of the top of the stack | 
					
						
							|  |  |  |  * @param entry_point Address of entry point for execution | 
					
						
							|  |  |  |  * @param arg User argument for thread | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void ResetThreadContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) { | 
					
						
							|  |  |  |     memset(&context, 0, sizeof(Core::ThreadContext)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context.cpu_registers[0] = arg; | 
					
						
							|  |  |  |     context.pc = entry_point; | 
					
						
							|  |  |  |     context.sp = stack_top; | 
					
						
							|  |  |  |     context.cpsr = USER32MODE | ((entry_point & 1) << 5); // Usermode and THUMB mode
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-29 10:55:30 -02:00
										 |  |  | ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority, | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         u32 arg, s32 processor_id, VAddr stack_top) { | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  |     if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | 
					
						
							| 
									
										
										
										
											2015-02-18 22:46:21 -08:00
										 |  |  |         s32 new_priority = MathUtil::Clamp<s32>(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  |         LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", | 
					
						
							| 
									
										
										
										
											2014-12-29 10:55:30 -02:00
										 |  |  |             name.c_str(), priority, new_priority); | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  |         // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
 | 
					
						
							|  |  |  |         // validity of this
 | 
					
						
							|  |  |  |         priority = new_priority; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-05-17 00:56:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-16 10:27:32 +01:00
										 |  |  |     if (!Memory::IsValidVirtualAddress(entry_point)) { | 
					
						
							| 
									
										
										
										
											2014-12-29 10:55:30 -02:00
										 |  |  |         LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point); | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  |         // TODO: Verify error
 | 
					
						
							|  |  |  |         return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, | 
					
						
							|  |  |  |                 ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-05-20 21:02:35 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-29 10:55:30 -02:00
										 |  |  |     SharedPtr<Thread> thread(new Thread); | 
					
						
							| 
									
										
										
										
											2014-06-05 22:35:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-29 11:32:05 -02:00
										 |  |  |     thread_list.push_back(thread); | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     ready_queue.prepare(priority); | 
					
						
							| 
									
										
										
										
											2014-06-05 22:35:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     thread->thread_id = NewThreadId(); | 
					
						
							| 
									
										
										
										
											2014-06-09 22:14:03 -04:00
										 |  |  |     thread->status = THREADSTATUS_DORMANT; | 
					
						
							|  |  |  |     thread->entry_point = entry_point; | 
					
						
							|  |  |  |     thread->stack_top = stack_top; | 
					
						
							| 
									
										
										
										
											2015-03-23 23:55:21 -04:00
										 |  |  |     thread->nominal_priority = thread->current_priority = priority; | 
					
						
							|  |  |  |     thread->last_running_ticks = CoreTiming::GetTicks(); | 
					
						
							| 
									
										
										
										
											2014-06-09 22:14:03 -04:00
										 |  |  |     thread->processor_id = processor_id; | 
					
						
							| 
									
										
										
										
											2015-01-20 20:53:52 -05:00
										 |  |  |     thread->wait_set_output = false; | 
					
						
							| 
									
										
										
										
											2015-01-17 02:03:44 -05:00
										 |  |  |     thread->wait_all = false; | 
					
						
							| 
									
										
										
										
											2015-01-14 23:41:33 -05:00
										 |  |  |     thread->wait_objects.clear(); | 
					
						
							| 
									
										
										
										
											2014-12-03 00:46:34 -05:00
										 |  |  |     thread->wait_address = 0; | 
					
						
							| 
									
										
										
										
											2014-12-29 10:55:30 -02:00
										 |  |  |     thread->name = std::move(name); | 
					
						
							| 
									
										
										
										
											2015-01-31 14:23:09 -02:00
										 |  |  |     thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); | 
					
						
							| 
									
										
										
										
											2015-05-11 09:15:10 -05:00
										 |  |  |     thread->owner_process = g_current_process; | 
					
						
							| 
									
										
										
										
											2015-06-07 23:39:37 -04:00
										 |  |  |     thread->waitsynch_waited = false; | 
					
						
							| 
									
										
										
										
											2015-05-10 23:19:54 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Find the next available TLS index, and mark it as used
 | 
					
						
							| 
									
										
										
										
											2016-04-19 17:12:48 -05:00
										 |  |  |     auto& tls_slots = Kernel::g_current_process->tls_slots; | 
					
						
							|  |  |  |     bool needs_allocation = true; | 
					
						
							|  |  |  |     u32 available_page; // Which allocated page has free space
 | 
					
						
							|  |  |  |     u32 available_slot; // Which slot within the page is free
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     std::tie(available_page, available_slot, needs_allocation) = GetFreeThreadLocalSlot(tls_slots); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (needs_allocation) { | 
					
						
							|  |  |  |         // There are no already-allocated pages with free slots, lets allocate a new one.
 | 
					
						
							|  |  |  |         // TLS pages are allocated from the BASE region in the linear heap.
 | 
					
						
							|  |  |  |         MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::BASE); | 
					
						
							|  |  |  |         auto& linheap_memory = memory_region->linear_heap_memory; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) { | 
					
						
							|  |  |  |             LOG_ERROR(Kernel_SVC, "Not enough space in region to allocate a new TLS page for thread"); | 
					
						
							|  |  |  |             return ResultCode(ErrorDescription::OutOfMemory, ErrorModule::Kernel, ErrorSummary::OutOfResource, ErrorLevel::Permanent); | 
					
						
							| 
									
										
										
										
											2015-05-10 23:19:54 -05:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-04-19 17:12:48 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         u32 offset = linheap_memory->size(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Allocate some memory from the end of the linear heap for this region.
 | 
					
						
							|  |  |  |         linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0); | 
					
						
							|  |  |  |         memory_region->used += Memory::PAGE_SIZE; | 
					
						
							|  |  |  |         Kernel::g_current_process->linear_heap_used += Memory::PAGE_SIZE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         tls_slots.emplace_back(0); // The page is completely available at the start
 | 
					
						
							|  |  |  |         available_page = tls_slots.size() - 1; | 
					
						
							|  |  |  |         available_slot = 0; // Use the first slot in the new page
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         auto& vm_manager = Kernel::g_current_process->vm_manager; | 
					
						
							|  |  |  |         vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Map the page to the current process' address space.
 | 
					
						
							|  |  |  |         // TODO(Subv): Find the correct MemoryState for this region.
 | 
					
						
							|  |  |  |         vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, | 
					
						
							|  |  |  |                                   linheap_memory, offset, Memory::PAGE_SIZE, MemoryState::Private); | 
					
						
							| 
									
										
										
										
											2015-05-10 23:19:54 -05:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-06-05 22:35:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-19 17:12:48 -05:00
										 |  |  |     // Mark the slot as used
 | 
					
						
							|  |  |  |     tls_slots[available_page].set(available_slot); | 
					
						
							|  |  |  |     thread->tls_address = Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE; | 
					
						
							| 
									
										
										
										
											2015-05-10 18:43:59 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
 | 
					
						
							|  |  |  |     // to initialize the context
 | 
					
						
							| 
									
										
										
										
											2016-09-02 08:53:42 -04:00
										 |  |  |     ResetThreadContext(thread->context, stack_top, entry_point, arg); | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     ready_queue.push_back(thread->current_priority, thread.get()); | 
					
						
							|  |  |  |     thread->status = THREADSTATUS_READY; | 
					
						
							| 
									
										
										
										
											2014-06-01 21:42:50 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-19 20:24:30 -04:00
										 |  |  |     HLE::Reschedule(__func__); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-29 10:55:30 -02:00
										 |  |  |     return MakeResult<SharedPtr<Thread>>(std::move(thread)); | 
					
						
							| 
									
										
										
										
											2014-06-01 22:12:54 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | // TODO(peachum): Remove this. Range checking should be done, and an appropriate error should be returned.
 | 
					
						
							|  |  |  | static void ClampPriority(const Thread* thread, s32* priority) { | 
					
						
							|  |  |  |     if (*priority < THREADPRIO_HIGHEST || *priority > THREADPRIO_LOWEST) { | 
					
						
							| 
									
										
										
										
											2015-01-20 17:16:47 -08:00
										 |  |  |         DEBUG_ASSERT_MSG(false, "Application passed an out of range priority. An error should be returned."); | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-18 22:46:21 -08:00
										 |  |  |         s32 new_priority = MathUtil::Clamp<s32>(*priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", | 
					
						
							|  |  |  |                     thread->name.c_str(), *priority, new_priority); | 
					
						
							| 
									
										
										
										
											2014-06-01 22:12:54 -04:00
										 |  |  |         // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
 | 
					
						
							|  |  |  |         // validity of this
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |         *priority = new_priority; | 
					
						
							| 
									
										
										
										
											2014-06-01 22:12:54 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-06-01 22:12:54 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | void Thread::SetPriority(s32 priority) { | 
					
						
							|  |  |  |     ClampPriority(this, &priority); | 
					
						
							| 
									
										
										
										
											2014-06-01 22:12:54 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-23 23:55:21 -04:00
										 |  |  |     // If thread was ready, adjust queues
 | 
					
						
							|  |  |  |     if (status == THREADSTATUS_READY) | 
					
						
							|  |  |  |         ready_queue.move(this, current_priority, priority); | 
					
						
							| 
									
										
										
										
											2015-05-11 19:38:10 -05:00
										 |  |  |     else | 
					
						
							|  |  |  |         ready_queue.prepare(priority); | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-23 23:55:21 -04:00
										 |  |  |     nominal_priority = current_priority = priority; | 
					
						
							| 
									
										
										
										
											2014-06-01 22:12:54 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-03 18:40:16 -04:00
										 |  |  | void Thread::BoostPriority(s32 priority) { | 
					
						
							|  |  |  |     ready_queue.move(this, current_priority, priority); | 
					
						
							|  |  |  |     current_priority = priority; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-11 05:08:47 -03:00
										 |  |  | SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) { | 
					
						
							| 
									
										
										
										
											2015-01-20 17:16:47 -08:00
										 |  |  |     DEBUG_ASSERT(!GetCurrentThread()); | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-15 18:27:08 -04:00
										 |  |  |     // Initialize new "main" thread
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     auto thread_res = Thread::Create("main", entry_point, priority, 0, | 
					
						
							| 
									
										
										
										
											2015-05-11 05:08:47 -03:00
										 |  |  |             THREADPROCESSORID_0, Memory::HEAP_VADDR_END); | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     SharedPtr<Thread> thread = thread_res.MoveFrom(); | 
					
						
							| 
									
										
										
										
											2014-11-19 08:49:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-14 15:45:04 +02:00
										 |  |  |     thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-15 18:27:08 -04:00
										 |  |  |     // Run new "main" thread
 | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     SwitchContext(thread.get()); | 
					
						
							| 
									
										
										
										
											2014-05-14 20:50:30 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-22 11:07:22 -02:00
										 |  |  |     return thread; | 
					
						
							| 
									
										
										
										
											2014-05-15 18:27:08 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-05-14 20:50:30 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-22 19:32:45 -04:00
										 |  |  | void Reschedule() { | 
					
						
							| 
									
										
										
										
											2015-03-23 23:55:21 -04:00
										 |  |  |     PriorityBoostStarvedThreads(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-11 16:09:10 -03:00
										 |  |  |     Thread* cur = GetCurrentThread(); | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  |     Thread* next = PopNextReadyThread(); | 
					
						
							| 
									
										
										
										
											2016-03-21 02:48:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     HLE::DoneRescheduling(); | 
					
						
							| 
									
										
										
										
											2014-11-19 08:49:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-29 12:52:29 +03:00
										 |  |  |     // Don't bother switching to the same thread.
 | 
					
						
							|  |  |  |     // But if the thread was waiting on objects, we still need to switch it
 | 
					
						
							|  |  |  |     // to perform PC modification, change state to RUNNING, etc.
 | 
					
						
							|  |  |  |     // This occurs in the case when an object the thread is waiting on immediately wakes up
 | 
					
						
							|  |  |  |     // the current thread before Reschedule() is called.
 | 
					
						
							|  |  |  |     if (next == cur && (next == nullptr || next->waitsynch_waited == false)) | 
					
						
							| 
									
										
										
										
											2015-05-11 16:09:10 -03:00
										 |  |  |         return; | 
					
						
							| 
									
										
										
										
											2014-05-15 18:27:08 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-11 16:09:10 -03:00
										 |  |  |     if (cur && next) { | 
					
						
							|  |  |  |         LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); | 
					
						
							|  |  |  |     } else if (cur) { | 
					
						
							|  |  |  |         LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId()); | 
					
						
							| 
									
										
										
										
											2015-05-14 11:10:04 -04:00
										 |  |  |     } else if (next) { | 
					
						
							| 
									
										
										
										
											2015-05-11 16:09:10 -03:00
										 |  |  |         LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId()); | 
					
						
							| 
									
										
										
										
											2014-05-22 18:50:36 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-05-25 20:34:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-11 16:09:10 -03:00
										 |  |  |     SwitchContext(next); | 
					
						
							| 
									
										
										
										
											2014-05-17 00:56:00 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-20 20:53:52 -05:00
										 |  |  | void Thread::SetWaitSynchronizationResult(ResultCode result) { | 
					
						
							|  |  |  |     context.cpu_registers[0] = result.raw; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Thread::SetWaitSynchronizationOutput(s32 output) { | 
					
						
							|  |  |  |     context.cpu_registers[1] = output; | 
					
						
							| 
									
										
										
										
											2015-01-17 02:03:44 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-17 00:56:00 -04:00
										 |  |  | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							| 
									
										
										
										
											2014-05-15 18:27:08 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-20 19:37:46 -04:00
										 |  |  | void ThreadingInit() { | 
					
						
							| 
									
										
										
										
											2015-01-07 16:40:08 -05:00
										 |  |  |     ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); | 
					
						
							| 
									
										
										
										
											2015-01-25 22:56:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-27 22:12:35 -04:00
										 |  |  |     current_thread = nullptr; | 
					
						
							|  |  |  |     next_thread_id = 1; | 
					
						
							| 
									
										
										
										
											2014-05-13 22:00:11 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-05-09 22:11:18 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-20 19:37:46 -04:00
										 |  |  | void ThreadingShutdown() { | 
					
						
							| 
									
										
										
										
											2015-07-17 02:24:13 -03:00
										 |  |  |     current_thread = nullptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (auto& t : thread_list) { | 
					
						
							|  |  |  |         t->Stop(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     thread_list.clear(); | 
					
						
							|  |  |  |     ready_queue.clear(); | 
					
						
							| 
									
										
										
										
											2014-05-13 22:00:11 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-05-20 19:37:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | } // namespace
 |