summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Woyak <jordan.woyak@gmail.com>2026-02-03 17:29:17 -0600
committerJordan Woyak <jordan.woyak@gmail.com>2026-02-03 17:29:17 -0600
commit201aa659066b41a2cfe89b615213bcb093fcbe79 (patch)
tree3bed7385c0f231ff27afea3efe226feed699754a
parentdd2b94cd4a71fb6abda72f58d27434ef2d8b0a5f (diff)
Core: Make RunOnCPUThread always non-blocking.
-rw-r--r--Source/Core/Core/Core.cpp22
-rw-r--r--Source/Core/Core/Core.h3
-rw-r--r--Source/Core/Core/State.cpp128
-rw-r--r--Source/Core/DolphinQt/GBAWidget.cpp98
4 files changed, 102 insertions, 149 deletions
diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp
index 95be762ba0..bdae112865 100644
--- a/Source/Core/Core/Core.cpp
+++ b/Source/Core/Core/Core.cpp
@@ -825,8 +825,7 @@ static void RestoreStateAndUnlock(Core::System& system, const bool unpause_on_un
system.GetCPU().RestoreStateAndUnlock(unpause_on_unlock);
}
-void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> function,
- bool wait_for_completion)
+void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> function)
{
if (IsCPUThread())
{
@@ -834,8 +833,6 @@ void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> funct
return;
}
- Common::OneShotEvent cpu_thread_job_finished;
-
// Pause the CPU (set it to stepping mode).
const bool was_running = PauseAndLock(system);
@@ -843,15 +840,6 @@ void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> funct
{
// If the core hasn't been started, there is no active CPU thread we can race against.
function();
- wait_for_completion = false;
- }
- else if (wait_for_completion)
- {
- // Queue the job function followed by triggering the event.
- system.GetCPU().AddCPUThreadJob([&function, &cpu_thread_job_finished] {
- function();
- cpu_thread_job_finished.Set();
- });
}
else
{
@@ -861,14 +849,6 @@ void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> funct
// Release the CPU thread, and let it execute the callback.
RestoreStateAndUnlock(system, was_running);
-
- // If we're waiting for completion, block until the event fires.
- if (wait_for_completion)
- {
- // Periodically yield to the UI thread, so we don't deadlock.
- while (!cpu_thread_job_finished.WaitFor(std::chrono::milliseconds(10)))
- Host_YieldToUI();
- }
}
// --- Callbacks for backends / engine ---
diff --git a/Source/Core/Core/Core.h b/Source/Core/Core/Core.h
index a50fa54937..7dff8e13d5 100644
--- a/Source/Core/Core/Core.h
+++ b/Source/Core/Core/Core.h
@@ -158,8 +158,7 @@ void FrameUpdateOnCPUThread();
void OnFrameEnd(Core::System& system);
// Run a function on the CPU thread, asynchronously.
-void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> function,
- bool wait_for_completion);
+void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> function);
// for calling back into UI code without introducing a dependency on it in core
using StateChangedCallbackFunc = std::function<void(Core::State)>;
diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp
index 36974a9bd5..d9f37b3f48 100644
--- a/Source/Core/Core/State.cpp
+++ b/Source/Core/Core/State.cpp
@@ -496,11 +496,9 @@ static void SaveAsFromCore(Core::System& system, std::string filename)
void SaveAs(Core::System& system, std::string filename)
{
Core::RunOnCPUThread(
- system,
- [&system, filename = std::move(filename), lock = GetStateSaveTaskLock()]() mutable {
+ system, [&system, filename = std::move(filename), lock = GetStateSaveTaskLock()]() mutable {
SaveAsFromCore(system, std::move(filename));
- },
- false);
+ });
}
static bool GetVersionFromLZO(StateHeader& header, File::IOFile& f)
@@ -873,12 +871,9 @@ void LoadAs(Core::System& system, std::string filename)
if (!CheckIfStateLoadIsAllowed(system))
return;
- Core::RunOnCPUThread(
- system,
- [&system, filename = std::move(filename)]() mutable {
- LoadAsFromCore(system, std::move(filename));
- },
- false);
+ Core::RunOnCPUThread(system, [&system, filename = std::move(filename)]() mutable {
+ LoadAsFromCore(system, std::move(filename));
+ });
}
void SetOnAfterLoadCallback(AfterLoadCallbackFunc callback)
@@ -919,45 +914,39 @@ void LoadLastSaved(Core::System& system, int i)
if (!CheckIfStateLoadIsAllowed(system))
return;
- Core::RunOnCPUThread(
- system,
- [&system, i] {
- // Data must reach the filesystem for up to date "UsedSlots".
- s_compress_and_dump_thread.WaitForCompletion();
+ Core::RunOnCPUThread(system, [&system, i] {
+ // Data must reach the filesystem for up to date "UsedSlots".
+ s_compress_and_dump_thread.WaitForCompletion();
- std::vector<SlotWithTimestamp> used_slots = GetUsedSlotsWithTimestamp();
- if (std::size_t(i) > used_slots.size())
- {
- Core::DisplayMessage("State doesn't exist", 2000);
- return;
- }
+ std::vector<SlotWithTimestamp> used_slots = GetUsedSlotsWithTimestamp();
+ if (std::size_t(i) > used_slots.size())
+ {
+ Core::DisplayMessage("State doesn't exist", 2000);
+ return;
+ }
- std::ranges::stable_sort(used_slots, std::ranges::greater{}, &SlotWithTimestamp::timestamp);
- LoadAsFromCore(system, MakeStateFilename(used_slots[i].slot));
- },
- false);
+ std::ranges::stable_sort(used_slots, std::ranges::greater{}, &SlotWithTimestamp::timestamp);
+ LoadAsFromCore(system, MakeStateFilename(used_slots[i].slot));
+ });
}
void SaveFirstSaved(Core::System& system)
{
- Core::RunOnCPUThread(
- system,
- [&system, lock = GetStateSaveTaskLock()] {
- // Data must reach the filesystem for up to date "UsedSlots".
- s_compress_and_dump_thread.WaitForCompletion();
+ Core::RunOnCPUThread(system, [&system, lock = GetStateSaveTaskLock()] {
+ // Data must reach the filesystem for up to date "UsedSlots".
+ s_compress_and_dump_thread.WaitForCompletion();
- std::vector<SlotWithTimestamp> used_slots = GetUsedSlotsWithTimestamp();
- auto slot = GetEmptySlot(used_slots);
- if (!slot.has_value())
- {
- // overwrite the oldest state
- std::ranges::stable_sort(used_slots, {}, &SlotWithTimestamp::timestamp);
- slot = used_slots.front().slot;
- }
+ std::vector<SlotWithTimestamp> used_slots = GetUsedSlotsWithTimestamp();
+ auto slot = GetEmptySlot(used_slots);
+ if (!slot.has_value())
+ {
+ // overwrite the oldest state
+ std::ranges::stable_sort(used_slots, {}, &SlotWithTimestamp::timestamp);
+ slot = used_slots.front().slot;
+ }
- SaveAsFromCore(system, MakeStateFilename(*slot));
- },
- false);
+ SaveAsFromCore(system, MakeStateFilename(*slot));
+ });
}
// Load the last state before loading the state
@@ -966,36 +955,33 @@ void UndoLoadState(Core::System& system)
if (!CheckIfStateLoadIsAllowed(system))
return;
- Core::RunOnCPUThread(
- system,
- [&system] {
- if (s_undo_load_buffer.empty())
- {
- PanicAlertFmtT("There is nothing to undo!");
- return;
- }
-
- auto& movie = system.GetMovie();
- if (movie.IsMovieActive())
- {
- // Note: Only the CPU thread writes to "undo.dtm".
- const std::string dtmpath = File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm";
- if (File::Exists(dtmpath))
- {
- LoadFromBuffer(system, s_undo_load_buffer);
- movie.LoadInput(dtmpath);
- }
- else
- {
- PanicAlertFmtT("No undo.dtm found, aborting undo load state to prevent movie desyncs");
- }
- }
- else
- {
- LoadFromBuffer(system, s_undo_load_buffer);
- }
- },
- false);
+ Core::RunOnCPUThread(system, [&system] {
+ if (s_undo_load_buffer.empty())
+ {
+ PanicAlertFmtT("There is nothing to undo!");
+ return;
+ }
+
+ auto& movie = system.GetMovie();
+ if (movie.IsMovieActive())
+ {
+ // Note: Only the CPU thread writes to "undo.dtm".
+ const std::string dtmpath = File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm";
+ if (File::Exists(dtmpath))
+ {
+ LoadFromBuffer(system, s_undo_load_buffer);
+ movie.LoadInput(dtmpath);
+ }
+ else
+ {
+ PanicAlertFmtT("No undo.dtm found, aborting undo load state to prevent movie desyncs");
+ }
+ }
+ else
+ {
+ LoadFromBuffer(system, s_undo_load_buffer);
+ }
+ });
}
// Load the state that the last save state overwritten on
diff --git a/Source/Core/DolphinQt/GBAWidget.cpp b/Source/Core/DolphinQt/GBAWidget.cpp
index d375b3c8c2..af319b890e 100644
--- a/Source/Core/DolphinQt/GBAWidget.cpp
+++ b/Source/Core/DolphinQt/GBAWidget.cpp
@@ -37,34 +37,28 @@
static void RestartCore(const std::weak_ptr<HW::GBA::Core>& core, std::string_view rom_path = {})
{
- Core::RunOnCPUThread(
- Core::System::GetInstance(),
- [core, rom_path = std::string(rom_path)] {
- if (auto core_ptr = core.lock())
- {
- auto& info = Config::MAIN_GBA_ROM_PATHS[core_ptr->GetCoreInfo().device_number];
- core_ptr->Stop();
- Config::SetCurrent(info, rom_path);
- auto& system = Core::System::GetInstance();
- auto& core_timing = system.GetCoreTiming();
- if (core_ptr->Start(core_timing.GetTicks()))
- return;
- Config::SetCurrent(info, Config::GetBase(info));
- core_ptr->Start(core_timing.GetTicks());
- }
- },
- false);
+ Core::RunOnCPUThread(Core::System::GetInstance(), [core, rom_path = std::string(rom_path)] {
+ if (auto core_ptr = core.lock())
+ {
+ auto& info = Config::MAIN_GBA_ROM_PATHS[core_ptr->GetCoreInfo().device_number];
+ core_ptr->Stop();
+ Config::SetCurrent(info, rom_path);
+ auto& system = Core::System::GetInstance();
+ auto& core_timing = system.GetCoreTiming();
+ if (core_ptr->Start(core_timing.GetTicks()))
+ return;
+ Config::SetCurrent(info, Config::GetBase(info));
+ core_ptr->Start(core_timing.GetTicks());
+ }
+ });
}
static void QueueEReaderCard(const std::weak_ptr<HW::GBA::Core>& core, std::string_view card_path)
{
- Core::RunOnCPUThread(
- Core::System::GetInstance(),
- [core, card_path = std::string(card_path)] {
- if (auto core_ptr = core.lock())
- core_ptr->EReaderQueueCard(card_path);
- },
- false);
+ Core::RunOnCPUThread(Core::System::GetInstance(), [core, card_path = std::string(card_path)] {
+ if (auto core_ptr = core.lock())
+ core_ptr->EReaderQueueCard(card_path);
+ });
}
GBAWidget::GBAWidget(std::weak_ptr<HW::GBA::Core> core, const HW::GBA::CoreInfo& info,
@@ -161,13 +155,11 @@ void GBAWidget::ToggleDisconnect()
m_force_disconnect = !m_force_disconnect;
- Core::RunOnCPUThread(
- Core::System::GetInstance(),
- [core = m_core, force_disconnect = m_force_disconnect] {
- if (auto core_ptr = core.lock())
- core_ptr->SetForceDisconnect(force_disconnect);
- },
- false);
+ Core::RunOnCPUThread(Core::System::GetInstance(),
+ [core = m_core, force_disconnect = m_force_disconnect] {
+ if (auto core_ptr = core.lock())
+ core_ptr->SetForceDisconnect(force_disconnect);
+ });
}
void GBAWidget::LoadROM()
@@ -224,18 +216,16 @@ void GBAWidget::DoState(bool export_state)
if (state_path.isEmpty())
return;
- Core::RunOnCPUThread(
- Core::System::GetInstance(),
- [export_state, core = m_core, state_path = state_path.toStdString()] {
- if (auto core_ptr = core.lock())
- {
- if (export_state)
- core_ptr->ExportState(state_path);
- else
- core_ptr->ImportState(state_path);
- }
- },
- false);
+ Core::RunOnCPUThread(Core::System::GetInstance(),
+ [export_state, core = m_core, state_path = state_path.toStdString()] {
+ if (auto core_ptr = core.lock())
+ {
+ if (export_state)
+ core_ptr->ExportState(state_path);
+ else
+ core_ptr->ImportState(state_path);
+ }
+ });
}
void GBAWidget::ImportExportSave(bool export_save)
@@ -255,18 +245,16 @@ void GBAWidget::ImportExportSave(bool export_save)
if (save_path.isEmpty())
return;
- Core::RunOnCPUThread(
- Core::System::GetInstance(),
- [export_save, core = m_core, save_path = save_path.toStdString()] {
- if (auto core_ptr = core.lock())
- {
- if (export_save)
- core_ptr->ExportSave(save_path);
- else
- core_ptr->ImportSave(save_path);
- }
- },
- false);
+ Core::RunOnCPUThread(Core::System::GetInstance(),
+ [export_save, core = m_core, save_path = save_path.toStdString()] {
+ if (auto core_ptr = core.lock())
+ {
+ if (export_save)
+ core_ptr->ExportSave(save_path);
+ else
+ core_ptr->ImportSave(save_path);
+ }
+ });
}
void GBAWidget::Resize(int scale)