From 6b264518a50ce21cb1be55ff3eac4e1c85582cfe Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 9 May 2014 22:11:18 -0400 Subject: added initial kernel/thread modules --- src/core/hle/kernel/thread.cpp | 228 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 src/core/hle/kernel/thread.cpp (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp new file mode 100644 index 000000000..0ed35de83 --- /dev/null +++ b/src/core/hle/kernel/thread.cpp @@ -0,0 +1,228 @@ +// Copyright 2014 Citra Emulator Project / PPSSPP Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include + +#include +#include +#include +#include + +#include "common/common.h" + +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/thread.h" + +// Real CTR struct, don't change the fields. +struct NativeThread { + //u32 Pointer to vtable + //u32 Reference count + //KProcess* Process the thread belongs to (virtual address) + //u32 Thread id + //u32* ptr = *(KThread+0x8C) - 0xB0 + //u32* End-address of the page for this thread allocated in the 0xFF4XX000 region. Thus, + // if the beginning of this mapped page is 0xFF401000, this ptr would be 0xFF402000. + //KThread* Previous ? (virtual address) + //KThread* Next ? (virtual address) +}; + +struct ThreadWaitInfo { + u32 wait_value; + u32 timeout_ptr; +}; + +class Thread : public KernelObject { +public: + /*const char *GetName() { return nt.name; }*/ + const char *GetTypeName() { return "Thread"; } + //void GetQuickInfo(char *ptr, int size) + //{ + // sprintf(ptr, "pc= %08x sp= %08x %s %s %s %s %s %s (wt=%i wid=%i wv= %08x )", + // context.pc, context.r[13], // 13 is stack pointer + // (nt.status & THREADSTATUS_RUNNING) ? "RUN" : "", + // (nt.status & THREADSTATUS_READY) ? "READY" : "", + // (nt.status & THREADSTATUS_WAIT) ? "WAIT" : "", + // (nt.status & THREADSTATUS_SUSPEND) ? "SUSPEND" : "", + // (nt.status & THREADSTATUS_DORMANT) ? "DORMANT" : "", + // (nt.status & THREADSTATUS_DEAD) ? "DEAD" : "", + // nt.waitType, + // nt.waitID, + // waitInfo.waitValue); + //} + + //static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; } + //static int GetStaticIDType() { return SCE_KERNEL_TMID_Thread; } + //int GetIDType() const { return SCE_KERNEL_TMID_Thread; } + + //bool AllocateStack(u32 &stack_size) { + // FreeStack(); + + // bool fromTop = (nt.attr & PSP_THREAD_ATTR_LOW_STACK) == 0; + // if (nt.attr & PSP_THREAD_ATTR_KERNEL) + // { + // // Allocate stacks for kernel threads (idle) in kernel RAM + // currentStack.start = kernelMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str()); + // } + // else + // { + // currentStack.start = userMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str()); + // } + // if (currentStack.start == (u32)-1) + // { + // currentStack.start = 0; + // nt.initialStack = 0; + // ERROR_LOG(KERNEL, "Failed to allocate stack for thread"); + // return false; + // } + + // nt.initialStack = currentStack.start; + // nt.stack_size = stack_size; + // return true; + //} + + //bool FillStack() { + // // Fill the stack. + // if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) { + // Memory::Memset(currentStack.start, 0xFF, nt.stack_size); + // } + // context.r[MIPS_REG_SP] = currentStack.start + nt.stack_size; + // currentStack.end = context.r[MIPS_REG_SP]; + // // The k0 section is 256 bytes at the top of the stack. + // context.r[MIPS_REG_SP] -= 256; + // context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP]; + // u32 k0 = context.r[MIPS_REG_K0]; + // Memory::Memset(k0, 0, 0x100); + // Memory::Write_U32(GetUID(), k0 + 0xc0); + // Memory::Write_U32(nt.initialStack, k0 + 0xc8); + // Memory::Write_U32(0xffffffff, k0 + 0xf8); + // Memory::Write_U32(0xffffffff, k0 + 0xfc); + // // After k0 comes the arguments, which is done by sceKernelStartThread(). + + // Memory::Write_U32(GetUID(), nt.initialStack); + // return true; + //} + + //void FreeStack() { + // if (currentStack.start != 0) { + // DEBUG_LOG(KERNEL, "Freeing thread stack %s", nt.name); + + // if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) { + // Memory::Memset(nt.initialStack, 0, nt.stack_size); + // } + + // if (nt.attr & PSP_THREAD_ATTR_KERNEL) { + // kernelMemory.Free(currentStack.start); + // } + // else { + // userMemory.Free(currentStack.start); + // } + // currentStack.start = 0; + // } + //} + + //bool PushExtendedStack(u32 size) { + // u32 stack = userMemory.Alloc(size, true, (std::string("extended/") + nt.name).c_str()); + // if (stack == (u32)-1) + // return false; + + // pushed_stacks.push_back(currentStack); + // currentStack.start = stack; + // currentStack.end = stack + size; + // nt.initialStack = currentStack.start; + // nt.stack_size = currentStack.end - currentStack.start; + + // // We still drop the threadID at the bottom and fill it, but there's no k0. + // Memory::Memset(currentStack.start, 0xFF, nt.stack_size); + // Memory::Write_U32(GetUID(), nt.initialStack); + // return true; + //} + + //bool PopExtendedStack() { + // if (pushed_stacks.size() == 0) { + // return false; + // } + // userMemory.Free(currentStack.start); + // currentStack = pushed_stacks.back(); + // pushed_stacks.pop_back(); + // nt.initialStack = currentStack.start; + // nt.stack_size = currentStack.end - currentStack.start; + // return true; + //} + + Thread() { + currentStack.start = 0; + } + + // Can't use a destructor since savestates will call that too. + //void Cleanup() { + // // Callbacks are automatically deleted when their owning thread is deleted. + // for (auto it = callbacks.begin(), end = callbacks.end(); it != end; ++it) + // kernelObjects.Destroy(*it); + + // if (pushed_stacks.size() != 0) + // { + // WARN_LOG(KERNEL, "Thread ended within an extended stack"); + // for (size_t i = 0; i < pushed_stacks.size(); ++i) + // userMemory.Free(pushed_stacks[i].start); + // } + // FreeStack(); + //} + + void setReturnValue(u32 retval); + void setReturnValue(u64 retval); + void resumeFromWait(); + //bool isWaitingFor(WaitType type, int id); + //int getWaitID(WaitType type); + ThreadWaitInfo getWaitInfo(); + + // Utils + //inline bool isRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; } + //inline bool isStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } + //inline bool isReady() const { return (nt.status & THREADSTATUS_READY) != 0; } + //inline bool isWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } + //inline bool isSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } + + NativeThread nt; + + ThreadWaitInfo waitInfo; + UID moduleId; + + bool isProcessingCallbacks; + u32 currentMipscallId; + UID currentCallbackId; + + ThreadContext context; + + std::vector callbacks; + + std::list pending_calls; + + struct StackInfo { + u32 start; + u32 end; + }; + // This is a stack of... stacks, since sceKernelExtendThreadStack() can recurse. + // These are stacks that aren't "active" right now, but will pop off once the func returns. + std::vector pushed_stacks; + + StackInfo currentStack; + + // For thread end. + std::vector waiting_threads; + // Key is the callback id it was for, or if no callback, the thread id. + std::map paused_waits; +}; + +void __KernelThreadingInit() { +} + +void __KernelThreadingShutdown() { +} + +//const char *__KernelGetThreadName(UID threadID); +// +//void __KernelSaveContext(ThreadContext *ctx); +//void __KernelLoadContext(ThreadContext *ctx); + +//void __KernelSwitchContext(Thread *target, const char *reason); \ No newline at end of file -- cgit v1.2.3 From 3838d46b9022964617b93a45f3feab5052c3538b Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 13 May 2014 22:00:11 -0400 Subject: 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 --- src/core/hle/kernel/thread.cpp | 588 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 524 insertions(+), 64 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') 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 @@ #include "common/common.h" +#include "core/core.h" +#include "core/mem_map.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" -// Real CTR struct, don't change the fields. +struct ThreadQueueList { + // Number of queues (number of priority levels starting at 0.) + static const int NUM_QUEUES = 128; + // Initial number of threads a single queue can handle. + static const int INITIAL_CAPACITY = 32; + + struct Queue { + // Next ever-been-used queue (worse priority.) + Queue *next; + // First valid item in data. + int first; + // One after last valid item in data. + int end; + // A too-large array with room on the front and end. + UID *data; + // Size of data array. + int capacity; + }; + + ThreadQueueList() { + memset(queues, 0, sizeof(queues)); + first = invalid(); + } + + ~ThreadQueueList() { + for (int i = 0; i < NUM_QUEUES; ++i) { + if (queues[i].data != NULL) { + free(queues[i].data); + } + } + } + + // Only for debugging, returns priority level. + int contains(const UID uid) { + for (int i = 0; i < NUM_QUEUES; ++i) { + if (queues[i].data == NULL) { + continue; + } + Queue *cur = &queues[i]; + for (int j = cur->first; j < cur->end; ++j) { + if (cur->data[j] == uid) { + return i; + } + } + } + return -1; + } + + inline UID pop_first() { + Queue *cur = first; + while (cur != invalid()) { + if (cur->end - cur->first > 0) { + return cur->data[cur->first++]; + } + cur = cur->next; + } + + _dbg_assert_msg_(KERNEL, false, "ThreadQueueList should not be empty."); + return 0; + } + + inline UID pop_first_better(u32 priority) { + Queue *cur = first; + Queue *stop = &queues[priority]; + while (cur < stop) { + if (cur->end - cur->first > 0) { + return cur->data[cur->first++]; + } + cur = cur->next; + } + return 0; + } + + inline void push_front(u32 priority, const UID thread_id) { + Queue *cur = &queues[priority]; + cur->data[--cur->first] = thread_id; + if (cur->first == 0) { + rebalance(priority); + } + } + + inline void push_back(u32 priority, const UID thread_id) + { + Queue *cur = &queues[priority]; + cur->data[cur->end++] = thread_id; + if (cur->end == cur->capacity) { + rebalance(priority); + } + } + + inline void remove(u32 priority, const UID thread_id) { + Queue *cur = &queues[priority]; + _dbg_assert_msg_(KERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); + + for (int i = cur->first; i < cur->end; ++i) { + if (cur->data[i] == thread_id) { + int remaining = --cur->end - i; + if (remaining > 0) { + memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(UID)); + } + return; + } + } + + // Wasn't there. + } + + inline void rotate(u32 priority) { + Queue *cur = &queues[priority]; + _dbg_assert_msg_(KERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); + + if (cur->end - cur->first > 1) { + cur->data[cur->end++] = cur->data[cur->first++]; + if (cur->end == cur->capacity) { + rebalance(priority); + } + } + } + + inline void clear() { + for (int i = 0; i < NUM_QUEUES; ++i) { + if (queues[i].data != NULL) { + free(queues[i].data); + } + } + memset(queues, 0, sizeof(queues)); + first = invalid(); + } + + inline bool empty(u32 priority) const { + const Queue *cur = &queues[priority]; + return cur->first == cur->end; + } + + inline void prepare(u32 priority) { + Queue *cur = &queues[priority]; + if (cur->next == NULL) { + link(priority, INITIAL_CAPACITY); + } + } + +private: + Queue *invalid() const { + return (Queue *)-1; + } + + void link(u32 priority, int size) { + _dbg_assert_msg_(KERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once."); + + if (size <= INITIAL_CAPACITY) { + size = INITIAL_CAPACITY; + } else { + int goal = size; + size = INITIAL_CAPACITY; + while (size < goal) + size *= 2; + } + Queue *cur = &queues[priority]; + cur->data = (UID*)malloc(sizeof(UID)* size); + cur->capacity = size; + cur->first = size / 2; + cur->end = size / 2; + + for (int i = (int)priority - 1; i >= 0; --i) { + if (queues[i].next != NULL) { + cur->next = queues[i].next; + queues[i].next = cur; + return; + } + } + + cur->next = first; + first = cur; + } + + void rebalance(u32 priority) { + Queue *cur = &queues[priority]; + int size = cur->end - cur->first; + if (size >= cur->capacity - 2) { + UID* new_data = (UID*)realloc(cur->data, cur->capacity * 2 * sizeof(UID)); + if (new_data != NULL) { + cur->capacity *= 2; + cur->data = new_data; + } + } + + int newFirst = (cur->capacity - size) / 2; + if (newFirst != cur->first) { + memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(UID)); + cur->first = newFirst; + cur->end = newFirst + size; + } + } + + // The first queue that's ever been used. + Queue* first; + // The priority level queues of thread ids. + Queue queues[NUM_QUEUES]; +}; + +// Supposed to represent a real CTR struct... but not sure of the correct fields yet. struct NativeThread { //u32 Pointer to vtable //u32 Reference count @@ -25,6 +227,22 @@ struct NativeThread { // if the beginning of this mapped page is 0xFF401000, this ptr would be 0xFF402000. //KThread* Previous ? (virtual address) //KThread* Next ? (virtual address) + + u32_le native_size; + char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; + + // Threading stuff + u32_le status; + u32_le entry_point; + u32_le initial_stack; + u32_le stack_top; + u32_le stack_size; + + u32_le arg; + u32_le processor_id; + + s32_le initial_priority; + s32_le current_priority; }; struct ThreadWaitInfo { @@ -52,42 +270,23 @@ public: //} //static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; } - //static int GetStaticIDType() { return SCE_KERNEL_TMID_Thread; } - //int GetIDType() const { return SCE_KERNEL_TMID_Thread; } - - //bool AllocateStack(u32 &stack_size) { - // FreeStack(); - - // bool fromTop = (nt.attr & PSP_THREAD_ATTR_LOW_STACK) == 0; - // if (nt.attr & PSP_THREAD_ATTR_KERNEL) - // { - // // Allocate stacks for kernel threads (idle) in kernel RAM - // currentStack.start = kernelMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str()); - // } - // else - // { - // currentStack.start = userMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str()); - // } - // if (currentStack.start == (u32)-1) - // { - // currentStack.start = 0; - // nt.initialStack = 0; - // ERROR_LOG(KERNEL, "Failed to allocate stack for thread"); - // return false; - // } - - // nt.initialStack = currentStack.start; - // nt.stack_size = stack_size; - // return true; - //} + static KernelIDType GetStaticIDType() { return KERNEL_ID_TYPE_THREAD; } + KernelIDType GetIDType() const { return KERNEL_ID_TYPE_THREAD; } + + bool SetupStack(u32 stack_top, int stack_size) { + current_stack.start = stack_top; + nt.initial_stack = current_stack.start; + nt.stack_size = stack_size; + return true; + } //bool FillStack() { // // Fill the stack. // if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) { - // Memory::Memset(currentStack.start, 0xFF, nt.stack_size); + // Memory::Memset(current_stack.start, 0xFF, nt.stack_size); // } - // context.r[MIPS_REG_SP] = currentStack.start + nt.stack_size; - // currentStack.end = context.r[MIPS_REG_SP]; + // context.r[MIPS_REG_SP] = current_stack.start + nt.stack_size; + // current_stack.end = context.r[MIPS_REG_SP]; // // The k0 section is 256 bytes at the top of the stack. // context.r[MIPS_REG_SP] -= 256; // context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP]; @@ -104,7 +303,7 @@ public: //} //void FreeStack() { - // if (currentStack.start != 0) { + // if (current_stack.start != 0) { // DEBUG_LOG(KERNEL, "Freeing thread stack %s", nt.name); // if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) { @@ -112,12 +311,12 @@ public: // } // if (nt.attr & PSP_THREAD_ATTR_KERNEL) { - // kernelMemory.Free(currentStack.start); + // kernelMemory.Free(current_stack.start); // } // else { - // userMemory.Free(currentStack.start); + // userMemory.Free(current_stack.start); // } - // currentStack.start = 0; + // current_stack.start = 0; // } //} @@ -126,14 +325,14 @@ public: // if (stack == (u32)-1) // return false; - // pushed_stacks.push_back(currentStack); - // currentStack.start = stack; - // currentStack.end = stack + size; - // nt.initialStack = currentStack.start; - // nt.stack_size = currentStack.end - currentStack.start; + // pushed_stacks.push_back(current_stack); + // current_stack.start = stack; + // current_stack.end = stack + size; + // nt.initialStack = current_stack.start; + // nt.stack_size = current_stack.end - current_stack.start; - // // We still drop the threadID at the bottom and fill it, but there's no k0. - // Memory::Memset(currentStack.start, 0xFF, nt.stack_size); + // // We still drop the thread_id at the bottom and fill it, but there's no k0. + // Memory::Memset(current_stack.start, 0xFF, nt.stack_size); // Memory::Write_U32(GetUID(), nt.initialStack); // return true; //} @@ -142,16 +341,16 @@ public: // if (pushed_stacks.size() == 0) { // return false; // } - // userMemory.Free(currentStack.start); - // currentStack = pushed_stacks.back(); + // userMemory.Free(current_stack.start); + // current_stack = pushed_stacks.back(); // pushed_stacks.pop_back(); - // nt.initialStack = currentStack.start; - // nt.stack_size = currentStack.end - currentStack.start; + // nt.initialStack = current_stack.start; + // nt.stack_size = current_stack.end - current_stack.start; // return true; //} Thread() { - currentStack.start = 0; + current_stack.start = 0; } // Can't use a destructor since savestates will call that too. @@ -177,20 +376,20 @@ public: ThreadWaitInfo getWaitInfo(); // Utils - //inline bool isRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; } - //inline bool isStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } - //inline bool isReady() const { return (nt.status & THREADSTATUS_READY) != 0; } - //inline bool isWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } - //inline bool isSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } + inline bool IsRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; } + inline bool IsStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } + inline bool IsReady() const { return (nt.status & THREADSTATUS_READY) != 0; } + inline bool IsWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } + inline bool IsSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } NativeThread nt; ThreadWaitInfo waitInfo; UID moduleId; - bool isProcessingCallbacks; - u32 currentMipscallId; - UID currentCallbackId; + //bool isProcessingCallbacks; + //u32 currentMipscallId; + //UID currentCallbackId; ThreadContext context; @@ -206,7 +405,7 @@ public: // These are stacks that aren't "active" right now, but will pop off once the func returns. std::vector pushed_stacks; - StackInfo currentStack; + StackInfo current_stack; // For thread end. std::vector waiting_threads; @@ -214,15 +413,276 @@ public: std::map paused_waits; }; -void __KernelThreadingInit() { +void ThreadContext::reset() { + for (int i = 0; i < 16; i++) { + reg[i] = 0; + } + reg[13] = Memory::SCRATCHPAD_VADDR_END; + cpsr = 0; } -void __KernelThreadingShutdown() { +// Lists all thread ids that aren't deleted/etc. +std::vector g_thread_queue; + +// Lists only ready thread ids +ThreadQueueList g_thread_ready_queue; + +UID g_current_thread; +Thread* g_current_thread_ptr; +const char *g_hle_current_thread_name = NULL; + +Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 priority, + u32 entrypoint, u32 arg, u32 stack_top, u32 processor_id, int stack_size) { + + Thread *t = new Thread; + id = g_kernel_objects.Create(t); + + g_thread_queue.push_back(id); + g_thread_ready_queue.prepare(priority); + + memset(&t->nt, 0xCD, sizeof(t->nt)); + + t->nt.entry_point = entrypoint; + t->nt.native_size = sizeof(t->nt); + t->nt.initial_priority = t->nt.current_priority = priority; + t->nt.status = THREADSTATUS_DORMANT; + t->nt.initial_stack = t->nt.stack_top = stack_top; + t->nt.stack_size = stack_size; + t->nt.processor_id = processor_id; + + strncpy(t->nt.name, name, KERNELOBJECT_MAX_NAME_LENGTH); + t->nt.name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0'; + + t->nt.stack_size = stack_size; + t->SetupStack(stack_top, stack_size); + + return t; +} + +void __KernelResetThread(Thread *t, int lowest_priority) { + t->context.reset(); + t->context.pc = t->nt.entry_point; + + // If the thread would be better than lowestPriority, reset to its initial. Yes, kinda odd... + if (t->nt.current_priority < lowest_priority) + t->nt.current_priority = t->nt.initial_priority; + + //t->nt.wait_type = WAITTYPE_NONE; + //t->nt.wait_id = 0; + memset(&t->waitInfo, 0, sizeof(t->waitInfo)); + + //t->nt.exitStatus = SCE_KERNEL_ERROR_NOT_DORMANT; + //t->isProcessingCallbacks = false; + //t->currentCallbackId = 0; + //t->currentMipscallId = 0; + //t->pendingMipsCalls.clear(); + + //t->context.r[MIPS_REG_RA] = threadReturnHackAddr; //hack! TODO fix + // TODO: Not sure if it's reset here, but this makes sense. + //t->context.r[MIPS_REG_GP] = t->nt.gpreg; + //t->FillStack(); + + //if (!t->waitingThreads.empty()) + // ERROR_LOG(KERNEL, "Resetting thread with threads waiting on end?"); +} + + +inline Thread *__GetCurrentThread() { + return g_current_thread_ptr; +} + +inline void __SetCurrentThread(Thread *thread, UID thread_id, const char *name) { + g_current_thread = thread_id; + g_current_thread_ptr = thread; + g_hle_current_thread_name = name; +} + +// TODO: Use __KernelChangeThreadState instead? It has other affects... +void __KernelChangeReadyState(Thread *thread, UID thread_id, bool ready) { + // Passing the id as a parameter is just an optimization, if it's wrong it will cause havoc. + _dbg_assert_msg_(KERNEL, thread->GetUID() == thread_id, "Incorrect thread_id"); + int prio = thread->nt.current_priority; + + if (thread->IsReady()) { + if (!ready) + g_thread_ready_queue.remove(prio, thread_id); + } else if (ready) { + if (thread->IsRunning()) { + g_thread_ready_queue.push_front(prio, thread_id); + } else { + g_thread_ready_queue.push_back(prio, thread_id); + } + thread->nt.status = THREADSTATUS_READY; + } +} + +void __KernelChangeReadyState(UID thread_id, bool ready) { + u32 error; + Thread *thread = g_kernel_objects.Get(thread_id, error); + if (thread) { + __KernelChangeReadyState(thread, thread_id, ready); + } else { + WARN_LOG(KERNEL, "Trying to change the ready state of an unknown thread?"); + } +} + +// Returns NULL if the current thread is fine. +Thread* __KernelNextThread() { + UID bestThread; + + // If the current thread is running, it's a valid candidate. + Thread *cur = __GetCurrentThread(); + if (cur && cur->IsRunning()) { + bestThread = g_thread_ready_queue.pop_first_better(cur->nt.current_priority); + if (bestThread != 0) { + __KernelChangeReadyState(cur, g_current_thread, true); + } + } else { + bestThread = g_thread_ready_queue.pop_first(); + } + + // Assume g_thread_ready_queue has not become corrupt. + if (bestThread != 0) { + return g_kernel_objects.GetFast(bestThread); + } else { + return NULL; + } +} + +// Saves the current CPU context +void __KernelSaveContext(ThreadContext *ctx) { + ctx->reg[0] = Core::g_app_core->GetReg(0); + ctx->reg[1] = Core::g_app_core->GetReg(1); + ctx->reg[2] = Core::g_app_core->GetReg(2); + ctx->reg[3] = Core::g_app_core->GetReg(3); + ctx->reg[4] = Core::g_app_core->GetReg(4); + ctx->reg[5] = Core::g_app_core->GetReg(5); + ctx->reg[6] = Core::g_app_core->GetReg(6); + ctx->reg[7] = Core::g_app_core->GetReg(7); + ctx->reg[8] = Core::g_app_core->GetReg(8); + ctx->reg[9] = Core::g_app_core->GetReg(9); + ctx->reg[10] = Core::g_app_core->GetReg(10); + ctx->reg[11] = Core::g_app_core->GetReg(11); + ctx->reg[12] = Core::g_app_core->GetReg(12); + ctx->reg[13] = Core::g_app_core->GetReg(13); + ctx->reg[14] = Core::g_app_core->GetReg(14); + ctx->reg[15] = Core::g_app_core->GetReg(15); + ctx->pc = Core::g_app_core->GetPC(); + ctx->cpsr = Core::g_app_core->GetCPSR(); +} + +// Loads a CPU context +void __KernelLoadContext(ThreadContext *ctx) { + Core::g_app_core->SetReg(0, ctx->reg[0]); + Core::g_app_core->SetReg(1, ctx->reg[1]); + Core::g_app_core->SetReg(2, ctx->reg[2]); + Core::g_app_core->SetReg(3, ctx->reg[3]); + Core::g_app_core->SetReg(4, ctx->reg[4]); + Core::g_app_core->SetReg(5, ctx->reg[5]); + Core::g_app_core->SetReg(6, ctx->reg[6]); + Core::g_app_core->SetReg(7, ctx->reg[7]); + Core::g_app_core->SetReg(8, ctx->reg[8]); + Core::g_app_core->SetReg(9, ctx->reg[9]); + Core::g_app_core->SetReg(10, ctx->reg[10]); + Core::g_app_core->SetReg(11, ctx->reg[11]); + Core::g_app_core->SetReg(12, ctx->reg[12]); + Core::g_app_core->SetReg(13, ctx->reg[13]); + Core::g_app_core->SetReg(14, ctx->reg[14]); + Core::g_app_core->SetReg(15, ctx->reg[15]); + Core::g_app_core->SetPC(ctx->pc); + Core::g_app_core->SetCPSR(ctx->cpsr); +} + +void __KernelSwitchContext(Thread *target, const char *reason) { + u32 oldPC = 0; + UID oldUID = 0; + const char *oldName = g_hle_current_thread_name != NULL ? g_hle_current_thread_name : "(none)"; + + Thread *cur = __GetCurrentThread(); + if (cur) { // It might just have been deleted. + __KernelSaveContext(&cur->context); + oldPC = Core::g_app_core->GetPC(); + oldUID = cur->GetUID(); + + // Normally this is taken care of in __KernelNextThread(). + if (cur->IsRunning()) + __KernelChangeReadyState(cur, oldUID, true); + } + + if (target) { + __SetCurrentThread(target, target->GetUID(), target->nt.name); + __KernelChangeReadyState(target, g_current_thread, false); + target->nt.status = (target->nt.status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; + + __KernelLoadContext(&target->context); + } else { + __SetCurrentThread(NULL, 0, NULL); + } + +#if DEBUG_LEVEL <= MAX_LOGLEVEL || DEBUG_LOG == NOTICE_LOG + //bool fromIdle = oldUID == threadIdleID[0] || oldUID == threadIdleID[1]; + //bool toIdle = currentThread == threadIdleID[0] || currentThread == threadIdleID[1]; + //if (!(fromIdle && toIdle)) + //{ + // u64 nowCycles = CoreTiming::GetTicks(); + // s64 consumedCycles = nowCycles - lastSwitchCycles; + // lastSwitchCycles = nowCycles; + + // DEBUG_LOG(SCEKERNEL, "Context switch: %s -> %s (%i->%i, pc: %08x->%08x, %s) +%lldus", + // oldName, hleCurrentThreadName, + // oldUID, currentThread, + // oldPC, currentMIPS->pc, + // reason, + // cyclesToUs(consumedCycles)); + //} +#endif + + if (target) { + //// No longer waiting. + //target->nt.waitType = WAITTYPE_NONE; + //target->nt.waitID = 0; + + //__KernelExecutePendingARMCalls(target, true); + } +} + +UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) { + UID id; + + Thread *thread = __KernelCreateThread(id, module_id, "root", prio, Core::g_app_core->GetPC(), + arg, Memory::SCRATCHPAD_VADDR_END, 0xFFFFFFFE, stack_size=stack_size); + + if (thread->current_stack.start == 0) { + ERROR_LOG(KERNEL, "Unable to allocate stack for root thread."); + } + __KernelResetThread(thread, 0); + + Thread *prev_thread = __GetCurrentThread(); + if (prev_thread && prev_thread->IsRunning()) + __KernelChangeReadyState(g_current_thread, true); + __SetCurrentThread(thread, id, "root"); + thread->nt.status = THREADSTATUS_RUNNING; // do not schedule + + strcpy(thread->nt.name, "root"); + + __KernelLoadContext(&thread->context); + + // NOTE(bunnei): Not sure this is really correct, ignore args for now... + //Core::g_app_core->SetReg(0, args); + //Core::g_app_core->SetReg(13, (args + 0xf) & ~0xf); // Setup SP - probably not correct + //u32 location = Core::g_app_core->GetReg(13); // SP + //Core::g_app_core->SetReg(1, location); + + //if (argp) + // Memory::Memcpy(location, argp, args); + //// Let's assume same as starting a new thread, 64 bytes for safety/kernel. + //Core::g_app_core->SetReg(13, Core::g_app_core->GetReg(13) - 64); + + return id; } -//const char *__KernelGetThreadName(UID threadID); -// -//void __KernelSaveContext(ThreadContext *ctx); -//void __KernelLoadContext(ThreadContext *ctx); +void __KernelThreadingInit() { +} -//void __KernelSwitchContext(Thread *target, const char *reason); \ No newline at end of file +void __KernelThreadingShutdown() { +} -- cgit v1.2.3 From 7d078189daec2db8a465a401b6867739fea5043d Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 13 May 2014 22:29:31 -0400 Subject: various cleanups / remove unused code --- src/core/hle/kernel/thread.cpp | 93 +++++++++++++----------------------------- 1 file changed, 28 insertions(+), 65 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 584276eec..95ef2c173 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -427,10 +427,11 @@ std::vector g_thread_queue; // Lists only ready thread ids ThreadQueueList g_thread_ready_queue; -UID g_current_thread; -Thread* g_current_thread_ptr; -const char *g_hle_current_thread_name = NULL; +UID g_current_thread = 0; +Thread* g_current_thread_ptr = NULL; +const char* g_hle_current_thread_name = NULL; +/// Creates a new thread Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 priority, u32 entrypoint, u32 arg, u32 stack_top, u32 processor_id, int stack_size) { @@ -459,38 +460,25 @@ Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 prior return t; } +/// Resets the specified thread back to initial calling state void __KernelResetThread(Thread *t, int lowest_priority) { t->context.reset(); t->context.pc = t->nt.entry_point; // If the thread would be better than lowestPriority, reset to its initial. Yes, kinda odd... - if (t->nt.current_priority < lowest_priority) + if (t->nt.current_priority < lowest_priority) { t->nt.current_priority = t->nt.initial_priority; + } - //t->nt.wait_type = WAITTYPE_NONE; - //t->nt.wait_id = 0; memset(&t->waitInfo, 0, sizeof(t->waitInfo)); - - //t->nt.exitStatus = SCE_KERNEL_ERROR_NOT_DORMANT; - //t->isProcessingCallbacks = false; - //t->currentCallbackId = 0; - //t->currentMipscallId = 0; - //t->pendingMipsCalls.clear(); - - //t->context.r[MIPS_REG_RA] = threadReturnHackAddr; //hack! TODO fix - // TODO: Not sure if it's reset here, but this makes sense. - //t->context.r[MIPS_REG_GP] = t->nt.gpreg; - //t->FillStack(); - - //if (!t->waitingThreads.empty()) - // ERROR_LOG(KERNEL, "Resetting thread with threads waiting on end?"); } - +/// Returns the current executing thread inline Thread *__GetCurrentThread() { return g_current_thread_ptr; } +/// Sets the current executing thread inline void __SetCurrentThread(Thread *thread, UID thread_id, const char *name) { g_current_thread = thread_id; g_current_thread_ptr = thread; @@ -526,30 +514,29 @@ void __KernelChangeReadyState(UID thread_id, bool ready) { } } -// Returns NULL if the current thread is fine. +/// Returns NULL if the current thread is fine. Thread* __KernelNextThread() { - UID bestThread; + UID best_thread; // If the current thread is running, it's a valid candidate. Thread *cur = __GetCurrentThread(); if (cur && cur->IsRunning()) { - bestThread = g_thread_ready_queue.pop_first_better(cur->nt.current_priority); - if (bestThread != 0) { + best_thread = g_thread_ready_queue.pop_first_better(cur->nt.current_priority); + if (best_thread != 0) { __KernelChangeReadyState(cur, g_current_thread, true); } } else { - bestThread = g_thread_ready_queue.pop_first(); + best_thread = g_thread_ready_queue.pop_first(); } - // Assume g_thread_ready_queue has not become corrupt. - if (bestThread != 0) { - return g_kernel_objects.GetFast(bestThread); + if (best_thread != 0) { + return g_kernel_objects.GetFast(best_thread); } else { return NULL; } } -// Saves the current CPU context +/// Saves the current CPU context void __KernelSaveContext(ThreadContext *ctx) { ctx->reg[0] = Core::g_app_core->GetReg(0); ctx->reg[1] = Core::g_app_core->GetReg(1); @@ -571,7 +558,7 @@ void __KernelSaveContext(ThreadContext *ctx) { ctx->cpsr = Core::g_app_core->GetCPSR(); } -// Loads a CPU context +/// Loads a CPU context void __KernelLoadContext(ThreadContext *ctx) { Core::g_app_core->SetReg(0, ctx->reg[0]); Core::g_app_core->SetReg(1, ctx->reg[1]); @@ -593,59 +580,35 @@ void __KernelLoadContext(ThreadContext *ctx) { Core::g_app_core->SetCPSR(ctx->cpsr); } +/// Switches thread context void __KernelSwitchContext(Thread *target, const char *reason) { - u32 oldPC = 0; - UID oldUID = 0; - const char *oldName = g_hle_current_thread_name != NULL ? g_hle_current_thread_name : "(none)"; - + u32 old_pc = 0; + UID old_uid = 0; + const char *old_name = g_hle_current_thread_name != NULL ? g_hle_current_thread_name : "(none)"; Thread *cur = __GetCurrentThread(); + if (cur) { // It might just have been deleted. __KernelSaveContext(&cur->context); - oldPC = Core::g_app_core->GetPC(); - oldUID = cur->GetUID(); + old_pc = Core::g_app_core->GetPC(); + old_uid = cur->GetUID(); // Normally this is taken care of in __KernelNextThread(). if (cur->IsRunning()) - __KernelChangeReadyState(cur, oldUID, true); + __KernelChangeReadyState(cur, old_uid, true); } - if (target) { __SetCurrentThread(target, target->GetUID(), target->nt.name); __KernelChangeReadyState(target, g_current_thread, false); + target->nt.status = (target->nt.status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; __KernelLoadContext(&target->context); } else { __SetCurrentThread(NULL, 0, NULL); } - -#if DEBUG_LEVEL <= MAX_LOGLEVEL || DEBUG_LOG == NOTICE_LOG - //bool fromIdle = oldUID == threadIdleID[0] || oldUID == threadIdleID[1]; - //bool toIdle = currentThread == threadIdleID[0] || currentThread == threadIdleID[1]; - //if (!(fromIdle && toIdle)) - //{ - // u64 nowCycles = CoreTiming::GetTicks(); - // s64 consumedCycles = nowCycles - lastSwitchCycles; - // lastSwitchCycles = nowCycles; - - // DEBUG_LOG(SCEKERNEL, "Context switch: %s -> %s (%i->%i, pc: %08x->%08x, %s) +%lldus", - // oldName, hleCurrentThreadName, - // oldUID, currentThread, - // oldPC, currentMIPS->pc, - // reason, - // cyclesToUs(consumedCycles)); - //} -#endif - - if (target) { - //// No longer waiting. - //target->nt.waitType = WAITTYPE_NONE; - //target->nt.waitID = 0; - - //__KernelExecutePendingARMCalls(target, true); - } } +/// Sets up the root (primary) thread of execution UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) { UID id; -- cgit v1.2.3 From 0de78eb3c4407cfedb8779422d96b7ee73fc19ed Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 13 May 2014 23:18:28 -0400 Subject: fixed thread reset to not set stack address --- src/core/hle/kernel/thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 95ef2c173..c59d2a689 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -417,7 +417,6 @@ void ThreadContext::reset() { for (int i = 0; i < 16; i++) { reg[i] = 0; } - reg[13] = Memory::SCRATCHPAD_VADDR_END; cpsr = 0; } @@ -464,6 +463,7 @@ Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 prior void __KernelResetThread(Thread *t, int lowest_priority) { t->context.reset(); t->context.pc = t->nt.entry_point; + t->context.reg[13] = t->nt.initial_stack; // If the thread would be better than lowestPriority, reset to its initial. Yes, kinda odd... if (t->nt.current_priority < lowest_priority) { -- cgit v1.2.3 From b99a5da65b76ce16e40fe05feb786aac11931904 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 14 May 2014 20:50:30 -0400 Subject: - added helper function for __KernelCreateThread - added __KernelSwitchToThread for enabling a thread - added __KernelRotateThreadReadyQueue --- src/core/hle/kernel/thread.cpp | 74 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 4 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index c59d2a689..b6d02aa12 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -13,6 +13,8 @@ #include "core/core.h" #include "core/mem_map.h" +#include "core/hle/hle.h" +#include "core/hle/syscall.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" @@ -357,7 +359,7 @@ public: //void Cleanup() { // // Callbacks are automatically deleted when their owning thread is deleted. // for (auto it = callbacks.begin(), end = callbacks.end(); it != end; ++it) - // kernelObjects.Destroy(*it); + // g_kernel_objects.Destroy(*it); // if (pushed_stacks.size() != 0) // { @@ -432,7 +434,7 @@ const char* g_hle_current_thread_name = NULL; /// Creates a new thread Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 priority, - u32 entrypoint, u32 arg, u32 stack_top, u32 processor_id, int stack_size) { + u32 entry_point, u32 arg, u32 stack_top, u32 processor_id, int stack_size) { Thread *t = new Thread; id = g_kernel_objects.Create(t); @@ -442,7 +444,7 @@ Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 prior memset(&t->nt, 0xCD, sizeof(t->nt)); - t->nt.entry_point = entrypoint; + t->nt.entry_point = entry_point; t->nt.native_size = sizeof(t->nt); t->nt.initial_priority = t->nt.current_priority = priority; t->nt.status = THREADSTATUS_DORMANT; @@ -459,6 +461,18 @@ Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 prior return t; } +UID __KernelCreateThread(UID module_id, const char* name, u32 priority, u32 entry_point, u32 arg, + u32 stack_top, u32 processor_id, int stack_size) { + UID id; + __KernelCreateThread(id, module_id, name, priority, entry_point, arg, stack_top, processor_id, + stack_size); + + HLE::EatCycles(32000); + HLE::ReSchedule("thread created"); + + return id; +} + /// Resets the specified thread back to initial calling state void __KernelResetThread(Thread *t, int lowest_priority) { t->context.reset(); @@ -608,6 +622,31 @@ void __KernelSwitchContext(Thread *target, const char *reason) { } } +bool __KernelSwitchToThread(UID thread_id, const char *reason) { + if (!reason) { + reason = "switch to thread"; + } + if (g_current_thread == thread_id) { + return false; + } + u32 error; + Thread *t = g_kernel_objects.Get(thread_id, error); + if (!t) { + ERROR_LOG(KERNEL, "__KernelSwitchToThread: %x doesn't exist", thread_id); + HLE::ReSchedule("switch to deleted thread"); + } else if (t->IsReady() || t->IsRunning()) { + Thread *current = __GetCurrentThread(); + if (current && current->IsRunning()) { + __KernelChangeReadyState(current, g_current_thread, true); + } + __KernelSwitchContext(t, reason); + return true; + } else { + HLE::ReSchedule("switch to waiting thread"); + } + return false; +} + /// Sets up the root (primary) thread of execution UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) { UID id; @@ -633,7 +672,7 @@ UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) { // NOTE(bunnei): Not sure this is really correct, ignore args for now... //Core::g_app_core->SetReg(0, args); //Core::g_app_core->SetReg(13, (args + 0xf) & ~0xf); // Setup SP - probably not correct - //u32 location = Core::g_app_core->GetReg(13); // SP + //u32 location = Core::g_app_core->GetReg(13); // SP //Core::g_app_core->SetReg(1, location); //if (argp) @@ -644,6 +683,33 @@ UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) { return id; } +int __KernelRotateThreadReadyQueue(int priority) { + Thread *cur = __GetCurrentThread(); + + // 0 is special, it means "my current priority." + if (priority == 0) { + priority = cur->nt.current_priority; + } + //if (priority <= 0x07 || priority > 0x77) + // return SCE_KERNEL_ERROR_ILLEGAL_PRIORITY; + + if (!g_thread_ready_queue.empty(priority)) { + // In other words, yield to everyone else. + if (cur->nt.current_priority == priority) { + g_thread_ready_queue.push_back(priority, g_current_thread); + cur->nt.status = (cur->nt.status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; + + // Yield the next thread of this priority to all other threads of same priority. + } else { + g_thread_ready_queue.rotate(priority); + } + } + HLE::EatCycles(250); + HLE::ReSchedule("rotatethreadreadyqueue"); + + return 0; +} + void __KernelThreadingInit() { } -- cgit v1.2.3 From 940330c6e12b3eefb9fb035f75f4b090c969cb75 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 15 May 2014 18:27:08 -0400 Subject: completely gutted/refactored threading code to be simpler --- src/core/hle/kernel/thread.cpp | 844 +++++++++++------------------------------ 1 file changed, 228 insertions(+), 616 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index b6d02aa12..833a1b4ba 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -10,6 +10,7 @@ #include #include "common/common.h" +#include "common/thread_queue_list.h" #include "core/core.h" #include "core/mem_map.h" @@ -18,698 +19,309 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" -struct ThreadQueueList { - // Number of queues (number of priority levels starting at 0.) - static const int NUM_QUEUES = 128; - // Initial number of threads a single queue can handle. - static const int INITIAL_CAPACITY = 32; - - struct Queue { - // Next ever-been-used queue (worse priority.) - Queue *next; - // First valid item in data. - int first; - // One after last valid item in data. - int end; - // A too-large array with room on the front and end. - UID *data; - // Size of data array. - int capacity; - }; - - ThreadQueueList() { - memset(queues, 0, sizeof(queues)); - first = invalid(); - } - - ~ThreadQueueList() { - for (int i = 0; i < NUM_QUEUES; ++i) { - if (queues[i].data != NULL) { - free(queues[i].data); - } - } - } - - // Only for debugging, returns priority level. - int contains(const UID uid) { - for (int i = 0; i < NUM_QUEUES; ++i) { - if (queues[i].data == NULL) { - continue; - } - Queue *cur = &queues[i]; - for (int j = cur->first; j < cur->end; ++j) { - if (cur->data[j] == uid) { - return i; - } - } - } - return -1; - } - - inline UID pop_first() { - Queue *cur = first; - while (cur != invalid()) { - if (cur->end - cur->first > 0) { - return cur->data[cur->first++]; - } - cur = cur->next; - } - - _dbg_assert_msg_(KERNEL, false, "ThreadQueueList should not be empty."); - return 0; - } - - inline UID pop_first_better(u32 priority) { - Queue *cur = first; - Queue *stop = &queues[priority]; - while (cur < stop) { - if (cur->end - cur->first > 0) { - return cur->data[cur->first++]; - } - cur = cur->next; - } - return 0; - } - - inline void push_front(u32 priority, const UID thread_id) { - Queue *cur = &queues[priority]; - cur->data[--cur->first] = thread_id; - if (cur->first == 0) { - rebalance(priority); - } - } - - inline void push_back(u32 priority, const UID thread_id) - { - Queue *cur = &queues[priority]; - cur->data[cur->end++] = thread_id; - if (cur->end == cur->capacity) { - rebalance(priority); - } - } - - inline void remove(u32 priority, const UID thread_id) { - Queue *cur = &queues[priority]; - _dbg_assert_msg_(KERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); - - for (int i = cur->first; i < cur->end; ++i) { - if (cur->data[i] == thread_id) { - int remaining = --cur->end - i; - if (remaining > 0) { - memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(UID)); - } - return; - } - } - - // Wasn't there. - } - - inline void rotate(u32 priority) { - Queue *cur = &queues[priority]; - _dbg_assert_msg_(KERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); - - if (cur->end - cur->first > 1) { - cur->data[cur->end++] = cur->data[cur->first++]; - if (cur->end == cur->capacity) { - rebalance(priority); - } - } - } - - inline void clear() { - for (int i = 0; i < NUM_QUEUES; ++i) { - if (queues[i].data != NULL) { - free(queues[i].data); - } - } - memset(queues, 0, sizeof(queues)); - first = invalid(); - } - - inline bool empty(u32 priority) const { - const Queue *cur = &queues[priority]; - return cur->first == cur->end; - } - - inline void prepare(u32 priority) { - Queue *cur = &queues[priority]; - if (cur->next == NULL) { - link(priority, INITIAL_CAPACITY); - } - } - -private: - Queue *invalid() const { - return (Queue *)-1; - } - - void link(u32 priority, int size) { - _dbg_assert_msg_(KERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once."); - - if (size <= INITIAL_CAPACITY) { - size = INITIAL_CAPACITY; - } else { - int goal = size; - size = INITIAL_CAPACITY; - while (size < goal) - size *= 2; - } - Queue *cur = &queues[priority]; - cur->data = (UID*)malloc(sizeof(UID)* size); - cur->capacity = size; - cur->first = size / 2; - cur->end = size / 2; - - for (int i = (int)priority - 1; i >= 0; --i) { - if (queues[i].next != NULL) { - cur->next = queues[i].next; - queues[i].next = cur; - return; - } - } - - cur->next = first; - first = cur; - } - - void rebalance(u32 priority) { - Queue *cur = &queues[priority]; - int size = cur->end - cur->first; - if (size >= cur->capacity - 2) { - UID* new_data = (UID*)realloc(cur->data, cur->capacity * 2 * sizeof(UID)); - if (new_data != NULL) { - cur->capacity *= 2; - cur->data = new_data; - } - } +// Enums - int newFirst = (cur->capacity - size) / 2; - if (newFirst != cur->first) { - memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(UID)); - cur->first = newFirst; - cur->end = newFirst + size; - } - } +enum ThreadPriority { + THREADPRIO_HIGHEST = 0, + THREADPRIO_DEFAULT = 16, + THREADPRIO_LOWEST = 31, +}; - // The first queue that's ever been used. - Queue* first; - // The priority level queues of thread ids. - Queue queues[NUM_QUEUES]; +enum ThreadStatus { + THREADSTATUS_RUNNING = 1, + THREADSTATUS_READY = 2, + THREADSTATUS_WAIT = 4, + THREADSTATUS_SUSPEND = 8, + THREADSTATUS_DORMANT = 16, + THREADSTATUS_DEAD = 32, + THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND }; -// Supposed to represent a real CTR struct... but not sure of the correct fields yet. -struct NativeThread { - //u32 Pointer to vtable - //u32 Reference count - //KProcess* Process the thread belongs to (virtual address) - //u32 Thread id - //u32* ptr = *(KThread+0x8C) - 0xB0 - //u32* End-address of the page for this thread allocated in the 0xFF4XX000 region. Thus, - // if the beginning of this mapped page is 0xFF401000, this ptr would be 0xFF402000. - //KThread* Previous ? (virtual address) - //KThread* Next ? (virtual address) - - u32_le native_size; - char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; - - // Threading stuff - u32_le status; - u32_le entry_point; - u32_le initial_stack; - u32_le stack_top; - u32_le stack_size; +enum WaitType { + WAITTYPE_NONE, + WAITTYPE_SLEEP, + WAITTYPE_SEMA, + WAITTYPE_EVENTFLAG, + WAITTYPE_THREADEND, + WAITTYPE_VBLANK, + WAITTYPE_MUTEX, + WAITTYPE_SYNCH, - u32_le arg; - u32_le processor_id; - - s32_le initial_priority; - s32_le current_priority; + NUM_WAITTYPES }; -struct ThreadWaitInfo { - u32 wait_value; - u32 timeout_ptr; -}; +typedef s32 Handle; class Thread : public KernelObject { public: - /*const char *GetName() { return nt.name; }*/ + + const char *GetName() { return name; } const char *GetTypeName() { return "Thread"; } - //void GetQuickInfo(char *ptr, int size) - //{ - // sprintf(ptr, "pc= %08x sp= %08x %s %s %s %s %s %s (wt=%i wid=%i wv= %08x )", - // context.pc, context.r[13], // 13 is stack pointer - // (nt.status & THREADSTATUS_RUNNING) ? "RUN" : "", - // (nt.status & THREADSTATUS_READY) ? "READY" : "", - // (nt.status & THREADSTATUS_WAIT) ? "WAIT" : "", - // (nt.status & THREADSTATUS_SUSPEND) ? "SUSPEND" : "", - // (nt.status & THREADSTATUS_DORMANT) ? "DORMANT" : "", - // (nt.status & THREADSTATUS_DEAD) ? "DEAD" : "", - // nt.waitType, - // nt.waitID, - // waitInfo.waitValue); - //} - - //static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; } + static KernelIDType GetStaticIDType() { return KERNEL_ID_TYPE_THREAD; } KernelIDType GetIDType() const { return KERNEL_ID_TYPE_THREAD; } - bool SetupStack(u32 stack_top, int stack_size) { - current_stack.start = stack_top; - nt.initial_stack = current_stack.start; - nt.stack_size = stack_size; - return true; - } - - //bool FillStack() { - // // Fill the stack. - // if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) { - // Memory::Memset(current_stack.start, 0xFF, nt.stack_size); - // } - // context.r[MIPS_REG_SP] = current_stack.start + nt.stack_size; - // current_stack.end = context.r[MIPS_REG_SP]; - // // The k0 section is 256 bytes at the top of the stack. - // context.r[MIPS_REG_SP] -= 256; - // context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP]; - // u32 k0 = context.r[MIPS_REG_K0]; - // Memory::Memset(k0, 0, 0x100); - // Memory::Write_U32(GetUID(), k0 + 0xc0); - // Memory::Write_U32(nt.initialStack, k0 + 0xc8); - // Memory::Write_U32(0xffffffff, k0 + 0xf8); - // Memory::Write_U32(0xffffffff, k0 + 0xfc); - // // After k0 comes the arguments, which is done by sceKernelStartThread(). - - // Memory::Write_U32(GetUID(), nt.initialStack); - // return true; - //} - - //void FreeStack() { - // if (current_stack.start != 0) { - // DEBUG_LOG(KERNEL, "Freeing thread stack %s", nt.name); - - // if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) { - // Memory::Memset(nt.initialStack, 0, nt.stack_size); - // } - - // if (nt.attr & PSP_THREAD_ATTR_KERNEL) { - // kernelMemory.Free(current_stack.start); - // } - // else { - // userMemory.Free(current_stack.start); - // } - // current_stack.start = 0; - // } - //} - - //bool PushExtendedStack(u32 size) { - // u32 stack = userMemory.Alloc(size, true, (std::string("extended/") + nt.name).c_str()); - // if (stack == (u32)-1) - // return false; - - // pushed_stacks.push_back(current_stack); - // current_stack.start = stack; - // current_stack.end = stack + size; - // nt.initialStack = current_stack.start; - // nt.stack_size = current_stack.end - current_stack.start; - - // // We still drop the thread_id at the bottom and fill it, but there's no k0. - // Memory::Memset(current_stack.start, 0xFF, nt.stack_size); - // Memory::Write_U32(GetUID(), nt.initialStack); - // return true; - //} - - //bool PopExtendedStack() { - // if (pushed_stacks.size() == 0) { - // return false; - // } - // userMemory.Free(current_stack.start); - // current_stack = pushed_stacks.back(); - // pushed_stacks.pop_back(); - // nt.initialStack = current_stack.start; - // nt.stack_size = current_stack.end - current_stack.start; - // return true; - //} - - Thread() { - current_stack.start = 0; - } - - // Can't use a destructor since savestates will call that too. - //void Cleanup() { - // // Callbacks are automatically deleted when their owning thread is deleted. - // for (auto it = callbacks.begin(), end = callbacks.end(); it != end; ++it) - // g_kernel_objects.Destroy(*it); - - // if (pushed_stacks.size() != 0) - // { - // WARN_LOG(KERNEL, "Thread ended within an extended stack"); - // for (size_t i = 0; i < pushed_stacks.size(); ++i) - // userMemory.Free(pushed_stacks[i].start); - // } - // FreeStack(); - //} - - void setReturnValue(u32 retval); - void setReturnValue(u64 retval); - void resumeFromWait(); - //bool isWaitingFor(WaitType type, int id); - //int getWaitID(WaitType type); - ThreadWaitInfo getWaitInfo(); - - // Utils - inline bool IsRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; } - inline bool IsStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } - inline bool IsReady() const { return (nt.status & THREADSTATUS_READY) != 0; } - inline bool IsWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } - inline bool IsSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } - - NativeThread nt; - - ThreadWaitInfo waitInfo; - UID moduleId; - - //bool isProcessingCallbacks; - //u32 currentMipscallId; - //UID currentCallbackId; + inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } + inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } + inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; } + inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } + inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } ThreadContext context; - std::vector callbacks; + u32 status; + u32 entry_point; + u32 stack_top; + u32 stack_size; - std::list pending_calls; + s32 initial_priority; + s32 current_priority; - struct StackInfo { - u32 start; - u32 end; - }; - // This is a stack of... stacks, since sceKernelExtendThreadStack() can recurse. - // These are stacks that aren't "active" right now, but will pop off once the func returns. - std::vector pushed_stacks; + s32 processor_id; - StackInfo current_stack; + WaitType wait_type; - // For thread end. - std::vector waiting_threads; - // Key is the callback id it was for, or if no callback, the thread id. - std::map paused_waits; + char name[KERNELOBJECT_MAX_NAME_LENGTH+1]; }; -void ThreadContext::reset() { - for (int i = 0; i < 16; i++) { - reg[i] = 0; - } - cpsr = 0; -} - // Lists all thread ids that aren't deleted/etc. -std::vector g_thread_queue; +std::vector g_thread_queue; -// Lists only ready thread ids -ThreadQueueList g_thread_ready_queue; +// Lists only ready thread ids. +Common::ThreadQueueList g_thread_ready_queue; -UID g_current_thread = 0; -Thread* g_current_thread_ptr = NULL; -const char* g_hle_current_thread_name = NULL; +Handle g_current_thread_handle; -/// Creates a new thread -Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 priority, - u32 entry_point, u32 arg, u32 stack_top, u32 processor_id, int stack_size) { +Thread* g_current_thread; - Thread *t = new Thread; - id = g_kernel_objects.Create(t); - - g_thread_queue.push_back(id); - g_thread_ready_queue.prepare(priority); - - memset(&t->nt, 0xCD, sizeof(t->nt)); - t->nt.entry_point = entry_point; - t->nt.native_size = sizeof(t->nt); - t->nt.initial_priority = t->nt.current_priority = priority; - t->nt.status = THREADSTATUS_DORMANT; - t->nt.initial_stack = t->nt.stack_top = stack_top; - t->nt.stack_size = stack_size; - t->nt.processor_id = processor_id; +inline Thread *__GetCurrentThread() { + return g_current_thread; +} - strncpy(t->nt.name, name, KERNELOBJECT_MAX_NAME_LENGTH); - t->nt.name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0'; +inline void __SetCurrentThread(Thread *t) { + g_current_thread = t; + g_current_thread_handle = t->GetHandle(); +} - t->nt.stack_size = stack_size; - t->SetupStack(stack_top, stack_size); +//////////////////////////////////////////////////////////////////////////////////////////////////// - return t; +/// Saves the current CPU context +void __KernelSaveContext(ThreadContext &ctx) { + ctx.cpu_registers[0] = Core::g_app_core->GetReg(0); + ctx.cpu_registers[1] = Core::g_app_core->GetReg(1); + ctx.cpu_registers[2] = Core::g_app_core->GetReg(2); + ctx.cpu_registers[3] = Core::g_app_core->GetReg(3); + ctx.cpu_registers[4] = Core::g_app_core->GetReg(4); + ctx.cpu_registers[5] = Core::g_app_core->GetReg(5); + ctx.cpu_registers[6] = Core::g_app_core->GetReg(6); + ctx.cpu_registers[7] = Core::g_app_core->GetReg(7); + ctx.cpu_registers[8] = Core::g_app_core->GetReg(8); + ctx.cpu_registers[9] = Core::g_app_core->GetReg(9); + ctx.cpu_registers[10] = Core::g_app_core->GetReg(10); + ctx.cpu_registers[11] = Core::g_app_core->GetReg(11); + ctx.cpu_registers[12] = Core::g_app_core->GetReg(12); + ctx.sp = Core::g_app_core->GetReg(13); + ctx.lr = Core::g_app_core->GetReg(14); + ctx.pc = Core::g_app_core->GetPC(); + ctx.cpsr = Core::g_app_core->GetCPSR(); } -UID __KernelCreateThread(UID module_id, const char* name, u32 priority, u32 entry_point, u32 arg, - u32 stack_top, u32 processor_id, int stack_size) { - UID id; - __KernelCreateThread(id, module_id, name, priority, entry_point, arg, stack_top, processor_id, - stack_size); - - HLE::EatCycles(32000); - HLE::ReSchedule("thread created"); - - return id; +/// Loads a CPU context +void __KernelLoadContext(const ThreadContext &ctx) { + Core::g_app_core->SetReg(0, ctx.cpu_registers[0]); + Core::g_app_core->SetReg(1, ctx.cpu_registers[1]); + Core::g_app_core->SetReg(2, ctx.cpu_registers[2]); + Core::g_app_core->SetReg(3, ctx.cpu_registers[3]); + Core::g_app_core->SetReg(4, ctx.cpu_registers[4]); + Core::g_app_core->SetReg(5, ctx.cpu_registers[5]); + Core::g_app_core->SetReg(6, ctx.cpu_registers[6]); + Core::g_app_core->SetReg(7, ctx.cpu_registers[7]); + Core::g_app_core->SetReg(8, ctx.cpu_registers[8]); + Core::g_app_core->SetReg(9, ctx.cpu_registers[9]); + Core::g_app_core->SetReg(10, ctx.cpu_registers[10]); + Core::g_app_core->SetReg(11, ctx.cpu_registers[11]); + Core::g_app_core->SetReg(12, ctx.cpu_registers[12]); + Core::g_app_core->SetReg(13, ctx.sp); + Core::g_app_core->SetReg(14, ctx.lr); + //Core::g_app_core->SetReg(15, ctx.pc); + + Core::g_app_core->SetPC(ctx.pc); + Core::g_app_core->SetCPSR(ctx.cpsr); } -/// Resets the specified thread back to initial calling state -void __KernelResetThread(Thread *t, int lowest_priority) { - t->context.reset(); - t->context.pc = t->nt.entry_point; - t->context.reg[13] = t->nt.initial_stack; +/// Resets a thread +void __KernelResetThread(Thread *t, s32 lowest_priority) { + memset(&t->context, 0, sizeof(ThreadContext)); - // If the thread would be better than lowestPriority, reset to its initial. Yes, kinda odd... - if (t->nt.current_priority < lowest_priority) { - t->nt.current_priority = t->nt.initial_priority; + t->context.pc = t->entry_point; + t->context.sp = t->stack_top; + + if (t->current_priority < lowest_priority) { + t->current_priority = t->initial_priority; } - - memset(&t->waitInfo, 0, sizeof(t->waitInfo)); -} - -/// Returns the current executing thread -inline Thread *__GetCurrentThread() { - return g_current_thread_ptr; + + t->wait_type = WAITTYPE_NONE; } -/// Sets the current executing thread -inline void __SetCurrentThread(Thread *thread, UID thread_id, const char *name) { - g_current_thread = thread_id; - g_current_thread_ptr = thread; - g_hle_current_thread_name = name; +/// Creates a new thread +Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size=0x4000) { + static u32 _handle_count = 1; + + Thread *t = new Thread; + + handle = (_handle_count++); + + g_thread_queue.push_back(handle); + g_thread_ready_queue.prepare(priority); + + t->status = THREADSTATUS_DORMANT; + t->entry_point = entry_point; + t->stack_top = stack_top; + t->stack_size = stack_size; + t->initial_priority = t->current_priority = priority; + t->processor_id = processor_id; + t->wait_type = WAITTYPE_NONE; + + strncpy(t->name, name, KERNELOBJECT_MAX_NAME_LENGTH); + t->name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0'; + + return t; } -// TODO: Use __KernelChangeThreadState instead? It has other affects... -void __KernelChangeReadyState(Thread *thread, UID thread_id, bool ready) { - // Passing the id as a parameter is just an optimization, if it's wrong it will cause havoc. - _dbg_assert_msg_(KERNEL, thread->GetUID() == thread_id, "Incorrect thread_id"); - int prio = thread->nt.current_priority; - - if (thread->IsReady()) { - if (!ready) - g_thread_ready_queue.remove(prio, thread_id); - } else if (ready) { - if (thread->IsRunning()) { - g_thread_ready_queue.push_front(prio, thread_id); +/// Change a thread to "ready" state +void __KernelChangeReadyState(Thread *t, bool ready) { + Handle handle = t->GetHandle(); + if (t->IsReady()) { + if (!ready) { + g_thread_ready_queue.remove(t->current_priority, handle); + } + } else if (ready) { + if (t->IsRunning()) { + g_thread_ready_queue.push_front(t->current_priority, handle); } else { - g_thread_ready_queue.push_back(prio, thread_id); + g_thread_ready_queue.push_back(t->current_priority, handle); } - thread->nt.status = THREADSTATUS_READY; + t->status = THREADSTATUS_READY; } } -void __KernelChangeReadyState(UID thread_id, bool ready) { - u32 error; - Thread *thread = g_kernel_objects.Get(thread_id, error); - if (thread) { - __KernelChangeReadyState(thread, thread_id, ready); - } else { - WARN_LOG(KERNEL, "Trying to change the ready state of an unknown thread?"); +/// Changes a threads state +void __KernelChangeThreadState(Thread *t, ThreadStatus new_status) { + if (!t || t->status == new_status) { + return; + } + __KernelChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); + t->status = new_status; + + if (new_status == THREADSTATUS_WAIT) { + if (t->wait_type == WAITTYPE_NONE) { + printf("ERROR: Waittype none not allowed here\n"); + } } } -/// Returns NULL if the current thread is fine. -Thread* __KernelNextThread() { - UID best_thread; - - // If the current thread is running, it's a valid candidate. +/// Switches CPU context to that of the specified thread +void __KernelSwitchContext(Thread* t, const char *reason) { Thread *cur = __GetCurrentThread(); - if (cur && cur->IsRunning()) { - best_thread = g_thread_ready_queue.pop_first_better(cur->nt.current_priority); - if (best_thread != 0) { - __KernelChangeReadyState(cur, g_current_thread, true); + + // Save context for current thread + if (cur) { + __KernelSaveContext(cur->context); + + if (cur->IsRunning()) { + __KernelChangeReadyState(cur, true); } - } else { - best_thread = g_thread_ready_queue.pop_first(); } - // Assume g_thread_ready_queue has not become corrupt. - if (best_thread != 0) { - return g_kernel_objects.GetFast(best_thread); + // Load context of new thread + if (t) { + __SetCurrentThread(t); + __KernelChangeReadyState(t, false); + t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; + t->wait_type = WAITTYPE_NONE; + __KernelLoadContext(t->context); } else { - return NULL; + __SetCurrentThread(NULL); } } -/// Saves the current CPU context -void __KernelSaveContext(ThreadContext *ctx) { - ctx->reg[0] = Core::g_app_core->GetReg(0); - ctx->reg[1] = Core::g_app_core->GetReg(1); - ctx->reg[2] = Core::g_app_core->GetReg(2); - ctx->reg[3] = Core::g_app_core->GetReg(3); - ctx->reg[4] = Core::g_app_core->GetReg(4); - ctx->reg[5] = Core::g_app_core->GetReg(5); - ctx->reg[6] = Core::g_app_core->GetReg(6); - ctx->reg[7] = Core::g_app_core->GetReg(7); - ctx->reg[8] = Core::g_app_core->GetReg(8); - ctx->reg[9] = Core::g_app_core->GetReg(9); - ctx->reg[10] = Core::g_app_core->GetReg(10); - ctx->reg[11] = Core::g_app_core->GetReg(11); - ctx->reg[12] = Core::g_app_core->GetReg(12); - ctx->reg[13] = Core::g_app_core->GetReg(13); - ctx->reg[14] = Core::g_app_core->GetReg(14); - ctx->reg[15] = Core::g_app_core->GetReg(15); - ctx->pc = Core::g_app_core->GetPC(); - ctx->cpsr = Core::g_app_core->GetCPSR(); -} - -/// Loads a CPU context -void __KernelLoadContext(ThreadContext *ctx) { - Core::g_app_core->SetReg(0, ctx->reg[0]); - Core::g_app_core->SetReg(1, ctx->reg[1]); - Core::g_app_core->SetReg(2, ctx->reg[2]); - Core::g_app_core->SetReg(3, ctx->reg[3]); - Core::g_app_core->SetReg(4, ctx->reg[4]); - Core::g_app_core->SetReg(5, ctx->reg[5]); - Core::g_app_core->SetReg(6, ctx->reg[6]); - Core::g_app_core->SetReg(7, ctx->reg[7]); - Core::g_app_core->SetReg(8, ctx->reg[8]); - Core::g_app_core->SetReg(9, ctx->reg[9]); - Core::g_app_core->SetReg(10, ctx->reg[10]); - Core::g_app_core->SetReg(11, ctx->reg[11]); - Core::g_app_core->SetReg(12, ctx->reg[12]); - Core::g_app_core->SetReg(13, ctx->reg[13]); - Core::g_app_core->SetReg(14, ctx->reg[14]); - Core::g_app_core->SetReg(15, ctx->reg[15]); - Core::g_app_core->SetPC(ctx->pc); - Core::g_app_core->SetCPSR(ctx->cpsr); -} - -/// Switches thread context -void __KernelSwitchContext(Thread *target, const char *reason) { - u32 old_pc = 0; - UID old_uid = 0; - const char *old_name = g_hle_current_thread_name != NULL ? g_hle_current_thread_name : "(none)"; +/// Gets the next thread that is ready to be run by priority +Thread *__KernelNextThread() { + Handle next; Thread *cur = __GetCurrentThread(); - - if (cur) { // It might just have been deleted. - __KernelSaveContext(&cur->context); - old_pc = Core::g_app_core->GetPC(); - old_uid = cur->GetUID(); - - // Normally this is taken care of in __KernelNextThread(). - if (cur->IsRunning()) - __KernelChangeReadyState(cur, old_uid, true); + + if (cur && cur->IsRunning()) { + next = g_thread_ready_queue.pop_first_better(cur->current_priority); + } else { + next = g_thread_ready_queue.pop_first(); } - if (target) { - __SetCurrentThread(target, target->GetUID(), target->nt.name); - __KernelChangeReadyState(target, g_current_thread, false); - - target->nt.status = (target->nt.status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; - - __KernelLoadContext(&target->context); - } else { - __SetCurrentThread(NULL, 0, NULL); + if (next < 0) { + return NULL; } + return g_kernel_objects.GetFast(next); } -bool __KernelSwitchToThread(UID thread_id, const char *reason) { - if (!reason) { - reason = "switch to thread"; +/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) +void __KernelCallThread(Thread *t) { + // Stop waiting + if (t->wait_type != WAITTYPE_NONE) { + t->wait_type = WAITTYPE_NONE; } - if (g_current_thread == thread_id) { - return false; - } - u32 error; - Thread *t = g_kernel_objects.Get(thread_id, error); - if (!t) { - ERROR_LOG(KERNEL, "__KernelSwitchToThread: %x doesn't exist", thread_id); - HLE::ReSchedule("switch to deleted thread"); - } else if (t->IsReady() || t->IsRunning()) { - Thread *current = __GetCurrentThread(); - if (current && current->IsRunning()) { - __KernelChangeReadyState(current, g_current_thread, true); - } - __KernelSwitchContext(t, reason); - return true; - } else { - HLE::ReSchedule("switch to waiting thread"); - } - return false; + __KernelChangeThreadState(t, THREADSTATUS_READY); } -/// Sets up the root (primary) thread of execution -UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) { - UID id; - - Thread *thread = __KernelCreateThread(id, module_id, "root", prio, Core::g_app_core->GetPC(), - arg, Memory::SCRATCHPAD_VADDR_END, 0xFFFFFFFE, stack_size=stack_size); - - if (thread->current_stack.start == 0) { - ERROR_LOG(KERNEL, "Unable to allocate stack for root thread."); - } - __KernelResetThread(thread, 0); - - Thread *prev_thread = __GetCurrentThread(); - if (prev_thread && prev_thread->IsRunning()) - __KernelChangeReadyState(g_current_thread, true); - __SetCurrentThread(thread, id, "root"); - thread->nt.status = THREADSTATUS_RUNNING; // do not schedule - - strcpy(thread->nt.name, "root"); - - __KernelLoadContext(&thread->context); +/// Sets up the primary application thread +Handle __KernelSetupMainThread(s32 priority, int stack_size) { + Handle handle; - // NOTE(bunnei): Not sure this is really correct, ignore args for now... - //Core::g_app_core->SetReg(0, args); - //Core::g_app_core->SetReg(13, (args + 0xf) & ~0xf); // Setup SP - probably not correct - //u32 location = Core::g_app_core->GetReg(13); // SP - //Core::g_app_core->SetReg(1, location); + // Initialize new "main" thread + Thread *t = __KernelCreateThread(handle, "main", Core::g_app_core->GetPC(), priority, + 0xFFFFFFFE, Memory::SCRATCHPAD_VADDR_END, stack_size); - //if (argp) - // Memory::Memcpy(location, argp, args); - //// Let's assume same as starting a new thread, 64 bytes for safety/kernel. - //Core::g_app_core->SetReg(13, Core::g_app_core->GetReg(13) - 64); - - return id; -} - -int __KernelRotateThreadReadyQueue(int priority) { + __KernelResetThread(t, 0); + + // If running another thread already, set it to "ready" state Thread *cur = __GetCurrentThread(); - - // 0 is special, it means "my current priority." - if (priority == 0) { - priority = cur->nt.current_priority; + if (cur && cur->IsRunning()) { + __KernelChangeReadyState(cur, true); } - //if (priority <= 0x07 || priority > 0x77) - // return SCE_KERNEL_ERROR_ILLEGAL_PRIORITY; + + // Run new "main" thread + __SetCurrentThread(t); + t->status = THREADSTATUS_RUNNING; + __KernelLoadContext(t->context); - if (!g_thread_ready_queue.empty(priority)) { - // In other words, yield to everyone else. - if (cur->nt.current_priority == priority) { - g_thread_ready_queue.push_back(priority, g_current_thread); - cur->nt.status = (cur->nt.status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; + return handle; +} - // Yield the next thread of this priority to all other threads of same priority. - } else { - g_thread_ready_queue.rotate(priority); +/// Resumes a thread from waiting by marking it as "ready" +void __KernelResumeThreadFromWait(Handle handle) { + u32 error; + Thread *t = g_kernel_objects.Get(handle, error); + if (t) { + t->status &= ~THREADSTATUS_WAIT; + if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { + __KernelChangeReadyState(t, true); } } - HLE::EatCycles(250); - HLE::ReSchedule("rotatethreadreadyqueue"); +} - return 0; +/// Puts a thread in the wait state for the given type/reason +void __KernelWaitCurThread(WaitType wait_type, const char *reason) { + Thread *t = __GetCurrentThread(); + t->wait_type = wait_type; + __KernelChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); } +/// Reschedules to the next available thread (call after current thread is suspended) +void __KernelReschedule(const char *reason) { + Thread *next = __KernelNextThread(); + if (next > 0) { + __KernelSwitchContext(next, reason); + } +} + + void __KernelThreadingInit() { } -- cgit v1.2.3 From 7cdb70505944b2ed456d7f5376594e05f3b3357f Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 16 May 2014 23:48:15 -0400 Subject: - replaced KERNELOBJECT_MAX_NAME_LENGTH with KERNEL_MAX_NAME_LENGTH - added KERNEL_DEFAULT_STACK_SIZE definition (0x4000) --- src/core/hle/kernel/thread.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 833a1b4ba..76a73747d 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -81,7 +81,7 @@ public: WaitType wait_type; - char name[KERNELOBJECT_MAX_NAME_LENGTH+1]; + char name[KERNEL_MAX_NAME_LENGTH+1]; }; // Lists all thread ids that aren't deleted/etc. @@ -165,7 +165,8 @@ void __KernelResetThread(Thread *t, s32 lowest_priority) { } /// Creates a new thread -Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size=0x4000) { +Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, + s32 processor_id, u32 stack_top, int stack_size) { static u32 _handle_count = 1; Thread *t = new Thread; @@ -183,8 +184,8 @@ Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, t->processor_id = processor_id; t->wait_type = WAITTYPE_NONE; - strncpy(t->name, name, KERNELOBJECT_MAX_NAME_LENGTH); - t->name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0'; + strncpy(t->name, name, KERNEL_MAX_NAME_LENGTH); + t->name[KERNEL_MAX_NAME_LENGTH] = '\0'; return t; } -- cgit v1.2.3 From 14ae026386cf3f984d60401e2104165c3ca3266b Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 May 2014 00:56:00 -0400 Subject: - added enum ThreadProcessorId - reorganized some kernel thread functions - added placeholder __KernelWaitThread_Synchronization function --- src/core/hle/kernel/thread.cpp | 141 ++++++++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 51 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 76a73747d..b967b3c62 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -21,20 +21,14 @@ // Enums -enum ThreadPriority { - THREADPRIO_HIGHEST = 0, - THREADPRIO_DEFAULT = 16, - THREADPRIO_LOWEST = 31, -}; - enum ThreadStatus { - THREADSTATUS_RUNNING = 1, - THREADSTATUS_READY = 2, - THREADSTATUS_WAIT = 4, - THREADSTATUS_SUSPEND = 8, - THREADSTATUS_DORMANT = 16, - THREADSTATUS_DEAD = 32, - THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND + THREADSTATUS_RUNNING = 1, + THREADSTATUS_READY = 2, + THREADSTATUS_WAIT = 4, + THREADSTATUS_SUSPEND = 8, + THREADSTATUS_DORMANT = 16, + THREADSTATUS_DEAD = 32, + THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND }; enum WaitType { @@ -46,8 +40,6 @@ enum WaitType { WAITTYPE_VBLANK, WAITTYPE_MUTEX, WAITTYPE_SYNCH, - - NUM_WAITTYPES }; typedef s32 Handle; @@ -164,32 +156,6 @@ void __KernelResetThread(Thread *t, s32 lowest_priority) { t->wait_type = WAITTYPE_NONE; } -/// Creates a new thread -Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, - s32 processor_id, u32 stack_top, int stack_size) { - static u32 _handle_count = 1; - - Thread *t = new Thread; - - handle = (_handle_count++); - - g_thread_queue.push_back(handle); - g_thread_ready_queue.prepare(priority); - - t->status = THREADSTATUS_DORMANT; - t->entry_point = entry_point; - t->stack_top = stack_top; - t->stack_size = stack_size; - t->initial_priority = t->current_priority = priority; - t->processor_id = processor_id; - t->wait_type = WAITTYPE_NONE; - - strncpy(t->name, name, KERNEL_MAX_NAME_LENGTH); - t->name[KERNEL_MAX_NAME_LENGTH] = '\0'; - - return t; -} - /// Change a thread to "ready" state void __KernelChangeReadyState(Thread *t, bool ready) { Handle handle = t->GetHandle(); @@ -222,6 +188,79 @@ void __KernelChangeThreadState(Thread *t, ThreadStatus new_status) { } } +/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) +void __KernelCallThread(Thread *t) { + // Stop waiting + if (t->wait_type != WAITTYPE_NONE) { + t->wait_type = WAITTYPE_NONE; + } + __KernelChangeThreadState(t, THREADSTATUS_READY); +} + +/// Creates a new thread +Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, + s32 processor_id, u32 stack_top, int stack_size) { + + Thread *t = new Thread; + + handle = g_kernel_objects.Create(t); + + g_thread_queue.push_back(handle); + g_thread_ready_queue.prepare(priority); + + t->status = THREADSTATUS_DORMANT; + t->entry_point = entry_point; + t->stack_top = stack_top; + t->stack_size = stack_size; + t->initial_priority = t->current_priority = priority; + t->processor_id = processor_id; + t->wait_type = WAITTYPE_NONE; + + strncpy(t->name, name, KERNEL_MAX_NAME_LENGTH); + t->name[KERNEL_MAX_NAME_LENGTH] = '\0'; + + return t; +} + +/// Creates a new thread - wrapper for external user +Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority, s32 processor_id, + u32 stack_top, int stack_size) { + if (name == NULL) { + ERROR_LOG(KERNEL, "__KernelCreateThread(): NULL name"); + return -1; + } + if ((u32)stack_size < 0x200) { + ERROR_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid stack_size=0x%08X", name, + stack_size); + return -1; + } + if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { + s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); + WARN_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", + name, priority, new_priority); + // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm + // validity of this + priority = new_priority; + } + if (!Memory::GetPointer(entry_point)) { + ERROR_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid entry %08x", name, entry_point); + return -1; + } + Handle handle; + Thread *t = __KernelCreateThread(handle, name, entry_point, priority, processor_id, stack_top, + stack_size); + + HLE::EatCycles(32000); + + // This won't schedule to the new thread, but it may to one woken from eating cycles. + // Technically, this should not eat all at once, and reschedule in the middle, but that's hard. + HLE::ReSchedule("thread created"); + + __KernelCallThread(t); + + return handle; +} + /// Switches CPU context to that of the specified thread void __KernelSwitchContext(Thread* t, const char *reason) { Thread *cur = __GetCurrentThread(); @@ -262,22 +301,13 @@ Thread *__KernelNextThread() { return g_kernel_objects.GetFast(next); } -/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) -void __KernelCallThread(Thread *t) { - // Stop waiting - if (t->wait_type != WAITTYPE_NONE) { - t->wait_type = WAITTYPE_NONE; - } - __KernelChangeThreadState(t, THREADSTATUS_READY); -} - /// Sets up the primary application thread Handle __KernelSetupMainThread(s32 priority, int stack_size) { Handle handle; // Initialize new "main" thread Thread *t = __KernelCreateThread(handle, "main", Core::g_app_core->GetPC(), priority, - 0xFFFFFFFE, Memory::SCRATCHPAD_VADDR_END, stack_size); + THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); __KernelResetThread(t, 0); @@ -322,6 +352,15 @@ void __KernelReschedule(const char *reason) { } } +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Wait thread - on WaitSynchronization +void __KernelWaitThread_Synchronization() { + // TODO(bunnei): Just a placeholder function for now... FixMe + __KernelWaitCurThread(WAITTYPE_SYNCH, "waitSynchronization called"); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// void __KernelThreadingInit() { } -- cgit v1.2.3 From 09b8e8fb6afbbcc3dd6127ee02f7ac1611eb85aa Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 May 2014 13:47:55 -0400 Subject: changed a comment --- src/core/hle/kernel/thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index b967b3c62..d0bc9c8d8 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -354,7 +354,7 @@ void __KernelReschedule(const char *reason) { //////////////////////////////////////////////////////////////////////////////////////////////////// -/// Wait thread - on WaitSynchronization +/// Put current thread in a wait state - on WaitSynchronization void __KernelWaitThread_Synchronization() { // TODO(bunnei): Just a placeholder function for now... FixMe __KernelWaitCurThread(WAITTYPE_SYNCH, "waitSynchronization called"); -- cgit v1.2.3 From 772abad77803809d8ee857efc0d7e29c36c6b2cb Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 May 2014 18:12:29 -0400 Subject: - moved Handle/Result definitions to kernel.h - added ResetType enum --- src/core/hle/kernel/thread.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index d0bc9c8d8..634218e8b 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -42,8 +42,6 @@ enum WaitType { WAITTYPE_SYNCH, }; -typedef s32 Handle; - class Thread : public KernelObject { public: -- cgit v1.2.3 From 44336329eddd7dbe1f76144e9a1e95e5f76ed372 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 18:13:25 -0400 Subject: - created a Kernel namespace - cleaned up Kernel code a bit (moved stuff into namespace, fixed whitespace issues) - added handle types for all different CTROS handles --- src/core/hle/kernel/thread.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 634218e8b..2955d6f5b 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -42,14 +42,14 @@ enum WaitType { WAITTYPE_SYNCH, }; -class Thread : public KernelObject { +class Thread : public Kernel::Object { public: const char *GetName() { return name; } const char *GetTypeName() { return "Thread"; } - static KernelIDType GetStaticIDType() { return KERNEL_ID_TYPE_THREAD; } - KernelIDType GetIDType() const { return KERNEL_ID_TYPE_THREAD; } + static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } + Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; } inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } @@ -71,7 +71,7 @@ public: WaitType wait_type; - char name[KERNEL_MAX_NAME_LENGTH+1]; + char name[Kernel::MAX_NAME_LENGTH + 1]; }; // Lists all thread ids that aren't deleted/etc. @@ -201,7 +201,7 @@ Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, Thread *t = new Thread; - handle = g_kernel_objects.Create(t); + handle = Kernel::g_object_pool.Create(t); g_thread_queue.push_back(handle); g_thread_ready_queue.prepare(priority); @@ -214,8 +214,8 @@ Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, t->processor_id = processor_id; t->wait_type = WAITTYPE_NONE; - strncpy(t->name, name, KERNEL_MAX_NAME_LENGTH); - t->name[KERNEL_MAX_NAME_LENGTH] = '\0'; + strncpy(t->name, name, Kernel::MAX_NAME_LENGTH); + t->name[Kernel::MAX_NAME_LENGTH] = '\0'; return t; } @@ -296,7 +296,7 @@ Thread *__KernelNextThread() { if (next < 0) { return NULL; } - return g_kernel_objects.GetFast(next); + return Kernel::g_object_pool.GetFast(next); } /// Sets up the primary application thread @@ -326,7 +326,7 @@ Handle __KernelSetupMainThread(s32 priority, int stack_size) { /// Resumes a thread from waiting by marking it as "ready" void __KernelResumeThreadFromWait(Handle handle) { u32 error; - Thread *t = g_kernel_objects.Get(handle, error); + Thread *t = Kernel::g_object_pool.Get(handle, error); if (t) { t->status &= ~THREADSTATUS_WAIT; if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { -- cgit v1.2.3 From f654a03f1f23bea8fe5bf7194614ce6e947d0c5c Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 18:20:35 -0400 Subject: thread: whitespace change - fixed * and & placement --- src/core/hle/kernel/thread.cpp | 50 +++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 2955d6f5b..cfc5327a3 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -45,8 +45,8 @@ enum WaitType { class Thread : public Kernel::Object { public: - const char *GetName() { return name; } - const char *GetTypeName() { return "Thread"; } + const char* GetName() { return name; } + const char* GetTypeName() { return "Thread"; } static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; } @@ -85,11 +85,11 @@ Handle g_current_thread_handle; Thread* g_current_thread; -inline Thread *__GetCurrentThread() { +inline Thread* __GetCurrentThread() { return g_current_thread; } -inline void __SetCurrentThread(Thread *t) { +inline void __SetCurrentThread(Thread* t) { g_current_thread = t; g_current_thread_handle = t->GetHandle(); } @@ -97,7 +97,7 @@ inline void __SetCurrentThread(Thread *t) { //////////////////////////////////////////////////////////////////////////////////////////////////// /// Saves the current CPU context -void __KernelSaveContext(ThreadContext &ctx) { +void __KernelSaveContext(ThreadContext& ctx) { ctx.cpu_registers[0] = Core::g_app_core->GetReg(0); ctx.cpu_registers[1] = Core::g_app_core->GetReg(1); ctx.cpu_registers[2] = Core::g_app_core->GetReg(2); @@ -118,7 +118,7 @@ void __KernelSaveContext(ThreadContext &ctx) { } /// Loads a CPU context -void __KernelLoadContext(const ThreadContext &ctx) { +void __KernelLoadContext(const ThreadContext& ctx) { Core::g_app_core->SetReg(0, ctx.cpu_registers[0]); Core::g_app_core->SetReg(1, ctx.cpu_registers[1]); Core::g_app_core->SetReg(2, ctx.cpu_registers[2]); @@ -141,7 +141,7 @@ void __KernelLoadContext(const ThreadContext &ctx) { } /// Resets a thread -void __KernelResetThread(Thread *t, s32 lowest_priority) { +void __KernelResetThread(Thread* t, s32 lowest_priority) { memset(&t->context, 0, sizeof(ThreadContext)); t->context.pc = t->entry_point; @@ -155,7 +155,7 @@ void __KernelResetThread(Thread *t, s32 lowest_priority) { } /// Change a thread to "ready" state -void __KernelChangeReadyState(Thread *t, bool ready) { +void __KernelChangeReadyState(Thread* t, bool ready) { Handle handle = t->GetHandle(); if (t->IsReady()) { if (!ready) { @@ -172,7 +172,7 @@ void __KernelChangeReadyState(Thread *t, bool ready) { } /// Changes a threads state -void __KernelChangeThreadState(Thread *t, ThreadStatus new_status) { +void __KernelChangeThreadState(Thread* t, ThreadStatus new_status) { if (!t || t->status == new_status) { return; } @@ -187,7 +187,7 @@ void __KernelChangeThreadState(Thread *t, ThreadStatus new_status) { } /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) -void __KernelCallThread(Thread *t) { +void __KernelCallThread(Thread* t) { // Stop waiting if (t->wait_type != WAITTYPE_NONE) { t->wait_type = WAITTYPE_NONE; @@ -196,10 +196,10 @@ void __KernelCallThread(Thread *t) { } /// Creates a new thread -Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, +Thread* __KernelCreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size) { - Thread *t = new Thread; + Thread* t = new Thread; handle = Kernel::g_object_pool.Create(t); @@ -221,7 +221,7 @@ Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, } /// Creates a new thread - wrapper for external user -Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority, s32 processor_id, +Handle __KernelCreateThread(const char* name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size) { if (name == NULL) { ERROR_LOG(KERNEL, "__KernelCreateThread(): NULL name"); @@ -245,7 +245,7 @@ Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority, s32 return -1; } Handle handle; - Thread *t = __KernelCreateThread(handle, name, entry_point, priority, processor_id, stack_top, + Thread* t = __KernelCreateThread(handle, name, entry_point, priority, processor_id, stack_top, stack_size); HLE::EatCycles(32000); @@ -260,8 +260,8 @@ Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority, s32 } /// Switches CPU context to that of the specified thread -void __KernelSwitchContext(Thread* t, const char *reason) { - Thread *cur = __GetCurrentThread(); +void __KernelSwitchContext(Thread* t, const char* reason) { + Thread* cur = __GetCurrentThread(); // Save context for current thread if (cur) { @@ -284,9 +284,9 @@ void __KernelSwitchContext(Thread* t, const char *reason) { } /// Gets the next thread that is ready to be run by priority -Thread *__KernelNextThread() { +Thread* __KernelNextThread() { Handle next; - Thread *cur = __GetCurrentThread(); + Thread* cur = __GetCurrentThread(); if (cur && cur->IsRunning()) { next = g_thread_ready_queue.pop_first_better(cur->current_priority); @@ -304,13 +304,13 @@ Handle __KernelSetupMainThread(s32 priority, int stack_size) { Handle handle; // Initialize new "main" thread - Thread *t = __KernelCreateThread(handle, "main", Core::g_app_core->GetPC(), priority, + Thread* t = __KernelCreateThread(handle, "main", Core::g_app_core->GetPC(), priority, THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); __KernelResetThread(t, 0); // If running another thread already, set it to "ready" state - Thread *cur = __GetCurrentThread(); + Thread* cur = __GetCurrentThread(); if (cur && cur->IsRunning()) { __KernelChangeReadyState(cur, true); } @@ -326,7 +326,7 @@ Handle __KernelSetupMainThread(s32 priority, int stack_size) { /// Resumes a thread from waiting by marking it as "ready" void __KernelResumeThreadFromWait(Handle handle) { u32 error; - Thread *t = Kernel::g_object_pool.Get(handle, error); + Thread* t = Kernel::g_object_pool.Get(handle, error); if (t) { t->status &= ~THREADSTATUS_WAIT; if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { @@ -336,15 +336,15 @@ void __KernelResumeThreadFromWait(Handle handle) { } /// Puts a thread in the wait state for the given type/reason -void __KernelWaitCurThread(WaitType wait_type, const char *reason) { - Thread *t = __GetCurrentThread(); +void __KernelWaitCurThread(WaitType wait_type, const char* reason) { + Thread* t = __GetCurrentThread(); t->wait_type = wait_type; __KernelChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); } /// Reschedules to the next available thread (call after current thread is suspended) -void __KernelReschedule(const char *reason) { - Thread *next = __KernelNextThread(); +void __KernelReschedule(const char* reason) { + Thread* next = __KernelNextThread(); if (next > 0) { __KernelSwitchContext(next, reason); } -- cgit v1.2.3 From 143bba20453036f0a4bcc74dad10d99605a84732 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 18:28:38 -0400 Subject: renamed "syscall" module to "svc" (more accurate naming) --- src/core/hle/kernel/thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index cfc5327a3..136fff021 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -15,7 +15,7 @@ #include "core/core.h" #include "core/mem_map.h" #include "core/hle/hle.h" -#include "core/hle/syscall.h" +#include "core/hle/svc.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" -- cgit v1.2.3 From 49dc2ce8ac4fc37a008fa28e0771c8c74c576b05 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 18:50:16 -0400 Subject: ARM_Interface: added SaveContext and LoadContext functions for HLE thread switching --- src/core/hle/kernel/thread.cpp | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 136fff021..b3d306c53 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -98,46 +98,12 @@ inline void __SetCurrentThread(Thread* t) { /// Saves the current CPU context void __KernelSaveContext(ThreadContext& ctx) { - ctx.cpu_registers[0] = Core::g_app_core->GetReg(0); - ctx.cpu_registers[1] = Core::g_app_core->GetReg(1); - ctx.cpu_registers[2] = Core::g_app_core->GetReg(2); - ctx.cpu_registers[3] = Core::g_app_core->GetReg(3); - ctx.cpu_registers[4] = Core::g_app_core->GetReg(4); - ctx.cpu_registers[5] = Core::g_app_core->GetReg(5); - ctx.cpu_registers[6] = Core::g_app_core->GetReg(6); - ctx.cpu_registers[7] = Core::g_app_core->GetReg(7); - ctx.cpu_registers[8] = Core::g_app_core->GetReg(8); - ctx.cpu_registers[9] = Core::g_app_core->GetReg(9); - ctx.cpu_registers[10] = Core::g_app_core->GetReg(10); - ctx.cpu_registers[11] = Core::g_app_core->GetReg(11); - ctx.cpu_registers[12] = Core::g_app_core->GetReg(12); - ctx.sp = Core::g_app_core->GetReg(13); - ctx.lr = Core::g_app_core->GetReg(14); - ctx.pc = Core::g_app_core->GetPC(); - ctx.cpsr = Core::g_app_core->GetCPSR(); + Core::g_app_core->SaveContext(ctx); } /// Loads a CPU context void __KernelLoadContext(const ThreadContext& ctx) { - Core::g_app_core->SetReg(0, ctx.cpu_registers[0]); - Core::g_app_core->SetReg(1, ctx.cpu_registers[1]); - Core::g_app_core->SetReg(2, ctx.cpu_registers[2]); - Core::g_app_core->SetReg(3, ctx.cpu_registers[3]); - Core::g_app_core->SetReg(4, ctx.cpu_registers[4]); - Core::g_app_core->SetReg(5, ctx.cpu_registers[5]); - Core::g_app_core->SetReg(6, ctx.cpu_registers[6]); - Core::g_app_core->SetReg(7, ctx.cpu_registers[7]); - Core::g_app_core->SetReg(8, ctx.cpu_registers[8]); - Core::g_app_core->SetReg(9, ctx.cpu_registers[9]); - Core::g_app_core->SetReg(10, ctx.cpu_registers[10]); - Core::g_app_core->SetReg(11, ctx.cpu_registers[11]); - Core::g_app_core->SetReg(12, ctx.cpu_registers[12]); - Core::g_app_core->SetReg(13, ctx.sp); - Core::g_app_core->SetReg(14, ctx.lr); - //Core::g_app_core->SetReg(15, ctx.pc); - - Core::g_app_core->SetPC(ctx.pc); - Core::g_app_core->SetCPSR(ctx.cpsr); + Core::g_app_core->LoadContext(ctx); } /// Resets a thread -- cgit v1.2.3 From 75c6d2a8fa3547946227094af6c179e5ccba0e1e Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 19:37:46 -0400 Subject: thread: moved threading calls to the Kernel namespace --- src/core/hle/kernel/thread.cpp | 184 +++++++++++++++++++++-------------------- 1 file changed, 95 insertions(+), 89 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index b3d306c53..7b4f0ea47 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -19,7 +19,7 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" -// Enums +namespace Kernel { enum ThreadStatus { THREADSTATUS_RUNNING = 1, @@ -81,33 +81,32 @@ std::vector g_thread_queue; Common::ThreadQueueList g_thread_ready_queue; Handle g_current_thread_handle; - Thread* g_current_thread; +/// Gets the current thread inline Thread* __GetCurrentThread() { return g_current_thread; } +/// Sets the current thread inline void __SetCurrentThread(Thread* t) { g_current_thread = t; g_current_thread_handle = t->GetHandle(); } -//////////////////////////////////////////////////////////////////////////////////////////////////// - /// Saves the current CPU context -void __KernelSaveContext(ThreadContext& ctx) { +void __SaveContext(ThreadContext& ctx) { Core::g_app_core->SaveContext(ctx); } /// Loads a CPU context -void __KernelLoadContext(const ThreadContext& ctx) { +void __LoadContext(const ThreadContext& ctx) { Core::g_app_core->LoadContext(ctx); } /// Resets a thread -void __KernelResetThread(Thread* t, s32 lowest_priority) { +void __ResetThread(Thread* t, s32 lowest_priority) { memset(&t->context, 0, sizeof(ThreadContext)); t->context.pc = t->entry_point; @@ -121,7 +120,7 @@ void __KernelResetThread(Thread* t, s32 lowest_priority) { } /// Change a thread to "ready" state -void __KernelChangeReadyState(Thread* t, bool ready) { +void __ChangeReadyState(Thread* t, bool ready) { Handle handle = t->GetHandle(); if (t->IsReady()) { if (!ready) { @@ -138,11 +137,11 @@ void __KernelChangeReadyState(Thread* t, bool ready) { } /// Changes a threads state -void __KernelChangeThreadState(Thread* t, ThreadStatus new_status) { +void __ChangeThreadState(Thread* t, ThreadStatus new_status) { if (!t || t->status == new_status) { return; } - __KernelChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); + __ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); t->status = new_status; if (new_status == THREADSTATUS_WAIT) { @@ -153,16 +152,75 @@ void __KernelChangeThreadState(Thread* t, ThreadStatus new_status) { } /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) -void __KernelCallThread(Thread* t) { +void __CallThread(Thread* t) { // Stop waiting if (t->wait_type != WAITTYPE_NONE) { t->wait_type = WAITTYPE_NONE; } - __KernelChangeThreadState(t, THREADSTATUS_READY); + __ChangeThreadState(t, THREADSTATUS_READY); +} + +/// Switches CPU context to that of the specified thread +void __SwitchContext(Thread* t, const char* reason) { + Thread* cur = __GetCurrentThread(); + + // Save context for current thread + if (cur) { + __SaveContext(cur->context); + + if (cur->IsRunning()) { + __ChangeReadyState(cur, true); + } + } + // Load context of new thread + if (t) { + __SetCurrentThread(t); + __ChangeReadyState(t, false); + t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; + t->wait_type = WAITTYPE_NONE; + __LoadContext(t->context); + } else { + __SetCurrentThread(NULL); + } +} + +/// Gets the next thread that is ready to be run by priority +Thread* __NextThread() { + Handle next; + Thread* cur = __GetCurrentThread(); + + if (cur && cur->IsRunning()) { + next = g_thread_ready_queue.pop_first_better(cur->current_priority); + } else { + next = g_thread_ready_queue.pop_first(); + } + if (next < 0) { + return NULL; + } + return Kernel::g_object_pool.GetFast(next); +} + +/// Resumes a thread from waiting by marking it as "ready" +void __ResumeThreadFromWait(Handle handle) { + u32 error; + Thread* t = Kernel::g_object_pool.Get(handle, error); + if (t) { + t->status &= ~THREADSTATUS_WAIT; + if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { + __ChangeReadyState(t, true); + } + } +} + +/// Puts a thread in the wait state for the given type/reason +void __WaitCurThread(WaitType wait_type, const char* reason) { + Thread* t = __GetCurrentThread(); + t->wait_type = wait_type; + __ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); } /// Creates a new thread -Thread* __KernelCreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, +Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size) { Thread* t = new Thread; @@ -187,31 +245,31 @@ Thread* __KernelCreateThread(Handle& handle, const char* name, u32 entry_point, } /// Creates a new thread - wrapper for external user -Handle __KernelCreateThread(const char* name, u32 entry_point, s32 priority, s32 processor_id, +Handle CreateThread(const char* name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size) { if (name == NULL) { - ERROR_LOG(KERNEL, "__KernelCreateThread(): NULL name"); + ERROR_LOG(KERNEL, "CreateThread(): NULL name"); return -1; } if ((u32)stack_size < 0x200) { - ERROR_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid stack_size=0x%08X", name, + ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name, stack_size); return -1; } if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); - WARN_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", + WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", name, priority, new_priority); // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm // validity of this priority = new_priority; } if (!Memory::GetPointer(entry_point)) { - ERROR_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid entry %08x", name, entry_point); + ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point); return -1; } Handle handle; - Thread* t = __KernelCreateThread(handle, name, entry_point, priority, processor_id, stack_top, + Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, stack_size); HLE::EatCycles(32000); @@ -220,114 +278,62 @@ Handle __KernelCreateThread(const char* name, u32 entry_point, s32 priority, s32 // Technically, this should not eat all at once, and reschedule in the middle, but that's hard. HLE::ReSchedule("thread created"); - __KernelCallThread(t); + __CallThread(t); return handle; } -/// Switches CPU context to that of the specified thread -void __KernelSwitchContext(Thread* t, const char* reason) { - Thread* cur = __GetCurrentThread(); - - // Save context for current thread - if (cur) { - __KernelSaveContext(cur->context); - - if (cur->IsRunning()) { - __KernelChangeReadyState(cur, true); - } - } - // Load context of new thread - if (t) { - __SetCurrentThread(t); - __KernelChangeReadyState(t, false); - t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; - t->wait_type = WAITTYPE_NONE; - __KernelLoadContext(t->context); - } else { - __SetCurrentThread(NULL); - } -} - -/// Gets the next thread that is ready to be run by priority -Thread* __KernelNextThread() { - Handle next; - Thread* cur = __GetCurrentThread(); - - if (cur && cur->IsRunning()) { - next = g_thread_ready_queue.pop_first_better(cur->current_priority); - } else { - next = g_thread_ready_queue.pop_first(); - } - if (next < 0) { - return NULL; - } - return Kernel::g_object_pool.GetFast(next); +/// Gets the current thread +Handle GetCurrentThread() { + return __GetCurrentThread()->GetHandle(); } /// Sets up the primary application thread -Handle __KernelSetupMainThread(s32 priority, int stack_size) { +Handle SetupMainThread(s32 priority, int stack_size) { Handle handle; // Initialize new "main" thread - Thread* t = __KernelCreateThread(handle, "main", Core::g_app_core->GetPC(), priority, + Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); - __KernelResetThread(t, 0); + __ResetThread(t, 0); // If running another thread already, set it to "ready" state Thread* cur = __GetCurrentThread(); if (cur && cur->IsRunning()) { - __KernelChangeReadyState(cur, true); + __ChangeReadyState(cur, true); } // Run new "main" thread __SetCurrentThread(t); t->status = THREADSTATUS_RUNNING; - __KernelLoadContext(t->context); + __LoadContext(t->context); return handle; } -/// Resumes a thread from waiting by marking it as "ready" -void __KernelResumeThreadFromWait(Handle handle) { - u32 error; - Thread* t = Kernel::g_object_pool.Get(handle, error); - if (t) { - t->status &= ~THREADSTATUS_WAIT; - if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { - __KernelChangeReadyState(t, true); - } - } -} - -/// Puts a thread in the wait state for the given type/reason -void __KernelWaitCurThread(WaitType wait_type, const char* reason) { - Thread* t = __GetCurrentThread(); - t->wait_type = wait_type; - __KernelChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); -} - /// Reschedules to the next available thread (call after current thread is suspended) -void __KernelReschedule(const char* reason) { - Thread* next = __KernelNextThread(); +void Reschedule(const char* reason) { + Thread* next = __NextThread(); if (next > 0) { - __KernelSwitchContext(next, reason); + __SwitchContext(next, reason); } } //////////////////////////////////////////////////////////////////////////////////////////////////// /// Put current thread in a wait state - on WaitSynchronization -void __KernelWaitThread_Synchronization() { +void WaitThread_Synchronization() { // TODO(bunnei): Just a placeholder function for now... FixMe - __KernelWaitCurThread(WAITTYPE_SYNCH, "waitSynchronization called"); + __WaitCurThread(WAITTYPE_SYNCH, "waitSynchronization called"); } //////////////////////////////////////////////////////////////////////////////////////////////////// -void __KernelThreadingInit() { +void ThreadingInit() { } -void __KernelThreadingShutdown() { +void ThreadingShutdown() { } + +} // namespace -- cgit v1.2.3 From bed4e920fa17c6ab1e1cfde1f3ee81d0ca4aaff9 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 21:00:10 -0400 Subject: thread: exposed ResumeThreadFromWait function for use in other kernel modules --- src/core/hle/kernel/thread.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 7b4f0ea47..af9188faa 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -200,8 +200,15 @@ Thread* __NextThread() { return Kernel::g_object_pool.GetFast(next); } +/// Puts a thread in the wait state for the given type/reason +void __WaitCurThread(WaitType wait_type, const char* reason) { + Thread* t = __GetCurrentThread(); + t->wait_type = wait_type; + __ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); +} + /// Resumes a thread from waiting by marking it as "ready" -void __ResumeThreadFromWait(Handle handle) { +void ResumeThreadFromWait(Handle handle) { u32 error; Thread* t = Kernel::g_object_pool.Get(handle, error); if (t) { @@ -212,13 +219,6 @@ void __ResumeThreadFromWait(Handle handle) { } } -/// Puts a thread in the wait state for the given type/reason -void __WaitCurThread(WaitType wait_type, const char* reason) { - Thread* t = __GetCurrentThread(); - t->wait_type = wait_type; - __ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); -} - /// Creates a new thread Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size) { -- cgit v1.2.3 From 203541da119cad61096fda20b3ff8a8cb5906fd6 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 21:02:35 -0400 Subject: thread: added correct lowest thread priority, added a thread priority check, and added some comments --- src/core/hle/kernel/thread.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index af9188faa..294e03ca6 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -223,6 +223,9 @@ void ResumeThreadFromWait(Handle handle) { Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size) { + _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), + "CreateThread priority=%d, outside of allowable range!", priority) + Thread* t = new Thread; handle = Kernel::g_object_pool.Create(t); -- cgit v1.2.3 From 6a78be5930fd5eabbea740a5cf17efa5a7f1bc98 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 21 May 2014 21:42:18 -0400 Subject: thread: fixed bug where result of __NextThread was not being properly checked when NULL --- src/core/hle/kernel/thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 294e03ca6..ef705e327 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -194,7 +194,7 @@ Thread* __NextThread() { } else { next = g_thread_ready_queue.pop_first(); } - if (next < 0) { + if (next == 0) { return NULL; } return Kernel::g_object_pool.GetFast(next); -- cgit v1.2.3 From 14bd37c5dc67c7777d4bea8d996bf2dfd8c7bdcc Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 18:50:36 -0400 Subject: thread: moved ThreadStatus/WaitType to header, added support for arg on CreateThread, added correct CPSR reset --- src/core/hle/kernel/thread.cpp | 49 +++++++++++++----------------------------- 1 file changed, 15 insertions(+), 34 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index ef705e327..934ca87c4 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -21,27 +21,6 @@ namespace Kernel { -enum ThreadStatus { - THREADSTATUS_RUNNING = 1, - THREADSTATUS_READY = 2, - THREADSTATUS_WAIT = 4, - THREADSTATUS_SUSPEND = 8, - THREADSTATUS_DORMANT = 16, - THREADSTATUS_DEAD = 32, - THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND -}; - -enum WaitType { - WAITTYPE_NONE, - WAITTYPE_SLEEP, - WAITTYPE_SEMA, - WAITTYPE_EVENTFLAG, - WAITTYPE_THREADEND, - WAITTYPE_VBLANK, - WAITTYPE_MUTEX, - WAITTYPE_SYNCH, -}; - class Thread : public Kernel::Object { public: @@ -101,16 +80,18 @@ void __SaveContext(ThreadContext& ctx) { } /// Loads a CPU context -void __LoadContext(const ThreadContext& ctx) { +void __LoadContext(ThreadContext& ctx) { Core::g_app_core->LoadContext(ctx); } /// Resets a thread -void __ResetThread(Thread* t, s32 lowest_priority) { +void __ResetThread(Thread* t, u32 arg, s32 lowest_priority) { memset(&t->context, 0, sizeof(ThreadContext)); + t->context.cpu_registers[0] = arg; t->context.pc = t->entry_point; t->context.sp = t->stack_top; + t->context.cpsr = 0x1F; // Usermode if (t->current_priority < lowest_priority) { t->current_priority = t->initial_priority; @@ -201,7 +182,7 @@ Thread* __NextThread() { } /// Puts a thread in the wait state for the given type/reason -void __WaitCurThread(WaitType wait_type, const char* reason) { +void WaitCurThread(WaitType wait_type, const char* reason) { Thread* t = __GetCurrentThread(); t->wait_type = wait_type; __ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); @@ -248,7 +229,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio } /// Creates a new thread - wrapper for external user -Handle CreateThread(const char* name, u32 entry_point, s32 priority, s32 processor_id, +Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, u32 stack_top, int stack_size) { if (name == NULL) { ERROR_LOG(KERNEL, "CreateThread(): NULL name"); @@ -275,6 +256,8 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, s32 process Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, stack_size); + __ResetThread(t, arg, 0); + HLE::EatCycles(32000); // This won't schedule to the new thread, but it may to one woken from eating cycles. @@ -299,7 +282,7 @@ Handle SetupMainThread(s32 priority, int stack_size) { Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); - __ResetThread(t, 0); + __ResetThread(t, 0, 0); // If running another thread already, set it to "ready" state Thread* cur = __GetCurrentThread(); @@ -317,18 +300,16 @@ Handle SetupMainThread(s32 priority, int stack_size) { /// Reschedules to the next available thread (call after current thread is suspended) void Reschedule(const char* reason) { + Thread* prev = __GetCurrentThread(); Thread* next = __NextThread(); if (next > 0) { __SwitchContext(next, reason); - } -} -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// Put current thread in a wait state - on WaitSynchronization -void WaitThread_Synchronization() { - // TODO(bunnei): Just a placeholder function for now... FixMe - __WaitCurThread(WAITTYPE_SYNCH, "waitSynchronization called"); + // Hack - automatically change previous thread (which would have been in "wait" state) to + // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to + // actually wait for whatever event it is supposed to be waiting on. + __ChangeReadyState(prev, true); + } } //////////////////////////////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From d26f3d4c1ff27f740fe7185e1bca7dcfc5851919 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 19:06:12 -0400 Subject: kernel: refactored function naming to remove "__" prefix --- src/core/hle/kernel/thread.cpp | 78 +++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 39 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 934ca87c4..5f1d5c400 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -64,28 +64,33 @@ Thread* g_current_thread; /// Gets the current thread -inline Thread* __GetCurrentThread() { +inline Thread* GetCurrentThread() { return g_current_thread; } +/// Gets the current thread handle +Handle GetCurrentThreadHandle() { + return GetCurrentThread()->GetHandle(); +} + /// Sets the current thread -inline void __SetCurrentThread(Thread* t) { +inline void SetCurrentThread(Thread* t) { g_current_thread = t; g_current_thread_handle = t->GetHandle(); } /// Saves the current CPU context -void __SaveContext(ThreadContext& ctx) { +void SaveContext(ThreadContext& ctx) { Core::g_app_core->SaveContext(ctx); } /// Loads a CPU context -void __LoadContext(ThreadContext& ctx) { +void LoadContext(ThreadContext& ctx) { Core::g_app_core->LoadContext(ctx); } /// Resets a thread -void __ResetThread(Thread* t, u32 arg, s32 lowest_priority) { +void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { memset(&t->context, 0, sizeof(ThreadContext)); t->context.cpu_registers[0] = arg; @@ -101,7 +106,7 @@ void __ResetThread(Thread* t, u32 arg, s32 lowest_priority) { } /// Change a thread to "ready" state -void __ChangeReadyState(Thread* t, bool ready) { +void ChangeReadyState(Thread* t, bool ready) { Handle handle = t->GetHandle(); if (t->IsReady()) { if (!ready) { @@ -118,11 +123,11 @@ void __ChangeReadyState(Thread* t, bool ready) { } /// Changes a threads state -void __ChangeThreadState(Thread* t, ThreadStatus new_status) { +void ChangeThreadState(Thread* t, ThreadStatus new_status) { if (!t || t->status == new_status) { return; } - __ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); + ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); t->status = new_status; if (new_status == THREADSTATUS_WAIT) { @@ -133,42 +138,42 @@ void __ChangeThreadState(Thread* t, ThreadStatus new_status) { } /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) -void __CallThread(Thread* t) { +void CallThread(Thread* t) { // Stop waiting if (t->wait_type != WAITTYPE_NONE) { t->wait_type = WAITTYPE_NONE; } - __ChangeThreadState(t, THREADSTATUS_READY); + ChangeThreadState(t, THREADSTATUS_READY); } /// Switches CPU context to that of the specified thread -void __SwitchContext(Thread* t, const char* reason) { - Thread* cur = __GetCurrentThread(); +void SwitchContext(Thread* t, const char* reason) { + Thread* cur = GetCurrentThread(); // Save context for current thread if (cur) { - __SaveContext(cur->context); + SaveContext(cur->context); if (cur->IsRunning()) { - __ChangeReadyState(cur, true); + ChangeReadyState(cur, true); } } // Load context of new thread if (t) { - __SetCurrentThread(t); - __ChangeReadyState(t, false); + SetCurrentThread(t); + ChangeReadyState(t, false); t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; t->wait_type = WAITTYPE_NONE; - __LoadContext(t->context); + LoadContext(t->context); } else { - __SetCurrentThread(NULL); + SetCurrentThread(NULL); } } /// Gets the next thread that is ready to be run by priority -Thread* __NextThread() { +Thread* NextThread() { Handle next; - Thread* cur = __GetCurrentThread(); + Thread* cur = GetCurrentThread(); if (cur && cur->IsRunning()) { next = g_thread_ready_queue.pop_first_better(cur->current_priority); @@ -183,9 +188,9 @@ Thread* __NextThread() { /// Puts a thread in the wait state for the given type/reason void WaitCurThread(WaitType wait_type, const char* reason) { - Thread* t = __GetCurrentThread(); + Thread* t = GetCurrentThread(); t->wait_type = wait_type; - __ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); + ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); } /// Resumes a thread from waiting by marking it as "ready" @@ -195,7 +200,7 @@ void ResumeThreadFromWait(Handle handle) { if (t) { t->status &= ~THREADSTATUS_WAIT; if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { - __ChangeReadyState(t, true); + ChangeReadyState(t, true); } } } @@ -256,7 +261,7 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, stack_size); - __ResetThread(t, arg, 0); + ResetThread(t, arg, 0); HLE::EatCycles(32000); @@ -264,16 +269,11 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 // Technically, this should not eat all at once, and reschedule in the middle, but that's hard. HLE::ReSchedule("thread created"); - __CallThread(t); + CallThread(t); return handle; } -/// Gets the current thread -Handle GetCurrentThread() { - return __GetCurrentThread()->GetHandle(); -} - /// Sets up the primary application thread Handle SetupMainThread(s32 priority, int stack_size) { Handle handle; @@ -282,33 +282,33 @@ Handle SetupMainThread(s32 priority, int stack_size) { Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); - __ResetThread(t, 0, 0); + ResetThread(t, 0, 0); // If running another thread already, set it to "ready" state - Thread* cur = __GetCurrentThread(); + Thread* cur = GetCurrentThread(); if (cur && cur->IsRunning()) { - __ChangeReadyState(cur, true); + ChangeReadyState(cur, true); } // Run new "main" thread - __SetCurrentThread(t); + SetCurrentThread(t); t->status = THREADSTATUS_RUNNING; - __LoadContext(t->context); + LoadContext(t->context); return handle; } /// Reschedules to the next available thread (call after current thread is suspended) void Reschedule(const char* reason) { - Thread* prev = __GetCurrentThread(); - Thread* next = __NextThread(); + Thread* prev = GetCurrentThread(); + Thread* next = NextThread(); if (next > 0) { - __SwitchContext(next, reason); + SwitchContext(next, reason); // Hack - automatically change previous thread (which would have been in "wait" state) to // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to // actually wait for whatever event it is supposed to be waiting on. - __ChangeReadyState(prev, true); + ChangeReadyState(prev, true); } } -- cgit v1.2.3 From 7c0b0060764e75738bc9d4417d0bfd510e54ae4e Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 19:32:45 -0400 Subject: thread: removed unused SwitchContext/Reschedule reason field, added missing arg parameter to SVC CreateThread --- src/core/hle/kernel/thread.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 5f1d5c400..189f7d5f5 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -147,7 +147,7 @@ void CallThread(Thread* t) { } /// Switches CPU context to that of the specified thread -void SwitchContext(Thread* t, const char* reason) { +void SwitchContext(Thread* t) { Thread* cur = GetCurrentThread(); // Save context for current thread @@ -299,11 +299,11 @@ Handle SetupMainThread(s32 priority, int stack_size) { } /// Reschedules to the next available thread (call after current thread is suspended) -void Reschedule(const char* reason) { +void Reschedule() { Thread* prev = GetCurrentThread(); Thread* next = NextThread(); if (next > 0) { - SwitchContext(next, reason); + SwitchContext(next); // Hack - automatically change previous thread (which would have been in "wait" state) to // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to -- cgit v1.2.3 From b99ac2c3d67e2bcaa2c6eac220f1e93f4576c4fe Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 19:36:56 -0400 Subject: thread: renamed "WaitCurThread" to "WaitCurrentThread", removed unused "reason" argument --- src/core/hle/kernel/thread.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/core/hle/kernel/thread.cpp') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 189f7d5f5..bf4c8353c 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -186,8 +186,8 @@ Thread* NextThread() { return Kernel::g_object_pool.GetFast(next); } -/// Puts a thread in the wait state for the given type/reason -void WaitCurThread(WaitType wait_type, const char* reason) { +/// Puts the current thread in the wait state for the given type +void WaitCurrentThread(WaitType wait_type) { Thread* t = GetCurrentThread(); t->wait_type = wait_type; ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); -- cgit v1.2.3