core_timing: Lock CoreTiming event queue while deserializing

To handle those classic asymmetric constructor/destructor side effects
This commit is contained in:
zhupengfei 2021-02-08 11:24:05 +08:00
parent b2531310b4
commit 996ca25a2e
No known key found for this signature in database
GPG key ID: DD129E108BD09378
3 changed files with 23 additions and 0 deletions

View file

@ -629,6 +629,7 @@ void System::serialize(Archive& ar, const unsigned int file_version) {
// This needs to be set from somewhere - might as well be here! // This needs to be set from somewhere - might as well be here!
if (Archive::is_loading::value) { if (Archive::is_loading::value) {
timing->UnlockEventQueue();
Service::GSP::SetGlobalModule(*this); Service::GSP::SetGlobalModule(*this);
memory->SetDSP(*dsp_core); memory->SetDSP(*dsp_core);
cheat_engine->Connect(); cheat_engine->Connect();

View file

@ -49,6 +49,10 @@ TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback ca
void Timing::ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, u64 userdata, void Timing::ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, u64 userdata,
std::size_t core_id) { std::size_t core_id) {
if (event_queue_locked) {
return;
}
ASSERT(event_type != nullptr); ASSERT(event_type != nullptr);
Timing::Timer* timer = nullptr; Timing::Timer* timer = nullptr;
if (core_id == std::numeric_limits<std::size_t>::max()) { if (core_id == std::numeric_limits<std::size_t>::max()) {
@ -74,6 +78,9 @@ void Timing::ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_
} }
void Timing::UnscheduleEvent(const TimingEventType* event_type, u64 userdata) { void Timing::UnscheduleEvent(const TimingEventType* event_type, u64 userdata) {
if (event_queue_locked) {
return;
}
for (auto timer : timers) { for (auto timer : timers) {
auto itr = std::remove_if( auto itr = std::remove_if(
timer->event_queue.begin(), timer->event_queue.end(), timer->event_queue.begin(), timer->event_queue.end(),
@ -89,6 +96,9 @@ void Timing::UnscheduleEvent(const TimingEventType* event_type, u64 userdata) {
} }
void Timing::RemoveEvent(const TimingEventType* event_type) { void Timing::RemoveEvent(const TimingEventType* event_type) {
if (event_queue_locked) {
return;
}
for (auto timer : timers) { for (auto timer : timers) {
auto itr = std::remove_if(timer->event_queue.begin(), timer->event_queue.end(), auto itr = std::remove_if(timer->event_queue.begin(), timer->event_queue.end(),
[&](const Event& e) { return e.type == event_type; }); [&](const Event& e) { return e.type == event_type; });

View file

@ -280,6 +280,11 @@ public:
std::shared_ptr<Timer> GetTimer(std::size_t cpu_id); std::shared_ptr<Timer> GetTimer(std::size_t cpu_id);
// Used after deserializing to unprotect the event queue.
void UnlockEventQueue() {
event_queue_locked = false;
}
private: private:
// unordered_map stores each element separately as a linked list node so pointers to // unordered_map stores each element separately as a linked list node so pointers to
// elements remain stable regardless of rehashes/resizing. // elements remain stable regardless of rehashes/resizing.
@ -292,6 +297,10 @@ private:
// under/overclocking the guest cpu // under/overclocking the guest cpu
double cpu_clock_scale = 1.0; double cpu_clock_scale = 1.0;
// When true, the event queue can't be modified. Used while deserializing to workaround
// destructor side effects.
bool event_queue_locked = false;
template <class Archive> template <class Archive>
void serialize(Archive& ar, const unsigned int file_version) { void serialize(Archive& ar, const unsigned int file_version) {
// event_types set during initialization of other things // event_types set during initialization of other things
@ -303,6 +312,9 @@ private:
} else { } else {
ar& current_timer; ar& current_timer;
} }
if (Archive::is_loading::value) {
event_queue_locked = true;
}
} }
friend class boost::serialization::access; friend class boost::serialization::access;
}; };