fix crash if event listener is destroyed while they are being iterated

This commit is contained in:
hjfod 2023-04-03 09:36:38 +03:00
parent f662251d98
commit 2efe772329
3 changed files with 19 additions and 2 deletions
loader
include/Geode/loader
src
loader
ui/internal/info

View file

@ -133,6 +133,8 @@ namespace geode {
private:
friend EventListenerProtocol;
static std::unordered_set<EventListenerProtocol*>& removedListeners();
public:
Mod* sender;
@ -144,6 +146,12 @@ 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<EventListenerProtocol*>& listeners();
/**
* Move an event listener to the front of the queue so it is always hit

View file

@ -8,6 +8,7 @@ void EventListenerProtocol::enable() {
}
void EventListenerProtocol::disable() {
Event::removedListeners().insert(this);
ranges::remove(Event::listeners(), this);
}
@ -21,12 +22,20 @@ void Event::postFrom(Mod* m) {
if (m) this->sender = m;
std::vector<EventListenerProtocol*> 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;
}
}
Event::removedListeners().clear();
}
std::unordered_set<EventListenerProtocol*>& Event::removedListeners() {
static std::unordered_set<EventListenerProtocol*> listeners;
return listeners;
}
std::vector<EventListenerProtocol*>& Event::listeners() {

View file

@ -559,7 +559,7 @@ bool IndexItemInfoPopup::init(IndexItemHandle item, ModListLayer* list) {
void IndexItemInfoPopup::onInstallProgress(ModInstallEvent* event) {
std::visit(makeVisitor {
[&](UpdateFinished) {
[&](UpdateFinished const&) {
this->setInstallStatus(std::nullopt);
FLAlertLayer::create(