mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	completely gutted/refactored threading code to be simpler
This commit is contained in:
		
							parent
							
								
									a7cc430aa4
								
							
						
					
					
						commit
						940330c6e1
					
				
					 2 changed files with 255 additions and 683 deletions
				
			
		|  | @ -10,6 +10,7 @@ | |||
| #include <string> | ||||
| 
 | ||||
| #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; | ||||
| // Enums
 | ||||
| 
 | ||||
|     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]; | ||||
| enum ThreadPriority { | ||||
|     THREADPRIO_HIGHEST = 0, | ||||
|     THREADPRIO_DEFAULT = 16, | ||||
|     THREADPRIO_LOWEST = 31, | ||||
| }; | ||||
| 
 | ||||
| // 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)
 | ||||
| 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 | ||||
| }; | ||||
| 
 | ||||
|     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 *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; }
 | ||||
|     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; } | ||||
| 
 | ||||
|     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<Callback>(*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<UID> callbacks; | ||||
|     u32 status; | ||||
|     u32 entry_point; | ||||
|     u32 stack_top; | ||||
|     u32 stack_size; | ||||
| 
 | ||||
|     std::list<u32> 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<StackInfo> pushed_stacks; | ||||
|     s32 processor_id; | ||||
| 
 | ||||
|     StackInfo current_stack; | ||||
|     WaitType wait_type; | ||||
| 
 | ||||
|     // For thread end.
 | ||||
|     std::vector<UID> waiting_threads; | ||||
|     // Key is the callback id it was for, or if no callback, the thread id.
 | ||||
|     std::map<UID, u64> 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<UID> g_thread_queue; | ||||
| std::vector<Handle> g_thread_queue; | ||||
| 
 | ||||
| // Lists only ready thread ids
 | ||||
| ThreadQueueList g_thread_ready_queue; | ||||
| // Lists only ready thread ids.
 | ||||
| Common::ThreadQueueList<Handle> 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; | ||||
| 
 | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
| 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(); | ||||
|     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) { | ||||
|         t->nt.current_priority = t->nt.initial_priority; | ||||
|     } | ||||
| 
 | ||||
|     memset(&t->waitInfo, 0, sizeof(t->waitInfo)); | ||||
| } | ||||
| 
 | ||||
| /// Returns the current executing thread
 | ||||
| inline Thread *__GetCurrentThread() { | ||||
|     return g_current_thread_ptr; | ||||
|     return g_current_thread; | ||||
| } | ||||
| 
 | ||||
| /// 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; | ||||
| inline void __SetCurrentThread(Thread *t) { | ||||
|     g_current_thread = t; | ||||
|     g_current_thread_handle = t->GetHandle(); | ||||
| } | ||||
| 
 | ||||
| // 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>(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 best_thread; | ||||
| 
 | ||||
|     // If the current thread is running, it's a valid candidate.
 | ||||
|     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); | ||||
|         } | ||||
|     } 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<Thread>(best_thread); | ||||
|     } 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(); | ||||
| 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(); | ||||
| } | ||||
| 
 | ||||
| /// 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 __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); | ||||
| } | ||||
| 
 | ||||
| /// 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)"; | ||||
|     Thread *cur = __GetCurrentThread(); | ||||
| /// Resets a thread
 | ||||
| void __KernelResetThread(Thread *t, s32 lowest_priority) { | ||||
|     memset(&t->context, 0, sizeof(ThreadContext)); | ||||
| 
 | ||||
|     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 (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); | ||||
|     t->context.pc = t->entry_point; | ||||
|     t->context.sp = t->stack_top; | ||||
|      | ||||
|     if (t->current_priority < lowest_priority) { | ||||
|         t->current_priority = t->initial_priority; | ||||
|     } | ||||
|          | ||||
|     t->wait_type = WAITTYPE_NONE; | ||||
| } | ||||
| 
 | ||||
| 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>(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); | ||||
| /// 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; | ||||
| } | ||||
| 
 | ||||
| /// 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); | ||||
|         } | ||||
|         __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; | ||||
| 
 | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
| 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 if (ready) { | ||||
|         if (t->IsRunning()) { | ||||
|             g_thread_ready_queue.push_front(t->current_priority, handle); | ||||
|         } else { | ||||
|             g_thread_ready_queue.rotate(priority); | ||||
|             g_thread_ready_queue.push_back(t->current_priority, handle); | ||||
|         } | ||||
|         t->status = THREADSTATUS_READY; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// 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"); | ||||
|         } | ||||
|     } | ||||
|     HLE::EatCycles(250); | ||||
|     HLE::ReSchedule("rotatethreadreadyqueue"); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /// 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 g_kernel_objects.GetFast<Thread>(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); | ||||
|      | ||||
|     __KernelResetThread(t, 0); | ||||
|      | ||||
|     // If running another thread already, set it to "ready" state
 | ||||
|     Thread *cur = __GetCurrentThread(); | ||||
|     if (cur && cur->IsRunning()) { | ||||
|         __KernelChangeReadyState(cur, true); | ||||
|     } | ||||
|      | ||||
|     // Run new "main" thread
 | ||||
|     __SetCurrentThread(t); | ||||
|     t->status = THREADSTATUS_RUNNING; | ||||
|     __KernelLoadContext(t->context); | ||||
| 
 | ||||
|     return handle; | ||||
| } | ||||
| 
 | ||||
| /// Resumes a thread from waiting by marking it as "ready"
 | ||||
| void __KernelResumeThreadFromWait(Handle handle) { | ||||
|     u32 error; | ||||
|     Thread *t = g_kernel_objects.Get<Thread>(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(); | ||||
|     if (next > 0) { | ||||
|         __KernelSwitchContext(next, reason); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void __KernelThreadingInit() { | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,50 +7,10 @@ | |||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| 
 | ||||
| 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 | ||||
| }; | ||||
| 
 | ||||
| struct ThreadContext { | ||||
|     void reset(); | ||||
| 
 | ||||
|     u32 reg[16]; | ||||
|     u32 cpsr; | ||||
|     u32 pc; | ||||
| }; | ||||
| 
 | ||||
| class 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=0x4000); | ||||
| 
 | ||||
| UID __KernelCreateThread(UID module_id, const char* name, u32 priority, u32 entry_point, u32 arg,  | ||||
|     u32 stack_top, u32 processor_id, int stack_size=0x4000); | ||||
| 
 | ||||
| void __KernelResetThread(Thread *t, int lowest_priority); | ||||
| void __KernelChangeReadyState(Thread *thread, UID thread_id, bool ready); | ||||
| void __KernelChangeReadyState(UID thread_id, bool ready); | ||||
| Thread* __KernelNextThread(); | ||||
| void __KernelSaveContext(ThreadContext *ctx); | ||||
| void __KernelLoadContext(ThreadContext *ctx); | ||||
| void __KernelSwitchContext(Thread *target, const char *reason); | ||||
| bool __KernelSwitchToThread(UID thread_id, const char *reason); | ||||
| UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size=0x4000); | ||||
| int __KernelRotateThreadReadyQueue(int priority=0); | ||||
| /// Sets up the primary application thread
 | ||||
| Handle __KernelSetupMainThread(s32 priority, int stack_size=0x4000); | ||||
| 
 | ||||
| void __KernelThreadingInit(); | ||||
| void __KernelThreadingShutdown(); | ||||
| 
 | ||||
| //const char *__KernelGetThreadName(SceUID threadID);
 | ||||
| //
 | ||||
| //void __KernelSaveContext(ThreadContext *ctx);
 | ||||
| //void __KernelLoadContext(ThreadContext *ctx);
 | ||||
| 
 | ||||
| //void __KernelSwitchContext(Thread *target, const char *reason);
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue