Skip to content

Commit 9b295c5

Browse files
committed
DolphinLibretro: defer Auto Load State until the EmuThread is live
RetroArch's Auto Load State calls retro_unserialize between retro_load_game and the first retro_run. In the libretro fork the EmuThread is launched lazily from inside retro_run, so at auto-load time the core's HW, Memory, PowerPC, FIFO and video subsystems are uninitialised. RunOnCPUThread falls back to inline execution when the core state is not Running, so State::DoState runs on the frontend thread against null/garbage pointers and segfaults. Buffer any savestate delivered before the EmuThread is live in a new Libretro::g_pending_load_state, and drain it from retro_run inside the existing one-shot launch block, after the IsRunningOrStarting wait and the memory-map registration. At that point the execution environment matches a manual Load State. Also guard retro_serialize and retro_serialize_size against the same uninitialised state with an early return. Closes #322.
1 parent 0cd3bb8 commit 9b295c5

3 files changed

Lines changed: 32 additions & 0 deletions

File tree

Source/Core/DolphinLibretro/Common/Globals.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ namespace Libretro
44
{
55
retro_environment_t environ_cb = nullptr;
66
bool g_emuthread_launched = false;
7+
std::vector<unsigned char> g_pending_load_state;
78
std::vector<Gecko::GeckoCode> g_gecko_codes;
89
std::vector<ActionReplay::ARCode> g_ar_codes;
910

Source/Core/DolphinLibretro/Common/Globals.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ namespace Libretro
1111
{
1212
extern retro_environment_t environ_cb;
1313
extern bool g_emuthread_launched;
14+
// Savestate buffered by retro_unserialize() before the EmuThread was alive
15+
extern std::vector<unsigned char> g_pending_load_state;
1416
extern std::vector<Gecko::GeckoCode> g_gecko_codes;
1517
extern std::vector<ActionReplay::ARCode> g_ar_codes;
1618

Source/Core/DolphinLibretro/Main.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,22 @@ void retro_run(void)
271271
struct retro_memory_map mmap = {descs, num_descs};
272272
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmap);
273273
}
274+
275+
if (!Libretro::g_pending_load_state.empty())
276+
{
277+
auto pending = std::move(Libretro::g_pending_load_state);
278+
Libretro::g_pending_load_state.clear();
279+
AsyncRequests* ar = AsyncRequests::GetInstance();
280+
if (system.IsDualCoreMode())
281+
ar->SetPassthrough(true);
282+
Core::RunOnCPUThread(system, [&] {
283+
u8* ptr = reinterpret_cast<u8*>(pending.data());
284+
PointerWrap p(&ptr, pending.size(), PointerWrap::Mode::Read);
285+
State::DoState(system, p);
286+
}, true);
287+
if (system.IsDualCoreMode())
288+
ar->SetPassthrough(false);
289+
}
274290
}
275291

276292
if(!Libretro::g_emuthread_launched)
@@ -462,6 +478,9 @@ void retro_run(void)
462478

463479
size_t retro_serialize_size(void)
464480
{
481+
if (!Libretro::g_emuthread_launched)
482+
return 0;
483+
465484
size_t size = 0;
466485

467486
Core::System& system = Core::System::GetInstance();
@@ -483,6 +502,9 @@ size_t retro_serialize_size(void)
483502

484503
bool retro_serialize(void* data, size_t size)
485504
{
505+
if (!Libretro::g_emuthread_launched)
506+
return false;
507+
486508
Core::System& system = Core::System::GetInstance();
487509
AsyncRequests* ar = AsyncRequests::GetInstance();
488510

@@ -502,6 +524,13 @@ bool retro_serialize(void* data, size_t size)
502524
}
503525
bool retro_unserialize(const void* data, size_t size)
504526
{
527+
if (!Libretro::g_emuthread_launched)
528+
{
529+
const auto* bytes = static_cast<const unsigned char*>(data);
530+
Libretro::g_pending_load_state.assign(bytes, bytes + size);
531+
return true;
532+
}
533+
505534
Core::System& system = Core::System::GetInstance();
506535
AsyncRequests* ar = AsyncRequests::GetInstance();
507536

0 commit comments

Comments
 (0)