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.cpp223
1 files changed, 168 insertions, 55 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index bf4c8353c..ab5a5559e 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -5,6 +5,7 @@
5#include <stdio.h> 5#include <stdio.h>
6 6
7#include <list> 7#include <list>
8#include <algorithm>
8#include <vector> 9#include <vector>
9#include <map> 10#include <map>
10#include <string> 11#include <string>
@@ -24,10 +25,10 @@ namespace Kernel {
24class Thread : public Kernel::Object { 25class Thread : public Kernel::Object {
25public: 26public:
26 27
27 const char* GetName() { return name; } 28 const char* GetName() const { return name; }
28 const char* GetTypeName() { return "Thread"; } 29 const char* GetTypeName() const { return "Thread"; }
29 30
30 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } 31 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; }
31 Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; } 32 Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; }
32 33
33 inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } 34 inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
@@ -36,6 +37,23 @@ public:
36 inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } 37 inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
37 inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } 38 inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
38 39
40 /**
41 * Wait for kernel object to synchronize
42 * @param wait Boolean wait set if current thread should wait as a result of sync operation
43 * @return Result of operation, 0 on success, otherwise error code
44 */
45 Result WaitSynchronization(bool* wait) {
46 if (status != THREADSTATUS_DORMANT) {
47 Handle thread = GetCurrentThreadHandle();
48 if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
49 waiting_threads.push_back(thread);
50 }
51 WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
52 *wait = true;
53 }
54 return 0;
55 }
56
39 ThreadContext context; 57 ThreadContext context;
40 58
41 u32 status; 59 u32 status;
@@ -49,6 +67,9 @@ public:
49 s32 processor_id; 67 s32 processor_id;
50 68
51 WaitType wait_type; 69 WaitType wait_type;
70 Handle wait_handle;
71
72 std::vector<Handle> waiting_threads;
52 73
53 char name[Kernel::MAX_NAME_LENGTH + 1]; 74 char name[Kernel::MAX_NAME_LENGTH + 1];
54}; 75};
@@ -62,7 +83,6 @@ Common::ThreadQueueList<Handle> g_thread_ready_queue;
62Handle g_current_thread_handle; 83Handle g_current_thread_handle;
63Thread* g_current_thread; 84Thread* g_current_thread;
64 85
65
66/// Gets the current thread 86/// Gets the current thread
67inline Thread* GetCurrentThread() { 87inline Thread* GetCurrentThread() {
68 return g_current_thread; 88 return g_current_thread;
@@ -94,15 +114,15 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
94 memset(&t->context, 0, sizeof(ThreadContext)); 114 memset(&t->context, 0, sizeof(ThreadContext));
95 115
96 t->context.cpu_registers[0] = arg; 116 t->context.cpu_registers[0] = arg;
97 t->context.pc = t->entry_point; 117 t->context.pc = t->context.reg_15 = t->entry_point;
98 t->context.sp = t->stack_top; 118 t->context.sp = t->stack_top;
99 t->context.cpsr = 0x1F; // Usermode 119 t->context.cpsr = 0x1F; // Usermode
100 120
101 if (t->current_priority < lowest_priority) { 121 if (t->current_priority < lowest_priority) {
102 t->current_priority = t->initial_priority; 122 t->current_priority = t->initial_priority;
103 } 123 }
104
105 t->wait_type = WAITTYPE_NONE; 124 t->wait_type = WAITTYPE_NONE;
125 t->wait_handle = 0;
106} 126}
107 127
108/// Change a thread to "ready" state 128/// Change a thread to "ready" state
@@ -122,6 +142,37 @@ void ChangeReadyState(Thread* t, bool ready) {
122 } 142 }
123} 143}
124 144
145/// Verify that a thread has not been released from waiting
146inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) {
147 Thread* thread = g_object_pool.GetFast<Thread>(handle);
148 _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
149
150 if (type != thread->wait_type || wait_handle != thread->wait_handle)
151 return false;
152
153 return true;
154}
155
156/// Stops the current thread
157void StopThread(Handle handle, const char* reason) {
158 Thread* thread = g_object_pool.GetFast<Thread>(handle);
159 _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
160
161 ChangeReadyState(thread, false);
162 thread->status = THREADSTATUS_DORMANT;
163 for (size_t i = 0; i < thread->waiting_threads.size(); ++i) {
164 const Handle waiting_thread = thread->waiting_threads[i];
165 if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
166 ResumeThreadFromWait(waiting_thread);
167 }
168 }
169 thread->waiting_threads.clear();
170
171 // Stopped threads are never waiting.
172 thread->wait_type = WAITTYPE_NONE;
173 thread->wait_handle = 0;
174}
175
125/// Changes a threads state 176/// Changes a threads state
126void ChangeThreadState(Thread* t, ThreadStatus new_status) { 177void ChangeThreadState(Thread* t, ThreadStatus new_status) {
127 if (!t || t->status == new_status) { 178 if (!t || t->status == new_status) {
@@ -132,7 +183,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
132 183
133 if (new_status == THREADSTATUS_WAIT) { 184 if (new_status == THREADSTATUS_WAIT) {
134 if (t->wait_type == WAITTYPE_NONE) { 185 if (t->wait_type == WAITTYPE_NONE) {
135 printf("ERROR: Waittype none not allowed here\n"); 186 ERROR_LOG(KERNEL, "Waittype none not allowed");
136 } 187 }
137 } 188 }
138} 189}
@@ -166,7 +217,7 @@ void SwitchContext(Thread* t) {
166 t->wait_type = WAITTYPE_NONE; 217 t->wait_type = WAITTYPE_NONE;
167 LoadContext(t->context); 218 LoadContext(t->context);
168 } else { 219 } else {
169 SetCurrentThread(NULL); 220 SetCurrentThread(nullptr);
170 } 221 }
171} 222}
172 223
@@ -181,26 +232,43 @@ Thread* NextThread() {
181 next = g_thread_ready_queue.pop_first(); 232 next = g_thread_ready_queue.pop_first();
182 } 233 }
183 if (next == 0) { 234 if (next == 0) {
184 return NULL; 235 return nullptr;
185 } 236 }
186 return Kernel::g_object_pool.GetFast<Thread>(next); 237 return Kernel::g_object_pool.GetFast<Thread>(next);
187} 238}
188 239
189/// Puts the current thread in the wait state for the given type 240/// Puts the current thread in the wait state for the given type
190void WaitCurrentThread(WaitType wait_type) { 241void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
191 Thread* t = GetCurrentThread(); 242 Thread* thread = GetCurrentThread();
192 t->wait_type = wait_type; 243 thread->wait_type = wait_type;
193 ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); 244 thread->wait_handle = wait_handle;
245 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
194} 246}
195 247
196/// Resumes a thread from waiting by marking it as "ready" 248/// Resumes a thread from waiting by marking it as "ready"
197void ResumeThreadFromWait(Handle handle) { 249void ResumeThreadFromWait(Handle handle) {
198 u32 error; 250 u32 error;
199 Thread* t = Kernel::g_object_pool.Get<Thread>(handle, error); 251 Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error);
200 if (t) { 252 if (thread) {
201 t->status &= ~THREADSTATUS_WAIT; 253 thread->status &= ~THREADSTATUS_WAIT;
202 if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { 254 if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
203 ChangeReadyState(t, true); 255 ChangeReadyState(thread, true);
256 }
257 }
258}
259
260/// Prints the thread queue for debugging purposes
261void DebugThreadQueue() {
262 Thread* thread = GetCurrentThread();
263 if (!thread) {
264 return;
265 }
266 INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
267 for (u32 i = 0; i < g_thread_queue.size(); i++) {
268 Handle handle = g_thread_queue[i];
269 s32 priority = g_thread_ready_queue.contains(handle);
270 if (priority != -1) {
271 INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle);
204 } 272 }
205 } 273 }
206} 274}
@@ -212,32 +280,34 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
212 _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), 280 _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
213 "CreateThread priority=%d, outside of allowable range!", priority) 281 "CreateThread priority=%d, outside of allowable range!", priority)
214 282
215 Thread* t = new Thread; 283 Thread* thread = new Thread;
216 284
217 handle = Kernel::g_object_pool.Create(t); 285 handle = Kernel::g_object_pool.Create(thread);
218 286
219 g_thread_queue.push_back(handle); 287 g_thread_queue.push_back(handle);
220 g_thread_ready_queue.prepare(priority); 288 g_thread_ready_queue.prepare(priority);
221 289
222 t->status = THREADSTATUS_DORMANT; 290 thread->status = THREADSTATUS_DORMANT;
223 t->entry_point = entry_point; 291 thread->entry_point = entry_point;
224 t->stack_top = stack_top; 292 thread->stack_top = stack_top;
225 t->stack_size = stack_size; 293 thread->stack_size = stack_size;
226 t->initial_priority = t->current_priority = priority; 294 thread->initial_priority = thread->current_priority = priority;
227 t->processor_id = processor_id; 295 thread->processor_id = processor_id;
228 t->wait_type = WAITTYPE_NONE; 296 thread->wait_type = WAITTYPE_NONE;
229 297 thread->wait_handle = 0;
230 strncpy(t->name, name, Kernel::MAX_NAME_LENGTH); 298
231 t->name[Kernel::MAX_NAME_LENGTH] = '\0'; 299 strncpy(thread->name, name, Kernel::MAX_NAME_LENGTH);
232 300 thread->name[Kernel::MAX_NAME_LENGTH] = '\0';
233 return t; 301
302 return thread;
234} 303}
235 304
236/// Creates a new thread - wrapper for external user 305/// Creates a new thread - wrapper for external user
237Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, 306Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
238 u32 stack_top, int stack_size) { 307 u32 stack_top, int stack_size) {
239 if (name == NULL) { 308
240 ERROR_LOG(KERNEL, "CreateThread(): NULL name"); 309 if (name == nullptr) {
310 ERROR_LOG(KERNEL, "CreateThread(): nullptr name");
241 return -1; 311 return -1;
242 } 312 }
243 if ((u32)stack_size < 0x200) { 313 if ((u32)stack_size < 0x200) {
@@ -258,20 +328,56 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
258 return -1; 328 return -1;
259 } 329 }
260 Handle handle; 330 Handle handle;
261 Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, 331 Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
262 stack_size); 332 stack_size);
263 333
264 ResetThread(t, arg, 0); 334 ResetThread(thread, arg, 0);
335 CallThread(thread);
336
337 return handle;
338}
265 339
266 HLE::EatCycles(32000); 340/// Get the priority of the thread specified by handle
341u32 GetThreadPriority(const Handle handle) {
342 Thread* thread = g_object_pool.GetFast<Thread>(handle);
343 _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
344 return thread->current_priority;
345}
267 346
268 // This won't schedule to the new thread, but it may to one woken from eating cycles. 347/// Set the priority of the thread specified by handle
269 // Technically, this should not eat all at once, and reschedule in the middle, but that's hard. 348Result SetThreadPriority(Handle handle, s32 priority) {
270 HLE::ReSchedule("thread created"); 349 Thread* thread = nullptr;
350 if (!handle) {
351 thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
352 } else {
353 thread = g_object_pool.GetFast<Thread>(handle);
354 }
355 _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
271 356
272 CallThread(t); 357 // If priority is invalid, clamp to valid range
273 358 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
274 return handle; 359 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
360 WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority);
361 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
362 // validity of this
363 priority = new_priority;
364 }
365
366 // Change thread priority
367 s32 old = thread->current_priority;
368 g_thread_ready_queue.remove(old, handle);
369 thread->current_priority = priority;
370 g_thread_ready_queue.prepare(thread->current_priority);
371
372 // Change thread status to "ready" and push to ready queue
373 if (thread->IsRunning()) {
374 thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
375 }
376 if (thread->IsReady()) {
377 g_thread_ready_queue.push_back(thread->current_priority, handle);
378 }
379
380 return 0;
275} 381}
276 382
277/// Sets up the primary application thread 383/// Sets up the primary application thread
@@ -279,10 +385,10 @@ Handle SetupMainThread(s32 priority, int stack_size) {
279 Handle handle; 385 Handle handle;
280 386
281 // Initialize new "main" thread 387 // Initialize new "main" thread
282 Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, 388 Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
283 THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); 389 THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
284 390
285 ResetThread(t, 0, 0); 391 ResetThread(thread, 0, 0);
286 392
287 // If running another thread already, set it to "ready" state 393 // If running another thread already, set it to "ready" state
288 Thread* cur = GetCurrentThread(); 394 Thread* cur = GetCurrentThread();
@@ -291,24 +397,31 @@ Handle SetupMainThread(s32 priority, int stack_size) {
291 } 397 }
292 398
293 // Run new "main" thread 399 // Run new "main" thread
294 SetCurrentThread(t); 400 SetCurrentThread(thread);
295 t->status = THREADSTATUS_RUNNING; 401 thread->status = THREADSTATUS_RUNNING;
296 LoadContext(t->context); 402 LoadContext(thread->context);
297 403
298 return handle; 404 return handle;
299} 405}
300 406
407
301/// Reschedules to the next available thread (call after current thread is suspended) 408/// Reschedules to the next available thread (call after current thread is suspended)
302void Reschedule() { 409void Reschedule() {
303 Thread* prev = GetCurrentThread(); 410 Thread* prev = GetCurrentThread();
304 Thread* next = NextThread(); 411 Thread* next = NextThread();
412 HLE::g_reschedule = false;
305 if (next > 0) { 413 if (next > 0) {
414 INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
415
306 SwitchContext(next); 416 SwitchContext(next);
307 417
308 // Hack - automatically change previous thread (which would have been in "wait" state) to 418 // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep
309 // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to 419 // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again.
310 // actually wait for whatever event it is supposed to be waiting on. 420 // This results in the current thread yielding on a VBLANK once, and then it will be
311 ChangeReadyState(prev, true); 421 // immediately placed back in the queue for execution.
422 if (prev->wait_type == WAITTYPE_VBLANK) {
423 ResumeThreadFromWait(prev->GetHandle());
424 }
312 } 425 }
313} 426}
314 427