diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/arm/arm_interface.h | 9 | ||||
| -rw-r--r-- | src/core/arm/dyncom/arm_dyncom.cpp | 10 | ||||
| -rw-r--r-- | src/core/arm/dyncom/arm_dyncom.h | 57 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 360 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 115 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 14 |
7 files changed, 287 insertions, 284 deletions
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index e612f7439..ef37ee055 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -86,6 +86,15 @@ public: | |||
| 86 | virtual void AddTicks(u64 ticks) = 0; | 86 | virtual void AddTicks(u64 ticks) = 0; |
| 87 | 87 | ||
| 88 | /** | 88 | /** |
| 89 | * Initializes a CPU context for use on this CPU | ||
| 90 | * @param context Thread context to reset | ||
| 91 | * @param stack_top Pointer to the top of the stack | ||
| 92 | * @param entry_point Entry point for execution | ||
| 93 | * @param arg User argument for thread | ||
| 94 | */ | ||
| 95 | virtual void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) = 0; | ||
| 96 | |||
| 97 | /** | ||
| 89 | * Saves the current CPU context | 98 | * Saves the current CPU context |
| 90 | * @param ctx Thread context to save | 99 | * @param ctx Thread context to save |
| 91 | */ | 100 | */ |
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index f6628ca33..68fddc94f 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp | |||
| @@ -93,6 +93,16 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) { | |||
| 93 | AddTicks(ticks_executed); | 93 | AddTicks(ticks_executed); |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | void ARM_DynCom::ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg) { | ||
| 97 | memset(&context, 0, sizeof(Core::ThreadContext)); | ||
| 98 | |||
| 99 | context.cpu_registers[0] = arg; | ||
| 100 | context.pc = entry_point; | ||
| 101 | context.sp = stack_top; | ||
| 102 | context.cpsr = 0x1F; // Usermode | ||
| 103 | context.mode = 8; // Instructs dyncom CPU core to start execution as if it's "resuming" a thread. | ||
| 104 | } | ||
| 105 | |||
| 96 | void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { | 106 | void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) { |
| 97 | memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); | 107 | memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); |
| 98 | memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); | 108 | memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); |
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index f16fb070c..9e2dda843 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h | |||
| @@ -13,79 +13,24 @@ | |||
| 13 | 13 | ||
| 14 | class ARM_DynCom final : virtual public ARM_Interface { | 14 | class ARM_DynCom final : virtual public ARM_Interface { |
| 15 | public: | 15 | public: |
| 16 | |||
| 17 | ARM_DynCom(); | 16 | ARM_DynCom(); |
| 18 | ~ARM_DynCom(); | 17 | ~ARM_DynCom(); |
| 19 | 18 | ||
| 20 | /** | ||
| 21 | * Set the Program Counter to an address | ||
| 22 | * @param pc Address to set PC to | ||
| 23 | */ | ||
| 24 | void SetPC(u32 pc) override; | 19 | void SetPC(u32 pc) override; |
| 25 | |||
| 26 | /* | ||
| 27 | * Get the current Program Counter | ||
| 28 | * @return Returns current PC | ||
| 29 | */ | ||
| 30 | u32 GetPC() const override; | 20 | u32 GetPC() const override; |
| 31 | |||
| 32 | /** | ||
| 33 | * Get an ARM register | ||
| 34 | * @param index Register index (0-15) | ||
| 35 | * @return Returns the value in the register | ||
| 36 | */ | ||
| 37 | u32 GetReg(int index) const override; | 21 | u32 GetReg(int index) const override; |
| 38 | |||
| 39 | /** | ||
| 40 | * Set an ARM register | ||
| 41 | * @param index Register index (0-15) | ||
| 42 | * @param value Value to set register to | ||
| 43 | */ | ||
| 44 | void SetReg(int index, u32 value) override; | 22 | void SetReg(int index, u32 value) override; |
| 45 | |||
| 46 | /** | ||
| 47 | * Get the current CPSR register | ||
| 48 | * @return Returns the value of the CPSR register | ||
| 49 | */ | ||
| 50 | u32 GetCPSR() const override; | 23 | u32 GetCPSR() const override; |
| 51 | |||
| 52 | /** | ||
| 53 | * Set the current CPSR register | ||
| 54 | * @param cpsr Value to set CPSR to | ||
| 55 | */ | ||
| 56 | void SetCPSR(u32 cpsr) override; | 24 | void SetCPSR(u32 cpsr) override; |
| 57 | 25 | ||
| 58 | /** | ||
| 59 | * Returns the number of clock ticks since the last reset | ||
| 60 | * @return Returns number of clock ticks | ||
| 61 | */ | ||
| 62 | u64 GetTicks() const override; | 26 | u64 GetTicks() const override; |
| 63 | |||
| 64 | /** | ||
| 65 | * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time) | ||
| 66 | * @param ticks Number of ticks to advance the CPU core | ||
| 67 | */ | ||
| 68 | void AddTicks(u64 ticks) override; | 27 | void AddTicks(u64 ticks) override; |
| 69 | 28 | ||
| 70 | /** | 29 | void ResetContext(Core::ThreadContext& context, u32 stack_top, u32 entry_point, u32 arg); |
| 71 | * Saves the current CPU context | ||
| 72 | * @param ctx Thread context to save | ||
| 73 | */ | ||
| 74 | void SaveContext(Core::ThreadContext& ctx) override; | 30 | void SaveContext(Core::ThreadContext& ctx) override; |
| 75 | |||
| 76 | /** | ||
| 77 | * Loads a CPU context | ||
| 78 | * @param ctx Thread context to load | ||
| 79 | */ | ||
| 80 | void LoadContext(const Core::ThreadContext& ctx) override; | 31 | void LoadContext(const Core::ThreadContext& ctx) override; |
| 81 | 32 | ||
| 82 | /// Prepare core for thread reschedule (if needed to correctly handle state) | ||
| 83 | void PrepareReschedule() override; | 33 | void PrepareReschedule() override; |
| 84 | |||
| 85 | /** | ||
| 86 | * Executes the given number of instructions | ||
| 87 | * @param num_instructions Number of instructions to executes | ||
| 88 | */ | ||
| 89 | void ExecuteInstructions(int num_instructions) override; | 34 | void ExecuteInstructions(int num_instructions) override; |
| 90 | 35 | ||
| 91 | private: | 36 | private: |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 52dca4dd8..a2ffbcdb7 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -153,12 +153,8 @@ void Shutdown() { | |||
| 153 | * @return True on success, otherwise false | 153 | * @return True on success, otherwise false |
| 154 | */ | 154 | */ |
| 155 | bool LoadExec(u32 entry_point) { | 155 | bool LoadExec(u32 entry_point) { |
| 156 | Core::g_app_core->SetPC(entry_point); | ||
| 157 | |||
| 158 | // 0x30 is the typical main thread priority I've seen used so far | 156 | // 0x30 is the typical main thread priority I've seen used so far |
| 159 | g_main_thread = Kernel::SetupMainThread(0x30, Kernel::DEFAULT_STACK_SIZE); | 157 | g_main_thread = Kernel::SetupMainThread(Kernel::DEFAULT_STACK_SIZE, entry_point, 0x30); |
| 160 | // Setup the idle thread | ||
| 161 | Kernel::SetupIdleThread(); | ||
| 162 | 158 | ||
| 163 | return true; | 159 | return true; |
| 164 | } | 160 | } |
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 | ||
| 22 | namespace Kernel { | 22 | namespace Kernel { |
| 23 | 23 | ||
| 24 | /// Event type for the thread wake up event | ||
| 25 | static int ThreadWakeupEventType = -1; | ||
| 26 | |||
| 24 | bool Thread::ShouldWait() { | 27 | bool Thread::ShouldWait() { |
| 25 | return status != THREADSTATUS_DORMANT; | 28 | return status != THREADSTATUS_DEAD; |
| 26 | } | 29 | } |
| 27 | 30 | ||
| 28 | void Thread::Acquire() { | 31 | void Thread::Acquire() { |
| @@ -33,12 +36,20 @@ void Thread::Acquire() { | |||
| 33 | static std::vector<SharedPtr<Thread>> thread_list; | 36 | static std::vector<SharedPtr<Thread>> thread_list; |
| 34 | 37 | ||
| 35 | // Lists only ready thread ids. | 38 | // Lists only ready thread ids. |
| 36 | static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> thread_ready_queue; | 39 | static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> ready_queue; |
| 37 | 40 | ||
| 38 | static Thread* current_thread; | 41 | static Thread* current_thread; |
| 39 | 42 | ||
| 40 | static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup | 43 | // The first available thread id at startup |
| 41 | static u32 next_thread_id; ///< The next available thread id | 44 | static u32 next_thread_id = 1; |
| 45 | |||
| 46 | /** | ||
| 47 | * Creates a new thread ID | ||
| 48 | * @return The new thread ID | ||
| 49 | */ | ||
| 50 | inline static u32 const NewThreadId() { | ||
| 51 | return next_thread_id++; | ||
| 52 | } | ||
| 42 | 53 | ||
| 43 | Thread::Thread() {} | 54 | Thread::Thread() {} |
| 44 | Thread::~Thread() {} | 55 | Thread::~Thread() {} |
| @@ -47,86 +58,53 @@ Thread* GetCurrentThread() { | |||
| 47 | return current_thread; | 58 | return current_thread; |
| 48 | } | 59 | } |
| 49 | 60 | ||
| 50 | /// Resets a thread | 61 | /** |
| 51 | static 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 | ||
| 73 | static 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 | ||
| 89 | static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { | 67 | static 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 | */ | ||
| 99 | static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { | 81 | static 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 | 85 | void Thread::Stop() { |
| 104 | void 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 | ||
| 121 | static 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 | ||
| 130 | Thread* ArbitrateHighestPriorityThread(u32 address) { | 108 | Thread* 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 | ||
| 157 | void ArbitrateAllThreads(u32 address) { | 134 | void 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 | /** |
| 167 | static 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 | } | 146 | static 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(); |
| 173 | static 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 | /** |
| 196 | static Thread* NextThread() { | 177 | * Pops and returns the next thread from the thread queue |
| 178 | * @return A pointer to the next ready thread | ||
| 179 | */ | ||
| 180 | static 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 | ||
| 211 | void WaitCurrentThread_Sleep() { | 195 | void 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 | ||
| 216 | void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all) { | 200 | void 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 | ||
| 228 | void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { | 208 | void 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 | ||
| 235 | static 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. |
| 238 | static Kernel::HandleTable wakeup_callback_handle_table; | 216 | static 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 | */ | ||
| 241 | static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { | 223 | static 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 | |||
| 258 | void Thread::WakeAfterDelay(s64 nanoseconds) { | 241 | void 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 | ||
| 267 | void Thread::ReleaseWaitObject(WaitObject* wait_object) { | 250 | void 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 | ||
| 309 | void Thread::ResumeFromWait() { | 292 | void 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 | */ | ||
| 330 | static void DebugThreadQueue() { | 325 | static 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 | ||
| 344 | ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority, | 341 | ResultVal<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. |
| 395 | void Thread::SetPriority(s32 priority) { | 388 | static 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 | 401 | void 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 | ||
| 420 | SharedPtr<Thread> SetupIdleThread() { | 418 | SharedPtr<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 | ||
| 430 | SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size) { | 427 | SharedPtr<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) | ||
| 454 | void Reschedule() { | 442 | void 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 | ||
| 482 | void ThreadingInit() { | 470 | void 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 | ||
| 487 | void ThreadingShutdown() { | 477 | void ThreadingShutdown() { |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 633bb7c98..cfd073a70 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -31,13 +31,13 @@ enum ThreadProcessorId { | |||
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | enum ThreadStatus { | 33 | enum ThreadStatus { |
| 34 | THREADSTATUS_RUNNING = 1, | 34 | THREADSTATUS_RUNNING, ///< Currently running |
| 35 | THREADSTATUS_READY = 2, | 35 | THREADSTATUS_READY, ///< Ready to run |
| 36 | THREADSTATUS_WAIT = 4, | 36 | THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter |
| 37 | THREADSTATUS_SUSPEND = 8, | 37 | THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC |
| 38 | THREADSTATUS_DORMANT = 16, | 38 | THREADSTATUS_WAIT_SYNCH, ///< Waiting due to a WaitSynchronization SVC |
| 39 | THREADSTATUS_DEAD = 32, | 39 | THREADSTATUS_DORMANT, ///< Created but not yet made ready |
| 40 | THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND | 40 | THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | namespace Kernel { | 43 | namespace Kernel { |
| @@ -46,8 +46,19 @@ class Mutex; | |||
| 46 | 46 | ||
| 47 | class Thread final : public WaitObject { | 47 | class Thread final : public WaitObject { |
| 48 | public: | 48 | public: |
| 49 | /** | ||
| 50 | * Creates and returns a new thread. The new thread is immediately scheduled | ||
| 51 | * @param name The friendly name desired for the thread | ||
| 52 | * @param entry_point The address at which the thread should start execution | ||
| 53 | * @param priority The thread's priority | ||
| 54 | * @param arg User data to pass to the thread | ||
| 55 | * @param processor_id The ID(s) of the processors on which the thread is desired to be run | ||
| 56 | * @param stack_top The address of the thread's stack top | ||
| 57 | * @param stack_size The size of the thread's stack | ||
| 58 | * @return A shared pointer to the newly created thread | ||
| 59 | */ | ||
| 49 | static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, | 60 | static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, |
| 50 | u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size); | 61 | u32 arg, s32 processor_id, VAddr stack_top); |
| 51 | 62 | ||
| 52 | std::string GetName() const override { return name; } | 63 | std::string GetName() const override { return name; } |
| 53 | std::string GetTypeName() const override { return "Thread"; } | 64 | std::string GetTypeName() const override { return "Thread"; } |
| @@ -55,22 +66,32 @@ public: | |||
| 55 | static const HandleType HANDLE_TYPE = HandleType::Thread; | 66 | static const HandleType HANDLE_TYPE = HandleType::Thread; |
| 56 | HandleType GetHandleType() const override { return HANDLE_TYPE; } | 67 | HandleType GetHandleType() const override { return HANDLE_TYPE; } |
| 57 | 68 | ||
| 58 | inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } | ||
| 59 | inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } | ||
| 60 | inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; } | ||
| 61 | inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } | ||
| 62 | inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } | ||
| 63 | inline bool IsIdle() const { return idle; } | ||
| 64 | |||
| 65 | bool ShouldWait() override; | 69 | bool ShouldWait() override; |
| 66 | void Acquire() override; | 70 | void Acquire() override; |
| 67 | 71 | ||
| 72 | /** | ||
| 73 | * Checks if the thread is an idle (stub) thread | ||
| 74 | * @return True if the thread is an idle (stub) thread, false otherwise | ||
| 75 | */ | ||
| 76 | inline bool IsIdle() const { return idle; } | ||
| 77 | |||
| 78 | /** | ||
| 79 | * Gets the thread's current priority | ||
| 80 | * @return The current thread's priority | ||
| 81 | */ | ||
| 68 | s32 GetPriority() const { return current_priority; } | 82 | s32 GetPriority() const { return current_priority; } |
| 83 | |||
| 84 | /** | ||
| 85 | * Sets the thread's current priority | ||
| 86 | * @param priority The new priority | ||
| 87 | */ | ||
| 69 | void SetPriority(s32 priority); | 88 | void SetPriority(s32 priority); |
| 70 | 89 | ||
| 90 | /** | ||
| 91 | * Gets the thread's thread ID | ||
| 92 | * @return The thread's ID | ||
| 93 | */ | ||
| 71 | u32 GetThreadId() const { return thread_id; } | 94 | u32 GetThreadId() const { return thread_id; } |
| 72 | |||
| 73 | void Stop(const char* reason); | ||
| 74 | 95 | ||
| 75 | /** | 96 | /** |
| 76 | * Release an acquired wait object | 97 | * Release an acquired wait object |
| @@ -78,12 +99,14 @@ public: | |||
| 78 | */ | 99 | */ |
| 79 | void ReleaseWaitObject(WaitObject* wait_object); | 100 | void ReleaseWaitObject(WaitObject* wait_object); |
| 80 | 101 | ||
| 81 | /// Resumes a thread from waiting by marking it as "ready" | 102 | /** |
| 103 | * Resumes a thread from waiting | ||
| 104 | */ | ||
| 82 | void ResumeFromWait(); | 105 | void ResumeFromWait(); |
| 83 | 106 | ||
| 84 | /** | 107 | /** |
| 85 | * Schedules an event to wake up the specified thread after the specified delay. | 108 | * Schedules an event to wake up the specified thread after the specified delay |
| 86 | * @param nanoseconds The time this thread will be allowed to sleep for. | 109 | * @param nanoseconds The time this thread will be allowed to sleep for |
| 87 | */ | 110 | */ |
| 88 | void WakeAfterDelay(s64 nanoseconds); | 111 | void WakeAfterDelay(s64 nanoseconds); |
| 89 | 112 | ||
| @@ -99,6 +122,11 @@ public: | |||
| 99 | */ | 122 | */ |
| 100 | void SetWaitSynchronizationOutput(s32 output); | 123 | void SetWaitSynchronizationOutput(s32 output); |
| 101 | 124 | ||
| 125 | /** | ||
| 126 | * Stops a thread, invalidating it from further use | ||
| 127 | */ | ||
| 128 | void Stop(); | ||
| 129 | |||
| 102 | Core::ThreadContext context; | 130 | Core::ThreadContext context; |
| 103 | 131 | ||
| 104 | u32 thread_id; | 132 | u32 thread_id; |
| @@ -106,7 +134,6 @@ public: | |||
| 106 | u32 status; | 134 | u32 status; |
| 107 | u32 entry_point; | 135 | u32 entry_point; |
| 108 | u32 stack_top; | 136 | u32 stack_top; |
| 109 | u32 stack_size; | ||
| 110 | 137 | ||
| 111 | s32 initial_priority; | 138 | s32 initial_priority; |
| 112 | s32 current_priority; | 139 | s32 current_priority; |
| @@ -136,31 +163,49 @@ private: | |||
| 136 | 163 | ||
| 137 | extern SharedPtr<Thread> g_main_thread; | 164 | extern SharedPtr<Thread> g_main_thread; |
| 138 | 165 | ||
| 139 | /// Sets up the primary application thread | 166 | /** |
| 140 | SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size); | 167 | * Sets up the primary application thread |
| 168 | * @param stack_size The size of the thread's stack | ||
| 169 | * @param entry_point The address at which the thread should start execution | ||
| 170 | * @param priority The priority to give the main thread | ||
| 171 | * @return A shared pointer to the main thread | ||
| 172 | */ | ||
| 173 | SharedPtr<Thread> SetupMainThread(u32 stack_size, u32 entry_point, s32 priority); | ||
| 141 | 174 | ||
| 142 | /// Reschedules to the next available thread (call after current thread is suspended) | 175 | /** |
| 176 | * Reschedules to the next available thread (call after current thread is suspended) | ||
| 177 | */ | ||
| 143 | void Reschedule(); | 178 | void Reschedule(); |
| 144 | 179 | ||
| 145 | /// Arbitrate the highest priority thread that is waiting | 180 | /** |
| 181 | * Arbitrate the highest priority thread that is waiting | ||
| 182 | * @param address The address for which waiting threads should be arbitrated | ||
| 183 | */ | ||
| 146 | Thread* ArbitrateHighestPriorityThread(u32 address); | 184 | Thread* ArbitrateHighestPriorityThread(u32 address); |
| 147 | 185 | ||
| 148 | /// Arbitrate all threads currently waiting... | 186 | /** |
| 187 | * Arbitrate all threads currently waiting. | ||
| 188 | * @param address The address for which waiting threads should be arbitrated | ||
| 189 | */ | ||
| 149 | void ArbitrateAllThreads(u32 address); | 190 | void ArbitrateAllThreads(u32 address); |
| 150 | 191 | ||
| 151 | /// Gets the current thread | 192 | /** |
| 193 | * Gets the current thread | ||
| 194 | */ | ||
| 152 | Thread* GetCurrentThread(); | 195 | Thread* GetCurrentThread(); |
| 153 | 196 | ||
| 154 | /// Waits the current thread on a sleep | 197 | /** |
| 198 | * Waits the current thread on a sleep | ||
| 199 | */ | ||
| 155 | void WaitCurrentThread_Sleep(); | 200 | void WaitCurrentThread_Sleep(); |
| 156 | 201 | ||
| 157 | /** | 202 | /** |
| 158 | * Waits the current thread from a WaitSynchronization call | 203 | * Waits the current thread from a WaitSynchronization call |
| 159 | * @param wait_object Kernel object that we are waiting on | 204 | * @param wait_objects Kernel objects that we are waiting on |
| 160 | * @param wait_set_output If true, set the output parameter on thread wakeup (for WaitSynchronizationN only) | 205 | * @param wait_set_output If true, set the output parameter on thread wakeup (for WaitSynchronizationN only) |
| 161 | * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only) | 206 | * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only) |
| 162 | */ | 207 | */ |
| 163 | void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all); | 208 | void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, bool wait_set_output, bool wait_all); |
| 164 | 209 | ||
| 165 | /** | 210 | /** |
| 166 | * Waits the current thread from an ArbitrateAddress call | 211 | * Waits the current thread from an ArbitrateAddress call |
| @@ -172,14 +217,18 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address); | |||
| 172 | * Sets up the idle thread, this is a thread that is intended to never execute instructions, | 217 | * Sets up the idle thread, this is a thread that is intended to never execute instructions, |
| 173 | * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue | 218 | * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue |
| 174 | * and will try to yield on every call. | 219 | * and will try to yield on every call. |
| 175 | * @returns The handle of the idle thread | 220 | * @return The handle of the idle thread |
| 176 | */ | 221 | */ |
| 177 | SharedPtr<Thread> SetupIdleThread(); | 222 | SharedPtr<Thread> SetupIdleThread(); |
| 178 | 223 | ||
| 179 | /// Initialize threading | 224 | /** |
| 225 | * Initialize threading | ||
| 226 | */ | ||
| 180 | void ThreadingInit(); | 227 | void ThreadingInit(); |
| 181 | 228 | ||
| 182 | /// Shutdown threading | 229 | /** |
| 230 | * Shutdown threading | ||
| 231 | */ | ||
| 183 | void ThreadingShutdown(); | 232 | void ThreadingShutdown(); |
| 184 | 233 | ||
| 185 | } // namespace | 234 | } // namespace |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index c4866fcce..96da29923 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -150,7 +150,7 @@ static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { | |||
| 150 | if (object->ShouldWait()) { | 150 | if (object->ShouldWait()) { |
| 151 | 151 | ||
| 152 | object->AddWaitingThread(Kernel::GetCurrentThread()); | 152 | object->AddWaitingThread(Kernel::GetCurrentThread()); |
| 153 | Kernel::WaitCurrentThread_WaitSynchronization(object, false, false); | 153 | Kernel::WaitCurrentThread_WaitSynchronization({ object }, false, false); |
| 154 | 154 | ||
| 155 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 155 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 156 | Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); | 156 | Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); |
| @@ -212,7 +212,6 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou | |||
| 212 | // NOTE: This should deadlock the current thread if no timeout was specified | 212 | // NOTE: This should deadlock the current thread if no timeout was specified |
| 213 | if (!wait_all) { | 213 | if (!wait_all) { |
| 214 | wait_thread = true; | 214 | wait_thread = true; |
| 215 | Kernel::WaitCurrentThread_WaitSynchronization(nullptr, true, wait_all); | ||
| 216 | } | 215 | } |
| 217 | } | 216 | } |
| 218 | 217 | ||
| @@ -222,12 +221,17 @@ static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_cou | |||
| 222 | if (wait_thread) { | 221 | if (wait_thread) { |
| 223 | 222 | ||
| 224 | // Actually wait the current thread on each object if we decided to wait... | 223 | // Actually wait the current thread on each object if we decided to wait... |
| 224 | std::vector<SharedPtr<Kernel::WaitObject>> wait_objects; | ||
| 225 | wait_objects.reserve(handle_count); | ||
| 226 | |||
| 225 | for (int i = 0; i < handle_count; ++i) { | 227 | for (int i = 0; i < handle_count; ++i) { |
| 226 | auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); | 228 | auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); |
| 227 | object->AddWaitingThread(Kernel::GetCurrentThread()); | 229 | object->AddWaitingThread(Kernel::GetCurrentThread()); |
| 228 | Kernel::WaitCurrentThread_WaitSynchronization(object, true, wait_all); | 230 | wait_objects.push_back(object); |
| 229 | } | 231 | } |
| 230 | 232 | ||
| 233 | Kernel::WaitCurrentThread_WaitSynchronization(std::move(wait_objects), true, wait_all); | ||
| 234 | |||
| 231 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 235 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 232 | Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); | 236 | Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); |
| 233 | 237 | ||
| @@ -319,7 +323,7 @@ static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u | |||
| 319 | } | 323 | } |
| 320 | 324 | ||
| 321 | CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create( | 325 | CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create( |
| 322 | name, entry_point, priority, arg, processor_id, stack_top, Kernel::DEFAULT_STACK_SIZE)); | 326 | name, entry_point, priority, arg, processor_id, stack_top)); |
| 323 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); | 327 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); |
| 324 | 328 | ||
| 325 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " | 329 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " |
| @@ -338,7 +342,7 @@ static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u | |||
| 338 | static void ExitThread() { | 342 | static void ExitThread() { |
| 339 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); | 343 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); |
| 340 | 344 | ||
| 341 | Kernel::GetCurrentThread()->Stop(__func__); | 345 | Kernel::GetCurrentThread()->Stop(); |
| 342 | HLE::Reschedule(__func__); | 346 | HLE::Reschedule(__func__); |
| 343 | } | 347 | } |
| 344 | 348 | ||