diff options
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 400 |
1 files changed, 137 insertions, 263 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 954bd09a0..0ae1a21df 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/common.h" | 10 | #include "common/common.h" |
| 11 | #include "common/thread_queue_list.h" | 11 | #include "common/thread_queue_list.h" |
| 12 | 12 | ||
| 13 | #include "core/arm/arm_interface.h" | ||
| 13 | #include "core/core.h" | 14 | #include "core/core.h" |
| 14 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 15 | #include "core/hle/hle.h" | 16 | #include "core/hle/hle.h" |
| @@ -21,68 +22,25 @@ | |||
| 21 | 22 | ||
| 22 | namespace Kernel { | 23 | namespace Kernel { |
| 23 | 24 | ||
| 24 | class Thread : public Kernel::Object { | 25 | ResultVal<bool> Thread::WaitSynchronization() { |
| 25 | public: | 26 | const bool wait = status != THREADSTATUS_DORMANT; |
| 26 | 27 | if (wait) { | |
| 27 | std::string GetName() const override { return name; } | 28 | Thread* thread = GetCurrentThread(); |
| 28 | std::string GetTypeName() const override { return "Thread"; } | 29 | if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { |
| 29 | 30 | waiting_threads.push_back(thread); | |
| 30 | static const HandleType HANDLE_TYPE = HandleType::Thread; | ||
| 31 | HandleType GetHandleType() const override { return HANDLE_TYPE; } | ||
| 32 | |||
| 33 | inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } | ||
| 34 | inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } | ||
| 35 | inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; } | ||
| 36 | inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } | ||
| 37 | inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } | ||
| 38 | inline bool IsIdle() const { return idle; } | ||
| 39 | |||
| 40 | ResultVal<bool> WaitSynchronization() override { | ||
| 41 | const bool wait = status != THREADSTATUS_DORMANT; | ||
| 42 | if (wait) { | ||
| 43 | Handle thread = GetCurrentThreadHandle(); | ||
| 44 | if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { | ||
| 45 | waiting_threads.push_back(thread); | ||
| 46 | } | ||
| 47 | WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle()); | ||
| 48 | } | 31 | } |
| 49 | 32 | WaitCurrentThread(WAITTYPE_THREADEND, this); | |
| 50 | return MakeResult<bool>(wait); | ||
| 51 | } | 33 | } |
| 52 | 34 | ||
| 53 | ThreadContext context; | 35 | return MakeResult<bool>(wait); |
| 54 | 36 | } | |
| 55 | u32 thread_id; | ||
| 56 | |||
| 57 | u32 status; | ||
| 58 | u32 entry_point; | ||
| 59 | u32 stack_top; | ||
| 60 | u32 stack_size; | ||
| 61 | |||
| 62 | s32 initial_priority; | ||
| 63 | s32 current_priority; | ||
| 64 | |||
| 65 | s32 processor_id; | ||
| 66 | |||
| 67 | WaitType wait_type; | ||
| 68 | Handle wait_handle; | ||
| 69 | VAddr wait_address; | ||
| 70 | |||
| 71 | std::vector<Handle> waiting_threads; | ||
| 72 | |||
| 73 | std::string name; | ||
| 74 | |||
| 75 | /// Whether this thread is intended to never actually be executed, i.e. always idle | ||
| 76 | bool idle = false; | ||
| 77 | }; | ||
| 78 | 37 | ||
| 79 | // Lists all thread ids that aren't deleted/etc. | 38 | // Lists all thread ids that aren't deleted/etc. |
| 80 | static std::vector<Handle> thread_queue; | 39 | static std::vector<Thread*> thread_list; // TODO(yuriks): Owned |
| 81 | 40 | ||
| 82 | // Lists only ready thread ids. | 41 | // Lists only ready thread ids. |
| 83 | static Common::ThreadQueueList<Handle, THREADPRIO_LOWEST+1> thread_ready_queue; | 42 | static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> thread_ready_queue; |
| 84 | 43 | ||
| 85 | static Handle current_thread_handle; | ||
| 86 | static Thread* current_thread; | 44 | static Thread* current_thread; |
| 87 | 45 | ||
| 88 | static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup | 46 | static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup |
| @@ -92,30 +50,9 @@ Thread* GetCurrentThread() { | |||
| 92 | return current_thread; | 50 | return current_thread; |
| 93 | } | 51 | } |
| 94 | 52 | ||
| 95 | /// Gets the current thread handle | ||
| 96 | Handle GetCurrentThreadHandle() { | ||
| 97 | return GetCurrentThread()->GetHandle(); | ||
| 98 | } | ||
| 99 | |||
| 100 | /// Sets the current thread | ||
| 101 | inline void SetCurrentThread(Thread* t) { | ||
| 102 | current_thread = t; | ||
| 103 | current_thread_handle = t->GetHandle(); | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Saves the current CPU context | ||
| 107 | void SaveContext(ThreadContext& ctx) { | ||
| 108 | Core::g_app_core->SaveContext(ctx); | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Loads a CPU context | ||
| 112 | void LoadContext(ThreadContext& ctx) { | ||
| 113 | Core::g_app_core->LoadContext(ctx); | ||
| 114 | } | ||
| 115 | |||
| 116 | /// Resets a thread | 53 | /// Resets a thread |
| 117 | void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | 54 | static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { |
| 118 | memset(&t->context, 0, sizeof(ThreadContext)); | 55 | memset(&t->context, 0, sizeof(Core::ThreadContext)); |
| 119 | 56 | ||
| 120 | t->context.cpu_registers[0] = arg; | 57 | t->context.cpu_registers[0] = arg; |
| 121 | t->context.pc = t->context.reg_15 = t->entry_point; | 58 | t->context.pc = t->context.reg_15 = t->entry_point; |
| @@ -131,22 +68,21 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | |||
| 131 | t->current_priority = t->initial_priority; | 68 | t->current_priority = t->initial_priority; |
| 132 | } | 69 | } |
| 133 | t->wait_type = WAITTYPE_NONE; | 70 | t->wait_type = WAITTYPE_NONE; |
| 134 | t->wait_handle = 0; | 71 | t->wait_object = nullptr; |
| 135 | t->wait_address = 0; | 72 | t->wait_address = 0; |
| 136 | } | 73 | } |
| 137 | 74 | ||
| 138 | /// Change a thread to "ready" state | 75 | /// Change a thread to "ready" state |
| 139 | void ChangeReadyState(Thread* t, bool ready) { | 76 | static void ChangeReadyState(Thread* t, bool ready) { |
| 140 | Handle handle = t->GetHandle(); | ||
| 141 | if (t->IsReady()) { | 77 | if (t->IsReady()) { |
| 142 | if (!ready) { | 78 | if (!ready) { |
| 143 | thread_ready_queue.remove(t->current_priority, handle); | 79 | thread_ready_queue.remove(t->current_priority, t); |
| 144 | } | 80 | } |
| 145 | } else if (ready) { | 81 | } else if (ready) { |
| 146 | if (t->IsRunning()) { | 82 | if (t->IsRunning()) { |
| 147 | thread_ready_queue.push_front(t->current_priority, handle); | 83 | thread_ready_queue.push_front(t->current_priority, t); |
| 148 | } else { | 84 | } else { |
| 149 | thread_ready_queue.push_back(t->current_priority, handle); | 85 | thread_ready_queue.push_back(t->current_priority, t); |
| 150 | } | 86 | } |
| 151 | t->status = THREADSTATUS_READY; | 87 | t->status = THREADSTATUS_READY; |
| 152 | } | 88 | } |
| @@ -158,43 +94,36 @@ static bool CheckWaitType(const Thread* thread, WaitType type) { | |||
| 158 | } | 94 | } |
| 159 | 95 | ||
| 160 | /// Check if a thread is blocking on a specified wait type with a specified handle | 96 | /// Check if a thread is blocking on a specified wait type with a specified handle |
| 161 | static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) { | 97 | static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) { |
| 162 | return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle); | 98 | return CheckWaitType(thread, type) && wait_object == thread->wait_object; |
| 163 | } | 99 | } |
| 164 | 100 | ||
| 165 | /// Check if a thread is blocking on a specified wait type with a specified handle and address | 101 | /// Check if a thread is blocking on a specified wait type with a specified handle and address |
| 166 | static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { | 102 | static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object, VAddr wait_address) { |
| 167 | return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address); | 103 | return CheckWaitType(thread, type, wait_object) && (wait_address == thread->wait_address); |
| 168 | } | 104 | } |
| 169 | 105 | ||
| 170 | /// Stops the current thread | 106 | /// Stops the current thread |
| 171 | ResultCode StopThread(Handle handle, const char* reason) { | 107 | void Thread::Stop(const char* reason) { |
| 172 | Thread* thread = g_handle_table.Get<Thread>(handle); | ||
| 173 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); | ||
| 174 | |||
| 175 | // Release all the mutexes that this thread holds | 108 | // Release all the mutexes that this thread holds |
| 176 | ReleaseThreadMutexes(handle); | 109 | ReleaseThreadMutexes(GetHandle()); |
| 177 | |||
| 178 | ChangeReadyState(thread, false); | ||
| 179 | thread->status = THREADSTATUS_DORMANT; | ||
| 180 | for (Handle waiting_handle : thread->waiting_threads) { | ||
| 181 | Thread* waiting_thread = g_handle_table.Get<Thread>(waiting_handle); | ||
| 182 | 110 | ||
| 183 | if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle)) | 111 | ChangeReadyState(this, false); |
| 184 | ResumeThreadFromWait(waiting_handle); | 112 | status = THREADSTATUS_DORMANT; |
| 113 | for (Thread* waiting_thread : waiting_threads) { | ||
| 114 | if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, this)) | ||
| 115 | waiting_thread->ResumeFromWait(); | ||
| 185 | } | 116 | } |
| 186 | thread->waiting_threads.clear(); | 117 | waiting_threads.clear(); |
| 187 | 118 | ||
| 188 | // Stopped threads are never waiting. | 119 | // Stopped threads are never waiting. |
| 189 | thread->wait_type = WAITTYPE_NONE; | 120 | wait_type = WAITTYPE_NONE; |
| 190 | thread->wait_handle = 0; | 121 | wait_object = nullptr; |
| 191 | thread->wait_address = 0; | 122 | wait_address = 0; |
| 192 | |||
| 193 | return RESULT_SUCCESS; | ||
| 194 | } | 123 | } |
| 195 | 124 | ||
| 196 | /// Changes a threads state | 125 | /// Changes a threads state |
| 197 | void ChangeThreadState(Thread* t, ThreadStatus new_status) { | 126 | static void ChangeThreadState(Thread* t, ThreadStatus new_status) { |
| 198 | if (!t || t->status == new_status) { | 127 | if (!t || t->status == new_status) { |
| 199 | return; | 128 | return; |
| 200 | } | 129 | } |
| @@ -209,14 +138,12 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) { | |||
| 209 | } | 138 | } |
| 210 | 139 | ||
| 211 | /// Arbitrate the highest priority thread that is waiting | 140 | /// Arbitrate the highest priority thread that is waiting |
| 212 | Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { | 141 | Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { |
| 213 | Handle highest_priority_thread = 0; | 142 | Thread* highest_priority_thread = nullptr; |
| 214 | s32 priority = THREADPRIO_LOWEST; | 143 | s32 priority = THREADPRIO_LOWEST; |
| 215 | 144 | ||
| 216 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | 145 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... |
| 217 | for (Handle handle : thread_queue) { | 146 | for (Thread* thread : thread_list) { |
| 218 | Thread* thread = g_handle_table.Get<Thread>(handle); | ||
| 219 | |||
| 220 | if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) | 147 | if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) |
| 221 | continue; | 148 | continue; |
| 222 | 149 | ||
| @@ -224,31 +151,31 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { | |||
| 224 | continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. | 151 | continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. |
| 225 | 152 | ||
| 226 | if(thread->current_priority <= priority) { | 153 | if(thread->current_priority <= priority) { |
| 227 | highest_priority_thread = handle; | 154 | highest_priority_thread = thread; |
| 228 | priority = thread->current_priority; | 155 | priority = thread->current_priority; |
| 229 | } | 156 | } |
| 230 | } | 157 | } |
| 158 | |||
| 231 | // If a thread was arbitrated, resume it | 159 | // If a thread was arbitrated, resume it |
| 232 | if (0 != highest_priority_thread) | 160 | if (nullptr != highest_priority_thread) { |
| 233 | ResumeThreadFromWait(highest_priority_thread); | 161 | highest_priority_thread->ResumeFromWait(); |
| 162 | } | ||
| 234 | 163 | ||
| 235 | return highest_priority_thread; | 164 | return highest_priority_thread; |
| 236 | } | 165 | } |
| 237 | 166 | ||
| 238 | /// Arbitrate all threads currently waiting | 167 | /// Arbitrate all threads currently waiting |
| 239 | void ArbitrateAllThreads(u32 arbiter, u32 address) { | 168 | void ArbitrateAllThreads(Object* arbiter, u32 address) { |
| 240 | 169 | ||
| 241 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | 170 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... |
| 242 | for (Handle handle : thread_queue) { | 171 | for (Thread* thread : thread_list) { |
| 243 | Thread* thread = g_handle_table.Get<Thread>(handle); | ||
| 244 | |||
| 245 | if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) | 172 | if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) |
| 246 | ResumeThreadFromWait(handle); | 173 | thread->ResumeFromWait(); |
| 247 | } | 174 | } |
| 248 | } | 175 | } |
| 249 | 176 | ||
| 250 | /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) | 177 | /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) |
| 251 | void CallThread(Thread* t) { | 178 | static void CallThread(Thread* t) { |
| 252 | // Stop waiting | 179 | // Stop waiting |
| 253 | if (t->wait_type != WAITTYPE_NONE) { | 180 | if (t->wait_type != WAITTYPE_NONE) { |
| 254 | t->wait_type = WAITTYPE_NONE; | 181 | t->wait_type = WAITTYPE_NONE; |
| @@ -257,12 +184,12 @@ void CallThread(Thread* t) { | |||
| 257 | } | 184 | } |
| 258 | 185 | ||
| 259 | /// Switches CPU context to that of the specified thread | 186 | /// Switches CPU context to that of the specified thread |
| 260 | void SwitchContext(Thread* t) { | 187 | static void SwitchContext(Thread* t) { |
| 261 | Thread* cur = GetCurrentThread(); | 188 | Thread* cur = GetCurrentThread(); |
| 262 | 189 | ||
| 263 | // Save context for current thread | 190 | // Save context for current thread |
| 264 | if (cur) { | 191 | if (cur) { |
| 265 | SaveContext(cur->context); | 192 | Core::g_app_core->SaveContext(cur->context); |
| 266 | 193 | ||
| 267 | if (cur->IsRunning()) { | 194 | if (cur->IsRunning()) { |
| 268 | ChangeReadyState(cur, true); | 195 | ChangeReadyState(cur, true); |
| @@ -270,19 +197,19 @@ void SwitchContext(Thread* t) { | |||
| 270 | } | 197 | } |
| 271 | // Load context of new thread | 198 | // Load context of new thread |
| 272 | if (t) { | 199 | if (t) { |
| 273 | SetCurrentThread(t); | 200 | current_thread = t; |
| 274 | ChangeReadyState(t, false); | 201 | ChangeReadyState(t, false); |
| 275 | t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; | 202 | t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; |
| 276 | t->wait_type = WAITTYPE_NONE; | 203 | t->wait_type = WAITTYPE_NONE; |
| 277 | LoadContext(t->context); | 204 | Core::g_app_core->LoadContext(t->context); |
| 278 | } else { | 205 | } else { |
| 279 | SetCurrentThread(nullptr); | 206 | current_thread = nullptr; |
| 280 | } | 207 | } |
| 281 | } | 208 | } |
| 282 | 209 | ||
| 283 | /// Gets the next thread that is ready to be run by priority | 210 | /// Gets the next thread that is ready to be run by priority |
| 284 | Thread* NextThread() { | 211 | static Thread* NextThread() { |
| 285 | Handle next; | 212 | Thread* next; |
| 286 | Thread* cur = GetCurrentThread(); | 213 | Thread* cur = GetCurrentThread(); |
| 287 | 214 | ||
| 288 | if (cur && cur->IsRunning()) { | 215 | if (cur && cur->IsRunning()) { |
| @@ -293,18 +220,18 @@ Thread* NextThread() { | |||
| 293 | if (next == 0) { | 220 | if (next == 0) { |
| 294 | return nullptr; | 221 | return nullptr; |
| 295 | } | 222 | } |
| 296 | return Kernel::g_handle_table.Get<Thread>(next); | 223 | return next; |
| 297 | } | 224 | } |
| 298 | 225 | ||
| 299 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { | 226 | void WaitCurrentThread(WaitType wait_type, Object* wait_object) { |
| 300 | Thread* thread = GetCurrentThread(); | 227 | Thread* thread = GetCurrentThread(); |
| 301 | thread->wait_type = wait_type; | 228 | thread->wait_type = wait_type; |
| 302 | thread->wait_handle = wait_handle; | 229 | thread->wait_object = wait_object; |
| 303 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | 230 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); |
| 304 | } | 231 | } |
| 305 | 232 | ||
| 306 | void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) { | 233 | void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) { |
| 307 | WaitCurrentThread(wait_type, wait_handle); | 234 | WaitCurrentThread(wait_type, wait_object); |
| 308 | GetCurrentThread()->wait_address = wait_address; | 235 | GetCurrentThread()->wait_address = wait_address; |
| 309 | } | 236 | } |
| 310 | 237 | ||
| @@ -320,67 +247,84 @@ static void ThreadWakeupCallback(u64 parameter, int cycles_late) { | |||
| 320 | return; | 247 | return; |
| 321 | } | 248 | } |
| 322 | 249 | ||
| 323 | Kernel::ResumeThreadFromWait(handle); | 250 | thread->ResumeFromWait(); |
| 324 | } | 251 | } |
| 325 | 252 | ||
| 326 | 253 | ||
| 327 | void WakeThreadAfterDelay(Handle handle, s64 nanoseconds) { | 254 | void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) { |
| 328 | // Don't schedule a wakeup if the thread wants to wait forever | 255 | // Don't schedule a wakeup if the thread wants to wait forever |
| 329 | if (nanoseconds == -1) | 256 | if (nanoseconds == -1) |
| 330 | return; | 257 | return; |
| 331 | 258 | _dbg_assert_(Kernel, thread != nullptr); | |
| 332 | Thread* thread = Kernel::g_handle_table.Get<Thread>(handle); | ||
| 333 | if (thread == nullptr) { | ||
| 334 | LOG_ERROR(Kernel, "Thread doesn't exist %u", handle); | ||
| 335 | return; | ||
| 336 | } | ||
| 337 | 259 | ||
| 338 | u64 microseconds = nanoseconds / 1000; | 260 | u64 microseconds = nanoseconds / 1000; |
| 339 | CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, handle); | 261 | CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); |
| 340 | } | 262 | } |
| 341 | 263 | ||
| 342 | /// Resumes a thread from waiting by marking it as "ready" | 264 | /// Resumes a thread from waiting by marking it as "ready" |
| 343 | void ResumeThreadFromWait(Handle handle) { | 265 | void Thread::ResumeFromWait() { |
| 344 | Thread* thread = Kernel::g_handle_table.Get<Thread>(handle); | 266 | status &= ~THREADSTATUS_WAIT; |
| 345 | if (thread) { | 267 | wait_object = nullptr; |
| 346 | thread->status &= ~THREADSTATUS_WAIT; | 268 | wait_type = WAITTYPE_NONE; |
| 347 | thread->wait_handle = 0; | 269 | if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { |
| 348 | thread->wait_type = WAITTYPE_NONE; | 270 | ChangeReadyState(this, true); |
| 349 | if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | ||
| 350 | ChangeReadyState(thread, true); | ||
| 351 | } | ||
| 352 | } | 271 | } |
| 353 | } | 272 | } |
| 354 | 273 | ||
| 355 | /// Prints the thread queue for debugging purposes | 274 | /// Prints the thread queue for debugging purposes |
| 356 | void DebugThreadQueue() { | 275 | static void DebugThreadQueue() { |
| 357 | Thread* thread = GetCurrentThread(); | 276 | Thread* thread = GetCurrentThread(); |
| 358 | if (!thread) { | 277 | if (!thread) { |
| 359 | return; | 278 | return; |
| 360 | } | 279 | } |
| 361 | LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); | 280 | LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThread()->GetHandle()); |
| 362 | for (u32 i = 0; i < thread_queue.size(); i++) { | 281 | for (Thread* t : thread_list) { |
| 363 | Handle handle = thread_queue[i]; | 282 | s32 priority = thread_ready_queue.contains(t); |
| 364 | s32 priority = thread_ready_queue.contains(handle); | ||
| 365 | if (priority != -1) { | 283 | if (priority != -1) { |
| 366 | LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle); | 284 | LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, t->GetHandle()); |
| 367 | } | 285 | } |
| 368 | } | 286 | } |
| 369 | } | 287 | } |
| 370 | 288 | ||
| 371 | /// Creates a new thread | 289 | ResultVal<Thread*> Thread::Create(const char* name, u32 entry_point, s32 priority, u32 arg, |
| 372 | Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, | 290 | s32 processor_id, u32 stack_top, int stack_size) { |
| 373 | s32 processor_id, u32 stack_top, int stack_size) { | 291 | _dbg_assert_(Kernel, name != nullptr); |
| 292 | |||
| 293 | if ((u32)stack_size < 0x200) { | ||
| 294 | LOG_ERROR(Kernel, "(name=%s): invalid stack_size=0x%08X", name, stack_size); | ||
| 295 | // TODO: Verify error | ||
| 296 | return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Kernel, | ||
| 297 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||
| 298 | } | ||
| 299 | |||
| 300 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | ||
| 301 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | ||
| 302 | LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", | ||
| 303 | name, priority, new_priority); | ||
| 304 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm | ||
| 305 | // validity of this | ||
| 306 | priority = new_priority; | ||
| 307 | } | ||
| 374 | 308 | ||
| 375 | _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), | 309 | if (!Memory::GetPointer(entry_point)) { |
| 376 | "priority=%d, outside of allowable range!", priority) | 310 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point); |
| 311 | // TODO: Verify error | ||
| 312 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, | ||
| 313 | ErrorSummary::InvalidArgument, ErrorLevel::Permanent); | ||
| 314 | } | ||
| 377 | 315 | ||
| 378 | Thread* thread = new Thread; | 316 | Thread* thread = new Thread; |
| 379 | 317 | ||
| 380 | // TOOD(yuriks): Fix error reporting | 318 | // TODO(yuriks): Thread requires a handle to be inserted into the various scheduling queues for |
| 381 | handle = Kernel::g_handle_table.Create(thread).ValueOr(INVALID_HANDLE); | 319 | // the time being. Create a handle here, it will be copied to the handle field in |
| 320 | // the object and use by the rest of the code. This should be removed when other | ||
| 321 | // code doesn't rely on the handle anymore. | ||
| 322 | ResultVal<Handle> handle = Kernel::g_handle_table.Create(thread); | ||
| 323 | // TODO(yuriks): Plug memory leak | ||
| 324 | if (handle.Failed()) | ||
| 325 | return handle.Code(); | ||
| 382 | 326 | ||
| 383 | thread_queue.push_back(handle); | 327 | thread_list.push_back(thread); |
| 384 | thread_ready_queue.prepare(priority); | 328 | thread_ready_queue.prepare(priority); |
| 385 | 329 | ||
| 386 | thread->thread_id = next_thread_id++; | 330 | thread->thread_id = next_thread_id++; |
| @@ -391,69 +335,18 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio | |||
| 391 | thread->initial_priority = thread->current_priority = priority; | 335 | thread->initial_priority = thread->current_priority = priority; |
| 392 | thread->processor_id = processor_id; | 336 | thread->processor_id = processor_id; |
| 393 | thread->wait_type = WAITTYPE_NONE; | 337 | thread->wait_type = WAITTYPE_NONE; |
| 394 | thread->wait_handle = 0; | 338 | thread->wait_object = nullptr; |
| 395 | thread->wait_address = 0; | 339 | thread->wait_address = 0; |
| 396 | thread->name = name; | 340 | thread->name = name; |
| 397 | 341 | ||
| 398 | return thread; | ||
| 399 | } | ||
| 400 | |||
| 401 | /// Creates a new thread - wrapper for external user | ||
| 402 | Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, | ||
| 403 | u32 stack_top, int stack_size) { | ||
| 404 | |||
| 405 | if (name == nullptr) { | ||
| 406 | LOG_ERROR(Kernel_SVC, "nullptr name"); | ||
| 407 | return -1; | ||
| 408 | } | ||
| 409 | if ((u32)stack_size < 0x200) { | ||
| 410 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name, | ||
| 411 | stack_size); | ||
| 412 | return -1; | ||
| 413 | } | ||
| 414 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | ||
| 415 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | ||
| 416 | LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", | ||
| 417 | name, priority, new_priority); | ||
| 418 | // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm | ||
| 419 | // validity of this | ||
| 420 | priority = new_priority; | ||
| 421 | } | ||
| 422 | if (!Memory::GetPointer(entry_point)) { | ||
| 423 | LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point); | ||
| 424 | return -1; | ||
| 425 | } | ||
| 426 | Handle handle; | ||
| 427 | Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, | ||
| 428 | stack_size); | ||
| 429 | |||
| 430 | ResetThread(thread, arg, 0); | 342 | ResetThread(thread, arg, 0); |
| 431 | CallThread(thread); | 343 | CallThread(thread); |
| 432 | 344 | ||
| 433 | return handle; | 345 | return MakeResult<Thread*>(thread); |
| 434 | } | ||
| 435 | |||
| 436 | /// Get the priority of the thread specified by handle | ||
| 437 | ResultVal<u32> GetThreadPriority(const Handle handle) { | ||
| 438 | Thread* thread = g_handle_table.Get<Thread>(handle); | ||
| 439 | if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); | ||
| 440 | |||
| 441 | return MakeResult<u32>(thread->current_priority); | ||
| 442 | } | 346 | } |
| 443 | 347 | ||
| 444 | /// Set the priority of the thread specified by handle | 348 | /// Set the priority of the thread specified by handle |
| 445 | ResultCode SetThreadPriority(Handle handle, s32 priority) { | 349 | void Thread::SetPriority(s32 priority) { |
| 446 | Thread* thread = nullptr; | ||
| 447 | if (!handle) { | ||
| 448 | thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? | ||
| 449 | } else { | ||
| 450 | thread = g_handle_table.Get<Thread>(handle); | ||
| 451 | if (thread == nullptr) { | ||
| 452 | return InvalidHandle(ErrorModule::Kernel); | ||
| 453 | } | ||
| 454 | } | ||
| 455 | _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); | ||
| 456 | |||
| 457 | // If priority is invalid, clamp to valid range | 350 | // If priority is invalid, clamp to valid range |
| 458 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { | 351 | if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { |
| 459 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); | 352 | s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); |
| @@ -464,38 +357,39 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) { | |||
| 464 | } | 357 | } |
| 465 | 358 | ||
| 466 | // Change thread priority | 359 | // Change thread priority |
| 467 | s32 old = thread->current_priority; | 360 | s32 old = current_priority; |
| 468 | thread_ready_queue.remove(old, handle); | 361 | thread_ready_queue.remove(old, this); |
| 469 | thread->current_priority = priority; | 362 | current_priority = priority; |
| 470 | thread_ready_queue.prepare(thread->current_priority); | 363 | thread_ready_queue.prepare(current_priority); |
| 471 | 364 | ||
| 472 | // Change thread status to "ready" and push to ready queue | 365 | // Change thread status to "ready" and push to ready queue |
| 473 | if (thread->IsRunning()) { | 366 | if (IsRunning()) { |
| 474 | thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; | 367 | status = (status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; |
| 475 | } | 368 | } |
| 476 | if (thread->IsReady()) { | 369 | if (IsReady()) { |
| 477 | thread_ready_queue.push_back(thread->current_priority, handle); | 370 | thread_ready_queue.push_back(current_priority, this); |
| 478 | } | 371 | } |
| 479 | |||
| 480 | return RESULT_SUCCESS; | ||
| 481 | } | 372 | } |
| 482 | 373 | ||
| 483 | Handle SetupIdleThread() { | 374 | Handle SetupIdleThread() { |
| 484 | Handle handle; | 375 | // We need to pass a few valid values to get around parameter checking in Thread::Create. |
| 485 | Thread* thread = CreateThread(handle, "idle", 0, THREADPRIO_LOWEST, THREADPROCESSORID_0, 0, 0); | 376 | auto thread_res = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, |
| 377 | THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE); | ||
| 378 | _dbg_assert_(Kernel, thread_res.Succeeded()); | ||
| 379 | Thread* thread = *thread_res; | ||
| 380 | |||
| 486 | thread->idle = true; | 381 | thread->idle = true; |
| 487 | CallThread(thread); | 382 | CallThread(thread); |
| 488 | return handle; | 383 | return thread->GetHandle(); |
| 489 | } | 384 | } |
| 490 | 385 | ||
| 491 | Handle SetupMainThread(s32 priority, int stack_size) { | 386 | Thread* SetupMainThread(s32 priority, int stack_size) { |
| 492 | Handle handle; | ||
| 493 | |||
| 494 | // Initialize new "main" thread | 387 | // Initialize new "main" thread |
| 495 | Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, | 388 | ResultVal<Thread*> thread_res = Thread::Create("main", Core::g_app_core->GetPC(), priority, 0, |
| 496 | THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); | 389 | THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); |
| 497 | 390 | // TODO(yuriks): Propagate error | |
| 498 | ResetThread(thread, 0, 0); | 391 | _dbg_assert_(Kernel, thread_res.Succeeded()); |
| 392 | Thread* thread = *thread_res; | ||
| 499 | 393 | ||
| 500 | // If running another thread already, set it to "ready" state | 394 | // If running another thread already, set it to "ready" state |
| 501 | Thread* cur = GetCurrentThread(); | 395 | Thread* cur = GetCurrentThread(); |
| @@ -504,11 +398,11 @@ Handle SetupMainThread(s32 priority, int stack_size) { | |||
| 504 | } | 398 | } |
| 505 | 399 | ||
| 506 | // Run new "main" thread | 400 | // Run new "main" thread |
| 507 | SetCurrentThread(thread); | 401 | current_thread = thread; |
| 508 | thread->status = THREADSTATUS_RUNNING; | 402 | thread->status = THREADSTATUS_RUNNING; |
| 509 | LoadContext(thread->context); | 403 | Core::g_app_core->LoadContext(thread->context); |
| 510 | 404 | ||
| 511 | return handle; | 405 | return thread; |
| 512 | } | 406 | } |
| 513 | 407 | ||
| 514 | 408 | ||
| @@ -524,34 +418,14 @@ void Reschedule() { | |||
| 524 | } else { | 418 | } else { |
| 525 | LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); | 419 | LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); |
| 526 | 420 | ||
| 527 | for (Handle handle : thread_queue) { | 421 | for (Thread* thread : thread_list) { |
| 528 | Thread* thread = g_handle_table.Get<Thread>(handle); | ||
| 529 | LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", | 422 | LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", |
| 530 | thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle); | 423 | thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, |
| 424 | (thread->wait_object ? thread->wait_object->GetHandle() : INVALID_HANDLE)); | ||
| 531 | } | 425 | } |
| 532 | } | 426 | } |
| 533 | } | 427 | } |
| 534 | 428 | ||
| 535 | bool IsIdleThread(Handle handle) { | ||
| 536 | Thread* thread = g_handle_table.Get<Thread>(handle); | ||
| 537 | if (!thread) { | ||
| 538 | LOG_ERROR(Kernel, "Thread not found %u", handle); | ||
| 539 | return false; | ||
| 540 | } | ||
| 541 | return thread->IsIdle(); | ||
| 542 | } | ||
| 543 | |||
| 544 | ResultCode GetThreadId(u32* thread_id, Handle handle) { | ||
| 545 | Thread* thread = g_handle_table.Get<Thread>(handle); | ||
| 546 | if (thread == nullptr) | ||
| 547 | return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, | ||
| 548 | ErrorSummary::WrongArgument, ErrorLevel::Permanent); | ||
| 549 | |||
| 550 | *thread_id = thread->thread_id; | ||
| 551 | |||
| 552 | return RESULT_SUCCESS; | ||
| 553 | } | ||
| 554 | |||
| 555 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 429 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 556 | 430 | ||
| 557 | void ThreadingInit() { | 431 | void ThreadingInit() { |