diff options
| author | 2015-02-10 00:53:06 -0500 | |
|---|---|---|
| committer | 2015-02-10 00:53:06 -0500 | |
| commit | dab0b5cefbadd8616515dba2babdb79edfce1a17 (patch) | |
| tree | 4cdac59449b025c7a07e8655f29eda2fbcb423fe /src/core/hle/kernel/thread.cpp | |
| parent | Merge pull request #551 from bunnei/mutex-fixes (diff) | |
| parent | Scheduler refactor Pt. 1 (diff) | |
| download | yuzu-dab0b5cefbadd8616515dba2babdb79edfce1a17.tar.gz yuzu-dab0b5cefbadd8616515dba2babdb79edfce1a17.tar.xz yuzu-dab0b5cefbadd8616515dba2babdb79edfce1a17.zip | |
Merge pull request #528 from kevinhartman/scheduling-logic
Scheduler refactor Pt. 1
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 360 |
1 files changed, 175 insertions, 185 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 3987f9608..7f629c20e 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -21,8 +21,11 @@ | |||
| 21 | 21 | ||
| 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() { |