Newer
Older
Import / projects / Gameloft / core / EventSystem / EventSystem.h
#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>