diff options
| author | 2014-06-05 22:35:36 -0400 | |
|---|---|---|
| committer | 2014-06-13 09:51:02 -0400 | |
| commit | f5c7c1543434e25a215286e6db5e71c055ba48cf (patch) | |
| tree | 488a3fd0c01051453c6f8ccc4867f6b6ea3f2843 /src/core | |
| parent | qt: updated disassembler to show 2X as many instructions (diff) | |
| download | yuzu-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.cpp | 71 | ||||
| -rw-r--r-- | src/core/hle/kernel/event.h | 7 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 121 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 9 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 59 |
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 | ||
| 13 | namespace Kernel { | 15 | namespace 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 | */ | ||
| 71 | Result 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 | */ |
| 80 | Result SetPermanentLock(Handle handle, const bool permanent_locked) { | 104 | Result 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 | */ |
| 95 | Result ClearEvent(Handle handle) { | 134 | Result 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); | |||
| 28 | Result SetPermanentLock(Handle handle, const bool permanent_locked); | 28 | Result 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 | */ | ||
| 35 | Result 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; | |||
| 82 | Handle g_current_thread_handle; | 93 | Handle g_current_thread_handle; |
| 83 | Thread* g_current_thread; | 94 | Thread* g_current_thread; |
| 84 | 95 | ||
| 85 | |||
| 86 | /// Gets the current thread | 96 | /// Gets the current thread |
| 87 | inline Thread* GetCurrentThread() { | 97 | inline 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 | ||
| 156 | inline 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 | ||
| 170 | void 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 |
| 146 | void ChangeThreadState(Thread* t, ThreadStatus new_status) { | 193 | void 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 |
| 210 | void WaitCurrentThread(WaitType wait_type) { | 257 | void 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 | ||
| 277 | void 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 |
| 229 | Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, | 293 | Thread* 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 |
| 257 | Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, | 322 | Handle 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) |
| 367 | void Reschedule() { | 434 | void 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) |
| 54 | void Reschedule(); | 54 | void Reschedule(); |
| 55 | 55 | ||
| 56 | /// Puts the current thread in the wait state for the given type | 56 | /// Stops the current thread |
| 57 | void WaitCurrentThread(WaitType wait_type); | 57 | void 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" |
| 60 | void ResumeThreadFromWait(Handle handle); | 60 | void ResumeThreadFromWait(Handle handle); |
| @@ -62,6 +62,9 @@ void ResumeThreadFromWait(Handle handle); | |||
| 62 | /// Gets the current thread handle | 62 | /// Gets the current thread handle |
| 63 | Handle GetCurrentThreadHandle(); | 63 | Handle GetCurrentThreadHandle(); |
| 64 | 64 | ||
| 65 | /// Puts the current thread in the wait state for the given type | ||
| 66 | void 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 |
| 66 | void WaitThread_Synchronization(); | 69 | void 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) { | |||
| 115 | Result WaitSynchronization1(Handle handle, s64 nano_seconds) { | 115 | Result 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 | ||
| 247 | u32 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 |
| 262 | Result GetThreadPriority(void* _priority, Handle handle) { | 258 | Result 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 | ||
| 326 | Result 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 |
| 330 | Result ClearEvent(Handle evt) { | 333 | Result 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"}, |