Core: Make RunOnCPUThread always non-blocking.

This commit is contained in:
Jordan Woyak
2026-02-03 17:29:17 -06:00
parent dd2b94cd4a
commit 201aa65906
4 changed files with 101 additions and 148 deletions
+1 -21
View File
@@ -825,8 +825,7 @@ static void RestoreStateAndUnlock(Core::System& system, const bool unpause_on_un
system.GetCPU().RestoreStateAndUnlock(unpause_on_unlock); system.GetCPU().RestoreStateAndUnlock(unpause_on_unlock);
} }
void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> function, void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> function)
bool wait_for_completion)
{ {
if (IsCPUThread()) if (IsCPUThread())
{ {
@@ -834,8 +833,6 @@ void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> funct
return; return;
} }
Common::OneShotEvent cpu_thread_job_finished;
// Pause the CPU (set it to stepping mode). // Pause the CPU (set it to stepping mode).
const bool was_running = PauseAndLock(system); 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. // If the core hasn't been started, there is no active CPU thread we can race against.
function(); 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 else
{ {
@@ -861,14 +849,6 @@ void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> funct
// Release the CPU thread, and let it execute the callback. // Release the CPU thread, and let it execute the callback.
RestoreStateAndUnlock(system, was_running); 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 --- // --- Callbacks for backends / engine ---
+1 -2
View File
@@ -158,8 +158,7 @@ void FrameUpdateOnCPUThread();
void OnFrameEnd(Core::System& system); void OnFrameEnd(Core::System& system);
// Run a function on the CPU thread, asynchronously. // Run a function on the CPU thread, asynchronously.
void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> function, void RunOnCPUThread(Core::System& system, Common::MoveOnlyFunction<void()> function);
bool wait_for_completion);
// for calling back into UI code without introducing a dependency on it in core // for calling back into UI code without introducing a dependency on it in core
using StateChangedCallbackFunc = std::function<void(Core::State)>; using StateChangedCallbackFunc = std::function<void(Core::State)>;
+56 -70
View File
@@ -496,11 +496,9 @@ static void SaveAsFromCore(Core::System& system, std::string filename)
void SaveAs(Core::System& system, std::string filename) void SaveAs(Core::System& system, std::string filename)
{ {
Core::RunOnCPUThread( Core::RunOnCPUThread(
system, system, [&system, filename = std::move(filename), lock = GetStateSaveTaskLock()]() mutable {
[&system, filename = std::move(filename), lock = GetStateSaveTaskLock()]() mutable {
SaveAsFromCore(system, std::move(filename)); SaveAsFromCore(system, std::move(filename));
}, });
false);
} }
static bool GetVersionFromLZO(StateHeader& header, File::IOFile& f) static bool GetVersionFromLZO(StateHeader& header, File::IOFile& f)
@@ -873,12 +871,9 @@ void LoadAs(Core::System& system, std::string filename)
if (!CheckIfStateLoadIsAllowed(system)) if (!CheckIfStateLoadIsAllowed(system))
return; return;
Core::RunOnCPUThread( Core::RunOnCPUThread(system, [&system, filename = std::move(filename)]() mutable {
system, LoadAsFromCore(system, std::move(filename));
[&system, filename = std::move(filename)]() mutable { });
LoadAsFromCore(system, std::move(filename));
},
false);
} }
void SetOnAfterLoadCallback(AfterLoadCallbackFunc callback) void SetOnAfterLoadCallback(AfterLoadCallbackFunc callback)
@@ -919,45 +914,39 @@ void LoadLastSaved(Core::System& system, int i)
if (!CheckIfStateLoadIsAllowed(system)) if (!CheckIfStateLoadIsAllowed(system))
return; return;
Core::RunOnCPUThread( Core::RunOnCPUThread(system, [&system, i] {
system, // Data must reach the filesystem for up to date "UsedSlots".
[&system, i] { s_compress_and_dump_thread.WaitForCompletion();
// Data must reach the filesystem for up to date "UsedSlots".
s_compress_and_dump_thread.WaitForCompletion();
std::vector<SlotWithTimestamp> used_slots = GetUsedSlotsWithTimestamp(); std::vector<SlotWithTimestamp> used_slots = GetUsedSlotsWithTimestamp();
if (std::size_t(i) > used_slots.size()) if (std::size_t(i) > used_slots.size())
{ {
Core::DisplayMessage("State doesn't exist", 2000); Core::DisplayMessage("State doesn't exist", 2000);
return; return;
} }
std::ranges::stable_sort(used_slots, std::ranges::greater{}, &SlotWithTimestamp::timestamp); std::ranges::stable_sort(used_slots, std::ranges::greater{}, &SlotWithTimestamp::timestamp);
LoadAsFromCore(system, MakeStateFilename(used_slots[i].slot)); LoadAsFromCore(system, MakeStateFilename(used_slots[i].slot));
}, });
false);
} }
void SaveFirstSaved(Core::System& system) void SaveFirstSaved(Core::System& system)
{ {
Core::RunOnCPUThread( Core::RunOnCPUThread(system, [&system, lock = GetStateSaveTaskLock()] {
system, // Data must reach the filesystem for up to date "UsedSlots".
[&system, lock = GetStateSaveTaskLock()] { s_compress_and_dump_thread.WaitForCompletion();
// Data must reach the filesystem for up to date "UsedSlots".
s_compress_and_dump_thread.WaitForCompletion();
std::vector<SlotWithTimestamp> used_slots = GetUsedSlotsWithTimestamp(); std::vector<SlotWithTimestamp> used_slots = GetUsedSlotsWithTimestamp();
auto slot = GetEmptySlot(used_slots); auto slot = GetEmptySlot(used_slots);
if (!slot.has_value()) if (!slot.has_value())
{ {
// overwrite the oldest state // overwrite the oldest state
std::ranges::stable_sort(used_slots, {}, &SlotWithTimestamp::timestamp); std::ranges::stable_sort(used_slots, {}, &SlotWithTimestamp::timestamp);
slot = used_slots.front().slot; slot = used_slots.front().slot;
} }
SaveAsFromCore(system, MakeStateFilename(*slot)); SaveAsFromCore(system, MakeStateFilename(*slot));
}, });
false);
} }
// Load the last state before loading the state // Load the last state before loading the state
@@ -966,36 +955,33 @@ void UndoLoadState(Core::System& system)
if (!CheckIfStateLoadIsAllowed(system)) if (!CheckIfStateLoadIsAllowed(system))
return; return;
Core::RunOnCPUThread( Core::RunOnCPUThread(system, [&system] {
system, if (s_undo_load_buffer.empty())
[&system] { {
if (s_undo_load_buffer.empty()) PanicAlertFmtT("There is nothing to undo!");
{ return;
PanicAlertFmtT("There is nothing to undo!"); }
return;
}
auto& movie = system.GetMovie(); auto& movie = system.GetMovie();
if (movie.IsMovieActive()) if (movie.IsMovieActive())
{ {
// Note: Only the CPU thread writes to "undo.dtm". // Note: Only the CPU thread writes to "undo.dtm".
const std::string dtmpath = File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm"; const std::string dtmpath = File::GetUserPath(D_STATESAVES_IDX) + "undo.dtm";
if (File::Exists(dtmpath)) if (File::Exists(dtmpath))
{ {
LoadFromBuffer(system, s_undo_load_buffer); LoadFromBuffer(system, s_undo_load_buffer);
movie.LoadInput(dtmpath); movie.LoadInput(dtmpath);
} }
else else
{ {
PanicAlertFmtT("No undo.dtm found, aborting undo load state to prevent movie desyncs"); PanicAlertFmtT("No undo.dtm found, aborting undo load state to prevent movie desyncs");
} }
} }
else else
{ {
LoadFromBuffer(system, s_undo_load_buffer); LoadFromBuffer(system, s_undo_load_buffer);
} }
}, });
false);
} }
// Load the state that the last save state overwritten on // Load the state that the last save state overwritten on
+43 -55
View File
@@ -37,34 +37,28 @@
static void RestartCore(const std::weak_ptr<HW::GBA::Core>& core, std::string_view rom_path = {}) static void RestartCore(const std::weak_ptr<HW::GBA::Core>& core, std::string_view rom_path = {})
{ {
Core::RunOnCPUThread( Core::RunOnCPUThread(Core::System::GetInstance(), [core, rom_path = std::string(rom_path)] {
Core::System::GetInstance(), if (auto core_ptr = core.lock())
[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();
auto& info = Config::MAIN_GBA_ROM_PATHS[core_ptr->GetCoreInfo().device_number]; Config::SetCurrent(info, rom_path);
core_ptr->Stop(); auto& system = Core::System::GetInstance();
Config::SetCurrent(info, rom_path); auto& core_timing = system.GetCoreTiming();
auto& system = Core::System::GetInstance(); if (core_ptr->Start(core_timing.GetTicks()))
auto& core_timing = system.GetCoreTiming(); return;
if (core_ptr->Start(core_timing.GetTicks())) Config::SetCurrent(info, Config::GetBase(info));
return; core_ptr->Start(core_timing.GetTicks());
Config::SetCurrent(info, Config::GetBase(info)); }
core_ptr->Start(core_timing.GetTicks()); });
}
},
false);
} }
static void QueueEReaderCard(const std::weak_ptr<HW::GBA::Core>& core, std::string_view card_path) static void QueueEReaderCard(const std::weak_ptr<HW::GBA::Core>& core, std::string_view card_path)
{ {
Core::RunOnCPUThread( Core::RunOnCPUThread(Core::System::GetInstance(), [core, card_path = std::string(card_path)] {
Core::System::GetInstance(), if (auto core_ptr = core.lock())
[core, card_path = std::string(card_path)] { core_ptr->EReaderQueueCard(card_path);
if (auto core_ptr = core.lock()) });
core_ptr->EReaderQueueCard(card_path);
},
false);
} }
GBAWidget::GBAWidget(std::weak_ptr<HW::GBA::Core> core, const HW::GBA::CoreInfo& info, 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; m_force_disconnect = !m_force_disconnect;
Core::RunOnCPUThread( Core::RunOnCPUThread(Core::System::GetInstance(),
Core::System::GetInstance(), [core = m_core, force_disconnect = m_force_disconnect] {
[core = m_core, force_disconnect = m_force_disconnect] { if (auto core_ptr = core.lock())
if (auto core_ptr = core.lock()) core_ptr->SetForceDisconnect(force_disconnect);
core_ptr->SetForceDisconnect(force_disconnect); });
},
false);
} }
void GBAWidget::LoadROM() void GBAWidget::LoadROM()
@@ -224,18 +216,16 @@ void GBAWidget::DoState(bool export_state)
if (state_path.isEmpty()) if (state_path.isEmpty())
return; return;
Core::RunOnCPUThread( Core::RunOnCPUThread(Core::System::GetInstance(),
Core::System::GetInstance(), [export_state, core = m_core, state_path = state_path.toStdString()] {
[export_state, core = m_core, state_path = state_path.toStdString()] { if (auto core_ptr = core.lock())
if (auto core_ptr = core.lock()) {
{ if (export_state)
if (export_state) core_ptr->ExportState(state_path);
core_ptr->ExportState(state_path); else
else core_ptr->ImportState(state_path);
core_ptr->ImportState(state_path); }
} });
},
false);
} }
void GBAWidget::ImportExportSave(bool export_save) void GBAWidget::ImportExportSave(bool export_save)
@@ -255,18 +245,16 @@ void GBAWidget::ImportExportSave(bool export_save)
if (save_path.isEmpty()) if (save_path.isEmpty())
return; return;
Core::RunOnCPUThread( Core::RunOnCPUThread(Core::System::GetInstance(),
Core::System::GetInstance(), [export_save, core = m_core, save_path = save_path.toStdString()] {
[export_save, core = m_core, save_path = save_path.toStdString()] { if (auto core_ptr = core.lock())
if (auto core_ptr = core.lock()) {
{ if (export_save)
if (export_save) core_ptr->ExportSave(save_path);
core_ptr->ExportSave(save_path); else
else core_ptr->ImportSave(save_path);
core_ptr->ImportSave(save_path); }
} });
},
false);
} }
void GBAWidget::Resize(int scale) void GBAWidget::Resize(int scale)