From 49884d7e600da087d3268aa543b5f679cbdc2006 Mon Sep 17 00:00:00 2001 From: sippeangelo Date: Wed, 6 Jan 2016 13:47:02 +0100 Subject: [PATCH] Fix for crash when unsubscription during event processing (untested) --- include/Engine/Core/EventBroker.h | 8 ++++-- src/Engine/Core/EventBroker.cpp | 47 ++++++++++++++++++------------- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/include/Engine/Core/EventBroker.h b/include/Engine/Core/EventBroker.h index f04c7715..dde5242a 100644 --- a/include/Engine/Core/EventBroker.h +++ b/include/Engine/Core/EventBroker.h @@ -13,6 +13,8 @@ relay = decltype(relay)(std::bind(handler, this, std::placeholders::_1)); \ m_EventBroker->Subscribe(relay); +typedef unsigned int EventID; + class EventBroker; class BaseEventRelay @@ -31,6 +33,7 @@ public: virtual bool Receive(const std::shared_ptr event) = 0; protected: + EventID m_EventID; std::string m_ContextTypeName; std::string m_EventTypeName; EventBroker* m_Broker; @@ -95,6 +98,7 @@ public: private: bool m_IsProcessing = false; + EventID m_NextEventID = 0; typedef std::string ContextTypeName_t; // typeid(ContextType).name() typedef std::string EventTypeName_t; // typeid(EventType).name() @@ -103,14 +107,14 @@ private: typedef std::unordered_map ContextRelays_t; ContextRelays_t m_ContextRelays; std::vector m_RelaysToSubscribe; - std::vector m_RelaysToUnsubscribe; + std::vector> m_RelaysToUnsubscribe; typedef std::list>> EventQueue_t; std::shared_ptr m_EventQueueRead; std::shared_ptr m_EventQueueWrite; void subscribeImmediate(BaseEventRelay& relay); - void unsubscribeImmediate(BaseEventRelay& relay); + void unsubscribeImmediate(std::tuple identifier); }; template diff --git a/src/Engine/Core/EventBroker.cpp b/src/Engine/Core/EventBroker.cpp index 76c243e8..6767878f 100644 --- a/src/Engine/Core/EventBroker.cpp +++ b/src/Engine/Core/EventBroker.cpp @@ -2,21 +2,24 @@ BaseEventRelay::~BaseEventRelay() { - if (m_Broker != nullptr) { - m_Broker->Unsubscribe(*this); - } + if (m_Broker != nullptr) { + m_Broker->Unsubscribe(*this); + } } -void EventBroker::Unsubscribe(BaseEventRelay &relay) // ? +void EventBroker::Unsubscribe(BaseEventRelay& relay) // ? { - if (m_IsProcessing) { - m_RelaysToUnsubscribe.push_back(&relay); - } else { - unsubscribeImmediate(relay); - } + auto identifier = std::make_tuple(relay.m_EventID, relay.m_ContextTypeName, relay.m_EventTypeName); + + relay.m_Broker = nullptr; + if (m_IsProcessing) { + m_RelaysToUnsubscribe.push_back(identifier); + } else { + unsubscribeImmediate(identifier); + } } -void EventBroker::Subscribe(BaseEventRelay &relay) +void EventBroker::Subscribe(BaseEventRelay& relay) { if (m_IsProcessing) { m_RelaysToSubscribe.push_back(&relay); @@ -38,12 +41,11 @@ int EventBroker::Process(std::string contextTypeName) int eventsProcessed = 0; for (auto &pair : *m_EventQueueRead) { - std::string &eventTypeName = pair.first; + std::string& eventTypeName = pair.first; std::shared_ptr event = pair.second; auto itpair = relays.equal_range(eventTypeName); - for (auto it2 = itpair.first; it2 != itpair.second; it2++) - { + for (auto it2 = itpair.first; it2 != itpair.second; it2++) { std::string name = it2->first; BaseEventRelay* relay = it2->second; relay->Receive(event); @@ -60,8 +62,8 @@ int EventBroker::Process(std::string contextTypeName) m_RelaysToSubscribe.clear(); // Process pending unsubscriptions - for (auto& r : m_RelaysToUnsubscribe) { - unsubscribeImmediate(*r); + for (auto& identifier : m_RelaysToUnsubscribe) { + unsubscribeImmediate(identifier); } m_RelaysToUnsubscribe.clear(); @@ -81,21 +83,26 @@ void EventBroker::Clear() void EventBroker::subscribeImmediate(BaseEventRelay& relay) { relay.m_Broker = this; + relay.m_EventID = m_NextEventID++; m_ContextRelays[relay.m_ContextTypeName].insert(std::make_pair(relay.m_EventTypeName, &relay)); } -void EventBroker::unsubscribeImmediate(BaseEventRelay& relay) +void EventBroker::unsubscribeImmediate(std::tuple identifier) { - auto contextIt = m_ContextRelays.find(relay.m_ContextTypeName); + EventID eventID; + ContextTypeName_t contextTypeName; + EventTypeName_t eventTypeName; + std::tie(eventID, contextTypeName, eventTypeName) = identifier; + + auto contextIt = m_ContextRelays.find(contextTypeName); if (contextIt == m_ContextRelays.end()) { return; } auto eventRelays = contextIt->second; - auto itpair = eventRelays.equal_range(relay.m_EventTypeName); + auto itpair = eventRelays.equal_range(eventTypeName); for (auto it = itpair.first; it != itpair.second; ++it) { - if (it->second == &relay) { - relay.m_Broker = nullptr; + if (it->second->m_EventID == eventID) { eventRelays.erase(it); break; }