diff --git a/src/core/core.cpp b/src/core/core.cpp index 5b6b5e75b3..355a4f9741 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -459,6 +459,7 @@ struct System::Impl { fs_controller.Reset(); cheat_engine.reset(); core_timing.ClearPendingEvents(); + core_timing.Reset(); app_loader.reset(); audio_core.reset(); gpu_core.reset(); diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index d2ee6769cc..b73ec6a822 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -304,10 +304,10 @@ std::optional CoreTiming::Advance() { void CoreTiming::Reset() { paused = true; - pause_event.Set(); - event.Set(); if (timer_thread.joinable()) { timer_thread.request_stop(); + pause_event.Set(); + event.Set(); timer_thread.join(); } has_started = false; diff --git a/src/core/hle/kernel/k_worker_task_manager.cpp b/src/core/hle/kernel/k_worker_task_manager.cpp index 4427f9859a..742d743022 100644 --- a/src/core/hle/kernel/k_worker_task_manager.cpp +++ b/src/core/hle/kernel/k_worker_task_manager.cpp @@ -4,6 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "common/assert.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_thread.h" @@ -26,7 +27,15 @@ void KWorkerTask::DoWorkerTask(KernelCore& kernel) { } } -KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {} +KWorkerTaskManager::KWorkerTaskManager() {} + +KWorkerTaskManager::~KWorkerTaskManager() { + if (m_waiting_thread.joinable()) { + m_waiting_thread.request_stop(); + m_task_cv.notify_one(); + m_waiting_thread.join(); + } +} void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) { ASSERT(type <= WorkerType::Count); @@ -35,10 +44,31 @@ void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTas void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) { KScopedSchedulerLock sl(kernel); - m_waiting_thread.QueueWork([&kernel, task]() { - // Do the task. - task->DoWorkerTask(kernel); - }); + + // spawn thread on demand + if (!m_waiting_thread.joinable()) { + LOG_INFO(Kernel, "spawning KWorkerTaskManager thread"); + m_waiting_thread = std::jthread([&kernel, this](std::stop_token stop_token) { + while (!stop_token.stop_requested()) { + KWorkerTask* t; + { + std::unique_lock lk{m_task_mutex}; + m_task_cv.wait(lk); + if (stop_token.stop_requested()) + break; + t = m_task_queue.back(); + m_task_queue.pop_back(); + } + t->DoWorkerTask(kernel); + } + }); + } + + { + std::scoped_lock lk{m_task_mutex}; + m_task_queue.emplace_back(task); + } + m_task_cv.notify_one(); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_worker_task_manager.h b/src/core/hle/kernel/k_worker_task_manager.h index 8745a4ce22..58e819b3f4 100644 --- a/src/core/hle/kernel/k_worker_task_manager.h +++ b/src/core/hle/kernel/k_worker_task_manager.h @@ -1,8 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include +#include #include "common/common_types.h" #include "common/thread_worker.h" @@ -19,14 +24,16 @@ public: }; KWorkerTaskManager(); + ~KWorkerTaskManager(); static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task); - private: void AddTask(KernelCore& kernel, KWorkerTask* task); + std::jthread m_waiting_thread; -private: - Common::ThreadWorker m_waiting_thread; + std::mutex m_task_mutex; + std::condition_variable m_task_cv; + std::vector m_task_queue; }; } // namespace Kernel diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp index 5025efc717..2abac82772 100644 --- a/src/core/hle/service/glue/time/worker.cpp +++ b/src/core/hle/service/glue/time/worker.cpp @@ -20,15 +20,17 @@ namespace Service::Glue::Time { -TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, - FileTimestampWorker& file_timestamp_worker) - : m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, m_event{m_ctx.CreateEvent( - "Glue:TimeWorker:Event")}, - m_steady_clock_resource{steady_clock_resource}, - m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent( - "Glue:TimeWorker:SteadyClockTimerEvent")}, - m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")}, - m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} { +TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, FileTimestampWorker& file_timestamp_worker) + : m_system{system} + , m_ctx{m_system, "Glue:TimeWorker"} + , m_event{m_ctx.CreateEvent("Glue:TimeWorker:Event")} + , m_steady_clock_resource{steady_clock_resource} + , m_file_timestamp_worker{file_timestamp_worker} + , m_timer_steady_clock{m_ctx.CreateEvent("Glue:TimeWorker:SteadyClockTimerEvent")} + , m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")} + , m_alarm_worker{m_system, m_steady_clock_resource} + , m_pm_state_change_handler{m_alarm_worker} +{ m_timer_steady_clock_timing_event = Core::Timing::CreateEvent( "Time::SteadyClockEvent", [this](s64 time, @@ -50,11 +52,12 @@ TimeWorker::~TimeWorker() { m_local_clock_event->Signal(m_system.Kernel()); m_network_clock_event->Signal(m_system.Kernel()); m_ephemeral_clock_event->Signal(m_system.Kernel()); - std::this_thread::sleep_for(std::chrono::milliseconds(16)); - m_thread.request_stop(); - m_event->Signal(m_system.Kernel()); - m_thread.join(); + if (m_thread.joinable()) { + m_thread.request_stop(); + m_event->Signal(m_system.Kernel()); + m_thread.join(); + } m_ctx.CloseEvent(m_event); m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event); @@ -122,166 +125,164 @@ void TimeWorker::Initialize(std::shared_ptr t } void TimeWorker::StartThread() { - m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this)); -} + m_thread = m_system.Kernel().RunOnHostCoreThread("TimeWorker", [this]() { + auto const stop_token = m_thread.get_stop_token(); + Common::SetCurrentThreadPriority(Common::ThreadPriority::Low); + while (!stop_token.stop_requested()) { + enum class EventType : s32 { + Exit = 0, + PowerStateChange = 1, + SignalAlarms = 2, + UpdateLocalSystemClock = 3, + UpdateNetworkSystemClock = 4, + UpdateEphemeralSystemClock = 5, + UpdateSteadyClock = 6, + UpdateFileTimestamp = 7, + AutoCorrect = 8, + }; -void TimeWorker::ThreadFunc(std::stop_token stop_token) { - Common::SetCurrentThreadName("TimeWorker"); - Common::SetCurrentThreadPriority(Common::ThreadPriority::Low); + s32 index{}; - while (!stop_token.stop_requested()) { - enum class EventType : s32 { - Exit = 0, - PowerStateChange = 1, - SignalAlarms = 2, - UpdateLocalSystemClock = 3, - UpdateNetworkSystemClock = 4, - UpdateEphemeralSystemClock = 5, - UpdateSteadyClock = 6, - UpdateFileTimestamp = 7, - AutoCorrect = 8, - }; - - s32 index{}; - - if (m_pm_state_change_handler.m_priority != 0) { - // TODO: gIPmModuleService::GetEvent() 1 - index = WaitAny(m_system.Kernel(), - &m_event->GetReadableEvent(), // 0 - &m_alarm_worker.GetEvent() // 1 - ); - } else { - // TODO: gIPmModuleService::GetEvent() 1 - index = WaitAny(m_system.Kernel(), - &m_event->GetReadableEvent(), // 0 - &m_alarm_worker.GetEvent(), // 1 - &m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2 - m_local_clock_event, // 3 - m_network_clock_event, // 4 - m_ephemeral_clock_event, // 5 - &m_timer_steady_clock->GetReadableEvent(), // 6 - &m_timer_file_system->GetReadableEvent(), // 7 - m_standard_user_auto_correct_clock_event // 8 - ); - } - - switch (static_cast(index)) { - case EventType::Exit: - return; - - case EventType::PowerStateChange: - m_alarm_worker.GetEvent().Clear(m_system.Kernel()); - if (m_pm_state_change_handler.m_priority <= 1) { - m_alarm_worker.OnPowerStateChanged(); + if (m_pm_state_change_handler.m_priority != 0) { + // TODO: gIPmModuleService::GetEvent() 1 + index = WaitAny( + m_system.Kernel(), + &m_event->GetReadableEvent(), // 0 + &m_alarm_worker.GetEvent() // 1 + ); + } else { + // TODO: gIPmModuleService::GetEvent() 1 + index = WaitAny( + m_system.Kernel(), + &m_event->GetReadableEvent(), // 0 + &m_alarm_worker.GetEvent(), // 1 + &m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2 + m_local_clock_event, // 3 + m_network_clock_event, // 4 + m_ephemeral_clock_event, // 5 + &m_timer_steady_clock->GetReadableEvent(), // 6 + &m_timer_file_system->GetReadableEvent(), // 7 + m_standard_user_auto_correct_clock_event // 8 + ); } - break; - case EventType::SignalAlarms: - m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel()); - m_time_m->CheckAndSignalAlarms(); - break; + if (stop_token.stop_requested()) + break; - case EventType::UpdateLocalSystemClock: { - m_local_clock_event->Clear(m_system.Kernel()); + switch (EventType(index)) { + case EventType::Exit: + return; - Service::PSC::Time::SystemClockContext context{}; - R_ASSERT(m_local_clock->GetSystemClockContext(&context)); + case EventType::PowerStateChange: + m_alarm_worker.GetEvent().Clear(m_system.Kernel()); + if (m_pm_state_change_handler.m_priority <= 1) { + m_alarm_worker.OnPowerStateChanged(); + } + break; - m_set_sys->SetUserSystemClockContext(context); - m_file_timestamp_worker.SetFilesystemPosixTime(); - break; - } + case EventType::SignalAlarms: + m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel()); + m_time_m->CheckAndSignalAlarms(); + break; - case EventType::UpdateNetworkSystemClock: { - m_network_clock_event->Clear(m_system.Kernel()); + case EventType::UpdateLocalSystemClock: { + m_local_clock_event->Clear(m_system.Kernel()); - Service::PSC::Time::SystemClockContext context{}; - R_ASSERT(m_network_clock->GetSystemClockContext(&context)); + Service::PSC::Time::SystemClockContext context{}; + R_ASSERT(m_local_clock->GetSystemClockContext(&context)); - m_set_sys->SetNetworkSystemClockContext(context); - - s64 time{}; - if (m_network_clock->GetCurrentTime(&time) != ResultSuccess) { + m_set_sys->SetUserSystemClockContext(context); + m_file_timestamp_worker.SetFilesystemPosixTime(); break; } - [[maybe_unused]] auto offset_before{ - m_ig_report_network_clock_context_set ? m_report_network_clock_context.offset : 0}; - // TODO system report "standard_netclock_operation" - // "clock_time" = time - // "context_offset_before" = offset_before - // "context_offset_after" = context.offset - m_report_network_clock_context = context; - if (!m_ig_report_network_clock_context_set) { - m_ig_report_network_clock_context_set = true; - } + case EventType::UpdateNetworkSystemClock: { + m_network_clock_event->Clear(m_system.Kernel()); - m_file_timestamp_worker.SetFilesystemPosixTime(); - break; - } + Service::PSC::Time::SystemClockContext context{}; + R_ASSERT(m_network_clock->GetSystemClockContext(&context)); - case EventType::UpdateEphemeralSystemClock: { - m_ephemeral_clock_event->Clear(m_system.Kernel()); + m_set_sys->SetNetworkSystemClockContext(context); - Service::PSC::Time::SystemClockContext context{}; - auto res = m_ephemeral_clock->GetSystemClockContext(&context); - if (res != ResultSuccess) { + s64 time{}; + if (m_network_clock->GetCurrentTime(&time) != ResultSuccess) { + break; + } + + [[maybe_unused]] auto offset_before{ + m_ig_report_network_clock_context_set ? m_report_network_clock_context.offset : 0}; + // TODO system report "standard_netclock_operation" + // "clock_time" = time + // "context_offset_before" = offset_before + // "context_offset_after" = context.offset + m_report_network_clock_context = context; + if (!m_ig_report_network_clock_context_set) { + m_ig_report_network_clock_context_set = true; + } + + m_file_timestamp_worker.SetFilesystemPosixTime(); break; } - s64 time{}; - res = m_ephemeral_clock->GetCurrentTime(&time); - if (res != ResultSuccess) { + case EventType::UpdateEphemeralSystemClock: { + m_ephemeral_clock_event->Clear(m_system.Kernel()); + + Service::PSC::Time::SystemClockContext context{}; + auto res = m_ephemeral_clock->GetSystemClockContext(&context); + if (res != ResultSuccess) { + break; + } + + s64 time{}; + res = m_ephemeral_clock->GetCurrentTime(&time); + if (res != ResultSuccess) { + break; + } + + [[maybe_unused]] auto offset_before{m_ig_report_ephemeral_clock_context_set + ? m_report_ephemeral_clock_context.offset + : 0}; + // TODO system report "ephemeral_netclock_operation" + // "clock_time" = time + // "context_offset_before" = offset_before + // "context_offset_after" = context.offset + m_report_ephemeral_clock_context = context; + if (!m_ig_report_ephemeral_clock_context_set) { + m_ig_report_ephemeral_clock_context_set = true; + } break; } - [[maybe_unused]] auto offset_before{m_ig_report_ephemeral_clock_context_set - ? m_report_ephemeral_clock_context.offset - : 0}; - // TODO system report "ephemeral_netclock_operation" - // "clock_time" = time - // "context_offset_before" = offset_before - // "context_offset_after" = context.offset - m_report_ephemeral_clock_context = context; - if (!m_ig_report_ephemeral_clock_context_set) { - m_ig_report_ephemeral_clock_context_set = true; + case EventType::UpdateSteadyClock: + m_timer_steady_clock->Clear(m_system.Kernel()); + + m_steady_clock_resource.UpdateTime(); + m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime()); + break; + + case EventType::UpdateFileTimestamp: + m_timer_file_system->Clear(m_system.Kernel()); + m_file_timestamp_worker.SetFilesystemPosixTime(); + break; + + case EventType::AutoCorrect: { + m_standard_user_auto_correct_clock_event->Clear(m_system.Kernel()); + + bool automatic_correction{}; + R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(&automatic_correction)); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + R_ASSERT(m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point)); + + m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction); + m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + break; + } + default: + UNREACHABLE(); } - break; } - - case EventType::UpdateSteadyClock: - m_timer_steady_clock->Clear(m_system.Kernel()); - - m_steady_clock_resource.UpdateTime(); - m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime()); - break; - - case EventType::UpdateFileTimestamp: - m_timer_file_system->Clear(m_system.Kernel()); - - m_file_timestamp_worker.SetFilesystemPosixTime(); - break; - - case EventType::AutoCorrect: { - m_standard_user_auto_correct_clock_event->Clear(m_system.Kernel()); - - bool automatic_correction{}; - R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled( - &automatic_correction)); - - Service::PSC::Time::SteadyClockTimePoint time_point{}; - R_ASSERT( - m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point)); - - m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction); - m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); - break; - } - - default: - UNREACHABLE(); - } - } + }); } } // namespace Service::Glue::Time diff --git a/src/core/hle/service/services.cpp b/src/core/hle/service/services.cpp index b90af960d6..de18fedd19 100644 --- a/src/core/hle/service/services.cpp +++ b/src/core/hle/service/services.cpp @@ -94,6 +94,9 @@ Services::Services(std::shared_ptr& sm, Core::System& system {"ldn", &LDN::LoopProcess}, {"nvservices", &Nvidia::LoopProcess}, {"bsdsocket", &Sockets::LoopProcess}, + // TODO: glue is only required here as otherwise the dummythread doesn't close + // TODO: rework sched so this isn't needed + {"glue", &Glue::LoopProcess}, }) kernel.RunOnHostCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }).detach(); kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach(); @@ -117,7 +120,6 @@ Services::Services(std::shared_ptr& sm, Core::System& system {"friends", &Friend::LoopProcess}, {"settings", &Set::LoopProcess}, {"psc", &PSC::LoopProcess}, - {"glue", &Glue::LoopProcess}, {"grc", &GRC::LoopProcess}, {"hid", &HID::LoopProcess}, {"jit", &JIT::LoopProcess}, diff --git a/src/core/hle/service/vi/conductor.cpp b/src/core/hle/service/vi/conductor.cpp index fd7233cf93..a2f5058e19 100644 --- a/src/core/hle/service/vi/conductor.cpp +++ b/src/core/hle/service/vi/conductor.cpp @@ -11,6 +11,7 @@ #include "common/thread.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/service/vi/conductor.h" #include "core/hle/service/vi/container.h" #include "core/hle/service/vi/display_list.h" @@ -38,7 +39,18 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di }); system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event); - m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); }); + m_thread = system.Kernel().RunOnHostCoreThread("VSyncThread", [this]() { + auto const stop_token = m_thread.get_stop_token(); + Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); + Common::SetCurrentThreadToPerformanceCores(); + while (!stop_token.stop_requested()) { + m_signal.Wait(); + if (stop_token.stop_requested() || m_system.IsShuttingDown()) { + break; + } + this->ProcessVsync(); + } + }); } else { m_event = Core::Timing::CreateEvent( "ScreenComposition", @@ -54,10 +66,10 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di Conductor::~Conductor() { m_system.CoreTiming().UnscheduleEvent(m_event); - - if (m_system.IsMulticore()) { + if (m_thread.joinable()) { m_thread.request_stop(); m_signal.Set(); + m_thread.join(); } } @@ -83,22 +95,6 @@ void Conductor::ProcessVsync() { } } -void Conductor::VsyncThread(std::stop_token token) { - Common::SetCurrentThreadName("VSyncThread"); - Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); - Common::SetCurrentThreadToPerformanceCores(); - - while (!token.stop_requested()) { - m_signal.Wait(); - - if (m_system.IsShuttingDown()) { - return; - } - - this->ProcessVsync(); - } -} - s64 Conductor::GetNextTicks() const { const auto& settings = Settings::values; auto speed_scale = 1.f;