fix event listeners not being removed from the right queue

This commit is contained in:
HJfod 2023-04-05 18:59:25 +03:00
parent 6d77f841c9
commit 6ba91482a6
3 changed files with 34 additions and 29 deletions

View file

@ -28,16 +28,13 @@ namespace geode {
EventListenerPool() = default; EventListenerPool() = default;
EventListenerPool(EventListenerPool const&) = delete; EventListenerPool(EventListenerPool const&) = delete;
EventListenerPool(EventListenerPool&&) = delete; EventListenerPool(EventListenerPool&&) = delete;
static EventListenerPool* getDefault();
}; };
class GEODE_DLL DefaultEventListenerPool : public EventListenerPool { class GEODE_DLL DefaultEventListenerPool : public EventListenerPool {
protected: protected:
std::atomic_size_t m_locked = 0; std::atomic_size_t m_locked = 0;
std::vector<EventListenerProtocol*> m_listeners; std::vector<EventListenerProtocol*> m_listeners;
std::unordered_set<EventListenerProtocol*> m_toAdd; std::vector<EventListenerProtocol*> m_toAdd;
std::unordered_set<EventListenerProtocol*> m_toRemove;
public: public:
bool add(EventListenerProtocol* listener) override; bool add(EventListenerProtocol* listener) override;
@ -47,7 +44,11 @@ namespace geode {
static DefaultEventListenerPool* get(); static DefaultEventListenerPool* get();
}; };
struct GEODE_DLL EventListenerProtocol { class GEODE_DLL EventListenerProtocol {
private:
EventListenerPool* m_pool = nullptr;
public:
bool enable(); bool enable();
void disable(); void disable();
@ -78,7 +79,7 @@ namespace geode {
} }
EventListenerPool* getPool() const { EventListenerPool* getPool() const {
return EventListenerPool::getDefault(); return DefaultEventListenerPool::get();
} }
}; };

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "Types.hpp" #include "Types.hpp"
#include "../utils/Result.hpp"
#include <Geode/DefaultInclude.hpp> #include <Geode/DefaultInclude.hpp>
#include <Geode/utils/ranges.hpp> #include <Geode/utils/ranges.hpp>

View file

@ -6,7 +6,7 @@ using namespace geode::prelude;
bool DefaultEventListenerPool::add(EventListenerProtocol* listener) { bool DefaultEventListenerPool::add(EventListenerProtocol* listener) {
if (m_locked) { if (m_locked) {
m_toAdd.insert(listener); m_toAdd.push_back(listener);
} }
else { else {
m_listeners.push_back(listener); m_listeners.push_back(listener);
@ -15,35 +15,31 @@ bool DefaultEventListenerPool::add(EventListenerProtocol* listener) {
} }
void DefaultEventListenerPool::remove(EventListenerProtocol* listener) { void DefaultEventListenerPool::remove(EventListenerProtocol* listener) {
if (m_locked) { for (size_t i = 0; i < m_listeners.size(); i++) {
m_toRemove.insert(listener); if (m_listeners[i] == listener) {
} m_listeners[i] = nullptr;
else { }
ranges::remove(m_listeners, listener);
} }
} }
void DefaultEventListenerPool::handle(Event* event) { void DefaultEventListenerPool::handle(Event* event) {
m_locked += 1; m_locked += 1;
for (auto h : m_listeners) { for (auto h : m_listeners) {
// if an event listener gets destroyed in the middle of this loop, we // if an event listener gets destroyed in the middle of this loop, it
// need to handle that // gets set to null
if (m_toRemove.contains(h)) continue; if (h && h->handle(event) == ListenerResult::Stop) {
if (h->handle(event) == ListenerResult::Stop) {
break; break;
} }
} }
m_locked -= 1; m_locked -= 1;
// only clear listeners once nothing is iterating (if there are recursive handle calls) // only mutate listeners once nothing is iterating
// (if there are recursive handle calls)
if (m_locked == 0) { if (m_locked == 0) {
ranges::remove(m_listeners, nullptr);
for (auto listener : m_toAdd) { for (auto listener : m_toAdd) {
m_listeners.push_back(listener); m_listeners.push_back(listener);
} }
for (auto listener : m_toRemove) {
ranges::remove(m_listeners, listener);
}
m_toAdd.clear(); m_toAdd.clear();
m_toRemove.clear();
} }
} }
@ -52,20 +48,27 @@ DefaultEventListenerPool* DefaultEventListenerPool::get() {
return inst; return inst;
} }
EventListenerPool* EventListenerPool::getDefault() { EventListenerPool* EventListenerProtocol::getPool() const {
return DefaultEventListenerPool::get(); return DefaultEventListenerPool::get();
} }
EventListenerPool* EventListenerProtocol::getPool() const {
return EventListenerPool::getDefault();
}
bool EventListenerProtocol::enable() { bool EventListenerProtocol::enable() {
return this->getPool()->add(this); // virtual calls from destructors always call the base class so we gotta
// store the subclass' pool in a member to be able to access it in disable
// this is actually better because now regardless of what getPool() does
// we can always be assured that whatever pool it returns this listener
// will be removed from that pool and can't be in multiple pools at once
if (m_pool || !(m_pool = this->getPool())) {
return false;
}
return m_pool->add(this);
} }
void EventListenerProtocol::disable() { void EventListenerProtocol::disable() {
this->getPool()->remove(this); if (m_pool) {
m_pool->remove(this);
m_pool = nullptr;
}
} }
EventListenerProtocol::~EventListenerProtocol() { EventListenerProtocol::~EventListenerProtocol() {
@ -75,7 +78,7 @@ EventListenerProtocol::~EventListenerProtocol() {
Event::~Event() {} Event::~Event() {}
EventListenerPool* Event::getPool() const { EventListenerPool* Event::getPool() const {
return EventListenerPool::getDefault(); return DefaultEventListenerPool::get();
} }
void Event::postFrom(Mod* m) { void Event::postFrom(Mod* m) {