From 9bf8462b96d34b30f62b8aca745dd558e7f0a450 Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Mon, 22 Dec 2014 11:07:22 -0200 Subject: Thread: Reduce use of Handles and move some funcs to inside the class. --- src/core/core.cpp | 2 +- src/core/hle/kernel/address_arbiter.cpp | 12 +- src/core/hle/kernel/event.cpp | 8 +- src/core/hle/kernel/kernel.cpp | 2 +- src/core/hle/kernel/kernel.h | 4 +- src/core/hle/kernel/mutex.cpp | 19 +- src/core/hle/kernel/semaphore.cpp | 8 +- src/core/hle/kernel/thread.cpp | 340 ++++++++++++-------------------- src/core/hle/kernel/thread.h | 68 +++---- src/core/hle/kernel/timer.cpp | 10 +- src/core/hle/svc.cpp | 51 +++-- 11 files changed, 222 insertions(+), 302 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index cf25f87c..e9e5c35c 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -25,7 +25,7 @@ ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core void RunLoop(int tight_loop) { // If the current thread is an idle thread, then don't execute instructions, // instead advance to the next event and try to yield to the next thread - if (Kernel::IsIdleThread(Kernel::GetCurrentThreadHandle())) { + if (Kernel::GetCurrentThread()->IsIdle()) { LOG_TRACE(Core_ARM11, "Idling"); CoreTiming::Idle(); CoreTiming::Advance(); diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 736bbc36..28adc550 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -30,24 +30,28 @@ public: /// Arbitrate an address ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) { + Object* object = Kernel::g_handle_table.GetGeneric(handle); + if (object == nullptr) + return InvalidHandle(ErrorModule::Kernel); + switch (type) { // Signal thread(s) waiting for arbitrate address... case ArbitrationType::Signal: // Negative value means resume all threads if (value < 0) { - ArbitrateAllThreads(handle, address); + ArbitrateAllThreads(object, address); } else { // Resume first N threads for(int i = 0; i < value; i++) - ArbitrateHighestPriorityThread(handle, address); + ArbitrateHighestPriorityThread(object, address); } break; // Wait current thread (acquire the arbiter)... case ArbitrationType::WaitIfLessThan: if ((s32)Memory::Read32(address) <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address); + Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); HLE::Reschedule(__func__); } break; @@ -57,7 +61,7 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 s32 memory_value = Memory::Read32(address) - 1; Memory::Write32(address, memory_value); if (memory_value <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address); + Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); HLE::Reschedule(__func__); } break; diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index e43c3ee4..697e0868 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -33,11 +33,11 @@ public: ResultVal WaitSynchronization() override { bool wait = locked; if (locked) { - Handle thread = GetCurrentThreadHandle(); + Handle thread = GetCurrentThread()->GetHandle(); if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { waiting_threads.push_back(thread); } - Kernel::WaitCurrentThread(WAITTYPE_EVENT, GetHandle()); + Kernel::WaitCurrentThread(WAITTYPE_EVENT, this); } if (reset_type != RESETTYPE_STICKY && !permanent_locked) { locked = true; @@ -88,7 +88,9 @@ ResultCode SignalEvent(const Handle handle) { // Resume threads waiting for event to signal bool event_caught = false; for (size_t i = 0; i < evt->waiting_threads.size(); ++i) { - ResumeThreadFromWait( evt->waiting_threads[i]); + Thread* thread = Kernel::g_handle_table.Get(evt->waiting_threads[i]); + if (thread != nullptr) + thread->ResumeFromWait(); // If any thread is signalled awake by this event, assume the event was "caught" and reset // the event. This will result in the next thread waiting on the event to block. Otherwise, diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 5dfc41ba..a1bc6c5d 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -14,7 +14,7 @@ namespace Kernel { -Handle g_main_thread = 0; +Thread* g_main_thread = nullptr; HandleTable g_handle_table; u64 g_program_id = 0; diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 3e381d77..31d80c7a 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -16,6 +16,8 @@ const Handle INVALID_HANDLE = 0; namespace Kernel { +class Thread; + // TODO: Verify code const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel, ErrorSummary::OutOfResource, ErrorLevel::Temporary); @@ -190,7 +192,7 @@ private: }; extern HandleTable g_handle_table; -extern Handle g_main_thread; +extern Thread* g_main_thread; /// The ID code of the currently running game /// TODO(Subv): This variable should not be here, diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 3dfeffc9..7d008f6c 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -40,14 +40,21 @@ static MutexMap g_mutex_held_locks; * @param mutex Mutex that is to be acquired * @param thread Thread that will acquired */ -void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) { +void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandle()) { g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); mutex->lock_thread = thread; } -bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { - MutexAcquireLock(mutex, thread); - Kernel::ResumeThreadFromWait(thread); +bool ReleaseMutexForThread(Mutex* mutex, Handle thread_handle) { + MutexAcquireLock(mutex, thread_handle); + + Thread* thread = Kernel::g_handle_table.Get(thread_handle); + if (thread == nullptr) { + LOG_ERROR(Kernel, "Called with invalid handle: %08X", thread_handle); + return false; + } + + thread->ResumeFromWait(); return true; } @@ -168,8 +175,8 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { ResultVal Mutex::WaitSynchronization() { bool wait = locked; if (locked) { - waiting_threads.push_back(GetCurrentThreadHandle()); - Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); + waiting_threads.push_back(GetCurrentThread()->GetHandle()); + Kernel::WaitCurrentThread(WAITTYPE_MUTEX, this); } else { // Lock the mutex when the first thread accesses it locked = true; diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 6bc8066a..d7eeaa3d 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -37,8 +37,8 @@ public: bool wait = !IsAvailable(); if (wait) { - Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle()); - waiting_threads.push(GetCurrentThreadHandle()); + Kernel::WaitCurrentThread(WAITTYPE_SEMA, this); + waiting_threads.push(GetCurrentThread()->GetHandle()); } else { --available_count; } @@ -84,7 +84,9 @@ ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { // Notify some of the threads that the semaphore has been released // stop once the semaphore is full again or there are no more waiting threads while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) { - Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front()); + Thread* thread = Kernel::g_handle_table.Get(semaphore->waiting_threads.front()); + if (thread != nullptr) + thread->ResumeFromWait(); semaphore->waiting_threads.pop(); --semaphore->available_count; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index ef63c5f4..03244f01 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -25,23 +25,22 @@ namespace Kernel { ResultVal Thread::WaitSynchronization() { const bool wait = status != THREADSTATUS_DORMANT; if (wait) { - Handle thread = GetCurrentThreadHandle(); + Thread* thread = GetCurrentThread(); if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { waiting_threads.push_back(thread); } - WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle()); + WaitCurrentThread(WAITTYPE_THREADEND, this); } return MakeResult(wait); } // Lists all thread ids that aren't deleted/etc. -static std::vector thread_queue; +static std::vector thread_queue; // TODO(yuriks): Owned // Lists only ready thread ids. -static Common::ThreadQueueList thread_ready_queue; +static Common::ThreadQueueList thread_ready_queue; -static Handle current_thread_handle; static Thread* current_thread; static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup @@ -51,29 +50,8 @@ Thread* GetCurrentThread() { return current_thread; } -/// Gets the current thread handle -Handle GetCurrentThreadHandle() { - return GetCurrentThread()->GetHandle(); -} - -/// Sets the current thread -inline void SetCurrentThread(Thread* t) { - current_thread = t; - current_thread_handle = t->GetHandle(); -} - -/// Saves the current CPU context -void SaveContext(Core::ThreadContext& ctx) { - Core::g_app_core->SaveContext(ctx); -} - -/// Loads a CPU context -void LoadContext(Core::ThreadContext& ctx) { - Core::g_app_core->LoadContext(ctx); -} - /// Resets a thread -void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { +static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { memset(&t->context, 0, sizeof(Core::ThreadContext)); t->context.cpu_registers[0] = arg; @@ -90,22 +68,21 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { t->current_priority = t->initial_priority; } t->wait_type = WAITTYPE_NONE; - t->wait_handle = 0; + t->wait_object = nullptr; t->wait_address = 0; } /// Change a thread to "ready" state -void ChangeReadyState(Thread* t, bool ready) { - Handle handle = t->GetHandle(); +static void ChangeReadyState(Thread* t, bool ready) { if (t->IsReady()) { if (!ready) { - thread_ready_queue.remove(t->current_priority, handle); + thread_ready_queue.remove(t->current_priority, t); } } else if (ready) { if (t->IsRunning()) { - thread_ready_queue.push_front(t->current_priority, handle); + thread_ready_queue.push_front(t->current_priority, t); } else { - thread_ready_queue.push_back(t->current_priority, handle); + thread_ready_queue.push_back(t->current_priority, t); } t->status = THREADSTATUS_READY; } @@ -117,43 +94,36 @@ static bool CheckWaitType(const Thread* thread, WaitType type) { } /// Check if a thread is blocking on a specified wait type with a specified handle -static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) { - return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle); +static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) { + return CheckWaitType(thread, type) && wait_object == thread->wait_object; } /// Check if a thread is blocking on a specified wait type with a specified handle and address -static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { - return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address); +static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object, VAddr wait_address) { + return CheckWaitType(thread, type, wait_object) && (wait_address == thread->wait_address); } /// Stops the current thread -ResultCode StopThread(Handle handle, const char* reason) { - Thread* thread = g_handle_table.Get(handle); - if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); - +void Thread::Stop(const char* reason) { // Release all the mutexes that this thread holds - ReleaseThreadMutexes(handle); - - ChangeReadyState(thread, false); - thread->status = THREADSTATUS_DORMANT; - for (Handle waiting_handle : thread->waiting_threads) { - Thread* waiting_thread = g_handle_table.Get(waiting_handle); + ReleaseThreadMutexes(GetHandle()); - if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle)) - ResumeThreadFromWait(waiting_handle); + ChangeReadyState(this, false); + status = THREADSTATUS_DORMANT; + for (Thread* waiting_thread : waiting_threads) { + if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, this)) + waiting_thread->ResumeFromWait(); } - thread->waiting_threads.clear(); + waiting_threads.clear(); // Stopped threads are never waiting. - thread->wait_type = WAITTYPE_NONE; - thread->wait_handle = 0; - thread->wait_address = 0; - - return RESULT_SUCCESS; + wait_type = WAITTYPE_NONE; + wait_object = nullptr; + wait_address = 0; } /// Changes a threads state -void ChangeThreadState(Thread* t, ThreadStatus new_status) { +static void ChangeThreadState(Thread* t, ThreadStatus new_status) { if (!t || t->status == new_status) { return; } @@ -168,14 +138,12 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) { } /// Arbitrate the highest priority thread that is waiting -Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { - Handle highest_priority_thread = 0; +Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { + Thread* highest_priority_thread = nullptr; s32 priority = THREADPRIO_LOWEST; // Iterate through threads, find highest priority thread that is waiting to be arbitrated... - for (Handle handle : thread_queue) { - Thread* thread = g_handle_table.Get(handle); - + for (Thread* thread : thread_queue) { if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) continue; @@ -183,31 +151,31 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. if(thread->current_priority <= priority) { - highest_priority_thread = handle; + highest_priority_thread = thread; priority = thread->current_priority; } } + // If a thread was arbitrated, resume it - if (0 != highest_priority_thread) - ResumeThreadFromWait(highest_priority_thread); + if (nullptr != highest_priority_thread) { + highest_priority_thread->ResumeFromWait(); + } return highest_priority_thread; } /// Arbitrate all threads currently waiting -void ArbitrateAllThreads(u32 arbiter, u32 address) { +void ArbitrateAllThreads(Object* arbiter, u32 address) { // Iterate through threads, find highest priority thread that is waiting to be arbitrated... - for (Handle handle : thread_queue) { - Thread* thread = g_handle_table.Get(handle); - + for (Thread* thread : thread_queue) { if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) - ResumeThreadFromWait(handle); + thread->ResumeFromWait(); } } /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) -void CallThread(Thread* t) { +static void CallThread(Thread* t) { // Stop waiting if (t->wait_type != WAITTYPE_NONE) { t->wait_type = WAITTYPE_NONE; @@ -216,12 +184,12 @@ void CallThread(Thread* t) { } /// Switches CPU context to that of the specified thread -void SwitchContext(Thread* t) { +static void SwitchContext(Thread* t) { Thread* cur = GetCurrentThread(); // Save context for current thread if (cur) { - SaveContext(cur->context); + Core::g_app_core->SaveContext(cur->context); if (cur->IsRunning()) { ChangeReadyState(cur, true); @@ -229,19 +197,19 @@ void SwitchContext(Thread* t) { } // Load context of new thread if (t) { - SetCurrentThread(t); + current_thread = t; ChangeReadyState(t, false); t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; t->wait_type = WAITTYPE_NONE; - LoadContext(t->context); + Core::g_app_core->LoadContext(t->context); } else { - SetCurrentThread(nullptr); + current_thread = nullptr; } } /// Gets the next thread that is ready to be run by priority -Thread* NextThread() { - Handle next; +static Thread* NextThread() { + Thread* next; Thread* cur = GetCurrentThread(); if (cur && cur->IsRunning()) { @@ -252,18 +220,18 @@ Thread* NextThread() { if (next == 0) { return nullptr; } - return Kernel::g_handle_table.Get(next); + return next; } -void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { +void WaitCurrentThread(WaitType wait_type, Object* wait_object) { Thread* thread = GetCurrentThread(); thread->wait_type = wait_type; - thread->wait_handle = wait_handle; + thread->wait_object = wait_object; ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } -void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) { - WaitCurrentThread(wait_type, wait_handle); +void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) { + WaitCurrentThread(wait_type, wait_object); GetCurrentThread()->wait_address = wait_address; } @@ -279,67 +247,84 @@ static void ThreadWakeupCallback(u64 parameter, int cycles_late) { return; } - Kernel::ResumeThreadFromWait(handle); + thread->ResumeFromWait(); } -void WakeThreadAfterDelay(Handle handle, s64 nanoseconds) { +void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) { // Don't schedule a wakeup if the thread wants to wait forever if (nanoseconds == -1) return; - - Thread* thread = Kernel::g_handle_table.Get(handle); - if (thread == nullptr) { - LOG_ERROR(Kernel, "Thread doesn't exist %u", handle); - return; - } + _dbg_assert_(Kernel, thread != nullptr); u64 microseconds = nanoseconds / 1000; - CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, handle); + CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); } /// Resumes a thread from waiting by marking it as "ready" -void ResumeThreadFromWait(Handle handle) { - Thread* thread = Kernel::g_handle_table.Get(handle); - if (thread) { - thread->status &= ~THREADSTATUS_WAIT; - thread->wait_handle = 0; - thread->wait_type = WAITTYPE_NONE; - if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { - ChangeReadyState(thread, true); - } +void Thread::ResumeFromWait() { + status &= ~THREADSTATUS_WAIT; + wait_object = nullptr; + wait_type = WAITTYPE_NONE; + if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { + ChangeReadyState(this, true); } } /// Prints the thread queue for debugging purposes -void DebugThreadQueue() { +static void DebugThreadQueue() { Thread* thread = GetCurrentThread(); if (!thread) { return; } - LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); - for (u32 i = 0; i < thread_queue.size(); i++) { - Handle handle = thread_queue[i]; - s32 priority = thread_ready_queue.contains(handle); + LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThread()->GetHandle()); + for (Thread* t : thread_queue) { + s32 priority = thread_ready_queue.contains(t); if (priority != -1) { - LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle); + LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, t->GetHandle()); } } } -/// 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) { +ResultVal Thread::Create(const char* name, u32 entry_point, s32 priority, u32 arg, + s32 processor_id, u32 stack_top, int stack_size) { + _dbg_assert_(Kernel, name != nullptr); + + if ((u32)stack_size < 0x200) { + LOG_ERROR(Kernel, "(name=%s): invalid stack_size=0x%08X", name, stack_size); + // TODO: Verify error + return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + } + + if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { + s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); + LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", + name, priority, new_priority); + // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm + // validity of this + priority = new_priority; + } - _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), - "priority=%d, outside of allowable range!", priority) + if (!Memory::GetPointer(entry_point)) { + LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point); + // TODO: Verify error + return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + } Thread* thread = new Thread; - // TOOD(yuriks): Fix error reporting - handle = Kernel::g_handle_table.Create(thread).ValueOr(INVALID_HANDLE); + // TODO(yuriks): Thread requires a handle to be inserted into the various scheduling queues for + // the time being. Create a handle here, it will be copied to the handle field in + // the object and use by the rest of the code. This should be removed when other + // code doesn't rely on the handle anymore. + ResultVal handle = Kernel::g_handle_table.Create(thread); + // TODO(yuriks): Plug memory leak + if (handle.Failed()) + return handle.Code(); - thread_queue.push_back(handle); + thread_queue.push_back(thread); thread_ready_queue.prepare(priority); thread->thread_id = next_thread_id++; @@ -350,69 +335,18 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio thread->initial_priority = thread->current_priority = priority; thread->processor_id = processor_id; thread->wait_type = WAITTYPE_NONE; - thread->wait_handle = 0; + thread->wait_object = nullptr; thread->wait_address = 0; thread->name = name; - return thread; -} - -/// Creates a new thread - wrapper for external user -Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, - u32 stack_top, int stack_size) { - - if (name == nullptr) { - LOG_ERROR(Kernel_SVC, "nullptr name"); - return -1; - } - if ((u32)stack_size < 0x200) { - LOG_ERROR(Kernel_SVC, "(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); - LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", - 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)) { - LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point); - return -1; - } - Handle handle; - Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, - stack_size); - ResetThread(thread, arg, 0); CallThread(thread); - return handle; -} - -/// Get the priority of the thread specified by handle -ResultVal GetThreadPriority(const Handle handle) { - Thread* thread = g_handle_table.Get(handle); - if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); - - return MakeResult(thread->current_priority); + return MakeResult(thread); } /// Set the priority of the thread specified by handle -ResultCode SetThreadPriority(Handle handle, s32 priority) { - Thread* thread = nullptr; - if (!handle) { - thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? - } else { - thread = g_handle_table.Get(handle); - if (thread == nullptr) { - return InvalidHandle(ErrorModule::Kernel); - } - } - _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); - +void Thread::SetPriority(s32 priority) { // If priority is invalid, clamp to valid range if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); @@ -423,38 +357,39 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) { } // Change thread priority - s32 old = thread->current_priority; - thread_ready_queue.remove(old, handle); - thread->current_priority = priority; - thread_ready_queue.prepare(thread->current_priority); + s32 old = current_priority; + thread_ready_queue.remove(old, this); + current_priority = priority; + thread_ready_queue.prepare(current_priority); // Change thread status to "ready" and push to ready queue - if (thread->IsRunning()) { - thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; + if (IsRunning()) { + status = (status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; } - if (thread->IsReady()) { - thread_ready_queue.push_back(thread->current_priority, handle); + if (IsReady()) { + thread_ready_queue.push_back(current_priority, this); } - - return RESULT_SUCCESS; } Handle SetupIdleThread() { - Handle handle; - Thread* thread = CreateThread(handle, "idle", 0, THREADPRIO_LOWEST, THREADPROCESSORID_0, 0, 0); + // We need to pass a few valid values to get around parameter checking in Thread::Create. + auto thread_res = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, + THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE); + _dbg_assert_(Kernel, thread_res.Succeeded()); + Thread* thread = *thread_res; + thread->idle = true; CallThread(thread); - return handle; + return thread->GetHandle(); } -Handle SetupMainThread(s32 priority, int stack_size) { - Handle handle; - +Thread* SetupMainThread(s32 priority, int stack_size) { // Initialize new "main" thread - Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, + ResultVal thread_res = Thread::Create("main", Core::g_app_core->GetPC(), priority, 0, THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); - - ResetThread(thread, 0, 0); + // TODO(yuriks): Propagate error + _dbg_assert_(Kernel, thread_res.Succeeded()); + Thread* thread = *thread_res; // If running another thread already, set it to "ready" state Thread* cur = GetCurrentThread(); @@ -463,11 +398,11 @@ Handle SetupMainThread(s32 priority, int stack_size) { } // Run new "main" thread - SetCurrentThread(thread); + current_thread = thread; thread->status = THREADSTATUS_RUNNING; - LoadContext(thread->context); + Core::g_app_core->LoadContext(thread->context); - return handle; + return thread; } @@ -483,34 +418,13 @@ void Reschedule() { } else { LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); - for (Handle handle : thread_queue) { - Thread* thread = g_handle_table.Get(handle); + for (Thread* thread : thread_queue) { LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", - thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle); + thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_object->GetHandle()); } } } -bool IsIdleThread(Handle handle) { - Thread* thread = g_handle_table.Get(handle); - if (!thread) { - LOG_ERROR(Kernel, "Thread not found %u", handle); - return false; - } - return thread->IsIdle(); -} - -ResultCode GetThreadId(u32* thread_id, Handle handle) { - Thread* thread = g_handle_table.Get(handle); - if (thread == nullptr) - return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, - ErrorSummary::WrongArgument, ErrorLevel::Permanent); - - *thread_id = thread->thread_id; - - return RESULT_SUCCESS; -} - //////////////////////////////////////////////////////////////////////////////////////////////////// void ThreadingInit() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 51290975..24450379 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -54,6 +54,9 @@ namespace Kernel { class Thread : public Kernel::Object { public: + static ResultVal Create(const char* name, u32 entry_point, s32 priority, u32 arg, + s32 processor_id, u32 stack_top, int stack_size = Kernel::DEFAULT_STACK_SIZE); + std::string GetName() const override { return name; } std::string GetTypeName() const override { return "Thread"; } @@ -69,6 +72,15 @@ public: ResultVal WaitSynchronization() override; + s32 GetPriority() const { return current_priority; } + void SetPriority(s32 priority); + + u32 GetThreadId() const { return thread_id; } + + void Stop(const char* reason); + /// Resumes a thread from waiting by marking it as "ready". + void ResumeFromWait(); + Core::ThreadContext context; u32 thread_id; @@ -84,10 +96,10 @@ public: s32 processor_id; WaitType wait_type; - Handle wait_handle; + Object* wait_object; VAddr wait_address; - std::vector waiting_threads; + std::vector waiting_threads; // TODO(yuriks): Owned std::string name; @@ -95,79 +107,47 @@ public: bool idle = false; private: - // TODO(yuriks) Temporary until the creation logic can be moved into a static function - friend Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, - s32 processor_id, u32 stack_top, int stack_size); - Thread() = default; }; -/// Creates a new thread - wrapper for external user -Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, - u32 stack_top, int stack_size=Kernel::DEFAULT_STACK_SIZE); - /// Sets up the primary application thread -Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); +Thread* SetupMainThread(s32 priority, int stack_size = Kernel::DEFAULT_STACK_SIZE); /// Reschedules to the next available thread (call after current thread is suspended) void Reschedule(); -/// Stops the current thread -ResultCode StopThread(Handle thread, const char* reason); - -/** - * Retrieves the ID of the specified thread handle - * @param thread_id Will contain the output thread id - * @param handle Handle to the thread we want - * @return Whether the function was successful or not - */ -ResultCode GetThreadId(u32* thread_id, Handle handle); - -/// Resumes a thread from waiting by marking it as "ready" -void ResumeThreadFromWait(Handle handle); - /// Arbitrate the highest priority thread that is waiting -Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address); +Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address); /// Arbitrate all threads currently waiting... -void ArbitrateAllThreads(u32 arbiter, u32 address); +void ArbitrateAllThreads(Object* arbiter, u32 address); /// Gets the current thread Thread* GetCurrentThread(); -/// Gets the current thread handle -Handle GetCurrentThreadHandle(); - /** * Puts the current thread in the wait state for the given type * @param wait_type Type of wait - * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread + * @param wait_object Kernel object that we are waiting on, defaults to current thread */ -void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); +void WaitCurrentThread(WaitType wait_type, Object* wait_object = GetCurrentThread()); /** * Schedules an event to wake up the specified thread after the specified delay. * @param handle The thread handle. * @param nanoseconds The time this thread will be allowed to sleep for. */ -void WakeThreadAfterDelay(Handle handle, s64 nanoseconds); +void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds); /** * Puts the current thread in the wait state for the given type * @param wait_type Type of wait - * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread + * @param wait_object Kernel object that we are waiting on * @param wait_address Arbitration address used to resume from wait */ -void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address); +void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address); -/// Put current thread in a wait state - on WaitSynchronization -void WaitThread_Synchronization(); -/// Get the priority of the thread specified by handle -ResultVal GetThreadPriority(const Handle handle); - -/// Set the priority of the thread specified by handle -ResultCode SetThreadPriority(Handle handle, s32 priority); /** * Sets up the idle thread, this is a thread that is intended to never execute instructions, @@ -176,10 +156,6 @@ ResultCode SetThreadPriority(Handle handle, s32 priority); * @returns The handle of the idle thread */ Handle SetupIdleThread(); - -/// Whether the current thread is an idle thread -bool IsIdleThread(Handle thread); - /// Initialize threading void ThreadingInit(); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 7ac669e3..685a202c 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -33,8 +33,8 @@ public: ResultVal WaitSynchronization() override { bool wait = !signaled; if (wait) { - waiting_threads.insert(GetCurrentThreadHandle()); - Kernel::WaitCurrentThread(WAITTYPE_TIMER, GetHandle()); + waiting_threads.insert(GetCurrentThread()->GetHandle()); + Kernel::WaitCurrentThread(WAITTYPE_TIMER, this); } return MakeResult(wait); } @@ -92,8 +92,10 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { timer->signaled = true; // Resume all waiting threads - for (Handle thread : timer->waiting_threads) - ResumeThreadFromWait(thread); + for (Handle thread_handle : timer->waiting_threads) { + if (Thread* thread = Kernel::g_handle_table.Get(thread_handle)) + thread->ResumeFromWait(); + } timer->waiting_threads.clear(); diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 92de442e..6380d214 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -231,41 +231,47 @@ static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top name = Common::StringFromFormat("unknown-%08x", entry_point); } - Handle thread = Kernel::CreateThread(name.c_str(), entry_point, priority, arg, processor_id, - stack_top); + ResultVal thread_res = Kernel::Thread::Create(name.c_str(), entry_point, priority, arg, + processor_id, stack_top); + if (thread_res.Failed()) + return thread_res.Code().raw; + Kernel::Thread* thread = *thread_res; - Core::g_app_core->SetReg(1, thread); + Core::g_app_core->SetReg(1, thread->GetHandle()); LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point, - name.c_str(), arg, stack_top, priority, processor_id, thread); + name.c_str(), arg, stack_top, priority, processor_id, thread->GetHandle()); return 0; } /// Called when a thread exits -static u32 ExitThread() { - Handle thread = Kernel::GetCurrentThreadHandle(); +static void ExitThread() { + LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); - LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C - - Kernel::StopThread(thread, __func__); + Kernel::GetCurrentThread()->Stop(__func__); HLE::Reschedule(__func__); - return 0; } /// Gets the priority for the specified thread static Result GetThreadPriority(s32* priority, Handle handle) { - ResultVal priority_result = Kernel::GetThreadPriority(handle); - if (priority_result.Succeeded()) { - *priority = *priority_result; - } - return priority_result.Code().raw; + const Kernel::Thread* thread = Kernel::g_handle_table.Get(handle); + if (thread == nullptr) + return InvalidHandle(ErrorModule::Kernel).raw; + + *priority = thread->GetPriority(); + return RESULT_SUCCESS.raw; } /// Sets the priority for the specified thread static Result SetThreadPriority(Handle handle, s32 priority) { - return Kernel::SetThreadPriority(handle, priority).raw; + Kernel::Thread* thread = Kernel::g_handle_table.Get(handle); + if (thread == nullptr) + return InvalidHandle(ErrorModule::Kernel).raw; + + thread->SetPriority(priority); + return RESULT_SUCCESS.raw; } /// Create a mutex @@ -286,8 +292,13 @@ static Result ReleaseMutex(Handle handle) { /// Get the ID for the specified thread. static Result GetThreadId(u32* thread_id, Handle handle) { LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle); - ResultCode result = Kernel::GetThreadId(thread_id, handle); - return result.raw; + + const Kernel::Thread* thread = Kernel::g_handle_table.Get(handle); + if (thread == nullptr) + return InvalidHandle(ErrorModule::Kernel).raw; + + *thread_id = thread->GetThreadId(); + return RESULT_SUCCESS.raw; } /// Creates a semaphore @@ -375,7 +386,7 @@ static void SleepThread(s64 nanoseconds) { Kernel::WaitCurrentThread(WAITTYPE_SLEEP); // Create an event to wake the thread up after the specified nanosecond delay has passed - Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThreadHandle(), nanoseconds); + Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nanoseconds); HLE::Reschedule(__func__); } @@ -407,7 +418,7 @@ const HLE::FunctionDef SVC_Table[] = { {0x06, nullptr, "GetProcessIdealProcessor"}, {0x07, nullptr, "SetProcessIdealProcessor"}, {0x08, HLE::Wrap, "CreateThread"}, - {0x09, HLE::Wrap, "ExitThread"}, + {0x09, ExitThread, "ExitThread"}, {0x0A, HLE::Wrap, "SleepThread"}, {0x0B, HLE::Wrap, "GetThreadPriority"}, {0x0C, HLE::Wrap, "SetThreadPriority"}, -- cgit v1.2.3