#pragma once
// DOCUMENTATION: https://confluence.gameloft.org/display/OT2/OT+-+Framework%3A+Event+System
#include <functional>
#include <list>
#include <vector>
#include <assert.h>
#include <unordered_map>
#include <mutex>
#include <atomic>
#include <memory>
#include <RKLog.h>
struct EventTypeSinkBase;
// Event receiver unique identification
// Can be derived from to mark classes as event receivers
class EventReceiverID
{
template <class T>
friend struct EventTypeSink;
public:
EventReceiverID();
// For tests
bool HasRegisteredSinks();
private:
typedef void(*UnregisterCallback)(EventReceiverID*, EventTypeSinkBase*);
void AddUnregisterCallback(UnregisterCallback call, EventTypeSinkBase* sink);
void RemoveUnregisterCallback(UnregisterCallback call, EventTypeSinkBase* sink);
void RemoveUnregisterCallback(EventTypeSinkBase* sink);
// Simple way around a virtual destructor
struct LifetimeTracker
{
~LifetimeTracker();
EventReceiverID* m_Receiver = nullptr;
std::vector<std::tuple<UnregisterCallback, EventTypeSinkBase*>> m_RegisteredCallbacks;
} m_Lifetime;
std::recursive_mutex m_CallbackLock;
};
// Interface for typed event sinks
struct EventTypeSinkBase
{
virtual ~EventTypeSinkBase() {}
virtual void DispatchQueued() = 0;
virtual bool HasListeners() = 0;
};
// Generic event sink
template <class T>
struct EventTypeSink : public EventTypeSinkBase
{
EventTypeSink()
{
m_IsDispatching = false; // #TODO OM: solve this properly x-platform
}
~EventTypeSink()
{
for (auto& receiver : m_Receivers)
{
// #TODO OM: remove the const_cast
const_cast<EventReceiverID*>(receiver.first)->RemoveUnregisterCallback(this);
}
}
// Register new listener for this type of event
// Events will be delivered to the given function. When the object pointed at by 'receiver'
// is destroyed, the listener will be automatically unregistered
void RegisterListener(EventReceiverID* receiver, std::function<void(const T&)>&& listener)
{
std::lock_guard<std::recursive_mutex> lock(m_EventLock);
assert(m_Receivers.find(receiver) == m_Receivers.end());
m_Callbacks.emplace_front(std::move(listener));
m_Receivers[receiver] = m_Callbacks.cbegin();
receiver->AddUnregisterCallback(EventTypeSink::UnregisterListenerInternalStatic, this);
}
// Unregister a previously registered event listener
void UnregisterListener(EventReceiverID* receiver)
{
UnregisterListenerInternal(receiver);
receiver->RemoveUnregisterCallback(EventTypeSink::UnregisterListenerInternalStatic, this);
}
// Immediately dispatch an event to listeners registered in this sink
void Dispatch(const T& data)
{
// Acquire recursion lock
bool exp = false;
if (!m_IsDispatching.compare_exchange_strong(exp, true))
{
// #TODO OM: we could try Enqueue() here, but that would change the expected order of execution...
RKLOG_FATAL("Recursive dispatch of events is not supported!");
assert(false);
return;
}
m_EventLock.lock();
// #TODO OM: super inefficient, should revisit this. This way is really nice because it takes
// care of potential deadlocks as well as possible recursion problems, but copying around
// std::function instances is quite heavy
CallbackList lst(m_Callbacks);
m_EventLock.unlock();
for (const auto& fn : lst)
{
fn(data);
}
// Release recursion lock
m_IsDispatching = false;
}
// Shortcut for dispatching an event with default constructed data structure
void Dispatch()
{
Dispatch(T());
}
// Enqueue event to be dispatched later using DispatchQueued()
void Enqueue(T data)
{
m_EventStore.Store(std::move(data));
}
// Shortcut to enqueue default-constructed version of an event
void Enqueue()
{
Enqueue(T());
}
// Dispatch all events previously enqueued using Enqueue()
void DispatchQueued() override
{
m_EventStore.DispatchAll(this);
}
// Check if this sink has any listeners handling its events
bool HasListeners() override
{
std::lock_guard<std::recursive_mutex> lock(m_EventLock);
return !m_Receivers.empty();
}
private:
void UnregisterListenerInternal(EventReceiverID* receiver)
{
// Registering new listeners while dispatching is fine. However, unregistering
// is potentially dangerous, as it could lead to situations where
// an event is delivered to a receiver that has already been deleted. At this
// moment, we just consider this to be a user error and warn about it
if (m_IsDispatching)
{
RKLOG_FATAL("Unregistering a listener while dispatching events to it is impossible!");
assert(false);
}
std::lock_guard<std::recursive_mutex> lock(m_EventLock);
auto it = m_Receivers.find(receiver);
if (it != m_Receivers.end())
{
m_Callbacks.erase(it->second);
m_Receivers.erase(it);
}
}
static void UnregisterListenerInternalStatic(EventReceiverID* receiver, EventTypeSinkBase* sink)
{
static_cast<EventTypeSink*>(sink)->UnregisterListenerInternal(receiver);
}
typedef std::list<std::function<void(const T&)>> CallbackList;
typedef std::unordered_map<const EventReceiverID*, typename CallbackList::const_iterator> ReceiverMap;
CallbackList m_Callbacks;
ReceiverMap m_Receivers;
std::recursive_mutex m_EventLock;
std::atomic_bool m_IsDispatching; // recursion guard
// Specialized event store for this event type
// #TODO OM: this is no longer needed, we can simplify the code by flattening it into EventTypeSink
class EventTypeStore
{
public:
EventTypeStore(std::recursive_mutex& mutex)
: m_Lock(mutex)
{}
void Store(T&& data)
{
std::lock_guard<std::recursive_mutex> lock(m_Lock);
if (!m_IsDispatching)
{
m_EvtData.emplace_back(std::move(data));
}
else
{
m_PendingEvtData.emplace_back(std::move(data));
}
}
void DispatchAll(EventTypeSinkBase* sink)
{
// #TODO OM: could we make this lock-less with atomics?
m_Lock.lock();
m_IsDispatching = true;
m_Lock.unlock();
for (auto& data : m_EvtData)
{
static_cast<EventTypeSink*>(sink)->Dispatch(data);
}
m_EvtData.clear();
m_Lock.lock();
m_IsDispatching = false;
if (!m_PendingEvtData.empty())
{
m_EvtData.swap(m_PendingEvtData);
}
m_Lock.unlock();
}
private:
std::vector<T> m_EvtData;
std::vector<T> m_PendingEvtData;
std::recursive_mutex& m_Lock;
bool m_IsDispatching = false;
};
EventTypeStore m_EventStore = EventTypeStore(m_EventLock);
};
// Untyped event sink that can dispatch any kind of event type
// Use this to direct events to specific listeners
struct EventSink
{
// #TODO OM: this should not be available on the server, presumably
static std::shared_ptr<EventSink> Global;
EventSink(bool global = false) : m_IsGlobal(global)
{
m_IsDispatching = false;
}
~EventSink()
{
std::lock_guard<std::recursive_mutex> lock(m_Lock);
if (!m_IsGlobal)
{
for (auto& sink : m_Sinks)
{
delete sink;
}
}
}
// Register new listener for the given event type
template <typename T>
void RegisterListener(EventReceiverID* receiver, std::function<void(const T&)>&& listener)
{
std::lock_guard<std::recursive_mutex> lock(m_Lock);
EventTypeSink<T>* sink = SinkForEventType<T>(true);
sink->RegisterListener(receiver, std::move(listener));
}
// Manually unregister a listener
template <typename T>
void UnregisterListener(EventReceiverID* receiver)
{
EventTypeSink<T>* sink = SinkForEventType<T>();
if (sink)
{
sink->UnregisterListener(receiver);
}
}
// Immediately dispatch the given event type to all listeners registered with this sink
template <typename T>
void Dispatch(const T& data)
{
EventTypeSink<T>* sink = SinkForEventType<T>();
if (sink)
{
sink->Dispatch(data);
}
}
// Shortcut to dispatch default constructed event type data
template <typename T>
void Dispatch()
{
Dispatch(T());
}
// Enqueue an event to be dispatched later using DispatchQueued()
template <typename T>
void Enqueue(T data)
{
EventTypeSink<T>* sink = SinkForEventType<T>();
if (sink)
{
sink->Enqueue(std::move(data));
}
}
// Shortcut to enqueue default constructed event type data
template <typename T>
void Enqueue()
{
Enqueue(T());
}
// Dispatch all events previously enqueued by Enqueue()
void DispatchQueued()
{
// #TODO OM: this function makes the use of a static variable, hoping that no one is going
// to be dispatching queued events from multiple threads. We detect that invalid situation here
bool exp = false;
if (!m_IsDispatching.compare_exchange_strong(exp, true))
{
RKLOG_FATAL("Recursive dispatch of events is not supported!");
assert(false);
return;
}
m_Lock.lock();
std::vector<EventTypeSinkBase*> sinks;
sinks = m_Sinks;
m_Lock.unlock();
for (auto& sink : sinks)
{
sink->DispatchQueued();
}
m_IsDispatching = false;
}
// Check if this sink has any listeners attached to it
bool HasListeners()
{
std::lock_guard<std::recursive_mutex> lock(m_Lock);
bool result = false;
for (auto it = m_Sinks.begin(); it != m_Sinks.end() && !result; ++it)
{
result |= (*it)->HasListeners();
}
return result;
}
// Check if this sink has any listeners for the given event type attached to it
template <typename T>
bool HasListenersForEvent()
{
std::lock_guard<std::recursive_mutex> lock(m_Lock);
EventTypeSink<T>* sink = SinkForEventType<T>();
if (sink)
{
return sink->HasListeners();
}
return false;
}
private:
int NextTypeId()
{
static int id = 0;
return id++;
}
template <typename T>
int GetIdFromType()
{
static int id = NextTypeId();
return id;
}
template <typename T>
EventTypeSink<T>* SinkForEventType(bool forceCreate = false)
{
std::lock_guard<std::recursive_mutex> lock(m_Lock);
int id = GetIdFromType<T>();
auto it = m_SinkMap.find(id);
if (it == m_SinkMap.end())
{
EventTypeSink<T>* sink = nullptr;
if (m_IsGlobal)
{
// Use the event type's default sink
sink = &T::DefaultSink;
}
else if (forceCreate)
{
// Create new sink
sink = new EventTypeSink<T>();
}
if (sink)
{
m_SinkMap[GetIdFromType<T>()] = sink;
m_Sinks.push_back(sink);
return sink;
}
else
{
return nullptr;
}
}
else
{
return static_cast<EventTypeSink<T>*>(it->second);
}
}
std::unordered_map<int, EventTypeSinkBase*> m_SinkMap;
std::vector<EventTypeSinkBase*> m_Sinks;
std::recursive_mutex m_Lock;
std::atomic_bool m_IsDispatching; // recursion guard
bool m_IsGlobal;
};
// Base for user event types
// This can be used to access the default event sink for an event type. See EventTypeSink<T> for
// member documentation. Note that use of this class is currently optional, but still recommended
template <class T>
struct EventBase
{
// #TODO OM: this should not be available on the server, presumably
// This is used in tandem with EventSink::Global to provide convenient access to a global event sink
static EventTypeSink<T> DefaultSink;
static void RegisterListener(EventReceiverID* receiver, std::function<void(const T&)>&& listener)
{
assert(EventSink::Global);
EventSink::Global->RegisterListener<T>(receiver, std::move(listener));
}
static void UnregisterListener(EventReceiverID* receiver)
{
assert(EventSink::Global);
EventSink::Global->UnregisterListener<T>(receiver);
}
static void Dispatch(const T& data)
{
assert(EventSink::Global);
EventSink::Global->Dispatch<T>(data);
}
static void Dispatch()
{
Dispatch(T());
}
static void Enqueue(T data)
{
assert(EventSink::Global);
EventSink::Global->Enqueue<T>(std::move(data));
}
static void Enqueue()
{
Enqueue(T());
}
static bool HasListeners()
{
assert(EventSink::Global);
return DefaultSink.HasListeners();
}
};
template <class T>
EventTypeSink<T> EventBase<T>::DefaultSink;
// Helper macros
#define DECLARE_EVENT(name) struct name : EventBase<name> {};
#define DECLARE_EVENT_STRUCT(name) struct name : EventBase<name>