BsEvent.h 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. #pragma once
  2. #include "BsPrerequisitesUtil.h"
  3. #include "BsModule.h"
  4. namespace BansheeEngine
  5. {
  6. /**
  7. * @brief Data common to all event connections.
  8. */
  9. class BaseConnectionData
  10. {
  11. public:
  12. bool isValid;
  13. };
  14. /**
  15. * @brief Event handle. Allows you to track to which events you subscribed to and
  16. * disconnect from them when needed.
  17. */
  18. class HEvent
  19. {
  20. public:
  21. HEvent()
  22. :mDisconnectCallback(nullptr), mConnection(nullptr), mEvent(nullptr)
  23. { }
  24. HEvent(std::shared_ptr<BaseConnectionData> connection, void* event, void(*disconnectCallback) (const std::shared_ptr<BaseConnectionData>&, void*))
  25. :mConnection(connection), mEvent(event), mDisconnectCallback(disconnectCallback)
  26. { }
  27. /**
  28. * @brief Disconnect from the event you are subscribed to.
  29. *
  30. * @note Caller must ensure the event is still valid.
  31. */
  32. void disconnect()
  33. {
  34. if (mConnection != nullptr && mConnection->isValid)
  35. mDisconnectCallback(mConnection, mEvent);
  36. }
  37. private:
  38. void(*mDisconnectCallback) (const std::shared_ptr<BaseConnectionData>&, void*);
  39. std::shared_ptr<BaseConnectionData> mConnection;
  40. void* mEvent;
  41. };
  42. /**
  43. * @brief Events allows you to register method callbacks that get notified
  44. * when the event is triggered.
  45. *
  46. * @note Callback method return value is ignored.
  47. */
  48. template <class RetType, class... Args>
  49. class TEvent
  50. {
  51. struct ConnectionData : BaseConnectionData
  52. {
  53. public:
  54. ConnectionData(std::function<RetType(Args...)> func)
  55. :func(func)
  56. { }
  57. std::function<RetType(Args...)> func;
  58. };
  59. struct InternalData
  60. {
  61. InternalData()
  62. :mHasDisconnectedCallbacks(false)
  63. { }
  64. Vector<std::shared_ptr<ConnectionData>> mConnections;
  65. bool mHasDisconnectedCallbacks;
  66. BS_RECURSIVE_MUTEX(mMutex);
  67. };
  68. public:
  69. TEvent()
  70. :mInternalData(bs_shared_ptr<InternalData>())
  71. { }
  72. ~TEvent()
  73. {
  74. clear();
  75. }
  76. /**
  77. * @brief Register a new callback that will get notified once
  78. * the event is triggered.
  79. */
  80. HEvent connect(std::function<RetType(Args...)> func)
  81. {
  82. std::shared_ptr<ConnectionData> connData = bs_shared_ptr<ConnectionData>(func);
  83. connData->isValid = true;
  84. {
  85. BS_LOCK_RECURSIVE_MUTEX(mInternalData->mMutex);
  86. mInternalData->mConnections.push_back(connData);
  87. }
  88. return HEvent(connData, this, &TEvent::disconnectCallback);
  89. }
  90. /**
  91. * @brief Trigger the event, notifying all register callback methods.
  92. */
  93. void operator() (Args... args)
  94. {
  95. // Increase ref count to ensure this event data isn't destroyed if one of the callbacks
  96. // deletes the event itself.
  97. std::shared_ptr<InternalData> internalData = mInternalData;
  98. BS_LOCK_RECURSIVE_MUTEX(internalData->mMutex);
  99. // Here is the only place we remove connections, in order to allow disconnect() and clear() to be called
  100. // recursively from the notify callbacks
  101. if (internalData->mHasDisconnectedCallbacks)
  102. {
  103. for (UINT32 i = 0; i < internalData->mConnections.size(); i++)
  104. {
  105. if (!internalData->mConnections[i]->isValid)
  106. {
  107. internalData->mConnections.erase(internalData->mConnections.begin() + i);
  108. i--;
  109. }
  110. }
  111. internalData->mHasDisconnectedCallbacks = false;
  112. }
  113. // Do not use an iterator here, as new connections might be added during iteration from
  114. // the notify callback
  115. UINT32 numConnections = (UINT32)internalData->mConnections.size(); // Remember current num. connections as we don't want to notify new ones
  116. for (UINT32 i = 0; i < numConnections; i++)
  117. {
  118. if (internalData->mConnections[i]->func != nullptr)
  119. internalData->mConnections[i]->func(args...);
  120. }
  121. }
  122. /**
  123. * @brief Clear all callbacks from the event.
  124. */
  125. void clear()
  126. {
  127. BS_LOCK_RECURSIVE_MUTEX(mInternalData->mMutex);
  128. for (auto& connection : mInternalData->mConnections)
  129. {
  130. connection->isValid = false;
  131. connection->func = nullptr;
  132. }
  133. if (mInternalData->mConnections.size() > 0)
  134. mInternalData->mHasDisconnectedCallbacks = true;
  135. }
  136. /**
  137. * @brief Check if event has any callbacks registered.
  138. *
  139. * @note It is safe to trigger an event even if no callbacks are registered.
  140. */
  141. bool empty()
  142. {
  143. BS_LOCK_RECURSIVE_MUTEX(mInternalData->mMutex);
  144. return mInternalData->mConnections.size() == 0;
  145. }
  146. private:
  147. std::shared_ptr<InternalData> mInternalData;
  148. /**
  149. * @brief Callback triggered by event handles when they want to disconnect from
  150. * an event.
  151. */
  152. static void disconnectCallback(const std::shared_ptr<BaseConnectionData>& connection, void* event)
  153. {
  154. TEvent<RetType, Args...>* castEvent = reinterpret_cast<TEvent<RetType, Args...>*>(event);
  155. castEvent->disconnect(connection);
  156. }
  157. /**
  158. * @brief Internal method that disconnects the callback described by the provided connection data.
  159. */
  160. void disconnect(const std::shared_ptr<BaseConnectionData>& connData)
  161. {
  162. BS_LOCK_RECURSIVE_MUTEX(mInternalData->mMutex);
  163. std::shared_ptr<ConnectionData> myConnData = std::static_pointer_cast<ConnectionData>(connData);
  164. for (auto& iter = mInternalData->mConnections.begin(); iter != mInternalData->mConnections.end(); ++iter)
  165. {
  166. if ((*iter) == myConnData)
  167. {
  168. myConnData->isValid = false;
  169. myConnData->func = nullptr;
  170. mInternalData->mHasDisconnectedCallbacks = true;
  171. return;
  172. }
  173. }
  174. }
  175. };
  176. /************************************************************************/
  177. /* SPECIALIZATIONS */
  178. /* SO YOU MAY USE FUNCTION LIKE SYNTAX FOR DECLARING EVENT SIGNATURE */
  179. /************************************************************************/
  180. /**
  181. * @copydoc TEvent
  182. */
  183. template <typename Signature>
  184. class Event;
  185. /**
  186. * @copydoc TEvent
  187. */
  188. template <class RetType, class... Args>
  189. class Event<RetType(Args...) > : public TEvent <RetType, Args...>
  190. { };
  191. }