From 993cd6fd7ed1d028e31d7a9d157d726b0d6a0848 Mon Sep 17 00:00:00 2001 From: hjfod Date: Wed, 5 Apr 2023 18:17:53 +0300 Subject: [PATCH] rework event listeners to add different queues for listeners --- loader/include/Geode/loader/Event.hpp | 64 +++++++++++----- loader/src/loader/Event.cpp | 102 ++++++++++++++++---------- 2 files changed, 109 insertions(+), 57 deletions(-) diff --git a/loader/include/Geode/loader/Event.hpp b/loader/include/Geode/loader/Event.hpp index d8674cb6..22702e0b 100644 --- a/loader/include/Geode/loader/Event.hpp +++ b/loader/include/Geode/loader/Event.hpp @@ -10,6 +10,7 @@ namespace geode { class Mod; class Event; + class EventListenerProtocol; Mod* getMod(); @@ -18,10 +19,40 @@ namespace geode { Stop }; + struct GEODE_DLL EventListenerPool { + virtual bool add(EventListenerProtocol* listener) = 0; + virtual void remove(EventListenerProtocol* listener) = 0; + virtual void handle(Event* event) = 0; + virtual ~EventListenerPool() = default; + + EventListenerPool() = default; + EventListenerPool(EventListenerPool const&) = delete; + EventListenerPool(EventListenerPool&&) = delete; + + static EventListenerPool* getDefault(); + }; + + class GEODE_DLL DefaultEventListenerPool : public EventListenerPool { + protected: + std::atomic_size_t m_locked = 0; + std::vector m_listeners; + std::unordered_set m_toAdd; + std::unordered_set m_toRemove; + + public: + bool add(EventListenerProtocol* listener) override; + void remove(EventListenerProtocol* listener) override; + void handle(Event* event) override; + + static DefaultEventListenerPool* get(); + }; + struct GEODE_DLL EventListenerProtocol { - virtual void enable(); - virtual void disable(); - virtual ListenerResult passThrough(Event*) = 0; + bool enable(); + void disable(); + + virtual EventListenerPool* getPool() const; + virtual ListenerResult handle(Event*) = 0; virtual ~EventListenerProtocol(); }; @@ -45,6 +76,10 @@ namespace geode { ListenerResult handle(utils::MiniFunction fn, T* e) { return fn(e); } + + EventListenerPool* getPool() const { + return EventListenerPool::getDefault(); + } }; template @@ -61,9 +96,8 @@ namespace geode { requires std::is_class_v using MemberFn = typename to_member::value; - ListenerResult passThrough(Event* e) override { + ListenerResult handle(Event* e) override { if (m_callback) { - // it is so silly to use dynamic cast in an interbinary context if (auto myev = cast::typeinfo_cast(e)) { return m_filter.handle(m_callback, myev); } @@ -71,6 +105,10 @@ namespace geode { return ListenerResult::Propagate; } + EventListenerPool* getPool() const override { + return m_filter.getPool(); + } + EventListener(T filter = T()) { this->enable(); } @@ -137,7 +175,8 @@ namespace geode { private: friend EventListenerProtocol; - static std::unordered_set& removedListeners(); + protected: + virtual EventListenerPool* getPool() const; public: Mod* sender; @@ -149,18 +188,5 @@ namespace geode { } virtual ~Event(); - - /** - * Get all active event listeners. You may use this to sort listeners - * that have an explicit order. You should never add/remove listeners - * manually however - use the enable() and disable() functions for that - * @warning Do not add/remove listeners manually - */ - static std::vector& listeners(); - /** - * Move an event listener to the front of the queue so it is always hit - * first - */ - static void prioritize(EventListenerProtocol* listener); }; } diff --git a/loader/src/loader/Event.cpp b/loader/src/loader/Event.cpp index 52410c5b..6f7238e4 100644 --- a/loader/src/loader/Event.cpp +++ b/loader/src/loader/Event.cpp @@ -1,17 +1,71 @@ #include #include +#include using namespace geode::prelude; -void EventListenerProtocol::enable() { - if (!ranges::contains(Event::listeners(), this)) { - Event::listeners().push_back(this); +bool DefaultEventListenerPool::add(EventListenerProtocol* listener) { + if (m_locked) { + m_toAdd.insert(listener); + } + else { + m_listeners.push_back(listener); + } + return true; +} + +void DefaultEventListenerPool::remove(EventListenerProtocol* listener) { + if (m_locked) { + m_toRemove.insert(listener); + } + else { + ranges::remove(m_listeners, listener); } } +void DefaultEventListenerPool::handle(Event* event) { + m_locked += 1; + for (auto h : m_listeners) { + // if an event listener gets destroyed in the middle of this loop, we + // need to handle that + if (m_toRemove.contains(h)) continue; + if (h->handle(event) == ListenerResult::Stop) { + break; + } + } + m_locked -= 1; + // only clear listeners once nothing is iterating (if there are recursive handle calls) + if (m_locked == 0) { + for (auto listener : m_toAdd) { + m_listeners.push_back(listener); + } + for (auto listener : m_toRemove) { + ranges::remove(m_listeners, listener); + } + m_toAdd.clear(); + m_toRemove.clear(); + } +} + +DefaultEventListenerPool* DefaultEventListenerPool::get() { + static auto inst = new DefaultEventListenerPool(); + return inst; +} + +EventListenerPool* EventListenerPool::getDefault() { + return DefaultEventListenerPool::get(); +} + +EventListenerPool* EventListenerProtocol::getPool() const { + return EventListenerPool::getDefault(); +} + +bool EventListenerProtocol::enable() { + return this->getPool()->add(this); +} + void EventListenerProtocol::disable() { - Event::removedListeners().insert(this); - ranges::remove(Event::listeners(), this); + this->getPool()->remove(this); } EventListenerProtocol::~EventListenerProtocol() { @@ -20,39 +74,11 @@ EventListenerProtocol::~EventListenerProtocol() { Event::~Event() {} +EventListenerPool* Event::getPool() const { + return EventListenerPool::getDefault(); +} + void Event::postFrom(Mod* m) { if (m) this->sender = m; - auto& listeners = Event::listeners(); - listeners.erase(std::remove_if( - listeners.begin(), - listeners.end(), - [](auto& a) { - return Event::removedListeners().contains(a); - } - ), listeners.end()); - Event::removedListeners().clear(); - - std::vector listeners_copy = Event::listeners(); - for (auto h : listeners_copy) { - // if an event listener gets destroyed in the middle of this loop, we - // need to handle that - if (Event::removedListeners().count(h)) continue; - if (h->passThrough(this) == ListenerResult::Stop) { - break; - } - } -} - -std::unordered_set& Event::removedListeners() { - static std::unordered_set listeners; - return listeners; -} - -std::vector& Event::listeners() { - static std::vector listeners; - return listeners; -} - -void Event::prioritize(EventListenerProtocol* listener) { - ranges::move(Event::listeners(), listener, 0); + this->getPool()->handle(this); }