2
0

BsEvent.h 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #pragma once
  4. #include "BsPrerequisitesUtil.h"
  5. namespace BansheeEngine
  6. {
  7. /** @addtogroup Internal-Utility
  8. * @{
  9. */
  10. /** @addtogroup General-Internal
  11. * @{
  12. */
  13. /** Data common to all event connections. */
  14. class BaseConnectionData
  15. {
  16. public:
  17. BaseConnectionData()
  18. :prev(nullptr), next(nullptr), isActive(true),
  19. handleLinks(0)
  20. {
  21. }
  22. virtual ~BaseConnectionData()
  23. {
  24. assert(!handleLinks && !isActive);
  25. }
  26. virtual void deactivate()
  27. {
  28. isActive = false;
  29. }
  30. BaseConnectionData* prev;
  31. BaseConnectionData* next;
  32. bool isActive;
  33. UINT32 handleLinks;
  34. };
  35. /** Internal data for an Event, storing all connections. */
  36. struct EventInternalData
  37. {
  38. EventInternalData()
  39. :mConnections(nullptr), mLastConnection(nullptr), mFreeConnections(nullptr), mNewConnections(nullptr),
  40. mIsCurrentlyTriggering(false)
  41. { }
  42. ~EventInternalData()
  43. {
  44. BaseConnectionData* conn = mConnections;
  45. while (conn != nullptr)
  46. {
  47. BaseConnectionData* next = conn->next;
  48. bs_free(conn);
  49. conn = next;
  50. }
  51. conn = mFreeConnections;
  52. while (conn != nullptr)
  53. {
  54. BaseConnectionData* next = conn->next;
  55. bs_free(conn);
  56. conn = next;
  57. }
  58. conn = mNewConnections;
  59. while (conn != nullptr)
  60. {
  61. BaseConnectionData* next = conn->next;
  62. bs_free(conn);
  63. conn = next;
  64. }
  65. }
  66. /** Appends a new connection to the active connection array. */
  67. void connect(BaseConnectionData* conn)
  68. {
  69. conn->prev = mLastConnection;
  70. if (mLastConnection != nullptr)
  71. mLastConnection->next = conn;
  72. mLastConnection = conn;
  73. // First connection
  74. if (mConnections == nullptr)
  75. mConnections = conn;
  76. }
  77. /**
  78. * Disconnects the connection with the specified data, ensuring the event doesn't call its callback again.
  79. *
  80. * @note Only call this once.
  81. */
  82. void disconnect(BaseConnectionData* conn)
  83. {
  84. RecursiveLock lock(mMutex);
  85. conn->deactivate();
  86. conn->handleLinks--;
  87. if (conn->handleLinks == 0)
  88. free(conn);
  89. }
  90. /** Disconnects all connections in the event. */
  91. void clear()
  92. {
  93. RecursiveLock lock(mMutex);
  94. BaseConnectionData* conn = mConnections;
  95. while (conn != nullptr)
  96. {
  97. BaseConnectionData* next = conn->next;
  98. conn->deactivate();
  99. if (conn->handleLinks == 0)
  100. free(conn);
  101. conn = next;
  102. }
  103. mConnections = nullptr;
  104. mLastConnection = nullptr;
  105. }
  106. /**
  107. * Called when the event handle no longer keeps a reference to the connection data. This means we might be able to
  108. * free (and reuse) its memory if the event is done with it too.
  109. */
  110. void freeHandle(BaseConnectionData* conn)
  111. {
  112. RecursiveLock lock(mMutex);
  113. conn->handleLinks--;
  114. if (conn->handleLinks == 0 && !conn->isActive)
  115. free(conn);
  116. }
  117. /** Releases connection data and makes it available for re-use when next connection is formed. */
  118. void free(BaseConnectionData* conn)
  119. {
  120. if (conn->prev != nullptr)
  121. conn->prev->next = conn->next;
  122. else
  123. mConnections = conn->next;
  124. if (conn->next != nullptr)
  125. conn->next->prev = conn->prev;
  126. else
  127. mLastConnection = conn->prev;
  128. conn->prev = nullptr;
  129. conn->next = nullptr;
  130. if (mFreeConnections != nullptr)
  131. {
  132. conn->next = mFreeConnections;
  133. mFreeConnections->prev = conn;
  134. }
  135. mFreeConnections = conn;
  136. mFreeConnections->~BaseConnectionData();
  137. }
  138. BaseConnectionData* mConnections;
  139. BaseConnectionData* mLastConnection;
  140. BaseConnectionData* mFreeConnections;
  141. BaseConnectionData* mNewConnections;
  142. RecursiveMutex mMutex;
  143. bool mIsCurrentlyTriggering;
  144. };
  145. /** @} */
  146. /** @} */
  147. /** @addtogroup General
  148. * @{
  149. */
  150. /** Event handle. Allows you to track to which events you subscribed to and disconnect from them when needed. */
  151. class HEvent
  152. {
  153. public:
  154. HEvent()
  155. :mConnection(nullptr)
  156. { }
  157. explicit HEvent(const SPtr<EventInternalData>& eventData, BaseConnectionData* connection)
  158. :mConnection(connection), mEventData(eventData)
  159. {
  160. connection->handleLinks++;
  161. }
  162. ~HEvent()
  163. {
  164. if (mConnection != nullptr)
  165. mEventData->freeHandle(mConnection);
  166. }
  167. /** Disconnect from the event you are subscribed to. */
  168. void disconnect()
  169. {
  170. if (mConnection != nullptr)
  171. {
  172. mEventData->disconnect(mConnection);
  173. mConnection = nullptr;
  174. mEventData = nullptr;
  175. }
  176. }
  177. /** @cond IGNORE */
  178. struct Bool_struct
  179. {
  180. int _Member;
  181. };
  182. /** @endcond */
  183. /**
  184. * Allows direct conversion of a handle to bool.
  185. *
  186. * @note
  187. * Additional struct is needed because we can't directly convert to bool since then we can assign pointer to bool
  188. * and that's wrong.
  189. */
  190. operator int Bool_struct::*() const
  191. {
  192. return (mConnection != nullptr ? &Bool_struct::_Member : 0);
  193. }
  194. HEvent& operator=(const HEvent& rhs)
  195. {
  196. mConnection = rhs.mConnection;
  197. mEventData = rhs.mEventData;
  198. if (mConnection != nullptr)
  199. mConnection->handleLinks++;
  200. return *this;
  201. }
  202. private:
  203. BaseConnectionData* mConnection;
  204. SPtr<EventInternalData> mEventData;
  205. };
  206. /** @} */
  207. /** @addtogroup Internal-Utility
  208. * @{
  209. */
  210. /** @addtogroup General-Internal
  211. * @{
  212. */
  213. /**
  214. * Events allows you to register method callbacks that get notified when the event is triggered.
  215. *
  216. * @note Callback method return value is ignored.
  217. */
  218. // Note: I could create a policy template argument that allows creation of
  219. // lockable and non-lockable events in the case mutex is causing too much overhead.
  220. template <class RetType, class... Args>
  221. class TEvent
  222. {
  223. struct ConnectionData : BaseConnectionData
  224. {
  225. public:
  226. void deactivate() override
  227. {
  228. func = nullptr;
  229. BaseConnectionData::deactivate();
  230. }
  231. std::function<RetType(Args...)> func;
  232. };
  233. public:
  234. TEvent()
  235. :mInternalData(bs_shared_ptr_new<EventInternalData>())
  236. { }
  237. ~TEvent()
  238. {
  239. clear();
  240. }
  241. /** Register a new callback that will get notified once the event is triggered. */
  242. HEvent connect(std::function<RetType(Args...)> func)
  243. {
  244. RecursiveLock lock(mInternalData->mMutex);
  245. ConnectionData* connData = nullptr;
  246. if (mInternalData->mFreeConnections != nullptr)
  247. {
  248. connData = static_cast<ConnectionData*>(mInternalData->mFreeConnections);
  249. mInternalData->mFreeConnections = connData->next;
  250. new (connData)ConnectionData();
  251. if (connData->next != nullptr)
  252. connData->next->prev = nullptr;
  253. connData->isActive = true;
  254. }
  255. if (connData == nullptr)
  256. connData = bs_new<ConnectionData>();
  257. // If currently iterating over the connection list, delay modifying it until done
  258. if(mInternalData->mIsCurrentlyTriggering)
  259. {
  260. connData->prev = mInternalData->mNewConnections;
  261. if (mInternalData->mNewConnections != nullptr)
  262. mInternalData->mNewConnections->next = connData;
  263. mInternalData->mNewConnections = connData;
  264. }
  265. else
  266. {
  267. mInternalData->connect(connData);
  268. }
  269. connData->func = func;
  270. return HEvent(mInternalData, connData);
  271. }
  272. /** Trigger the event, notifying all register callback methods. */
  273. void operator() (Args... args)
  274. {
  275. // Increase ref count to ensure this event data isn't destroyed if one of the callbacks
  276. // deletes the event itself.
  277. SPtr<EventInternalData> internalData = mInternalData;
  278. RecursiveLock lock(internalData->mMutex);
  279. internalData->mIsCurrentlyTriggering = true;
  280. ConnectionData* conn = static_cast<ConnectionData*>(internalData->mConnections);
  281. while (conn != nullptr)
  282. {
  283. // Save next here in case the callback itself disconnects this connection
  284. ConnectionData* next = static_cast<ConnectionData*>(conn->next);
  285. if (conn->func != nullptr)
  286. conn->func(std::forward<Args>(args)...);
  287. conn = next;
  288. }
  289. internalData->mIsCurrentlyTriggering = false;
  290. // If any new connections were added during the above calls, add them to the connection list
  291. if(internalData->mNewConnections != nullptr)
  292. {
  293. BaseConnectionData* lastNewConnection = internalData->mNewConnections;
  294. while (lastNewConnection != nullptr)
  295. lastNewConnection = lastNewConnection->next;
  296. BaseConnectionData* currentConnection = lastNewConnection;
  297. while(currentConnection != nullptr)
  298. {
  299. BaseConnectionData* prevConnection = currentConnection->prev;
  300. currentConnection->next = nullptr;
  301. currentConnection->prev = nullptr;
  302. mInternalData->connect(currentConnection);
  303. currentConnection = prevConnection;
  304. }
  305. internalData->mNewConnections = nullptr;
  306. }
  307. }
  308. /** Clear all callbacks from the event. */
  309. void clear()
  310. {
  311. mInternalData->clear();
  312. }
  313. /**
  314. * Check if event has any callbacks registered.
  315. *
  316. * @note It is safe to trigger an event even if no callbacks are registered.
  317. */
  318. bool empty() const
  319. {
  320. RecursiveLock lock(mInternalData->mMutex);
  321. return mInternalData->mConnections == nullptr;
  322. }
  323. private:
  324. SPtr<EventInternalData> mInternalData;
  325. };
  326. /** @} */
  327. /** @} */
  328. /** @addtogroup General
  329. * @{
  330. */
  331. /************************************************************************/
  332. /* SPECIALIZATIONS */
  333. /* SO YOU MAY USE FUNCTION LIKE SYNTAX FOR DECLARING EVENT SIGNATURE */
  334. /************************************************************************/
  335. /** @copydoc TEvent */
  336. template <typename Signature>
  337. class Event;
  338. /** @copydoc TEvent */
  339. template <class RetType, class... Args>
  340. class Event<RetType(Args...) > : public TEvent <RetType, Args...>
  341. { };
  342. /** @} */
  343. }