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