diff options
| author | 2014-05-13 22:00:11 -0400 | |
|---|---|---|
| committer | 2014-05-13 22:00:11 -0400 | |
| commit | 3838d46b9022964617b93a45f3feab5052c3538b (patch) | |
| tree | e9bb520a6e8e86543f0827524c45e0ce2e0edb4a /src/core/hle/kernel/thread.cpp | |
| parent | changed loader to use __KernelLoadExec (diff) | |
| download | yuzu-3838d46b9022964617b93a45f3feab5052c3538b.tar.gz yuzu-3838d46b9022964617b93a45f3feab5052c3538b.tar.xz yuzu-3838d46b9022964617b93a45f3feab5052c3538b.zip | |
added a bunch of threading code, recycled from PPSSPP, with lots of hacks in for 3DS... doesn't really do much yet. Just a jumping off point
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 588 |
1 files changed, 524 insertions, 64 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 0ed35de83..584276eec 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -11,10 +11,212 @@ | |||
| 11 | 11 | ||
| 12 | #include "common/common.h" | 12 | #include "common/common.h" |
| 13 | 13 | ||
| 14 | #include "core/core.h" | ||
| 15 | #include "core/mem_map.h" | ||
| 14 | #include "core/hle/kernel/kernel.h" | 16 | #include "core/hle/kernel/kernel.h" |
| 15 | #include "core/hle/kernel/thread.h" | 17 | #include "core/hle/kernel/thread.h" |
| 16 | 18 | ||
| 17 | // Real CTR struct, don't change the fields. | 19 | struct ThreadQueueList { |
| 20 | // Number of queues (number of priority levels starting at 0.) | ||
| 21 | static const int NUM_QUEUES = 128; | ||
| 22 | // Initial number of threads a single queue can handle. | ||
| 23 | static const int INITIAL_CAPACITY = 32; | ||
| 24 | |||
| 25 | struct Queue { | ||
| 26 | // Next ever-been-used queue (worse priority.) | ||
| 27 | Queue *next; | ||
| 28 | // First valid item in data. | ||
| 29 | int first; | ||
| 30 | // One after last valid item in data. | ||
| 31 | int end; | ||
| 32 | // A too-large array with room on the front and end. | ||
| 33 | UID *data; | ||
| 34 | // Size of data array. | ||
| 35 | int capacity; | ||
| 36 | }; | ||
| 37 | |||
| 38 | ThreadQueueList() { | ||
| 39 | memset(queues, 0, sizeof(queues)); | ||
| 40 | first = invalid(); | ||
| 41 | } | ||
| 42 | |||
| 43 | ~ThreadQueueList() { | ||
| 44 | for (int i = 0; i < NUM_QUEUES; ++i) { | ||
| 45 | if (queues[i].data != NULL) { | ||
| 46 | free(queues[i].data); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | // Only for debugging, returns priority level. | ||
| 52 | int contains(const UID uid) { | ||
| 53 | for (int i = 0; i < NUM_QUEUES; ++i) { | ||
| 54 | if (queues[i].data == NULL) { | ||
| 55 | continue; | ||
| 56 | } | ||
| 57 | Queue *cur = &queues[i]; | ||
| 58 | for (int j = cur->first; j < cur->end; ++j) { | ||
| 59 | if (cur->data[j] == uid) { | ||
| 60 | return i; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | return -1; | ||
| 65 | } | ||
| 66 | |||
| 67 | inline UID pop_first() { | ||
| 68 | Queue *cur = first; | ||
| 69 | while (cur != invalid()) { | ||
| 70 | if (cur->end - cur->first > 0) { | ||
| 71 | return cur->data[cur->first++]; | ||
| 72 | } | ||
| 73 | cur = cur->next; | ||
| 74 | } | ||
| 75 | |||
| 76 | _dbg_assert_msg_(KERNEL, false, "ThreadQueueList should not be empty."); | ||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | |||
| 80 | inline UID pop_first_better(u32 priority) { | ||
| 81 | Queue *cur = first; | ||
| 82 | Queue *stop = &queues[priority]; | ||
| 83 | while (cur < stop) { | ||
| 84 | if (cur->end - cur->first > 0) { | ||
| 85 | return cur->data[cur->first++]; | ||
| 86 | } | ||
| 87 | cur = cur->next; | ||
| 88 | } | ||
| 89 | return 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | inline void push_front(u32 priority, const UID thread_id) { | ||
| 93 | Queue *cur = &queues[priority]; | ||
| 94 | cur->data[--cur->first] = thread_id; | ||
| 95 | if (cur->first == 0) { | ||
| 96 | rebalance(priority); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | inline void push_back(u32 priority, const UID thread_id) | ||
| 101 | { | ||
| 102 | Queue *cur = &queues[priority]; | ||
| 103 | cur->data[cur->end++] = thread_id; | ||
| 104 | if (cur->end == cur->capacity) { | ||
| 105 | rebalance(priority); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | inline void remove(u32 priority, const UID thread_id) { | ||
| 110 | Queue *cur = &queues[priority]; | ||
| 111 | _dbg_assert_msg_(KERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); | ||
| 112 | |||
| 113 | for (int i = cur->first; i < cur->end; ++i) { | ||
| 114 | if (cur->data[i] == thread_id) { | ||
| 115 | int remaining = --cur->end - i; | ||
| 116 | if (remaining > 0) { | ||
| 117 | memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(UID)); | ||
| 118 | } | ||
| 119 | return; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | // Wasn't there. | ||
| 124 | } | ||
| 125 | |||
| 126 | inline void rotate(u32 priority) { | ||
| 127 | Queue *cur = &queues[priority]; | ||
| 128 | _dbg_assert_msg_(KERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); | ||
| 129 | |||
| 130 | if (cur->end - cur->first > 1) { | ||
| 131 | cur->data[cur->end++] = cur->data[cur->first++]; | ||
| 132 | if (cur->end == cur->capacity) { | ||
| 133 | rebalance(priority); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | inline void clear() { | ||
| 139 | for (int i = 0; i < NUM_QUEUES; ++i) { | ||
| 140 | if (queues[i].data != NULL) { | ||
| 141 | free(queues[i].data); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | memset(queues, 0, sizeof(queues)); | ||
| 145 | first = invalid(); | ||
| 146 | } | ||
| 147 | |||
| 148 | inline bool empty(u32 priority) const { | ||
| 149 | const Queue *cur = &queues[priority]; | ||
| 150 | return cur->first == cur->end; | ||
| 151 | } | ||
| 152 | |||
| 153 | inline void prepare(u32 priority) { | ||
| 154 | Queue *cur = &queues[priority]; | ||
| 155 | if (cur->next == NULL) { | ||
| 156 | link(priority, INITIAL_CAPACITY); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | private: | ||
| 161 | Queue *invalid() const { | ||
| 162 | return (Queue *)-1; | ||
| 163 | } | ||
| 164 | |||
| 165 | void link(u32 priority, int size) { | ||
| 166 | _dbg_assert_msg_(KERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once."); | ||
| 167 | |||
| 168 | if (size <= INITIAL_CAPACITY) { | ||
| 169 | size = INITIAL_CAPACITY; | ||
| 170 | } else { | ||
| 171 | int goal = size; | ||
| 172 | size = INITIAL_CAPACITY; | ||
| 173 | while (size < goal) | ||
| 174 | size *= 2; | ||
| 175 | } | ||
| 176 | Queue *cur = &queues[priority]; | ||
| 177 | cur->data = (UID*)malloc(sizeof(UID)* size); | ||
| 178 | cur->capacity = size; | ||
| 179 | cur->first = size / 2; | ||
| 180 | cur->end = size / 2; | ||
| 181 | |||
| 182 | for (int i = (int)priority - 1; i >= 0; --i) { | ||
| 183 | if (queues[i].next != NULL) { | ||
| 184 | cur->next = queues[i].next; | ||
| 185 | queues[i].next = cur; | ||
| 186 | return; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | cur->next = first; | ||
| 191 | first = cur; | ||
| 192 | } | ||
| 193 | |||
| 194 | void rebalance(u32 priority) { | ||
| 195 | Queue *cur = &queues[priority]; | ||
| 196 | int size = cur->end - cur->first; | ||
| 197 | if (size >= cur->capacity - 2) { | ||
| 198 | UID* new_data = (UID*)realloc(cur->data, cur->capacity * 2 * sizeof(UID)); | ||
| 199 | if (new_data != NULL) { | ||
| 200 | cur->capacity *= 2; | ||
| 201 | cur->data = new_data; | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | int newFirst = (cur->capacity - size) / 2; | ||
| 206 | if (newFirst != cur->first) { | ||
| 207 | memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(UID)); | ||
| 208 | cur->first = newFirst; | ||
| 209 | cur->end = newFirst + size; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | // The first queue that's ever been used. | ||
| 214 | Queue* first; | ||
| 215 | // The priority level queues of thread ids. | ||
| 216 | Queue queues[NUM_QUEUES]; | ||
| 217 | }; | ||
| 218 | |||
| 219 | // Supposed to represent a real CTR struct... but not sure of the correct fields yet. | ||
| 18 | struct NativeThread { | 220 | struct NativeThread { |
| 19 | //u32 Pointer to vtable | 221 | //u32 Pointer to vtable |
| 20 | //u32 Reference count | 222 | //u32 Reference count |
| @@ -25,6 +227,22 @@ struct NativeThread { | |||
| 25 | // if the beginning of this mapped page is 0xFF401000, this ptr would be 0xFF402000. | 227 | // if the beginning of this mapped page is 0xFF401000, this ptr would be 0xFF402000. |
| 26 | //KThread* Previous ? (virtual address) | 228 | //KThread* Previous ? (virtual address) |
| 27 | //KThread* Next ? (virtual address) | 229 | //KThread* Next ? (virtual address) |
| 230 | |||
| 231 | u32_le native_size; | ||
| 232 | char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; | ||
| 233 | |||
| 234 | // Threading stuff | ||
| 235 | u32_le status; | ||
| 236 | u32_le entry_point; | ||
| 237 | u32_le initial_stack; | ||
| 238 | u32_le stack_top; | ||
| 239 | u32_le stack_size; | ||
| 240 | |||
| 241 | u32_le arg; | ||
| 242 | u32_le processor_id; | ||
| 243 | |||
| 244 | s32_le initial_priority; | ||
| 245 | s32_le current_priority; | ||
| 28 | }; | 246 | }; |
| 29 | 247 | ||
| 30 | struct ThreadWaitInfo { | 248 | struct ThreadWaitInfo { |
| @@ -52,42 +270,23 @@ public: | |||
| 52 | //} | 270 | //} |
| 53 | 271 | ||
| 54 | //static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; } | 272 | //static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; } |
| 55 | //static int GetStaticIDType() { return SCE_KERNEL_TMID_Thread; } | 273 | static KernelIDType GetStaticIDType() { return KERNEL_ID_TYPE_THREAD; } |
| 56 | //int GetIDType() const { return SCE_KERNEL_TMID_Thread; } | 274 | KernelIDType GetIDType() const { return KERNEL_ID_TYPE_THREAD; } |
| 57 | 275 | ||
| 58 | //bool AllocateStack(u32 &stack_size) { | 276 | bool SetupStack(u32 stack_top, int stack_size) { |
| 59 | // FreeStack(); | 277 | current_stack.start = stack_top; |
| 60 | 278 | nt.initial_stack = current_stack.start; | |
| 61 | // bool fromTop = (nt.attr & PSP_THREAD_ATTR_LOW_STACK) == 0; | 279 | nt.stack_size = stack_size; |
| 62 | // if (nt.attr & PSP_THREAD_ATTR_KERNEL) | 280 | return true; |
| 63 | // { | 281 | } |
| 64 | // // Allocate stacks for kernel threads (idle) in kernel RAM | ||
| 65 | // currentStack.start = kernelMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str()); | ||
| 66 | // } | ||
| 67 | // else | ||
| 68 | // { | ||
| 69 | // currentStack.start = userMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str()); | ||
| 70 | // } | ||
| 71 | // if (currentStack.start == (u32)-1) | ||
| 72 | // { | ||
| 73 | // currentStack.start = 0; | ||
| 74 | // nt.initialStack = 0; | ||
| 75 | // ERROR_LOG(KERNEL, "Failed to allocate stack for thread"); | ||
| 76 | // return false; | ||
| 77 | // } | ||
| 78 | |||
| 79 | // nt.initialStack = currentStack.start; | ||
| 80 | // nt.stack_size = stack_size; | ||
| 81 | // return true; | ||
| 82 | //} | ||
| 83 | 282 | ||
| 84 | //bool FillStack() { | 283 | //bool FillStack() { |
| 85 | // // Fill the stack. | 284 | // // Fill the stack. |
| 86 | // if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) { | 285 | // if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) { |
| 87 | // Memory::Memset(currentStack.start, 0xFF, nt.stack_size); | 286 | // Memory::Memset(current_stack.start, 0xFF, nt.stack_size); |
| 88 | // } | 287 | // } |
| 89 | // context.r[MIPS_REG_SP] = currentStack.start + nt.stack_size; | 288 | // context.r[MIPS_REG_SP] = current_stack.start + nt.stack_size; |
| 90 | // currentStack.end = context.r[MIPS_REG_SP]; | 289 | // current_stack.end = context.r[MIPS_REG_SP]; |
| 91 | // // The k0 section is 256 bytes at the top of the stack. | 290 | // // The k0 section is 256 bytes at the top of the stack. |
| 92 | // context.r[MIPS_REG_SP] -= 256; | 291 | // context.r[MIPS_REG_SP] -= 256; |
| 93 | // context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP]; | 292 | // context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP]; |
| @@ -104,7 +303,7 @@ public: | |||
| 104 | //} | 303 | //} |
| 105 | 304 | ||
| 106 | //void FreeStack() { | 305 | //void FreeStack() { |
| 107 | // if (currentStack.start != 0) { | 306 | // if (current_stack.start != 0) { |
| 108 | // DEBUG_LOG(KERNEL, "Freeing thread stack %s", nt.name); | 307 | // DEBUG_LOG(KERNEL, "Freeing thread stack %s", nt.name); |
| 109 | 308 | ||
| 110 | // if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) { | 309 | // if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) { |
| @@ -112,12 +311,12 @@ public: | |||
| 112 | // } | 311 | // } |
| 113 | 312 | ||
| 114 | // if (nt.attr & PSP_THREAD_ATTR_KERNEL) { | 313 | // if (nt.attr & PSP_THREAD_ATTR_KERNEL) { |
| 115 | // kernelMemory.Free(currentStack.start); | 314 | // kernelMemory.Free(current_stack.start); |
| 116 | // } | 315 | // } |
| 117 | // else { | 316 | // else { |
| 118 | // userMemory.Free(currentStack.start); | 317 | // userMemory.Free(current_stack.start); |
| 119 | // } | 318 | // } |
| 120 | // currentStack.start = 0; | 319 | // current_stack.start = 0; |
| 121 | // } | 320 | // } |
| 122 | //} | 321 | //} |
| 123 | 322 | ||
| @@ -126,14 +325,14 @@ public: | |||
| 126 | // if (stack == (u32)-1) | 325 | // if (stack == (u32)-1) |
| 127 | // return false; | 326 | // return false; |
| 128 | 327 | ||
| 129 | // pushed_stacks.push_back(currentStack); | 328 | // pushed_stacks.push_back(current_stack); |
| 130 | // currentStack.start = stack; | 329 | // current_stack.start = stack; |
| 131 | // currentStack.end = stack + size; | 330 | // current_stack.end = stack + size; |
| 132 | // nt.initialStack = currentStack.start; | 331 | // nt.initialStack = current_stack.start; |
| 133 | // nt.stack_size = currentStack.end - currentStack.start; | 332 | // nt.stack_size = current_stack.end - current_stack.start; |
| 134 | 333 | ||
| 135 | // // We still drop the threadID at the bottom and fill it, but there's no k0. | 334 | // // We still drop the thread_id at the bottom and fill it, but there's no k0. |
| 136 | // Memory::Memset(currentStack.start, 0xFF, nt.stack_size); | 335 | // Memory::Memset(current_stack.start, 0xFF, nt.stack_size); |
| 137 | // Memory::Write_U32(GetUID(), nt.initialStack); | 336 | // Memory::Write_U32(GetUID(), nt.initialStack); |
| 138 | // return true; | 337 | // return true; |
| 139 | //} | 338 | //} |
| @@ -142,16 +341,16 @@ public: | |||
| 142 | // if (pushed_stacks.size() == 0) { | 341 | // if (pushed_stacks.size() == 0) { |
| 143 | // return false; | 342 | // return false; |
| 144 | // } | 343 | // } |
| 145 | // userMemory.Free(currentStack.start); | 344 | // userMemory.Free(current_stack.start); |
| 146 | // currentStack = pushed_stacks.back(); | 345 | // current_stack = pushed_stacks.back(); |
| 147 | // pushed_stacks.pop_back(); | 346 | // pushed_stacks.pop_back(); |
| 148 | // nt.initialStack = currentStack.start; | 347 | // nt.initialStack = current_stack.start; |
| 149 | // nt.stack_size = currentStack.end - currentStack.start; | 348 | // nt.stack_size = current_stack.end - current_stack.start; |
| 150 | // return true; | 349 | // return true; |
| 151 | //} | 350 | //} |
| 152 | 351 | ||
| 153 | Thread() { | 352 | Thread() { |
| 154 | currentStack.start = 0; | 353 | current_stack.start = 0; |
| 155 | } | 354 | } |
| 156 | 355 | ||
| 157 | // Can't use a destructor since savestates will call that too. | 356 | // Can't use a destructor since savestates will call that too. |
| @@ -177,20 +376,20 @@ public: | |||
| 177 | ThreadWaitInfo getWaitInfo(); | 376 | ThreadWaitInfo getWaitInfo(); |
| 178 | 377 | ||
| 179 | // Utils | 378 | // Utils |
| 180 | //inline bool isRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; } | 379 | inline bool IsRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; } |
| 181 | //inline bool isStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } | 380 | inline bool IsStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } |
| 182 | //inline bool isReady() const { return (nt.status & THREADSTATUS_READY) != 0; } | 381 | inline bool IsReady() const { return (nt.status & THREADSTATUS_READY) != 0; } |
| 183 | //inline bool isWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } | 382 | inline bool IsWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } |
| 184 | //inline bool isSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } | 383 | inline bool IsSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } |
| 185 | 384 | ||
| 186 | NativeThread nt; | 385 | NativeThread nt; |
| 187 | 386 | ||
| 188 | ThreadWaitInfo waitInfo; | 387 | ThreadWaitInfo waitInfo; |
| 189 | UID moduleId; | 388 | UID moduleId; |
| 190 | 389 | ||
| 191 | bool isProcessingCallbacks; | 390 | //bool isProcessingCallbacks; |
| 192 | u32 currentMipscallId; | 391 | //u32 currentMipscallId; |
| 193 | UID currentCallbackId; | 392 | //UID currentCallbackId; |
| 194 | 393 | ||
| 195 | ThreadContext context; | 394 | ThreadContext context; |
| 196 | 395 | ||
| @@ -206,7 +405,7 @@ public: | |||
| 206 | // These are stacks that aren't "active" right now, but will pop off once the func returns. | 405 | // These are stacks that aren't "active" right now, but will pop off once the func returns. |
| 207 | std::vector<StackInfo> pushed_stacks; | 406 | std::vector<StackInfo> pushed_stacks; |
| 208 | 407 | ||
| 209 | StackInfo currentStack; | 408 | StackInfo current_stack; |
| 210 | 409 | ||
| 211 | // For thread end. | 410 | // For thread end. |
| 212 | std::vector<UID> waiting_threads; | 411 | std::vector<UID> waiting_threads; |
| @@ -214,15 +413,276 @@ public: | |||
| 214 | std::map<UID, u64> paused_waits; | 413 | std::map<UID, u64> paused_waits; |
| 215 | }; | 414 | }; |
| 216 | 415 | ||
| 217 | void __KernelThreadingInit() { | 416 | void ThreadContext::reset() { |
| 417 | for (int i = 0; i < 16; i++) { | ||
| 418 | reg[i] = 0; | ||
| 419 | } | ||
| 420 | reg[13] = Memory::SCRATCHPAD_VADDR_END; | ||
| 421 | cpsr = 0; | ||
| 218 | } | 422 | } |
| 219 | 423 | ||
| 220 | void __KernelThreadingShutdown() { | 424 | // Lists all thread ids that aren't deleted/etc. |
| 425 | std::vector<UID> g_thread_queue; | ||
| 426 | |||
| 427 | // Lists only ready thread ids | ||
| 428 | ThreadQueueList g_thread_ready_queue; | ||
| 429 | |||
| 430 | UID g_current_thread; | ||
| 431 | Thread* g_current_thread_ptr; | ||
| 432 | const char *g_hle_current_thread_name = NULL; | ||
| 433 | |||
| 434 | Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 priority, | ||
| 435 | u32 entrypoint, u32 arg, u32 stack_top, u32 processor_id, int stack_size) { | ||
| 436 | |||
| 437 | Thread *t = new Thread; | ||
| 438 | id = g_kernel_objects.Create(t); | ||
| 439 | |||
| 440 | g_thread_queue.push_back(id); | ||
| 441 | g_thread_ready_queue.prepare(priority); | ||
| 442 | |||
| 443 | memset(&t->nt, 0xCD, sizeof(t->nt)); | ||
| 444 | |||
| 445 | t->nt.entry_point = entrypoint; | ||
| 446 | t->nt.native_size = sizeof(t->nt); | ||
| 447 | t->nt.initial_priority = t->nt.current_priority = priority; | ||
| 448 | t->nt.status = THREADSTATUS_DORMANT; | ||
| 449 | t->nt.initial_stack = t->nt.stack_top = stack_top; | ||
| 450 | t->nt.stack_size = stack_size; | ||
| 451 | t->nt.processor_id = processor_id; | ||
| 452 | |||
| 453 | strncpy(t->nt.name, name, KERNELOBJECT_MAX_NAME_LENGTH); | ||
| 454 | t->nt.name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0'; | ||
| 455 | |||
| 456 | t->nt.stack_size = stack_size; | ||
| 457 | t->SetupStack(stack_top, stack_size); | ||
| 458 | |||
| 459 | return t; | ||
| 460 | } | ||
| 461 | |||
| 462 | void __KernelResetThread(Thread *t, int lowest_priority) { | ||
| 463 | t->context.reset(); | ||
| 464 | t->context.pc = t->nt.entry_point; | ||
| 465 | |||
| 466 | // If the thread would be better than lowestPriority, reset to its initial. Yes, kinda odd... | ||
| 467 | if (t->nt.current_priority < lowest_priority) | ||
| 468 | t->nt.current_priority = t->nt.initial_priority; | ||
| 469 | |||
| 470 | //t->nt.wait_type = WAITTYPE_NONE; | ||
| 471 | //t->nt.wait_id = 0; | ||
| 472 | memset(&t->waitInfo, 0, sizeof(t->waitInfo)); | ||
| 473 | |||
| 474 | //t->nt.exitStatus = SCE_KERNEL_ERROR_NOT_DORMANT; | ||
| 475 | //t->isProcessingCallbacks = false; | ||
| 476 | //t->currentCallbackId = 0; | ||
| 477 | //t->currentMipscallId = 0; | ||
| 478 | //t->pendingMipsCalls.clear(); | ||
| 479 | |||
| 480 | //t->context.r[MIPS_REG_RA] = threadReturnHackAddr; //hack! TODO fix | ||
| 481 | // TODO: Not sure if it's reset here, but this makes sense. | ||
| 482 | //t->context.r[MIPS_REG_GP] = t->nt.gpreg; | ||
| 483 | //t->FillStack(); | ||
| 484 | |||
| 485 | //if (!t->waitingThreads.empty()) | ||
| 486 | // ERROR_LOG(KERNEL, "Resetting thread with threads waiting on end?"); | ||
| 487 | } | ||
| 488 | |||
| 489 | |||
| 490 | inline Thread *__GetCurrentThread() { | ||
| 491 | return g_current_thread_ptr; | ||
| 492 | } | ||
| 493 | |||
| 494 | inline void __SetCurrentThread(Thread *thread, UID thread_id, const char *name) { | ||
| 495 | g_current_thread = thread_id; | ||
| 496 | g_current_thread_ptr = thread; | ||
| 497 | g_hle_current_thread_name = name; | ||
| 498 | } | ||
| 499 | |||
| 500 | // TODO: Use __KernelChangeThreadState instead? It has other affects... | ||
| 501 | void __KernelChangeReadyState(Thread *thread, UID thread_id, bool ready) { | ||
| 502 | // Passing the id as a parameter is just an optimization, if it's wrong it will cause havoc. | ||
| 503 | _dbg_assert_msg_(KERNEL, thread->GetUID() == thread_id, "Incorrect thread_id"); | ||
| 504 | int prio = thread->nt.current_priority; | ||
| 505 | |||
| 506 | if (thread->IsReady()) { | ||
| 507 | if (!ready) | ||
| 508 | g_thread_ready_queue.remove(prio, thread_id); | ||
| 509 | } else if (ready) { | ||
| 510 | if (thread->IsRunning()) { | ||
| 511 | g_thread_ready_queue.push_front(prio, thread_id); | ||
| 512 | } else { | ||
| 513 | g_thread_ready_queue.push_back(prio, thread_id); | ||
| 514 | } | ||
| 515 | thread->nt.status = THREADSTATUS_READY; | ||
| 516 | } | ||
| 517 | } | ||
| 518 | |||
| 519 | void __KernelChangeReadyState(UID thread_id, bool ready) { | ||
| 520 | u32 error; | ||
| 521 | Thread *thread = g_kernel_objects.Get<Thread>(thread_id, error); | ||
| 522 | if (thread) { | ||
| 523 | __KernelChangeReadyState(thread, thread_id, ready); | ||
| 524 | } else { | ||
| 525 | WARN_LOG(KERNEL, "Trying to change the ready state of an unknown thread?"); | ||
| 526 | } | ||
| 527 | } | ||
| 528 | |||
| 529 | // Returns NULL if the current thread is fine. | ||
| 530 | Thread* __KernelNextThread() { | ||
| 531 | UID bestThread; | ||
| 532 | |||
| 533 | // If the current thread is running, it's a valid candidate. | ||
| 534 | Thread *cur = __GetCurrentThread(); | ||
| 535 | if (cur && cur->IsRunning()) { | ||
| 536 | bestThread = g_thread_ready_queue.pop_first_better(cur->nt.current_priority); | ||
| 537 | if (bestThread != 0) { | ||
| 538 | __KernelChangeReadyState(cur, g_current_thread, true); | ||
| 539 | } | ||
| 540 | } else { | ||
| 541 | bestThread = g_thread_ready_queue.pop_first(); | ||
| 542 | } | ||
| 543 | |||
| 544 | // Assume g_thread_ready_queue has not become corrupt. | ||
| 545 | if (bestThread != 0) { | ||
| 546 | return g_kernel_objects.GetFast<Thread>(bestThread); | ||
| 547 | } else { | ||
| 548 | return NULL; | ||
| 549 | } | ||
| 550 | } | ||
| 551 | |||
| 552 | // Saves the current CPU context | ||
| 553 | void __KernelSaveContext(ThreadContext *ctx) { | ||
| 554 | ctx->reg[0] = Core::g_app_core->GetReg(0); | ||
| 555 | ctx->reg[1] = Core::g_app_core->GetReg(1); | ||
| 556 | ctx->reg[2] = Core::g_app_core->GetReg(2); | ||
| 557 | ctx->reg[3] = Core::g_app_core->GetReg(3); | ||
| 558 | ctx->reg[4] = Core::g_app_core->GetReg(4); | ||
| 559 | ctx->reg[5] = Core::g_app_core->GetReg(5); | ||
| 560 | ctx->reg[6] = Core::g_app_core->GetReg(6); | ||
| 561 | ctx->reg[7] = Core::g_app_core->GetReg(7); | ||
| 562 | ctx->reg[8] = Core::g_app_core->GetReg(8); | ||
| 563 | ctx->reg[9] = Core::g_app_core->GetReg(9); | ||
| 564 | ctx->reg[10] = Core::g_app_core->GetReg(10); | ||
| 565 | ctx->reg[11] = Core::g_app_core->GetReg(11); | ||
| 566 | ctx->reg[12] = Core::g_app_core->GetReg(12); | ||
| 567 | ctx->reg[13] = Core::g_app_core->GetReg(13); | ||
| 568 | ctx->reg[14] = Core::g_app_core->GetReg(14); | ||
| 569 | ctx->reg[15] = Core::g_app_core->GetReg(15); | ||
| 570 | ctx->pc = Core::g_app_core->GetPC(); | ||
| 571 | ctx->cpsr = Core::g_app_core->GetCPSR(); | ||
| 572 | } | ||
| 573 | |||
| 574 | // Loads a CPU context | ||
| 575 | void __KernelLoadContext(ThreadContext *ctx) { | ||
| 576 | Core::g_app_core->SetReg(0, ctx->reg[0]); | ||
| 577 | Core::g_app_core->SetReg(1, ctx->reg[1]); | ||
| 578 | Core::g_app_core->SetReg(2, ctx->reg[2]); | ||
| 579 | Core::g_app_core->SetReg(3, ctx->reg[3]); | ||
| 580 | Core::g_app_core->SetReg(4, ctx->reg[4]); | ||
| 581 | Core::g_app_core->SetReg(5, ctx->reg[5]); | ||
| 582 | Core::g_app_core->SetReg(6, ctx->reg[6]); | ||
| 583 | Core::g_app_core->SetReg(7, ctx->reg[7]); | ||
| 584 | Core::g_app_core->SetReg(8, ctx->reg[8]); | ||
| 585 | Core::g_app_core->SetReg(9, ctx->reg[9]); | ||
| 586 | Core::g_app_core->SetReg(10, ctx->reg[10]); | ||
| 587 | Core::g_app_core->SetReg(11, ctx->reg[11]); | ||
| 588 | Core::g_app_core->SetReg(12, ctx->reg[12]); | ||
| 589 | Core::g_app_core->SetReg(13, ctx->reg[13]); | ||
| 590 | Core::g_app_core->SetReg(14, ctx->reg[14]); | ||
| 591 | Core::g_app_core->SetReg(15, ctx->reg[15]); | ||
| 592 | Core::g_app_core->SetPC(ctx->pc); | ||
| 593 | Core::g_app_core->SetCPSR(ctx->cpsr); | ||
| 594 | } | ||
| 595 | |||
| 596 | void __KernelSwitchContext(Thread *target, const char *reason) { | ||
| 597 | u32 oldPC = 0; | ||
| 598 | UID oldUID = 0; | ||
| 599 | const char *oldName = g_hle_current_thread_name != NULL ? g_hle_current_thread_name : "(none)"; | ||
| 600 | |||
| 601 | Thread *cur = __GetCurrentThread(); | ||
| 602 | if (cur) { // It might just have been deleted. | ||
| 603 | __KernelSaveContext(&cur->context); | ||
| 604 | oldPC = Core::g_app_core->GetPC(); | ||
| 605 | oldUID = cur->GetUID(); | ||
| 606 | |||
| 607 | // Normally this is taken care of in __KernelNextThread(). | ||
| 608 | if (cur->IsRunning()) | ||
| 609 | __KernelChangeReadyState(cur, oldUID, true); | ||
| 610 | } | ||
| 611 | |||
| 612 | if (target) { | ||
| 613 | __SetCurrentThread(target, target->GetUID(), target->nt.name); | ||
| 614 | __KernelChangeReadyState(target, g_current_thread, false); | ||
| 615 | target->nt.status = (target->nt.status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; | ||
| 616 | |||
| 617 | __KernelLoadContext(&target->context); | ||
| 618 | } else { | ||
| 619 | __SetCurrentThread(NULL, 0, NULL); | ||
| 620 | } | ||
| 621 | |||
| 622 | #if DEBUG_LEVEL <= MAX_LOGLEVEL || DEBUG_LOG == NOTICE_LOG | ||
| 623 | //bool fromIdle = oldUID == threadIdleID[0] || oldUID == threadIdleID[1]; | ||
| 624 | //bool toIdle = currentThread == threadIdleID[0] || currentThread == threadIdleID[1]; | ||
| 625 | //if (!(fromIdle && toIdle)) | ||
| 626 | //{ | ||
| 627 | // u64 nowCycles = CoreTiming::GetTicks(); | ||
| 628 | // s64 consumedCycles = nowCycles - lastSwitchCycles; | ||
| 629 | // lastSwitchCycles = nowCycles; | ||
| 630 | |||
| 631 | // DEBUG_LOG(SCEKERNEL, "Context switch: %s -> %s (%i->%i, pc: %08x->%08x, %s) +%lldus", | ||
| 632 | // oldName, hleCurrentThreadName, | ||
| 633 | // oldUID, currentThread, | ||
| 634 | // oldPC, currentMIPS->pc, | ||
| 635 | // reason, | ||
| 636 | // cyclesToUs(consumedCycles)); | ||
| 637 | //} | ||
| 638 | #endif | ||
| 639 | |||
| 640 | if (target) { | ||
| 641 | //// No longer waiting. | ||
| 642 | //target->nt.waitType = WAITTYPE_NONE; | ||
| 643 | //target->nt.waitID = 0; | ||
| 644 | |||
| 645 | //__KernelExecutePendingARMCalls(target, true); | ||
| 646 | } | ||
| 647 | } | ||
| 648 | |||
| 649 | UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) { | ||
| 650 | UID id; | ||
| 651 | |||
| 652 | Thread *thread = __KernelCreateThread(id, module_id, "root", prio, Core::g_app_core->GetPC(), | ||
| 653 | arg, Memory::SCRATCHPAD_VADDR_END, 0xFFFFFFFE, stack_size=stack_size); | ||
| 654 | |||
| 655 | if (thread->current_stack.start == 0) { | ||
| 656 | ERROR_LOG(KERNEL, "Unable to allocate stack for root thread."); | ||
| 657 | } | ||
| 658 | __KernelResetThread(thread, 0); | ||
| 659 | |||
| 660 | Thread *prev_thread = __GetCurrentThread(); | ||
| 661 | if (prev_thread && prev_thread->IsRunning()) | ||
| 662 | __KernelChangeReadyState(g_current_thread, true); | ||
| 663 | __SetCurrentThread(thread, id, "root"); | ||
| 664 | thread->nt.status = THREADSTATUS_RUNNING; // do not schedule | ||
| 665 | |||
| 666 | strcpy(thread->nt.name, "root"); | ||
| 667 | |||
| 668 | __KernelLoadContext(&thread->context); | ||
| 669 | |||
| 670 | // NOTE(bunnei): Not sure this is really correct, ignore args for now... | ||
| 671 | //Core::g_app_core->SetReg(0, args); | ||
| 672 | //Core::g_app_core->SetReg(13, (args + 0xf) & ~0xf); // Setup SP - probably not correct | ||
| 673 | //u32 location = Core::g_app_core->GetReg(13); // SP | ||
| 674 | //Core::g_app_core->SetReg(1, location); | ||
| 675 | |||
| 676 | //if (argp) | ||
| 677 | // Memory::Memcpy(location, argp, args); | ||
| 678 | //// Let's assume same as starting a new thread, 64 bytes for safety/kernel. | ||
| 679 | //Core::g_app_core->SetReg(13, Core::g_app_core->GetReg(13) - 64); | ||
| 680 | |||
| 681 | return id; | ||
| 221 | } | 682 | } |
| 222 | 683 | ||
| 223 | //const char *__KernelGetThreadName(UID threadID); | 684 | void __KernelThreadingInit() { |
| 224 | // | 685 | } |
| 225 | //void __KernelSaveContext(ThreadContext *ctx); | ||
| 226 | //void __KernelLoadContext(ThreadContext *ctx); | ||
| 227 | 686 | ||
| 228 | //void __KernelSwitchContext(Thread *target, const char *reason); \ No newline at end of file | 687 | void __KernelThreadingShutdown() { |
| 688 | } | ||