Removed redundant 'Bloom' namespace from entire codebase

This commit is contained in:
Nav
2023-08-13 15:47:51 +01:00
parent 0935ba65cf
commit 5896306f1a
555 changed files with 6254 additions and 6510 deletions

View File

@@ -2,110 +2,107 @@
#include "src/Logger/Logger.hpp"
namespace Bloom
{
using namespace Bloom::Events;
using namespace Events;
std::set<Events::EventType> EventListener::getRegisteredEventTypes() {
return *(this->registeredEventTypes.accessor());
}
std::set<Events::EventType> EventListener::getRegisteredEventTypes() {
return *(this->registeredEventTypes.accessor());
}
void EventListener::registerEvent(SharedGenericEventPointer event) {
Logger::debug(
"Event \"" + event->getName() + "\" (" + std::to_string(event->id) + ") registered for listener "
+ this->name
);
void EventListener::registerEvent(SharedGenericEventPointer event) {
Logger::debug(
"Event \"" + event->getName() + "\" (" + std::to_string(event->id) + ") registered for listener "
+ this->name
);
auto eventQueueByTypeAccessor = this->eventQueueByEventType.accessor();
auto& eventQueueByType = *(eventQueueByTypeAccessor);
auto eventQueueByTypeAccessor = this->eventQueueByEventType.accessor();
auto& eventQueueByType = *(eventQueueByTypeAccessor);
eventQueueByType[event->getType()].push(std::move(event));
this->eventQueueByEventTypeCV.notify_all();
eventQueueByType[event->getType()].push(std::move(event));
this->eventQueueByEventTypeCV.notify_all();
if (this->interruptEventNotifier != nullptr) {
this->interruptEventNotifier->notify();
}
}
void EventListener::waitAndDispatch(int msTimeout) {
{
auto queueLock = this->eventQueueByEventType.lock();
const auto& eventQueueByType = this->eventQueueByEventType.unsafeReference();
const auto registeredEventTypes = this->getRegisteredEventTypes();
std::optional<SharedGenericEventPointer> event;
const auto eventsFound = [&registeredEventTypes, &event, &eventQueueByType]() -> bool {
for (auto& eventQueue: eventQueueByType) {
if (registeredEventTypes.contains(eventQueue.first) && !eventQueue.second.empty()) {
return true;
}
}
return false;
};
if (msTimeout > 0) {
this->eventQueueByEventTypeCV.wait_for(queueLock, std::chrono::milliseconds(msTimeout), eventsFound);
} else {
this->eventQueueByEventTypeCV.wait(queueLock, eventsFound);
}
}
this->dispatchCurrentEvents();
}
void EventListener::dispatchEvent(const SharedGenericEventPointer& event) {
Logger::debug("Dispatching event " + event->getName() + " (" + std::to_string(event->id) + ").");
// Dispatch the event to all registered handlers
auto callbacks = std::vector<std::function<void(const Events::Event&)>>();
{
const auto callbackMappingAccessor = this->eventTypeToCallbacksMapping.accessor();
const auto callbacksIt = callbackMappingAccessor->find(event->getType());
if (callbacksIt != callbackMappingAccessor->end()) {
callbacks = callbacksIt->second;
}
}
for (auto& callback : callbacks) {
callback(*(event.get()));
}
}
void EventListener::dispatchCurrentEvents() {
auto events = this->getEvents();
for (const auto& event: events) {
dispatchEvent(event);
}
}
std::vector<SharedGenericEventPointer> EventListener::getEvents() {
auto eventQueueByType = this->eventQueueByEventType.accessor();
std::vector<SharedGenericEventPointer> output;
for (auto& eventQueue: *eventQueueByType) {
while (!eventQueue.second.empty()) {
output.push_back(std::move(eventQueue.second.front()));
eventQueue.second.pop();
}
}
std::sort(
output.begin(),
output.end(),
[] (const SharedGenericEventPointer& a, const SharedGenericEventPointer& b) {
return a->id < b->id;
}
);
return output;
}
void EventListener::clearAllCallbacks() {
this->eventTypeToCallbacksMapping.accessor()->clear();
if (this->interruptEventNotifier != nullptr) {
this->interruptEventNotifier->notify();
}
}
void EventListener::waitAndDispatch(int msTimeout) {
{
auto queueLock = this->eventQueueByEventType.lock();
const auto& eventQueueByType = this->eventQueueByEventType.unsafeReference();
const auto registeredEventTypes = this->getRegisteredEventTypes();
std::optional<SharedGenericEventPointer> event;
const auto eventsFound = [&registeredEventTypes, &event, &eventQueueByType]() -> bool {
for (auto& eventQueue: eventQueueByType) {
if (registeredEventTypes.contains(eventQueue.first) && !eventQueue.second.empty()) {
return true;
}
}
return false;
};
if (msTimeout > 0) {
this->eventQueueByEventTypeCV.wait_for(queueLock, std::chrono::milliseconds(msTimeout), eventsFound);
} else {
this->eventQueueByEventTypeCV.wait(queueLock, eventsFound);
}
}
this->dispatchCurrentEvents();
}
void EventListener::dispatchEvent(const SharedGenericEventPointer& event) {
Logger::debug("Dispatching event " + event->getName() + " (" + std::to_string(event->id) + ").");
// Dispatch the event to all registered handlers
auto callbacks = std::vector<std::function<void(const Events::Event&)>>();
{
const auto callbackMappingAccessor = this->eventTypeToCallbacksMapping.accessor();
const auto callbacksIt = callbackMappingAccessor->find(event->getType());
if (callbacksIt != callbackMappingAccessor->end()) {
callbacks = callbacksIt->second;
}
}
for (auto& callback : callbacks) {
callback(*(event.get()));
}
}
void EventListener::dispatchCurrentEvents() {
auto events = this->getEvents();
for (const auto& event: events) {
dispatchEvent(event);
}
}
std::vector<SharedGenericEventPointer> EventListener::getEvents() {
auto eventQueueByType = this->eventQueueByEventType.accessor();
std::vector<SharedGenericEventPointer> output;
for (auto& eventQueue: *eventQueueByType) {
while (!eventQueue.second.empty()) {
output.push_back(std::move(eventQueue.second.front()));
eventQueue.second.pop();
}
}
std::sort(
output.begin(),
output.end(),
[] (const SharedGenericEventPointer& a, const SharedGenericEventPointer& b) {
return a->id < b->id;
}
);
return output;
}
void EventListener::clearAllCallbacks() {
this->eventTypeToCallbacksMapping.accessor()->clear();
}

View File

@@ -17,347 +17,344 @@
#include "src/Helpers/Synchronised.hpp"
#include "src/Helpers/NotifierInterface.hpp"
namespace Bloom
/**
* The EventListener allows specific threads the ability to handle any events, from other threads, that
* are of interest.
*
* Usage is fairly simple:
* - Thread A creates an instance to EventListener.
* - Thread A registers callbacks for specific event types via EventListener::registerCallbackForEventType().
* - Thread A waits for events via EventListener::waitAndDispatch() (or similar methods).
* - Thread B triggers an event of a type that Thread A registered a callback for.
* - Thread A is woken and the triggered event is dispatched to the registered callbacks for the event type.
*
* Events are distributed with shared pointers to const event objects, as each registered handler will own
* the memory (but should never make any changes to it, hence the const state). Once the final handler has
* processed the event, the shared pointer reference count will reach 0 and the event object will be destroyed.
* The type of object within the shared pointers will match that of the specific event type, once it reaches the
* callback functions. We do this by downcasting the events before we dispatch them to the callback functions.
*
* @TODO Whilst event managing should be thread safe, the same cannot be said for all of the event types.
* We need to ensure that all event types are thread safe.
*/
class EventListener
{
/**
* The EventListener allows specific threads the ability to handle any events, from other threads, that
* are of interest.
*
* Usage is fairly simple:
* - Thread A creates an instance to EventListener.
* - Thread A registers callbacks for specific event types via EventListener::registerCallbackForEventType().
* - Thread A waits for events via EventListener::waitAndDispatch() (or similar methods).
* - Thread B triggers an event of a type that Thread A registered a callback for.
* - Thread A is woken and the triggered event is dispatched to the registered callbacks for the event type.
*
* Events are distributed with shared pointers to const event objects, as each registered handler will own
* the memory (but should never make any changes to it, hence the const state). Once the final handler has
* processed the event, the shared pointer reference count will reach 0 and the event object will be destroyed.
* The type of object within the shared pointers will match that of the specific event type, once it reaches the
* callback functions. We do this by downcasting the events before we dispatch them to the callback functions.
*
* @TODO Whilst event managing should be thread safe, the same cannot be said for all of the event types.
* We need to ensure that all event types are thread safe.
*/
class EventListener
{
public:
explicit EventListener(std::string name): name(std::move(name)) {};
public:
explicit EventListener(std::string name): name(std::move(name)) {};
std::size_t getId() const {
return this->id;
};
/**
* Generates a list of event types currently registered in the listener.
*
* @return
*/
std::set<Events::EventType> getRegisteredEventTypes();
bool isEventTypeRegistered(Events::EventType eventType) {
return this->registeredEventTypes.accessor()->contains(eventType);
};
/**
* Registers an event type for the listener.
*
* Any events of EventType that are triggered from the point of calling this function, will be stored in
* listener queue until they are dispatched to a callback, or retrieved via a call to this->waitForEvent() or
* similar.
*
* @tparam EventType
*/
template<class EventType>
void registerEventType() {
this->registeredEventTypes.accessor()->insert(EventType::type);
}
template<class EventType>
void deRegisterEventType() {
this->registeredEventTypes.accessor()->erase(EventType::type);
}
/**
* Registers an event with the event listener
*
* @param event
*/
void registerEvent(Events::SharedGenericEventPointer event);
void setInterruptEventNotifier(NotifierInterface* interruptEventNotifier) {
this->interruptEventNotifier = interruptEventNotifier;
}
[[nodiscard]] NotifierInterface* getInterruptEventNotifier() {
return this->interruptEventNotifier;
}
/**
* Registers a callback function for an event type. The callback function will be
* invoked upon an event of type EventType being dispatched to the listener.
*
* @tparam EventType
* @param callback
*/
template<class EventType>
void registerCallbackForEventType(std::function<void(const EventType&)> callback) {
// We encapsulate the callback in a lambda to handle the downcasting.
std::function<void(const Events::Event&)> parentCallback =
[callback] (const Events::Event& event) {
// Downcast the event to the expected type
callback(dynamic_cast<const EventType&>(event));
}
;
auto mappingAccessor = this->eventTypeToCallbacksMapping.accessor();
auto& mapping = *(mappingAccessor);
mapping[EventType::type].push_back(parentCallback);
this->template registerEventType<EventType>();
}
/**
* Clears all registered callbacks for a specific event type.
*
* @tparam EventType
*/
template<class EventType>
void deregisterCallbacksForEventType() {
static_assert(
std::is_base_of<Events::Event, EventType>::value,
"EventType is not a derivation of Event"
);
{
auto mappingAccessor = this->eventTypeToCallbacksMapping.accessor();
auto& mapping = *(mappingAccessor);
if (mapping.contains(EventType::type)) {
mapping.at(EventType::type).clear();
}
}
this->registeredEventTypes.accessor()->erase(EventType::type);
auto eventQueueByType = this->eventQueueByEventType.accessor();
if (eventQueueByType->contains(EventType::type)) {
eventQueueByType->erase(EventType::type);
}
}
/**
* Waits for an event (of type EventTypeA, EventTypeB or EventTypeC) to be dispatched to the listener.
* Then returns the event object. If timeout is reached, an std::nullopt object will be returned.
*
* @tparam EventType
* @param timeout
* Millisecond duration to wait for an event to be dispatched to the listener.
* A value of std::nullopt will disable the timeout, meaning the function will block until the appropriate
* event has been dispatched.
*
* @param correlationId
* If a correlation ID is provided, this function will ignore any events that do not contain a matching
* correlation ID.
*
* @return
* If only one event type is passed (EventTypeA), an std::optional will be returned, carrying an
* event pointer to that event type (or std::nullopt if timeout was reached). If numerous event types are
* passed, an std::optional will carry an std::variant of the event pointers to the passed event types
* (or std::nullopt if timeout was reached).
*/
template<class EventTypeA, class EventTypeB = EventTypeA, class EventTypeC = EventTypeB>
auto waitForEvent(
std::optional<std::chrono::milliseconds> timeout = std::nullopt
) {
// Different return types, depending on how many event type arguments are passed in.
using MonoType = std::optional<Events::SharedEventPointer<EventTypeA>>;
using BiVariantType = std::optional<
std::variant<
std::monostate,
Events::SharedEventPointer<EventTypeA>,
Events::SharedEventPointer<EventTypeB>
>
>;
using TriVariantType = std::optional<
std::variant<
std::monostate,
Events::SharedEventPointer<EventTypeA>,
Events::SharedEventPointer<EventTypeB>,
Events::SharedEventPointer<EventTypeC>
>
>;
using ReturnType = typename std::conditional<
!std::is_same_v<EventTypeA, EventTypeB> && !std::is_same_v<EventTypeB, EventTypeC>,
TriVariantType,
typename std::conditional<!std::is_same_v<EventTypeA, EventTypeB>,
BiVariantType,
MonoType
>::type
>::type;
ReturnType output = std::nullopt;
auto queueLock = this->eventQueueByEventType.lock();
auto& eventQueueByType = this->eventQueueByEventType.unsafeReference();
auto eventTypes = std::set<Events::EventType>({EventTypeA::type});
auto eventTypesToDeRegister = std::set<Events::EventType>();
if constexpr (!std::is_same_v<EventTypeA, EventTypeB>) {
static_assert(
std::is_base_of_v<Events::Event, EventTypeB>,
"All event types must be derived from the Event base class."
);
eventTypes.insert(EventTypeB::type);
}
if constexpr (!std::is_same_v<EventTypeB, EventTypeC>) {
static_assert(
std::is_base_of_v<Events::Event, EventTypeC>,
"All event types must be derived from the Event base class."
);
eventTypes.insert(EventTypeC::type);
}
{
auto registeredEventTypes = this->registeredEventTypes.accessor();
for (const auto& eventType : eventTypes) {
if (!registeredEventTypes->contains(eventType)) {
registeredEventTypes->insert(eventType);
eventTypesToDeRegister.insert(eventType);
}
}
}
Events::SharedGenericEventPointer foundEvent = nullptr;
auto eventsFound = [&eventTypes, &eventQueueByType, &foundEvent] () -> bool {
for (const auto& eventType : eventTypes) {
if (eventQueueByType.find(eventType) != eventQueueByType.end()
&& !eventQueueByType.find(eventType)->second.empty()
) {
auto& queue = eventQueueByType.find(eventType)->second;
while (!queue.empty()) {
foundEvent = queue.front();
queue.pop();
return true;
}
}
}
return false;
};
if (timeout.has_value()) {
this->eventQueueByEventTypeCV.wait_for(queueLock, timeout.value(), eventsFound);
} else {
this->eventQueueByEventTypeCV.wait(queueLock, eventsFound);
}
if (!eventTypesToDeRegister.empty()) {
auto registeredEventTypes = this->registeredEventTypes.accessor();
for (const auto& eventType : eventTypesToDeRegister) {
registeredEventTypes->erase(eventType);
}
}
if (foundEvent != nullptr) {
// If we're looking for multiple event types, use an std::variant.
if constexpr (!std::is_same_v<EventTypeA, EventTypeB> || !std::is_same_v<EventTypeB, EventTypeC>) {
if (foundEvent->getType() == EventTypeA::type) {
output = std::optional<typename decltype(output)::value_type>(
std::dynamic_pointer_cast<const EventTypeA>(foundEvent)
);
} else if constexpr (!std::is_same_v<EventTypeA, EventTypeB>) {
if (foundEvent->getType() == EventTypeB::type) {
output = std::optional<typename decltype(output)::value_type>(
std::dynamic_pointer_cast<const EventTypeB>(foundEvent)
);
}
}
if constexpr (!std::is_same_v<EventTypeB, EventTypeC>) {
if (foundEvent->getType() == EventTypeC::type) {
output = std::optional<typename decltype(output)::value_type>(
std::dynamic_pointer_cast<const EventTypeC>(foundEvent)
);
}
}
} else {
if (foundEvent->getType() == EventTypeA::type) {
output = std::dynamic_pointer_cast<const EventTypeA>(foundEvent);
}
}
}
return output;
}
/**
* Waits for new events with types that have been registered with registerCallbackForEventType() and dispatches
* any events to their appropriate registered callback handlers.
*
* This method will return after one event has been handled.
*/
void waitAndDispatch(int msTimeout = 0);
void dispatchEvent(const Events::SharedGenericEventPointer& event);
void dispatchCurrentEvents();
/**
* Removes all callbacks registered for the event listener.
*/
void clearAllCallbacks();
private:
/**
* Human readable name for event listeners.
*
* TODO: This was useful during development, but may no longer be needed.
*/
std::string name;
static inline std::atomic<std::size_t> lastId = 0;
std::size_t id = ++(EventListener::lastId);
/**
* Holds all events registered to this listener.
*
* Events are grouped by event type, and removed from their queue just *before* the dispatching to
* registered handlers begins.
*/
Synchronised<std::map<Events::EventType, std::queue<Events::SharedGenericEventPointer>>> eventQueueByEventType;
std::condition_variable eventQueueByEventTypeCV;
/**
* A mapping of event types to a vector of callback functions. Events will be dispatched to these
* callback functions, during a call to EventListener::dispatchEvent().
*
* Each callback will be passed a reference to the event (we wrap all registered callbacks in a lambda, where
* we perform a downcast before invoking the callback. See EventListener::registerCallbackForEventType()
* for more)
*/
Synchronised<std::map<Events::EventType, std::vector<std::function<void(const Events::Event&)>>>> eventTypeToCallbacksMapping;
Synchronised<std::set<Events::EventType>> registeredEventTypes;
NotifierInterface* interruptEventNotifier = nullptr;
std::vector<Events::SharedGenericEventPointer> getEvents();
std::size_t getId() const {
return this->id;
};
/**
* Every component within Bloom that requires access to events will possess an instance to the EventListener class.
* At some point (usually at component initialisation), the event listener will be registered with the EventManager,
* using EventManager::registerListener(). Upon registering the listener, the EventManager obtains partial ownership
* of the event listener.
* Generates a list of event types currently registered in the listener.
*
* In other words, event listeners are managed by numerous entities and that's why we use a shared_ptr here.
* @return
*/
using EventListenerPointer = std::shared_ptr<EventListener>;
}
std::set<Events::EventType> getRegisteredEventTypes();
bool isEventTypeRegistered(Events::EventType eventType) {
return this->registeredEventTypes.accessor()->contains(eventType);
};
/**
* Registers an event type for the listener.
*
* Any events of EventType that are triggered from the point of calling this function, will be stored in
* listener queue until they are dispatched to a callback, or retrieved via a call to this->waitForEvent() or
* similar.
*
* @tparam EventType
*/
template<class EventType>
void registerEventType() {
this->registeredEventTypes.accessor()->insert(EventType::type);
}
template<class EventType>
void deRegisterEventType() {
this->registeredEventTypes.accessor()->erase(EventType::type);
}
/**
* Registers an event with the event listener
*
* @param event
*/
void registerEvent(Events::SharedGenericEventPointer event);
void setInterruptEventNotifier(NotifierInterface* interruptEventNotifier) {
this->interruptEventNotifier = interruptEventNotifier;
}
[[nodiscard]] NotifierInterface* getInterruptEventNotifier() {
return this->interruptEventNotifier;
}
/**
* Registers a callback function for an event type. The callback function will be
* invoked upon an event of type EventType being dispatched to the listener.
*
* @tparam EventType
* @param callback
*/
template<class EventType>
void registerCallbackForEventType(std::function<void(const EventType&)> callback) {
// We encapsulate the callback in a lambda to handle the downcasting.
std::function<void(const Events::Event&)> parentCallback =
[callback] (const Events::Event& event) {
// Downcast the event to the expected type
callback(dynamic_cast<const EventType&>(event));
}
;
auto mappingAccessor = this->eventTypeToCallbacksMapping.accessor();
auto& mapping = *(mappingAccessor);
mapping[EventType::type].push_back(parentCallback);
this->template registerEventType<EventType>();
}
/**
* Clears all registered callbacks for a specific event type.
*
* @tparam EventType
*/
template<class EventType>
void deregisterCallbacksForEventType() {
static_assert(
std::is_base_of<Events::Event, EventType>::value,
"EventType is not a derivation of Event"
);
{
auto mappingAccessor = this->eventTypeToCallbacksMapping.accessor();
auto& mapping = *(mappingAccessor);
if (mapping.contains(EventType::type)) {
mapping.at(EventType::type).clear();
}
}
this->registeredEventTypes.accessor()->erase(EventType::type);
auto eventQueueByType = this->eventQueueByEventType.accessor();
if (eventQueueByType->contains(EventType::type)) {
eventQueueByType->erase(EventType::type);
}
}
/**
* Waits for an event (of type EventTypeA, EventTypeB or EventTypeC) to be dispatched to the listener.
* Then returns the event object. If timeout is reached, an std::nullopt object will be returned.
*
* @tparam EventType
* @param timeout
* Millisecond duration to wait for an event to be dispatched to the listener.
* A value of std::nullopt will disable the timeout, meaning the function will block until the appropriate
* event has been dispatched.
*
* @param correlationId
* If a correlation ID is provided, this function will ignore any events that do not contain a matching
* correlation ID.
*
* @return
* If only one event type is passed (EventTypeA), an std::optional will be returned, carrying an
* event pointer to that event type (or std::nullopt if timeout was reached). If numerous event types are
* passed, an std::optional will carry an std::variant of the event pointers to the passed event types
* (or std::nullopt if timeout was reached).
*/
template<class EventTypeA, class EventTypeB = EventTypeA, class EventTypeC = EventTypeB>
auto waitForEvent(
std::optional<std::chrono::milliseconds> timeout = std::nullopt
) {
// Different return types, depending on how many event type arguments are passed in.
using MonoType = std::optional<Events::SharedEventPointer<EventTypeA>>;
using BiVariantType = std::optional<
std::variant<
std::monostate,
Events::SharedEventPointer<EventTypeA>,
Events::SharedEventPointer<EventTypeB>
>
>;
using TriVariantType = std::optional<
std::variant<
std::monostate,
Events::SharedEventPointer<EventTypeA>,
Events::SharedEventPointer<EventTypeB>,
Events::SharedEventPointer<EventTypeC>
>
>;
using ReturnType = typename std::conditional<
!std::is_same_v<EventTypeA, EventTypeB> && !std::is_same_v<EventTypeB, EventTypeC>,
TriVariantType,
typename std::conditional<!std::is_same_v<EventTypeA, EventTypeB>,
BiVariantType,
MonoType
>::type
>::type;
ReturnType output = std::nullopt;
auto queueLock = this->eventQueueByEventType.lock();
auto& eventQueueByType = this->eventQueueByEventType.unsafeReference();
auto eventTypes = std::set<Events::EventType>({EventTypeA::type});
auto eventTypesToDeRegister = std::set<Events::EventType>();
if constexpr (!std::is_same_v<EventTypeA, EventTypeB>) {
static_assert(
std::is_base_of_v<Events::Event, EventTypeB>,
"All event types must be derived from the Event base class."
);
eventTypes.insert(EventTypeB::type);
}
if constexpr (!std::is_same_v<EventTypeB, EventTypeC>) {
static_assert(
std::is_base_of_v<Events::Event, EventTypeC>,
"All event types must be derived from the Event base class."
);
eventTypes.insert(EventTypeC::type);
}
{
auto registeredEventTypes = this->registeredEventTypes.accessor();
for (const auto& eventType : eventTypes) {
if (!registeredEventTypes->contains(eventType)) {
registeredEventTypes->insert(eventType);
eventTypesToDeRegister.insert(eventType);
}
}
}
Events::SharedGenericEventPointer foundEvent = nullptr;
auto eventsFound = [&eventTypes, &eventQueueByType, &foundEvent] () -> bool {
for (const auto& eventType : eventTypes) {
if (eventQueueByType.find(eventType) != eventQueueByType.end()
&& !eventQueueByType.find(eventType)->second.empty()
) {
auto& queue = eventQueueByType.find(eventType)->second;
while (!queue.empty()) {
foundEvent = queue.front();
queue.pop();
return true;
}
}
}
return false;
};
if (timeout.has_value()) {
this->eventQueueByEventTypeCV.wait_for(queueLock, timeout.value(), eventsFound);
} else {
this->eventQueueByEventTypeCV.wait(queueLock, eventsFound);
}
if (!eventTypesToDeRegister.empty()) {
auto registeredEventTypes = this->registeredEventTypes.accessor();
for (const auto& eventType : eventTypesToDeRegister) {
registeredEventTypes->erase(eventType);
}
}
if (foundEvent != nullptr) {
// If we're looking for multiple event types, use an std::variant.
if constexpr (!std::is_same_v<EventTypeA, EventTypeB> || !std::is_same_v<EventTypeB, EventTypeC>) {
if (foundEvent->getType() == EventTypeA::type) {
output = std::optional<typename decltype(output)::value_type>(
std::dynamic_pointer_cast<const EventTypeA>(foundEvent)
);
} else if constexpr (!std::is_same_v<EventTypeA, EventTypeB>) {
if (foundEvent->getType() == EventTypeB::type) {
output = std::optional<typename decltype(output)::value_type>(
std::dynamic_pointer_cast<const EventTypeB>(foundEvent)
);
}
}
if constexpr (!std::is_same_v<EventTypeB, EventTypeC>) {
if (foundEvent->getType() == EventTypeC::type) {
output = std::optional<typename decltype(output)::value_type>(
std::dynamic_pointer_cast<const EventTypeC>(foundEvent)
);
}
}
} else {
if (foundEvent->getType() == EventTypeA::type) {
output = std::dynamic_pointer_cast<const EventTypeA>(foundEvent);
}
}
}
return output;
}
/**
* Waits for new events with types that have been registered with registerCallbackForEventType() and dispatches
* any events to their appropriate registered callback handlers.
*
* This method will return after one event has been handled.
*/
void waitAndDispatch(int msTimeout = 0);
void dispatchEvent(const Events::SharedGenericEventPointer& event);
void dispatchCurrentEvents();
/**
* Removes all callbacks registered for the event listener.
*/
void clearAllCallbacks();
private:
/**
* Human readable name for event listeners.
*
* TODO: This was useful during development, but may no longer be needed.
*/
std::string name;
static inline std::atomic<std::size_t> lastId = 0;
std::size_t id = ++(EventListener::lastId);
/**
* Holds all events registered to this listener.
*
* Events are grouped by event type, and removed from their queue just *before* the dispatching to
* registered handlers begins.
*/
Synchronised<std::map<Events::EventType, std::queue<Events::SharedGenericEventPointer>>> eventQueueByEventType;
std::condition_variable eventQueueByEventTypeCV;
/**
* A mapping of event types to a vector of callback functions. Events will be dispatched to these
* callback functions, during a call to EventListener::dispatchEvent().
*
* Each callback will be passed a reference to the event (we wrap all registered callbacks in a lambda, where
* we perform a downcast before invoking the callback. See EventListener::registerCallbackForEventType()
* for more)
*/
Synchronised<std::map<Events::EventType, std::vector<std::function<void(const Events::Event&)>>>> eventTypeToCallbacksMapping;
Synchronised<std::set<Events::EventType>> registeredEventTypes;
NotifierInterface* interruptEventNotifier = nullptr;
std::vector<Events::SharedGenericEventPointer> getEvents();
};
/**
* Every component within Bloom that requires access to events will possess an instance to the EventListener class.
* At some point (usually at component initialisation), the event listener will be registered with the EventManager,
* using EventManager::registerListener(). Upon registering the listener, the EventManager obtains partial ownership
* of the event listener.
*
* In other words, event listeners are managed by numerous entities and that's why we use a shared_ptr here.
*/
using EventListenerPointer = std::shared_ptr<EventListener>;

View File

@@ -1,36 +1,33 @@
#include "EventManager.hpp"
namespace Bloom
{
void EventManager::registerListener(std::shared_ptr<EventListener> listener) {
auto registerListenersLock = std::unique_lock(EventManager::registerListenerMutex);
EventManager::registeredListeners.insert(std::pair(listener->getId(), std::move(listener)));
}
void EventManager::registerListener(std::shared_ptr<EventListener> listener) {
auto registerListenersLock = std::unique_lock(EventManager::registerListenerMutex);
EventManager::registeredListeners.insert(std::pair(listener->getId(), std::move(listener)));
}
void EventManager::deregisterListener(size_t listenerId) {
auto registerListenersLock = std::unique_lock(EventManager::registerListenerMutex);
EventManager::registeredListeners.erase(listenerId);
}
void EventManager::deregisterListener(size_t listenerId) {
auto registerListenersLock = std::unique_lock(EventManager::registerListenerMutex);
EventManager::registeredListeners.erase(listenerId);
}
void EventManager::triggerEvent(const std::shared_ptr<const Events::Event>& event) {
auto registerListenersLock = std::unique_lock(EventManager::registerListenerMutex);
void EventManager::triggerEvent(const std::shared_ptr<const Events::Event>& event) {
auto registerListenersLock = std::unique_lock(EventManager::registerListenerMutex);
for (const auto&[listenerId, listener] : EventManager::registeredListeners) {
if (listener->isEventTypeRegistered(event->getType())) {
listener->registerEvent(event);
}
for (const auto&[listenerId, listener] : EventManager::registeredListeners) {
if (listener->isEventTypeRegistered(event->getType())) {
listener->registerEvent(event);
}
}
bool EventManager::isEventTypeListenedFor(Events::EventType eventType) {
auto registerListenersLock = std::unique_lock(EventManager::registerListenerMutex);
for (const auto& [listenerId, listener] : EventManager::registeredListeners) {
if (listener->isEventTypeRegistered(eventType)) {
return true;
}
}
return false;
}
}
bool EventManager::isEventTypeListenedFor(Events::EventType eventType) {
auto registerListenersLock = std::unique_lock(EventManager::registerListenerMutex);
for (const auto& [listenerId, listener] : EventManager::registeredListeners) {
if (listener->isEventTypeRegistered(eventType)) {
return true;
}
}
return false;
}

View File

@@ -6,55 +6,52 @@
#include "Events/Events.hpp"
#include "EventListener.hpp"
namespace Bloom
/**
* The static EventManager class provides a method of dispatching events to a set of listeners.
*/
class EventManager
{
public:
/**
* The static EventManager class provides a method of dispatching events to a set of listeners.
* Registers an EventListener instance with the manager.
*
* All EventListener instances must be registered with the EventManager before any events can
* be dispatched to them.
*
* The EventManager possesses partial ownership of the EventListener. This is why we use a shared_ptr here.
*
* @param listenerName
*/
class EventManager
{
public:
/**
* Registers an EventListener instance with the manager.
*
* All EventListener instances must be registered with the EventManager before any events can
* be dispatched to them.
*
* The EventManager possesses partial ownership of the EventListener. This is why we use a shared_ptr here.
*
* @param listenerName
*/
static void registerListener(std::shared_ptr<EventListener> listener);
static void registerListener(std::shared_ptr<EventListener> listener);
/**
* Deregister an EventListener instance.
*
* @param listenerId
* The ID of the EventListener to deregister. See EventListener::getId();
*/
static void deregisterListener(size_t listenerId);
/**
* Deregister an EventListener instance.
*
* @param listenerId
* The ID of the EventListener to deregister. See EventListener::getId();
*/
static void deregisterListener(size_t listenerId);
/**
* Dispatches an event to all registered listeners, if they have registered an interest in the event type.
* See EventListener::registeredEventTypes for more.
*
* @param event
*/
static void triggerEvent(const Events::SharedGenericEventPointer& event);
/**
* Dispatches an event to all registered listeners, if they have registered an interest in the event type.
* See EventListener::registeredEventTypes for more.
*
* @param event
*/
static void triggerEvent(const Events::SharedGenericEventPointer& event);
/**
* Checks if any registered listener is listening for a particular event type.
*
* @param eventType
* @return
*/
static bool isEventTypeListenedFor(Events::EventType eventType);
/**
* Checks if any registered listener is listening for a particular event type.
*
* @param eventType
* @return
*/
static bool isEventTypeListenedFor(Events::EventType eventType);
private:
/**
* A mapping of listener IDs to registered listeners. Each registered listener is given an interger ID.
*/
static inline std::map<size_t, std::shared_ptr<EventListener>> registeredListeners;
static inline std::mutex registerListenerMutex;
};
}
private:
/**
* A mapping of listener IDs to registered listeners. Each registered listener is given an interger ID.
*/
static inline std::map<size_t, std::shared_ptr<EventListener>> registeredListeners;
static inline std::mutex registerListenerMutex;
};

View File

@@ -5,7 +5,7 @@
#include "Event.hpp"
#include "src/Helpers/Thread.hpp"
namespace Bloom::Events
namespace Events
{
class DebugServerThreadStateChanged: public Event
{

View File

@@ -4,7 +4,7 @@
#include "Event.hpp"
namespace Bloom::Events
namespace Events
{
class DebugSessionFinished: public Event
{

View File

@@ -4,7 +4,7 @@
#include "Event.hpp"
namespace Bloom::Events
namespace Events
{
class DebugSessionStarted: public Event
{

View File

@@ -8,7 +8,7 @@
#include "src/Services/DateTimeService.hpp"
namespace Bloom::Events
namespace Events
{
static_assert(std::atomic<int>::is_always_lock_free);

View File

@@ -24,7 +24,7 @@
#include "InsightMainWindowClosed.hpp"
#endif
namespace Bloom::Events
namespace Events
{
template <class EventType>
using SharedEventPointer = std::shared_ptr<const EventType>;

View File

@@ -5,7 +5,7 @@
#include "Event.hpp"
namespace Bloom::Events
namespace Events
{
class InsightActivationRequested: public Event
{

View File

@@ -4,7 +4,7 @@
#include "Event.hpp"
namespace Bloom::Events
namespace Events
{
class InsightMainWindowClosed: public Event
{

View File

@@ -5,7 +5,7 @@
#include "Event.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace Bloom::Events
namespace Events
{
class MemoryWrittenToTarget: public Event
{

View File

@@ -4,7 +4,7 @@
#include "Event.hpp"
namespace Bloom::Events
namespace Events
{
class ProgrammingModeDisabled: public Event
{

View File

@@ -4,7 +4,7 @@
#include "Event.hpp"
namespace Bloom::Events
namespace Events
{
class ProgrammingModeEnabled: public Event
{

View File

@@ -5,7 +5,7 @@
#include "Event.hpp"
#include "src/Targets/TargetRegister.hpp"
namespace Bloom::Events
namespace Events
{
class RegistersWrittenToTarget: public Event
{

View File

@@ -4,7 +4,7 @@
#include "Event.hpp"
namespace Bloom::Events
namespace Events
{
class ShutdownApplication: public Event
{

View File

@@ -4,7 +4,7 @@
#include "Event.hpp"
namespace Bloom::Events
namespace Events
{
class ShutdownDebugServer: public Event
{

View File

@@ -4,7 +4,7 @@
#include "Event.hpp"
namespace Bloom::Events
namespace Events
{
class ShutdownTargetController: public Event
{

View File

@@ -4,7 +4,7 @@
#include "Event.hpp"
namespace Bloom::Events
namespace Events
{
class TargetControllerErrorOccurred: public Event
{

View File

@@ -5,7 +5,7 @@
#include "Event.hpp"
#include "src/Helpers/Thread.hpp"
namespace Bloom::Events
namespace Events
{
class TargetControllerThreadStateChanged: public Event
{

View File

@@ -4,7 +4,7 @@
#include "Event.hpp"
namespace Bloom::Events
namespace Events
{
class TargetExecutionResumed: public Event
{

View File

@@ -8,7 +8,7 @@
#include "src/Targets/TargetBreakpoint.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace Bloom::Events
namespace Events
{
class TargetExecutionStopped: public Event
{

View File

@@ -4,7 +4,7 @@
#include "Event.hpp"
namespace Bloom::Events
namespace Events
{
class TargetReset: public Event
{