From 60a373a7862a85b8b030ea1b18d01d364ddf8a8b Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 7 Jan 2015 10:10:58 -0500 Subject: Threads: Use a dummy idle thread when no other are ready. This thread will not actually execute instructions, it will only advance the timing/events and try to yield immediately to the next ready thread, if there aren't any ready threads then it will be rescheduled and start its job again. --- src/core/core.cpp | 13 ++++++++++++- src/core/hle/kernel/kernel.cpp | 2 ++ src/core/hle/kernel/thread.cpp | 23 ++++++++++++++++++++++- src/core/hle/kernel/thread.h | 11 +++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 8ac4481cc..98f8a7dff 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -5,6 +5,7 @@ #include "common/common_types.h" #include "core/core.h" +#include "core/core_timing.h" #include "core/settings.h" #include "core/arm/disassembler/arm_disasm.h" @@ -23,7 +24,17 @@ ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core /// Run the core CPU loop void RunLoop(int tight_loop) { - g_app_core->Run(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())) { + LOG_TRACE(Core_ARM11, "Idling"); + CoreTiming::Idle(); + CoreTiming::Advance(); + HLE::Reschedule(__func__); + } else { + g_app_core->Run(tight_loop); + } + HW::Update(); if (HLE::g_reschedule) { Kernel::Reschedule(); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index e59ed1b57..ae2c11a1c 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -124,6 +124,8 @@ bool LoadExec(u32 entry_point) { // 0x30 is the typical main thread priority I've seen used so far g_main_thread = Kernel::SetupMainThread(0x30); + // Setup the idle thread + Kernel::SetupIdleThread(); return true; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index d76451146..58fb62e89 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -11,6 +11,7 @@ #include "common/thread_queue_list.h" #include "core/core.h" +#include "core/core_timing.h" #include "core/hle/hle.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" @@ -34,6 +35,7 @@ public: 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; } + inline bool IsIdle() const { return idle; } ResultVal WaitSynchronization() override { const bool wait = status != THREADSTATUS_DORMANT; @@ -69,6 +71,9 @@ public: std::vector waiting_threads; std::string name; + + /// Whether this thread is intended to never actually be executed, i.e. always idle + bool idle = false; }; // Lists all thread ids that aren't deleted/etc. @@ -444,7 +449,14 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) { return RESULT_SUCCESS; } -/// Sets up the primary application thread +Handle SetupIdleThread() { + Handle handle; + Thread* thread = CreateThread(handle, "idle", 0, THREADPRIO_LOWEST, THREADPROCESSORID_0, 0, 0); + thread->idle = true; + CallThread(thread); + return handle; +} + Handle SetupMainThread(s32 priority, int stack_size) { Handle handle; @@ -497,6 +509,15 @@ void Reschedule() { ResumeThreadFromWait(prev->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) diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 0e1397cd9..dfe92d162 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -104,6 +104,17 @@ 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, + * only to advance the timing. It is scheduled when there are no other ready threads in the thread queue + * and will try to yield on every call. + * @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(); -- cgit v1.2.3