Don't run stack frame destructors when deserializing libretro save states

This commit is contained in:
刘皓 2025-07-25 11:22:31 -04:00
parent 4ada800de3
commit 33153783e8
No known key found for this signature in database
GPG key ID: 7901753DB465B711
3 changed files with 18 additions and 10 deletions

View file

@ -299,6 +299,9 @@ namespace mkxp_sandbox {
inline wasm_ptr_t get_stack_pointer() const noexcept {
return stack_ptr;
}
inline void forget() noexcept {
destructor = nullptr;
}
private:
void *coroutine;
void (*destructor)(void *coroutine);
@ -308,14 +311,10 @@ namespace mkxp_sandbox {
struct fiber {
friend struct binding_base;
fiber(key_t key) : key(key), stack_index(0) {};
inline const std::vector<struct stack_frame> &get_stack() const noexcept {
return stack;
}
public:
const key_t key;
wasm_size_t stack_index;
std::vector<struct deser_stack_frame> deser_stack;
private:
std::vector<struct stack_frame> stack;
};

View file

@ -97,7 +97,10 @@ namespace mkxp_sandbox {
{
struct mkxp_sandbox::bindings::stack_frame_guard<T> frame = bindings->bind<T>();
auto result = frame()();
if (frame().is_complete()) return result;
if (frame().is_complete()) {
assert(!yielding);
return result;
}
}
if (yielding) {
yielding = false;

View file

@ -1741,15 +1741,15 @@ extern "C" RETRO_API bool retro_serialize(void *data, size_t len) {
if (!sandbox_serialize(fiber.stack_index, data, max_size)) return false;
// Write the number of frames in the fiber
if (!sandbox_serialize(std::max((wasm_size_t)fiber.get_stack().size(), (wasm_size_t)fiber.deser_stack.size()), data, max_size)) return false;
if (!sandbox_serialize(std::max((wasm_size_t)fiber.stack.size(), (wasm_size_t)fiber.deser_stack.size()), data, max_size)) return false;
// Write the stack pointer and state of each frame
for (const auto &frame : fiber.get_stack()) {
for (const auto &frame : fiber.stack) {
if (!sandbox_serialize(frame.get_stack_pointer(), data, max_size)) return false;
if (!sandbox_serialize((int32_t)frame, data, max_size)) return false;
}
if (fiber.deser_stack.size() > fiber.get_stack().size()) {
for (auto it = fiber.deser_stack.begin() + fiber.get_stack().size(); it != fiber.deser_stack.end(); ++it) {
if (fiber.deser_stack.size() > fiber.stack.size()) {
for (auto it = fiber.deser_stack.begin() + fiber.stack.size(); it != fiber.deser_stack.end(); ++it) {
if (!sandbox_serialize(it->stack_ptr, data, max_size)) return false;
if (!sandbox_serialize(it->state, data, max_size)) return false;
}
@ -1911,6 +1911,12 @@ extern "C" RETRO_API bool retro_unserialize(const void *data, size_t len) {
wasm_size_t num_fibers;
if (!sandbox_deserialize(num_fibers, data, max_size)) DESER_FAIL;
for (auto &fiber : sb()->fiber_list) {
for (auto &frame : fiber.stack) {
// Make sure the destructors of the existing stack frames don't run when we call `sb()->fiber_list.clear()` a few lines from now
frame.forget();
}
}
sb()->fiber_map.clear();
sb()->fiber_list.clear();
sb()->fiber_map.reserve(num_fibers);
@ -2019,7 +2025,7 @@ extern "C" RETRO_API bool retro_unserialize(const void *data, size_t len) {
if (typenum > SANDBOX_NUM_TYPENUMS) DESER_OBJECTS_END_FAIL;
bool should_be_disposed;
if (!sandbox_deserialize(should_be_disposed, data, max_size)) return false;
if (!sandbox_deserialize(should_be_disposed, data, max_size)) DESER_OBJECTS_END_FAIL;
// Destroy and recreate objects that don't match the type in the save state, or are currently disposed but not disposed in the save state
auto &object = sb()->objects[object_key - 1];