summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar bunnei2014-06-05 22:35:36 -0400
committerGravatar bunnei2014-06-13 09:51:02 -0400
commitf5c7c1543434e25a215286e6db5e71c055ba48cf (patch)
tree488a3fd0c01051453c6f8ccc4867f6b6ea3f2843 /src/core
parentqt: updated disassembler to show 2X as many instructions (diff)
downloadyuzu-f5c7c1543434e25a215286e6db5e71c055ba48cf.tar.gz
yuzu-f5c7c1543434e25a215286e6db5e71c055ba48cf.tar.xz
yuzu-f5c7c1543434e25a215286e6db5e71c055ba48cf.zip
Kernel: Added real support for thread and event blocking
- SVC: Added ExitThread support - SVC: Added SignalEvent support - Thread: Added WAITTYPE_EVENT for waiting threads for event signals - Thread: Added support for blocking on other threads to finish (e.g. Thread::Join) - Thread: Added debug function for printing current threads ready for execution - Thread: Removed hack/broken thread ready state code from Kernel::Reschedule - Mutex: Moved WaitCurrentThread from SVC to Mutex::WaitSynchronization - Event: Added support for blocking threads on event signalling Kernel: Added missing algorithm #include for use of std::find on non-Windows platforms.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/hle/kernel/event.cpp71
-rw-r--r--src/core/hle/kernel/event.h7
-rw-r--r--src/core/hle/kernel/mutex.cpp5
-rw-r--r--src/core/hle/kernel/thread.cpp121
-rw-r--r--src/core/hle/kernel/thread.h9
-rw-r--r--src/core/hle/svc.cpp59
6 files changed, 196 insertions, 76 deletions
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 70e50115d..787e9f5fd 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -3,12 +3,14 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map> 5#include <map>
6#include <algorithm>
6#include <vector> 7#include <vector>
7 8
8#include "common/common.h" 9#include "common/common.h"
9 10
10#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/event.h" 12#include "core/hle/kernel/event.h"
13#include "core/hle/kernel/thread.h"
12 14
13namespace Kernel { 15namespace Kernel {
14 16
@@ -20,12 +22,13 @@ public:
20 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; } 22 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; }
21 Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Event; } 23 Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Event; }
22 24
23 ResetType intitial_reset_type; ///< ResetType specified at Event initialization 25 ResetType intitial_reset_type; ///< ResetType specified at Event initialization
24 ResetType reset_type; ///< Current ResetType 26 ResetType reset_type; ///< Current ResetType
25 27
26 bool locked; ///< Current locked state 28 bool locked; ///< Event signal wait
27 bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough) 29 bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough)
28 std::string name; ///< Name of event (optional) 30 std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
31 std::string name; ///< Name of event (optional)
29 32
30 /** 33 /**
31 * Synchronize kernel object 34 * Synchronize kernel object
@@ -44,8 +47,14 @@ public:
44 * @return Result of operation, 0 on success, otherwise error code 47 * @return Result of operation, 0 on success, otherwise error code
45 */ 48 */
46 Result WaitSynchronization(bool* wait) { 49 Result WaitSynchronization(bool* wait) {
47 // TODO(bunnei): ImplementMe
48 *wait = locked; 50 *wait = locked;
51 if (locked) {
52 Handle thread = GetCurrentThreadHandle();
53 if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
54 waiting_threads.push_back(thread);
55 }
56 Kernel::WaitCurrentThread(WAITTYPE_EVENT);
57 }
49 if (reset_type != RESETTYPE_STICKY && !permanent_locked) { 58 if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
50 locked = true; 59 locked = true;
51 } 60 }
@@ -54,6 +63,22 @@ public:
54}; 63};
55 64
56/** 65/**
66 * Hackish function to set an events permanent lock state, used to pass through synch blocks
67 * @param handle Handle to event to change
68 * @param permanent_locked Boolean permanent locked value to set event
69 * @return Result of operation, 0 on success, otherwise error code
70 */
71Result SetPermanentLock(Handle handle, const bool permanent_locked) {
72 Event* evt = g_object_pool.GetFast<Event>(handle);
73 if (!evt) {
74 ERROR_LOG(KERNEL, "called with unknown handle=0x%08X", handle);
75 return -1;
76 }
77 evt->permanent_locked = permanent_locked;
78 return 0;
79}
80
81/**
57 * Changes whether an event is locked or not 82 * Changes whether an event is locked or not
58 * @param handle Handle to event to change 83 * @param handle Handle to event to change
59 * @param locked Boolean locked value to set event 84 * @param locked Boolean locked value to set event
@@ -72,18 +97,32 @@ Result SetEventLocked(const Handle handle, const bool locked) {
72} 97}
73 98
74/** 99/**
75 * Hackish function to set an events permanent lock state, used to pass through synch blocks 100 * Signals an event
76 * @param handle Handle to event to change 101 * @param handle Handle to event to signal
77 * @param permanent_locked Boolean permanent locked value to set event
78 * @return Result of operation, 0 on success, otherwise error code 102 * @return Result of operation, 0 on success, otherwise error code
79 */ 103 */
80Result SetPermanentLock(Handle handle, const bool permanent_locked) { 104Result SignalEvent(const Handle handle) {
81 Event* evt = g_object_pool.GetFast<Event>(handle); 105 Event* evt = g_object_pool.GetFast<Event>(handle);
82 if (!evt) { 106 if (!evt) {
83 ERROR_LOG(KERNEL, "called with unknown handle=0x%08X", handle); 107 ERROR_LOG(KERNEL, "called with unknown handle=0x%08X", handle);
84 return -1; 108 return -1;
85 } 109 }
86 evt->permanent_locked = permanent_locked; 110 // Resume threads waiting for event to signal
111 bool event_caught = false;
112 for (size_t i = 0; i < evt->waiting_threads.size(); ++i) {
113 ResumeThreadFromWait( evt->waiting_threads[i]);
114
115 // If any thread is signalled awake by this event, assume the event was "caught" and reset
116 // the event. This will result in the next thread waiting on the event to block. Otherwise,
117 // the event will not be reset, and the next thread to call WaitSynchronization on it will
118 // not block. Not sure if this is correct behavior, but it seems to work.
119 event_caught = true;
120 }
121 evt->waiting_threads.clear();
122
123 if (!evt->permanent_locked) {
124 evt->locked = event_caught;
125 }
87 return 0; 126 return 0;
88} 127}
89 128
@@ -93,7 +132,15 @@ Result SetPermanentLock(Handle handle, const bool permanent_locked) {
93 * @return Result of operation, 0 on success, otherwise error code 132 * @return Result of operation, 0 on success, otherwise error code
94 */ 133 */
95Result ClearEvent(Handle handle) { 134Result ClearEvent(Handle handle) {
96 return SetEventLocked(handle, true); 135 Event* evt = g_object_pool.GetFast<Event>(handle);
136 if (!evt) {
137 ERROR_LOG(KERNEL, "called with unknown handle=0x%08X", handle);
138 return -1;
139 }
140 if (!evt->permanent_locked) {
141 evt->locked = true;
142 }
143 return 0;
97} 144}
98 145
99/** 146/**
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index eed09f0e3..3527b01fd 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -28,6 +28,13 @@ Result SetEventLocked(const Handle handle, const bool locked);
28Result SetPermanentLock(Handle handle, const bool permanent_locked); 28Result SetPermanentLock(Handle handle, const bool permanent_locked);
29 29
30/** 30/**
31 * Signals an event
32 * @param handle Handle to event to signal
33 * @return Result of operation, 0 on success, otherwise error code
34 */
35Result SignalEvent(const Handle handle);
36
37/**
31 * Clears an event 38 * Clears an event
32 * @param handle Handle to event to clear 39 * @param handle Handle to event to clear
33 * @return Result of operation, 0 on success, otherwise error code 40 * @return Result of operation, 0 on success, otherwise error code
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 7e60fbfe0..133c43079 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -46,6 +46,11 @@ public:
46 Result WaitSynchronization(bool* wait) { 46 Result WaitSynchronization(bool* wait) {
47 // TODO(bunnei): ImplementMe 47 // TODO(bunnei): ImplementMe
48 *wait = locked; 48 *wait = locked;
49
50 if (locked) {
51 Kernel::WaitCurrentThread(WAITTYPE_MUTEX);
52 }
53
49 return 0; 54 return 0;
50 } 55 }
51}; 56};
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index c84fdf91d..d372df709 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>
@@ -52,7 +53,14 @@ public:
52 * @return Result of operation, 0 on success, otherwise error code 53 * @return Result of operation, 0 on success, otherwise error code
53 */ 54 */
54 Result WaitSynchronization(bool* wait) { 55 Result WaitSynchronization(bool* wait) {
55 // TODO(bunnei): ImplementMe 56 if (status != THREADSTATUS_DORMANT) {
57 Handle thread = GetCurrentThreadHandle();
58 if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
59 waiting_threads.push_back(thread);
60 }
61 WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
62 *wait = true;
63 }
56 return 0; 64 return 0;
57 } 65 }
58 66
@@ -69,6 +77,9 @@ public:
69 s32 processor_id; 77 s32 processor_id;
70 78
71 WaitType wait_type; 79 WaitType wait_type;
80 Handle wait_handle;
81
82 std::vector<Handle> waiting_threads;
72 83
73 char name[Kernel::MAX_NAME_LENGTH + 1]; 84 char name[Kernel::MAX_NAME_LENGTH + 1];
74}; 85};
@@ -82,7 +93,6 @@ Common::ThreadQueueList<Handle> g_thread_ready_queue;
82Handle g_current_thread_handle; 93Handle g_current_thread_handle;
83Thread* g_current_thread; 94Thread* g_current_thread;
84 95
85
86/// Gets the current thread 96/// Gets the current thread
87inline Thread* GetCurrentThread() { 97inline Thread* GetCurrentThread() {
88 return g_current_thread; 98 return g_current_thread;
@@ -114,15 +124,15 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
114 memset(&t->context, 0, sizeof(ThreadContext)); 124 memset(&t->context, 0, sizeof(ThreadContext));
115 125
116 t->context.cpu_registers[0] = arg; 126 t->context.cpu_registers[0] = arg;
117 t->context.pc = t->entry_point; 127 t->context.pc = t->context.cpu_registers[15] = t->entry_point;
118 t->context.sp = t->stack_top; 128 t->context.sp = t->stack_top;
119 t->context.cpsr = 0x1F; // Usermode 129 t->context.cpsr = 0x1F; // Usermode
120 130
121 if (t->current_priority < lowest_priority) { 131 if (t->current_priority < lowest_priority) {
122 t->current_priority = t->initial_priority; 132 t->current_priority = t->initial_priority;
123 } 133 }
124
125 t->wait_type = WAITTYPE_NONE; 134 t->wait_type = WAITTYPE_NONE;
135 t->wait_handle = 0;
126} 136}
127 137
128/// Change a thread to "ready" state 138/// Change a thread to "ready" state
@@ -142,6 +152,43 @@ void ChangeReadyState(Thread* t, bool ready) {
142 } 152 }
143} 153}
144 154
155/// Verify that a thread has not been released from waiting
156inline bool VerifyWait(const Handle& thread, WaitType type, Handle handle) {
157 Handle wait_id = 0;
158 Thread *t = g_object_pool.GetFast<Thread>(thread);
159 if (t) {
160 if (type == t->wait_type && handle == t->wait_handle) {
161 return true;
162 }
163 } else {
164 ERROR_LOG(KERNEL, "thread 0x%08X does not exist", thread);
165 }
166 return false;
167}
168
169/// Stops the current thread
170void StopThread(Handle thread, const char* reason) {
171 u32 error;
172 Thread *t = g_object_pool.Get<Thread>(thread, error);
173 if (t) {
174 ChangeReadyState(t, false);
175 t->status = THREADSTATUS_DORMANT;
176 for (size_t i = 0; i < t->waiting_threads.size(); ++i) {
177 const Handle waiting_thread = t->waiting_threads[i];
178 if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, thread)) {
179 ResumeThreadFromWait(waiting_thread);
180 }
181 }
182 t->waiting_threads.clear();
183
184 // Stopped threads are never waiting.
185 t->wait_type = WAITTYPE_NONE;
186 t->wait_handle = 0;
187 } else {
188 ERROR_LOG(KERNEL, "thread 0x%08X does not exist", thread);
189 }
190}
191
145/// Changes a threads state 192/// Changes a threads state
146void ChangeThreadState(Thread* t, ThreadStatus new_status) { 193void ChangeThreadState(Thread* t, ThreadStatus new_status) {
147 if (!t || t->status == new_status) { 194 if (!t || t->status == new_status) {
@@ -152,7 +199,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
152 199
153 if (new_status == THREADSTATUS_WAIT) { 200 if (new_status == THREADSTATUS_WAIT) {
154 if (t->wait_type == WAITTYPE_NONE) { 201 if (t->wait_type == WAITTYPE_NONE) {
155 printf("ERROR: Waittype none not allowed here\n"); 202 ERROR_LOG(KERNEL, "Waittype none not allowed");
156 } 203 }
157 } 204 }
158} 205}
@@ -207,9 +254,10 @@ Thread* NextThread() {
207} 254}
208 255
209/// Puts the current thread in the wait state for the given type 256/// Puts the current thread in the wait state for the given type
210void WaitCurrentThread(WaitType wait_type) { 257void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
211 Thread* t = GetCurrentThread(); 258 Thread* t = GetCurrentThread();
212 t->wait_type = wait_type; 259 t->wait_type = wait_type;
260 t->wait_handle = wait_handle;
213 ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); 261 ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND)));
214} 262}
215 263
@@ -225,6 +273,22 @@ void ResumeThreadFromWait(Handle handle) {
225 } 273 }
226} 274}
227 275
276/// Prints the thread queue for debugging purposes
277void DebugThreadQueue() {
278 Thread* thread = GetCurrentThread();
279 if (!thread) {
280 return;
281 }
282 INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
283 for (u32 i = 0; i < g_thread_queue.size(); i++) {
284 Handle handle = g_thread_queue[i];
285 s32 priority = g_thread_ready_queue.contains(handle);
286 if (priority != -1) {
287 INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle);
288 }
289 }
290}
291
228/// Creates a new thread 292/// Creates a new thread
229Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, 293Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
230 s32 processor_id, u32 stack_top, int stack_size) { 294 s32 processor_id, u32 stack_top, int stack_size) {
@@ -233,12 +297,12 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
233 "CreateThread priority=%d, outside of allowable range!", priority) 297 "CreateThread priority=%d, outside of allowable range!", priority)
234 298
235 Thread* t = new Thread; 299 Thread* t = new Thread;
236 300
237 handle = Kernel::g_object_pool.Create(t); 301 handle = Kernel::g_object_pool.Create(t);
238 302
239 g_thread_queue.push_back(handle); 303 g_thread_queue.push_back(handle);
240 g_thread_ready_queue.prepare(priority); 304 g_thread_ready_queue.prepare(priority);
241 305
242 t->status = THREADSTATUS_DORMANT; 306 t->status = THREADSTATUS_DORMANT;
243 t->entry_point = entry_point; 307 t->entry_point = entry_point;
244 t->stack_top = stack_top; 308 t->stack_top = stack_top;
@@ -246,16 +310,18 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
246 t->initial_priority = t->current_priority = priority; 310 t->initial_priority = t->current_priority = priority;
247 t->processor_id = processor_id; 311 t->processor_id = processor_id;
248 t->wait_type = WAITTYPE_NONE; 312 t->wait_type = WAITTYPE_NONE;
249 313 t->wait_handle = 0;
314
250 strncpy(t->name, name, Kernel::MAX_NAME_LENGTH); 315 strncpy(t->name, name, Kernel::MAX_NAME_LENGTH);
251 t->name[Kernel::MAX_NAME_LENGTH] = '\0'; 316 t->name[Kernel::MAX_NAME_LENGTH] = '\0';
252 317
253 return t; 318 return t;
254} 319}
255 320
256/// Creates a new thread - wrapper for external user 321/// Creates a new thread - wrapper for external user
257Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, 322Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
258 u32 stack_top, int stack_size) { 323 u32 stack_top, int stack_size) {
324
259 if (name == NULL) { 325 if (name == NULL) {
260 ERROR_LOG(KERNEL, "CreateThread(): NULL name"); 326 ERROR_LOG(KERNEL, "CreateThread(): NULL name");
261 return -1; 327 return -1;
@@ -289,7 +355,7 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
289 355
290 // This won't schedule to the new thread, but it may to one woken from eating cycles. 356 // This won't schedule to the new thread, but it may to one woken from eating cycles.
291 // Technically, this should not eat all at once, and reschedule in the middle, but that's hard. 357 // Technically, this should not eat all at once, and reschedule in the middle, but that's hard.
292 //HLE::Reschedule("thread created"); 358 //HLE::Reschedule(__func__);
293 359
294 return handle; 360 return handle;
295} 361}
@@ -363,35 +429,24 @@ Handle SetupMainThread(s32 priority, int stack_size) {
363 return handle; 429 return handle;
364} 430}
365 431
432
366/// Reschedules to the next available thread (call after current thread is suspended) 433/// Reschedules to the next available thread (call after current thread is suspended)
367void Reschedule() { 434void Reschedule() {
368 Thread* prev = GetCurrentThread(); 435 Thread* prev = GetCurrentThread();
369 Thread* next = NextThread(); 436 Thread* next = NextThread();
437 HLE::g_reschedule = false;
370 if (next > 0) { 438 if (next > 0) {
371 INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); 439 INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
372 440
373 SwitchContext(next); 441 SwitchContext(next);
374 442
375 // Hack - automatically change previous thread (which would have been in "wait" state) to 443 // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep
376 // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to 444 // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again.
377 // actually wait for whatever event it is supposed to be waiting on. 445 // This results in the current thread yielding on a VBLANK once, and then it will be
378 446 // immediately placed back in the queue for execution.
379 ChangeReadyState(prev, true); 447 if (prev->wait_type == WAITTYPE_VBLANK) {
380 } else { 448 ResumeThreadFromWait(prev->GetHandle());
381 INFO_LOG(KERNEL, "no ready threads, staying on 0x%08X", prev->GetHandle()); 449 }
382
383 // Hack - no other threads are available, so decrement current PC to the last instruction,
384 // and then resume current thread. This should always be called on a blocking instruction
385 // (e.g. svcWaitSynchronization), and the result should be that the instruction is repeated
386 // until it no longer blocks.
387
388 // TODO(bunnei): A better solution: Have the CPU switch to an idle thread
389
390 ThreadContext ctx;
391 SaveContext(ctx);
392 ctx.pc -= 4;
393 LoadContext(ctx);
394 ChangeReadyState(prev, true);
395 } 450 }
396} 451}
397 452
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 094c8d43e..04914ba90 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -34,7 +34,7 @@ enum WaitType {
34 WAITTYPE_NONE, 34 WAITTYPE_NONE,
35 WAITTYPE_SLEEP, 35 WAITTYPE_SLEEP,
36 WAITTYPE_SEMA, 36 WAITTYPE_SEMA,
37 WAITTYPE_EVENTFLAG, 37 WAITTYPE_EVENT,
38 WAITTYPE_THREADEND, 38 WAITTYPE_THREADEND,
39 WAITTYPE_VBLANK, 39 WAITTYPE_VBLANK,
40 WAITTYPE_MUTEX, 40 WAITTYPE_MUTEX,
@@ -53,8 +53,8 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
53/// Reschedules to the next available thread (call after current thread is suspended) 53/// Reschedules to the next available thread (call after current thread is suspended)
54void Reschedule(); 54void Reschedule();
55 55
56/// Puts the current thread in the wait state for the given type 56/// Stops the current thread
57void WaitCurrentThread(WaitType wait_type); 57void StopThread(Handle thread, const char* reason);
58 58
59/// Resumes a thread from waiting by marking it as "ready" 59/// Resumes a thread from waiting by marking it as "ready"
60void ResumeThreadFromWait(Handle handle); 60void ResumeThreadFromWait(Handle handle);
@@ -62,6 +62,9 @@ void ResumeThreadFromWait(Handle handle);
62/// Gets the current thread handle 62/// Gets the current thread handle
63Handle GetCurrentThreadHandle(); 63Handle GetCurrentThreadHandle();
64 64
65/// Puts the current thread in the wait state for the given type
66void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
67
65/// Put current thread in a wait state - on WaitSynchronization 68/// Put current thread in a wait state - on WaitSynchronization
66void WaitThread_Synchronization(); 69void WaitThread_Synchronization();
67 70
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index c8eb8ea80..0ce831103 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -93,8 +93,8 @@ Result SendSyncRequest(Handle handle) {
93 bool wait = false; 93 bool wait = false;
94 Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle); 94 Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
95 95
96 DEBUG_LOG(SVC, "called handle=0x%08X", handle);
97 _assert_msg_(KERNEL, object, "called, but kernel object is NULL!"); 96 _assert_msg_(KERNEL, object, "called, but kernel object is NULL!");
97 DEBUG_LOG(SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName());
98 98
99 Result res = object->SyncRequest(&wait); 99 Result res = object->SyncRequest(&wait);
100 if (wait) { 100 if (wait) {
@@ -115,29 +115,21 @@ Result CloseHandle(Handle handle) {
115Result WaitSynchronization1(Handle handle, s64 nano_seconds) { 115Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
116 // TODO(bunnei): Do something with nano_seconds, currently ignoring this 116 // TODO(bunnei): Do something with nano_seconds, currently ignoring this
117 bool wait = false; 117 bool wait = false;
118 bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
118 119
119 Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle); 120 Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
120 121
121 DEBUG_LOG(SVC, "called handle=0x%08X, nanoseconds=%d", handle, 122 DEBUG_LOG(SVC, "called handle=0x%08X(%s:%s), nanoseconds=%d", handle, object->GetTypeName(),
122 nano_seconds); 123 object->GetName(), nano_seconds);
124
123 _assert_msg_(KERNEL, object, "called, but kernel object is NULL!"); 125 _assert_msg_(KERNEL, object, "called, but kernel object is NULL!");
124 126
125 Result res = object->WaitSynchronization(&wait); 127 Result res = object->WaitSynchronization(&wait);
126 128
129 // Check for next thread to schedule
127 if (wait) { 130 if (wait) {
128 // Set current thread to wait state if handle was not unlocked
129 Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
130
131 // Check for next thread to schedule
132 HLE::Reschedule(__func__); 131 HLE::Reschedule(__func__);
133 132 return 0;
134 // Context switch - Function blocked, is not actually returning (will be "called" again)
135
136 // TODO(bunnei): This saves handle to R0 so that it's correctly reloaded on context switch
137 // (otherwise R0 will be set to whatever is returned, and handle will be invalid when this
138 // thread is resumed). There is probably a better way of keeping track of state so that we
139 // don't necessarily have to do this.
140 return (Result)PARAM(0);
141 } 133 }
142 134
143 return res; 135 return res;
@@ -150,6 +142,7 @@ Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wa
150 s32* out = (s32*)_out; 142 s32* out = (s32*)_out;
151 Handle* handles = (Handle*)_handles; 143 Handle* handles = (Handle*)_handles;
152 bool unlock_all = true; 144 bool unlock_all = true;
145 bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
153 146
154 DEBUG_LOG(SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%d", 147 DEBUG_LOG(SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%d",
155 handle_count, (wait_all ? "true" : "false"), nano_seconds); 148 handle_count, (wait_all ? "true" : "false"), nano_seconds);
@@ -162,7 +155,8 @@ Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wa
162 _assert_msg_(KERNEL, object, "called handle=0x%08X, but kernel object " 155 _assert_msg_(KERNEL, object, "called handle=0x%08X, but kernel object "
163 "is NULL!", handles[i]); 156 "is NULL!", handles[i]);
164 157
165 DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X", i, handles[i]); 158 DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName(),
159 object->GetName());
166 160
167 Result res = object->WaitSynchronization(&wait); 161 Result res = object->WaitSynchronization(&wait);
168 162
@@ -179,19 +173,10 @@ Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wa
179 return 0; 173 return 0;
180 } 174 }
181 175
182 // Set current thread to wait state if not all handles were unlocked
183 Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
184
185 // Check for next thread to schedule 176 // Check for next thread to schedule
186 HLE::Reschedule(__func__); 177 HLE::Reschedule(__func__);
187 178
188 // Context switch - Function blocked, is not actually returning (will be "called" again) 179 return 0;
189
190 // TODO(bunnei): This saves handle to R0 so that it's correctly reloaded on context switch
191 // (otherwise R0 will be set to whatever is returned, and handle will be invalid when this
192 // thread is resumed). There is probably a better way of keeping track of state so that we
193 // don't necessarily have to do this.
194 return (Result)PARAM(0);
195} 180}
196 181
197/// Create an address arbiter (to allocate access to shared resources) 182/// Create an address arbiter (to allocate access to shared resources)
@@ -258,6 +243,17 @@ Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 p
258 return 0; 243 return 0;
259} 244}
260 245
246/// Called when a thread exits
247u32 ExitThread() {
248 Handle thread = Kernel::GetCurrentThreadHandle();
249
250 DEBUG_LOG(SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
251
252 Kernel::StopThread(thread, __func__);
253 HLE::Reschedule(__func__);
254 return 0;
255}
256
261/// Gets the priority for the specified thread 257/// Gets the priority for the specified thread
262Result GetThreadPriority(void* _priority, Handle handle) { 258Result GetThreadPriority(void* _priority, Handle handle) {
263 s32* priority = (s32*)_priority; 259 s32* priority = (s32*)_priority;
@@ -326,6 +322,13 @@ Result DuplicateHandle(void* _out, Handle handle) {
326 return 0; 322 return 0;
327} 323}
328 324
325/// Signals an event
326Result SignalEvent(Handle evt) {
327 Result res = Kernel::SignalEvent(evt);
328 DEBUG_LOG(SVC, "called event=0x%08X", evt);
329 return res;
330}
331
329/// Clears an event 332/// Clears an event
330Result ClearEvent(Handle evt) { 333Result ClearEvent(Handle evt) {
331 Result res = Kernel::ClearEvent(evt); 334 Result res = Kernel::ClearEvent(evt);
@@ -348,7 +351,7 @@ const HLE::FunctionDef SVC_Table[] = {
348 {0x06, NULL, "GetProcessIdealProcessor"}, 351 {0x06, NULL, "GetProcessIdealProcessor"},
349 {0x07, NULL, "SetProcessIdealProcessor"}, 352 {0x07, NULL, "SetProcessIdealProcessor"},
350 {0x08, WrapI_UUUUU<CreateThread>, "CreateThread"}, 353 {0x08, WrapI_UUUUU<CreateThread>, "CreateThread"},
351 {0x09, NULL, "ExitThread"}, 354 {0x09, WrapU_V<ExitThread>, "ExitThread"},
352 {0x0A, WrapV_S64<SleepThread>, "SleepThread"}, 355 {0x0A, WrapV_S64<SleepThread>, "SleepThread"},
353 {0x0B, WrapI_VU<GetThreadPriority>, "GetThreadPriority"}, 356 {0x0B, WrapI_VU<GetThreadPriority>, "GetThreadPriority"},
354 {0x0C, WrapI_UI<SetThreadPriority>, "SetThreadPriority"}, 357 {0x0C, WrapI_UI<SetThreadPriority>, "SetThreadPriority"},
@@ -363,7 +366,7 @@ const HLE::FunctionDef SVC_Table[] = {
363 {0x15, NULL, "CreateSemaphore"}, 366 {0x15, NULL, "CreateSemaphore"},
364 {0x16, NULL, "ReleaseSemaphore"}, 367 {0x16, NULL, "ReleaseSemaphore"},
365 {0x17, WrapI_VU<CreateEvent>, "CreateEvent"}, 368 {0x17, WrapI_VU<CreateEvent>, "CreateEvent"},
366 {0x18, NULL, "SignalEvent"}, 369 {0x18, WrapI_U<SignalEvent>, "SignalEvent"},
367 {0x19, WrapI_U<ClearEvent>, "ClearEvent"}, 370 {0x19, WrapI_U<ClearEvent>, "ClearEvent"},
368 {0x1A, NULL, "CreateTimer"}, 371 {0x1A, NULL, "CreateTimer"},
369 {0x1B, NULL, "SetTimer"}, 372 {0x1B, NULL, "SetTimer"},