summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/thread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
-rw-r--r--src/core/hle/kernel/thread.cpp136
1 files changed, 90 insertions, 46 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index f3f54a4e9..872df2d14 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -1,5 +1,5 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project 1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
@@ -14,6 +14,7 @@
14#include "core/hle/hle.h" 14#include "core/hle/hle.h"
15#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/thread.h" 16#include "core/hle/kernel/thread.h"
17#include "core/hle/kernel/mutex.h"
17#include "core/hle/result.h" 18#include "core/hle/result.h"
18#include "core/mem_map.h" 19#include "core/mem_map.h"
19 20
@@ -25,8 +26,8 @@ public:
25 std::string GetName() const override { return name; } 26 std::string GetName() const override { return name; }
26 std::string GetTypeName() const override { return "Thread"; } 27 std::string GetTypeName() const override { return "Thread"; }
27 28
28 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } 29 static const HandleType HANDLE_TYPE = HandleType::Thread;
29 Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Thread; } 30 HandleType GetHandleType() const override { return HANDLE_TYPE; }
30 31
31 inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } 32 inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
32 inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } 33 inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; }
@@ -49,6 +50,8 @@ public:
49 50
50 ThreadContext context; 51 ThreadContext context;
51 52
53 u32 thread_id;
54
52 u32 status; 55 u32 status;
53 u32 entry_point; 56 u32 entry_point;
54 u32 stack_top; 57 u32 stack_top;
@@ -61,6 +64,7 @@ public:
61 64
62 WaitType wait_type; 65 WaitType wait_type;
63 Handle wait_handle; 66 Handle wait_handle;
67 VAddr wait_address;
64 68
65 std::vector<Handle> waiting_threads; 69 std::vector<Handle> waiting_threads;
66 70
@@ -76,8 +80,10 @@ static Common::ThreadQueueList<Handle> thread_ready_queue;
76static Handle current_thread_handle; 80static Handle current_thread_handle;
77static Thread* current_thread; 81static Thread* current_thread;
78 82
79/// Gets the current thread 83static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup
80inline Thread* GetCurrentThread() { 84static u32 next_thread_id; ///< The next available thread id
85
86Thread* GetCurrentThread() {
81 return current_thread; 87 return current_thread;
82} 88}
83 89
@@ -121,6 +127,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
121 } 127 }
122 t->wait_type = WAITTYPE_NONE; 128 t->wait_type = WAITTYPE_NONE;
123 t->wait_handle = 0; 129 t->wait_handle = 0;
130 t->wait_address = 0;
124} 131}
125 132
126/// Change a thread to "ready" state 133/// Change a thread to "ready" state
@@ -140,30 +147,43 @@ void ChangeReadyState(Thread* t, bool ready) {
140 } 147 }
141} 148}
142 149
143/// Verify that a thread has not been released from waiting 150/// Check if a thread is blocking on a specified wait type
144inline bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { 151static bool CheckWaitType(const Thread* thread, WaitType type) {
145 _dbg_assert_(KERNEL, thread != nullptr); 152 return (type == thread->wait_type) && (thread->IsWaiting());
146 return type == thread->wait_type && wait_handle == thread->wait_handle; 153}
154
155/// Check if a thread is blocking on a specified wait type with a specified handle
156static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) {
157 return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle);
158}
159
160/// Check if a thread is blocking on a specified wait type with a specified handle and address
161static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) {
162 return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address);
147} 163}
148 164
149/// Stops the current thread 165/// Stops the current thread
150ResultCode StopThread(Handle handle, const char* reason) { 166ResultCode StopThread(Handle handle, const char* reason) {
151 Thread* thread = g_object_pool.Get<Thread>(handle); 167 Thread* thread = g_handle_table.Get<Thread>(handle);
152 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); 168 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
153 169
170 // Release all the mutexes that this thread holds
171 ReleaseThreadMutexes(handle);
172
154 ChangeReadyState(thread, false); 173 ChangeReadyState(thread, false);
155 thread->status = THREADSTATUS_DORMANT; 174 thread->status = THREADSTATUS_DORMANT;
156 for (Handle waiting_handle : thread->waiting_threads) { 175 for (Handle waiting_handle : thread->waiting_threads) {
157 Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle); 176 Thread* waiting_thread = g_handle_table.Get<Thread>(waiting_handle);
158 if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) { 177
178 if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle))
159 ResumeThreadFromWait(waiting_handle); 179 ResumeThreadFromWait(waiting_handle);
160 }
161 } 180 }
162 thread->waiting_threads.clear(); 181 thread->waiting_threads.clear();
163 182
164 // Stopped threads are never waiting. 183 // Stopped threads are never waiting.
165 thread->wait_type = WAITTYPE_NONE; 184 thread->wait_type = WAITTYPE_NONE;
166 thread->wait_handle = 0; 185 thread->wait_handle = 0;
186 thread->wait_address = 0;
167 187
168 return RESULT_SUCCESS; 188 return RESULT_SUCCESS;
169} 189}
@@ -178,7 +198,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
178 198
179 if (new_status == THREADSTATUS_WAIT) { 199 if (new_status == THREADSTATUS_WAIT) {
180 if (t->wait_type == WAITTYPE_NONE) { 200 if (t->wait_type == WAITTYPE_NONE) {
181 ERROR_LOG(KERNEL, "Waittype none not allowed"); 201 LOG_ERROR(Kernel, "Waittype none not allowed");
182 } 202 }
183 } 203 }
184} 204}
@@ -190,14 +210,14 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
190 210
191 // Iterate through threads, find highest priority thread that is waiting to be arbitrated... 211 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
192 for (Handle handle : thread_queue) { 212 for (Handle handle : thread_queue) {
193 Thread* thread = g_object_pool.Get<Thread>(handle); 213 Thread* thread = g_handle_table.Get<Thread>(handle);
194 214
195 // TODO(bunnei): Verify arbiter address... 215 if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
196 if (!VerifyWait(thread, WAITTYPE_ARB, arbiter))
197 continue; 216 continue;
198 217
199 if (thread == nullptr) 218 if (thread == nullptr)
200 continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. 219 continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
220
201 if(thread->current_priority <= priority) { 221 if(thread->current_priority <= priority) {
202 highest_priority_thread = handle; 222 highest_priority_thread = handle;
203 priority = thread->current_priority; 223 priority = thread->current_priority;
@@ -215,10 +235,9 @@ void ArbitrateAllThreads(u32 arbiter, u32 address) {
215 235
216 // Iterate through threads, find highest priority thread that is waiting to be arbitrated... 236 // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
217 for (Handle handle : thread_queue) { 237 for (Handle handle : thread_queue) {
218 Thread* thread = g_object_pool.Get<Thread>(handle); 238 Thread* thread = g_handle_table.Get<Thread>(handle);
219 239
220 // TODO(bunnei): Verify arbiter address... 240 if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
221 if (VerifyWait(thread, WAITTYPE_ARB, arbiter))
222 ResumeThreadFromWait(handle); 241 ResumeThreadFromWait(handle);
223 } 242 }
224} 243}
@@ -269,14 +288,9 @@ Thread* NextThread() {
269 if (next == 0) { 288 if (next == 0) {
270 return nullptr; 289 return nullptr;
271 } 290 }
272 return Kernel::g_object_pool.Get<Thread>(next); 291 return Kernel::g_handle_table.Get<Thread>(next);
273} 292}
274 293
275/**
276 * Puts the current thread in the wait state for the given type
277 * @param wait_type Type of wait
278 * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
279 */
280void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { 294void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
281 Thread* thread = GetCurrentThread(); 295 Thread* thread = GetCurrentThread();
282 thread->wait_type = wait_type; 296 thread->wait_type = wait_type;
@@ -284,11 +298,18 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
284 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); 298 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
285} 299}
286 300
301void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) {
302 WaitCurrentThread(wait_type, wait_handle);
303 GetCurrentThread()->wait_address = wait_address;
304}
305
287/// Resumes a thread from waiting by marking it as "ready" 306/// Resumes a thread from waiting by marking it as "ready"
288void ResumeThreadFromWait(Handle handle) { 307void ResumeThreadFromWait(Handle handle) {
289 Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); 308 Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
290 if (thread) { 309 if (thread) {
291 thread->status &= ~THREADSTATUS_WAIT; 310 thread->status &= ~THREADSTATUS_WAIT;
311 thread->wait_handle = 0;
312 thread->wait_type = WAITTYPE_NONE;
292 if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { 313 if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
293 ChangeReadyState(thread, true); 314 ChangeReadyState(thread, true);
294 } 315 }
@@ -301,12 +322,12 @@ void DebugThreadQueue() {
301 if (!thread) { 322 if (!thread) {
302 return; 323 return;
303 } 324 }
304 INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); 325 LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
305 for (u32 i = 0; i < thread_queue.size(); i++) { 326 for (u32 i = 0; i < thread_queue.size(); i++) {
306 Handle handle = thread_queue[i]; 327 Handle handle = thread_queue[i];
307 s32 priority = thread_ready_queue.contains(handle); 328 s32 priority = thread_ready_queue.contains(handle);
308 if (priority != -1) { 329 if (priority != -1) {
309 INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle); 330 LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle);
310 } 331 }
311 } 332 }
312} 333}
@@ -316,15 +337,17 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
316 s32 processor_id, u32 stack_top, int stack_size) { 337 s32 processor_id, u32 stack_top, int stack_size) {
317 338
318 _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), 339 _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
319 "CreateThread priority=%d, outside of allowable range!", priority) 340 "priority=%d, outside of allowable range!", priority)
320 341
321 Thread* thread = new Thread; 342 Thread* thread = new Thread;
322 343
323 handle = Kernel::g_object_pool.Create(thread); 344 // TOOD(yuriks): Fix error reporting
345 handle = Kernel::g_handle_table.Create(thread).ValueOr(INVALID_HANDLE);
324 346
325 thread_queue.push_back(handle); 347 thread_queue.push_back(handle);
326 thread_ready_queue.prepare(priority); 348 thread_ready_queue.prepare(priority);
327 349
350 thread->thread_id = next_thread_id++;
328 thread->status = THREADSTATUS_DORMANT; 351 thread->status = THREADSTATUS_DORMANT;
329 thread->entry_point = entry_point; 352 thread->entry_point = entry_point;
330 thread->stack_top = stack_top; 353 thread->stack_top = stack_top;
@@ -333,6 +356,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
333 thread->processor_id = processor_id; 356 thread->processor_id = processor_id;
334 thread->wait_type = WAITTYPE_NONE; 357 thread->wait_type = WAITTYPE_NONE;
335 thread->wait_handle = 0; 358 thread->wait_handle = 0;
359 thread->wait_address = 0;
336 thread->name = name; 360 thread->name = name;
337 361
338 return thread; 362 return thread;
@@ -343,24 +367,24 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
343 u32 stack_top, int stack_size) { 367 u32 stack_top, int stack_size) {
344 368
345 if (name == nullptr) { 369 if (name == nullptr) {
346 ERROR_LOG(KERNEL, "CreateThread(): nullptr name"); 370 LOG_ERROR(Kernel_SVC, "nullptr name");
347 return -1; 371 return -1;
348 } 372 }
349 if ((u32)stack_size < 0x200) { 373 if ((u32)stack_size < 0x200) {
350 ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name, 374 LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name,
351 stack_size); 375 stack_size);
352 return -1; 376 return -1;
353 } 377 }
354 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { 378 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
355 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); 379 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
356 WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", 380 LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
357 name, priority, new_priority); 381 name, priority, new_priority);
358 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm 382 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
359 // validity of this 383 // validity of this
360 priority = new_priority; 384 priority = new_priority;
361 } 385 }
362 if (!Memory::GetPointer(entry_point)) { 386 if (!Memory::GetPointer(entry_point)) {
363 ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point); 387 LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point);
364 return -1; 388 return -1;
365 } 389 }
366 Handle handle; 390 Handle handle;
@@ -375,7 +399,7 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
375 399
376/// Get the priority of the thread specified by handle 400/// Get the priority of the thread specified by handle
377ResultVal<u32> GetThreadPriority(const Handle handle) { 401ResultVal<u32> GetThreadPriority(const Handle handle) {
378 Thread* thread = g_object_pool.Get<Thread>(handle); 402 Thread* thread = g_handle_table.Get<Thread>(handle);
379 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); 403 if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
380 404
381 return MakeResult<u32>(thread->current_priority); 405 return MakeResult<u32>(thread->current_priority);
@@ -387,7 +411,7 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) {
387 if (!handle) { 411 if (!handle) {
388 thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? 412 thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
389 } else { 413 } else {
390 thread = g_object_pool.Get<Thread>(handle); 414 thread = g_handle_table.Get<Thread>(handle);
391 if (thread == nullptr) { 415 if (thread == nullptr) {
392 return InvalidHandle(ErrorModule::Kernel); 416 return InvalidHandle(ErrorModule::Kernel);
393 } 417 }
@@ -397,7 +421,7 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) {
397 // If priority is invalid, clamp to valid range 421 // If priority is invalid, clamp to valid range
398 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { 422 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
399 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); 423 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
400 WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority); 424 LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority);
401 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm 425 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
402 // validity of this 426 // validity of this
403 priority = new_priority; 427 priority = new_priority;
@@ -450,24 +474,44 @@ void Reschedule() {
450 Thread* prev = GetCurrentThread(); 474 Thread* prev = GetCurrentThread();
451 Thread* next = NextThread(); 475 Thread* next = NextThread();
452 HLE::g_reschedule = false; 476 HLE::g_reschedule = false;
453 if (next > 0) {
454 INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
455 477
478 if (next != nullptr) {
479 LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
456 SwitchContext(next); 480 SwitchContext(next);
481 } else {
482 LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle());
457 483
458 // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep 484 for (Handle handle : thread_queue) {
459 // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again. 485 Thread* thread = g_handle_table.Get<Thread>(handle);
460 // This results in the current thread yielding on a VBLANK once, and then it will be 486 LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X",
461 // immediately placed back in the queue for execution. 487 thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle);
462 if (prev->wait_type == WAITTYPE_VBLANK) {
463 ResumeThreadFromWait(prev->GetHandle());
464 } 488 }
465 } 489 }
490
491 // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put
492 // to sleep. So, we'll just immediately set it to "ready" again after an attempted context
493 // switch has occurred. This results in the current thread yielding on a sleep once, and then it
494 // will immediately be placed back in the queue for execution.
495
496 if (CheckWaitType(prev, WAITTYPE_SLEEP))
497 ResumeThreadFromWait(prev->GetHandle());
498}
499
500ResultCode GetThreadId(u32* thread_id, Handle handle) {
501 Thread* thread = g_handle_table.Get<Thread>(handle);
502 if (thread == nullptr)
503 return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS,
504 ErrorSummary::WrongArgument, ErrorLevel::Permanent);
505
506 *thread_id = thread->thread_id;
507
508 return RESULT_SUCCESS;
466} 509}
467 510
468//////////////////////////////////////////////////////////////////////////////////////////////////// 511////////////////////////////////////////////////////////////////////////////////////////////////////
469 512
470void ThreadingInit() { 513void ThreadingInit() {
514 next_thread_id = INITIAL_THREAD_ID;
471} 515}
472 516
473void ThreadingShutdown() { 517void ThreadingShutdown() {