BsEvent.h 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. #pragma once
  2. #include "BsPrerequisitesUtil.h"
  3. /** @addtogroup General
  4. * @{
  5. */
  6. namespace BansheeEngine
  7. {
  8. /** Data common to all event connections. */
  9. class BaseConnectionData
  10. {
  11. public:
  12. BaseConnectionData()
  13. :prev(nullptr), next(nullptr), isActive(true),
  14. handleLinks(0)
  15. {
  16. }
  17. virtual ~BaseConnectionData()
  18. {
  19. assert(!handleLinks && !isActive);
  20. }
  21. virtual void deactivate()
  22. {
  23. isActive = false;
  24. }
  25. BaseConnectionData* prev;
  26. BaseConnectionData* next;
  27. bool isActive;
  28. UINT32 handleLinks;
  29. };
  30. /** Internal data for an Event, storing all connections. */
  31. struct EventInternalData
  32. {
  33. EventInternalData()
  34. :mConnections(nullptr), mFreeConnections(nullptr)
  35. { }
  36. ~EventInternalData()
  37. {
  38. BaseConnectionData* conn = mConnections;
  39. while (conn != nullptr)
  40. {
  41. BaseConnectionData* next = conn->next;
  42. bs_free(conn);
  43. conn = next;
  44. }
  45. conn = mFreeConnections;
  46. while (conn != nullptr)
  47. {
  48. BaseConnectionData* next = conn->next;
  49. bs_free(conn);
  50. conn = next;
  51. }
  52. }
  53. /**
  54. * Disconnects the connection with the specified data, ensuring the event doesn't call its callback again.
  55. *
  56. * @note Only call this once.
  57. */
  58. void disconnect(BaseConnectionData* conn)
  59. {
  60. BS_LOCK_RECURSIVE_MUTEX(mMutex);
  61. conn->deactivate();
  62. conn->handleLinks--;
  63. if (conn->handleLinks == 0)
  64. free(conn);
  65. }
  66. /** Disconnects all connections in the event. */
  67. void clear()
  68. {
  69. BS_LOCK_RECURSIVE_MUTEX(mMutex);
  70. BaseConnectionData* conn = mConnections;
  71. while (conn != nullptr)
  72. {
  73. BaseConnectionData* next = conn->next;
  74. conn->deactivate();
  75. if (conn->handleLinks == 0)
  76. free(conn);
  77. conn = next;
  78. }
  79. }
  80. /**
  81. * Called when the event handle no longer keeps a reference to the connection data. This means we might be able to
  82. * free (and reuse) its memory if the event is done with it too.
  83. */
  84. void freeHandle(BaseConnectionData* conn)
  85. {
  86. BS_LOCK_RECURSIVE_MUTEX(mMutex);
  87. conn->handleLinks--;
  88. if (conn->handleLinks == 0 && !conn->isActive)
  89. free(conn);
  90. }
  91. /** Releases connection data and makes it available for re-use when next connection is formed. */
  92. void free(BaseConnectionData* conn)
  93. {
  94. if (conn->prev != nullptr)
  95. conn->prev->next = conn->next;
  96. else
  97. mConnections = conn->next;
  98. if (conn->next != nullptr)
  99. conn->next->prev = conn->prev;
  100. conn->prev = nullptr;
  101. conn->next = nullptr;
  102. if (mFreeConnections != nullptr)
  103. {
  104. conn->next = mFreeConnections;
  105. mFreeConnections->prev = conn;
  106. }
  107. mFreeConnections = conn;
  108. mFreeConnections->~BaseConnectionData();
  109. }
  110. BaseConnectionData* mConnections;
  111. BaseConnectionData* mFreeConnections;
  112. BS_RECURSIVE_MUTEX(mMutex);
  113. };
  114. /** Event handle. Allows you to track to which events you subscribed to and disconnect from them when needed. */
  115. class HEvent
  116. {
  117. public:
  118. HEvent()
  119. :mConnection(nullptr)
  120. { }
  121. explicit HEvent(const SPtr<EventInternalData>& eventData, BaseConnectionData* connection)
  122. :mConnection(connection), mEventData(eventData)
  123. {
  124. connection->handleLinks++;
  125. }
  126. ~HEvent()
  127. {
  128. if (mConnection != nullptr)
  129. mEventData->freeHandle(mConnection);
  130. }
  131. /** Disconnect from the event you are subscribed to. */
  132. void disconnect()
  133. {
  134. if (mConnection != nullptr)
  135. {
  136. mEventData->disconnect(mConnection);
  137. mConnection = nullptr;
  138. mEventData = nullptr;
  139. }
  140. }
  141. struct Bool_struct
  142. {
  143. int _Member;
  144. };
  145. /**
  146. * Allows direct conversion of a handle to bool.
  147. *
  148. * @note
  149. * Additional struct is needed because we can't directly convert to bool since then we can assign pointer to bool
  150. * and that's wrong.
  151. */
  152. operator int Bool_struct::*() const
  153. {
  154. return (mConnection != nullptr ? &Bool_struct::_Member : 0);
  155. }
  156. HEvent& operator=(const HEvent& rhs)
  157. {
  158. mConnection = rhs.mConnection;
  159. mEventData = rhs.mEventData;
  160. if (mConnection != nullptr)
  161. mConnection->handleLinks++;
  162. return *this;
  163. }
  164. private:
  165. BaseConnectionData* mConnection;
  166. SPtr<EventInternalData> mEventData;
  167. };
  168. /**
  169. * Events allows you to register method callbacks that get notified when the event is triggered.
  170. *
  171. * @note Callback method return value is ignored.
  172. */
  173. // Note: I could create a policy template argument that allows creation of
  174. // lockable and non-lockable events in the case mutex is causing too much overhead.
  175. template <class RetType, class... Args>
  176. class TEvent
  177. {
  178. struct ConnectionData : BaseConnectionData
  179. {
  180. public:
  181. void deactivate() override
  182. {
  183. func = nullptr;
  184. BaseConnectionData::deactivate();
  185. }
  186. std::function<RetType(Args...)> func;
  187. };
  188. public:
  189. TEvent()
  190. :mInternalData(bs_shared_ptr_new<EventInternalData>())
  191. { }
  192. ~TEvent()
  193. {
  194. clear();
  195. }
  196. /** Register a new callback that will get notified once the event is triggered. */
  197. HEvent connect(std::function<RetType(Args...)> func)
  198. {
  199. BS_LOCK_RECURSIVE_MUTEX(mInternalData->mMutex);
  200. ConnectionData* connData = nullptr;
  201. if (mInternalData->mFreeConnections != nullptr)
  202. {
  203. connData = static_cast<ConnectionData*>(mInternalData->mFreeConnections);
  204. mInternalData->mFreeConnections = connData->next;
  205. new (connData)ConnectionData();
  206. if (connData->next != nullptr)
  207. connData->next->prev = nullptr;
  208. connData->isActive = true;
  209. }
  210. if (connData == nullptr)
  211. connData = bs_new<ConnectionData>();
  212. connData->next = mInternalData->mConnections;
  213. if (mInternalData->mConnections != nullptr)
  214. mInternalData->mConnections->prev = connData;
  215. mInternalData->mConnections = connData;
  216. connData->func = func;
  217. return HEvent(mInternalData, connData);
  218. }
  219. /** Trigger the event, notifying all register callback methods. */
  220. void operator() (Args... args)
  221. {
  222. // Increase ref count to ensure this event data isn't destroyed if one of the callbacks
  223. // deletes the event itself.
  224. std::shared_ptr<EventInternalData> internalData = mInternalData;
  225. BS_LOCK_RECURSIVE_MUTEX(internalData->mMutex);
  226. // Hidden dependency: If any new connections are made during these callbacks they must be
  227. // inserted at the start of the linked list so that we don't trigger them here.
  228. ConnectionData* conn = static_cast<ConnectionData*>(internalData->mConnections);
  229. while (conn != nullptr)
  230. {
  231. // Save next here in case the callback itself disconnects this connection
  232. ConnectionData* next = static_cast<ConnectionData*>(conn->next);
  233. if (conn->func != nullptr)
  234. conn->func(std::forward<Args>(args)...);
  235. conn = next;
  236. }
  237. }
  238. /** Clear all callbacks from the event. */
  239. void clear()
  240. {
  241. mInternalData->clear();
  242. }
  243. /**
  244. * Check if event has any callbacks registered.
  245. *
  246. * @note It is safe to trigger an event even if no callbacks are registered.
  247. */
  248. bool empty() const
  249. {
  250. BS_LOCK_RECURSIVE_MUTEX(mInternalData->mMutex);
  251. return mInternalData->mConnections == nullptr;
  252. }
  253. private:
  254. SPtr<EventInternalData> mInternalData;
  255. };
  256. /************************************************************************/
  257. /* SPECIALIZATIONS */
  258. /* SO YOU MAY USE FUNCTION LIKE SYNTAX FOR DECLARING EVENT SIGNATURE */
  259. /************************************************************************/
  260. /** @copydoc TEvent */
  261. template <typename Signature>
  262. class Event;
  263. /** @copydoc TEvent */
  264. template <class RetType, class... Args>
  265. class Event<RetType(Args...) > : public TEvent <RetType, Args...>
  266. { };
  267. }
  268. /** @} */