core_timing: Use better reference tracking for EventType. (#3159)

* core_timing: Use better reference tracking for EventType.

- Moves ownership of the event to the caller, ensuring we don't fire events for destroyed objects.
- Removes need for unique names - we won't be using this for save states anyways.
This commit is contained in:
bunnei 2019-11-26 21:48:56 -05:00 committed by GitHub
parent 56ddd34c5e
commit 8b35acbd29
17 changed files with 103 additions and 161 deletions

View file

@ -17,11 +17,15 @@ namespace Core::Timing {
constexpr int MAX_SLICE_LENGTH = 10000;
std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
return std::make_shared<EventType>(std::move(callback), std::move(name));
}
struct CoreTiming::Event {
s64 time;
u64 fifo_order;
u64 userdata;
const EventType* type;
std::weak_ptr<EventType> type;
// Sort by time, unless the times are the same, in which case sort by
// the order added to the queue
@ -54,36 +58,15 @@ void CoreTiming::Initialize() {
event_fifo_id = 0;
const auto empty_timed_callback = [](u64, s64) {};
ev_lost = RegisterEvent("_lost_event", empty_timed_callback);
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
}
void CoreTiming::Shutdown() {
ClearPendingEvents();
UnregisterAllEvents();
}
EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) {
std::lock_guard guard{inner_mutex};
// check for existing type with same name.
// we want event type names to remain unique so that we can use them for serialization.
ASSERT_MSG(event_types.find(name) == event_types.end(),
"CoreTiming Event \"{}\" is already registered. Events should only be registered "
"during Init to avoid breaking save states.",
name.c_str());
auto info = event_types.emplace(name, EventType{callback, nullptr});
EventType* event_type = &info.first->second;
event_type->name = &info.first->first;
return event_type;
}
void CoreTiming::UnregisterAllEvents() {
ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending");
event_types.clear();
}
void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
ASSERT(event_type != nullptr);
void CoreTiming::ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type,
u64 userdata) {
std::lock_guard guard{inner_mutex};
const s64 timeout = GetTicks() + cycles_into_future;
@ -93,13 +76,15 @@ void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_ty
}
event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) {
std::lock_guard guard{inner_mutex};
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
return e.type == event_type && e.userdata == userdata;
return e.type.lock().get() == event_type.get() && e.userdata == userdata;
});
// Removing random items breaks the invariant so we have to re-establish it.
@ -130,10 +115,12 @@ void CoreTiming::ClearPendingEvents() {
event_queue.clear();
}
void CoreTiming::RemoveEvent(const EventType* event_type) {
void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
std::lock_guard guard{inner_mutex};
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
[&](const Event& e) { return e.type == event_type; });
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
return e.type.lock().get() == event_type.get();
});
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != event_queue.end()) {
@ -181,7 +168,11 @@ void CoreTiming::Advance() {
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back();
inner_mutex.unlock();
evt.type->callback(evt.userdata, global_timer - evt.time);
if (auto event_type{evt.type.lock()}) {
event_type->callback(evt.userdata, global_timer - evt.time);
}
inner_mutex.lock();
}