diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index ff1935d5c7..c0e454a7dd 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -45,6 +45,8 @@ public: std::function GetSuspendThreadStartFunc(); void* GetStartFuncParamater(); + void PreemptSingleCore(); + std::size_t CurrentCore() const { return current_core.load(); } @@ -71,8 +73,6 @@ private: void RunThread(std::size_t core); - void PreemptSingleCore(); - struct CoreData { std::shared_ptr host_context; std::unique_ptr enter_barrier; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 4a091ea381..2a1b917527 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -642,6 +642,10 @@ void KernelCore::Suspend(bool in_suspention) { } } +bool KernelCore::IsMulticore() const { + return impl->is_multicore; +} + void KernelCore::ExceptionalExit() { exception_exited = true; Suspend(true); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 162bbd2f8d..50eeb50ec1 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -212,6 +212,8 @@ public: /// Exceptional exit the OS. void ExceptionalExit(); + bool IsMulticore() const; + private: friend class Object; friend class Process; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index aad2ac5493..eca92b3567 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -19,6 +19,7 @@ #include "core/core_manager.h" #include "core/core_timing.h" #include "core/core_timing_util.h" +#include "core/cpu_manager.h" #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" @@ -1509,21 +1510,31 @@ static void SleepThread(Core::System& system, s64 nanoseconds) { if (nanoseconds <= 0) { switch (static_cast(nanoseconds)) { - case SleepType::YieldWithoutLoadBalancing: - current_thread->YieldSimple(); + case SleepType::YieldWithoutLoadBalancing: { + auto pair = current_thread->YieldSimple(); + is_redundant = pair.second; break; - case SleepType::YieldWithLoadBalancing: - current_thread->YieldAndBalanceLoad(); + } + case SleepType::YieldWithLoadBalancing: { + auto pair = current_thread->YieldAndBalanceLoad(); + is_redundant = pair.second; break; - case SleepType::YieldAndWaitForLoadBalancing: - current_thread->YieldAndWaitForLoadBalancing(); + } + case SleepType::YieldAndWaitForLoadBalancing: { + auto pair = current_thread->YieldAndWaitForLoadBalancing(); + is_redundant = pair.second; break; + } default: UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); } } else { current_thread->Sleep(nanoseconds); } + + if (is_redundant && !system.Kernel().IsMulticore()) { + system.GetCpuManager().PreemptSingleCore(); + } } /// Wait process wide key atomic diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index fc6c0bc857..1c32552b12 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -435,28 +435,31 @@ ResultCode Thread::Sleep(s64 nanoseconds) { return RESULT_SUCCESS; } -ResultCode Thread::YieldSimple() { +std::pair Thread::YieldSimple() { + bool is_redundant = false; { SchedulerLock lock(kernel); - kernel.GlobalScheduler().YieldThread(this); + is_redundant = kernel.GlobalScheduler().YieldThread(this); } - return RESULT_SUCCESS; + return {RESULT_SUCCESS, is_redundant}; } -ResultCode Thread::YieldAndBalanceLoad() { +std::pair Thread::YieldAndBalanceLoad() { + bool is_redundant = false; { SchedulerLock lock(kernel); - kernel.GlobalScheduler().YieldThreadAndBalanceLoad(this); + is_redundant = kernel.GlobalScheduler().YieldThreadAndBalanceLoad(this); } - return RESULT_SUCCESS; + return {RESULT_SUCCESS, is_redundant}; } -ResultCode Thread::YieldAndWaitForLoadBalancing() { +std::pair Thread::YieldAndWaitForLoadBalancing() { + bool is_redundant = false; { SchedulerLock lock(kernel); - kernel.GlobalScheduler().YieldThreadAndWaitForLoadBalancing(this); + is_redundant = kernel.GlobalScheduler().YieldThreadAndWaitForLoadBalancing(this); } - return RESULT_SUCCESS; + return {RESULT_SUCCESS, is_redundant}; } void Thread::AddSchedulingFlag(ThreadSchedFlags flag) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 953b023b5a..9a29875ac0 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "common/common_types.h" @@ -503,13 +504,13 @@ public: ResultCode Sleep(s64 nanoseconds); /// Yields this thread without rebalancing loads. - ResultCode YieldSimple(); + std::pair YieldSimple(); /// Yields this thread and does a load rebalancing. - ResultCode YieldAndBalanceLoad(); + std::pair YieldAndBalanceLoad(); /// Yields this thread and if the core is left idle, loads are rebalanced - ResultCode YieldAndWaitForLoadBalancing(); + std::pair YieldAndWaitForLoadBalancing(); void IncrementYieldCount() { yield_count++; @@ -587,7 +588,7 @@ private: ThreadContext32 context_32{}; ThreadContext64 context_64{}; Common::SpinLock context_guard{}; - std::shared_ptr host_context{}; + std::shared_ptr host_context{}; u64 thread_id = 0;