| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
- #pragma once
- #include "BsPrerequisitesUtil.h"
- namespace BansheeEngine
- {
- /** @addtogroup Internal-Utility
- * @{
- */
- /** @addtogroup General-Internal
- * @{
- */
- /** Data common to all event connections. */
- class BaseConnectionData
- {
- public:
- BaseConnectionData()
- :prev(nullptr), next(nullptr), isActive(true),
- handleLinks(0)
- {
-
- }
- virtual ~BaseConnectionData()
- {
- assert(!handleLinks && !isActive);
- }
- virtual void deactivate()
- {
- isActive = false;
- }
- BaseConnectionData* prev;
- BaseConnectionData* next;
- bool isActive;
- UINT32 handleLinks;
- };
- /** Internal data for an Event, storing all connections. */
- struct EventInternalData
- {
- EventInternalData()
- :mConnections(nullptr), mLastConnection(nullptr), mFreeConnections(nullptr), mNewConnections(nullptr),
- mIsCurrentlyTriggering(false)
- { }
- ~EventInternalData()
- {
- BaseConnectionData* conn = mConnections;
- while (conn != nullptr)
- {
- BaseConnectionData* next = conn->next;
- bs_free(conn);
- conn = next;
- }
- conn = mFreeConnections;
- while (conn != nullptr)
- {
- BaseConnectionData* next = conn->next;
- bs_free(conn);
- conn = next;
- }
- conn = mNewConnections;
- while (conn != nullptr)
- {
- BaseConnectionData* next = conn->next;
- bs_free(conn);
- conn = next;
- }
- }
- /** Appends a new connection to the active connection array. */
- void connect(BaseConnectionData* conn)
- {
- conn->prev = mLastConnection;
- if (mLastConnection != nullptr)
- mLastConnection->next = conn;
- mLastConnection = conn;
- // First connection
- if (mConnections == nullptr)
- mConnections = conn;
- }
- /**
- * Disconnects the connection with the specified data, ensuring the event doesn't call its callback again.
- *
- * @note Only call this once.
- */
- void disconnect(BaseConnectionData* conn)
- {
- RecursiveLock lock(mMutex);
- conn->deactivate();
- conn->handleLinks--;
- if (conn->handleLinks == 0)
- free(conn);
- }
- /** Disconnects all connections in the event. */
- void clear()
- {
- RecursiveLock lock(mMutex);
- BaseConnectionData* conn = mConnections;
- while (conn != nullptr)
- {
- BaseConnectionData* next = conn->next;
- conn->deactivate();
- if (conn->handleLinks == 0)
- free(conn);
- conn = next;
- }
- mConnections = nullptr;
- mLastConnection = nullptr;
- }
- /**
- * Called when the event handle no longer keeps a reference to the connection data. This means we might be able to
- * free (and reuse) its memory if the event is done with it too.
- */
- void freeHandle(BaseConnectionData* conn)
- {
- RecursiveLock lock(mMutex);
- conn->handleLinks--;
- if (conn->handleLinks == 0 && !conn->isActive)
- free(conn);
- }
- /** Releases connection data and makes it available for re-use when next connection is formed. */
- void free(BaseConnectionData* conn)
- {
- if (conn->prev != nullptr)
- conn->prev->next = conn->next;
- else
- mConnections = conn->next;
- if (conn->next != nullptr)
- conn->next->prev = conn->prev;
- else
- mLastConnection = conn->prev;
- conn->prev = nullptr;
- conn->next = nullptr;
- if (mFreeConnections != nullptr)
- {
- conn->next = mFreeConnections;
- mFreeConnections->prev = conn;
- }
- mFreeConnections = conn;
- mFreeConnections->~BaseConnectionData();
- }
- BaseConnectionData* mConnections;
- BaseConnectionData* mLastConnection;
- BaseConnectionData* mFreeConnections;
- BaseConnectionData* mNewConnections;
- RecursiveMutex mMutex;
- bool mIsCurrentlyTriggering;
- };
- /** @} */
- /** @} */
- /** @addtogroup General
- * @{
- */
- /** Event handle. Allows you to track to which events you subscribed to and disconnect from them when needed. */
- class HEvent
- {
- public:
- HEvent()
- :mConnection(nullptr)
- { }
- explicit HEvent(const SPtr<EventInternalData>& eventData, BaseConnectionData* connection)
- :mConnection(connection), mEventData(eventData)
- {
- connection->handleLinks++;
- }
- ~HEvent()
- {
- if (mConnection != nullptr)
- mEventData->freeHandle(mConnection);
- }
- /** Disconnect from the event you are subscribed to. */
- void disconnect()
- {
- if (mConnection != nullptr)
- {
- mEventData->disconnect(mConnection);
- mConnection = nullptr;
- mEventData = nullptr;
- }
- }
- /** @cond IGNORE */
- struct Bool_struct
- {
- int _Member;
- };
- /** @endcond */
- /**
- * Allows direct conversion of a handle to bool.
- *
- * @note
- * Additional struct is needed because we can't directly convert to bool since then we can assign pointer to bool
- * and that's wrong.
- */
- operator int Bool_struct::*() const
- {
- return (mConnection != nullptr ? &Bool_struct::_Member : 0);
- }
- HEvent& operator=(const HEvent& rhs)
- {
- mConnection = rhs.mConnection;
- mEventData = rhs.mEventData;
- if (mConnection != nullptr)
- mConnection->handleLinks++;
- return *this;
- }
- private:
- BaseConnectionData* mConnection;
- SPtr<EventInternalData> mEventData;
- };
- /** @} */
- /** @addtogroup Internal-Utility
- * @{
- */
- /** @addtogroup General-Internal
- * @{
- */
- /**
- * Events allows you to register method callbacks that get notified when the event is triggered.
- *
- * @note Callback method return value is ignored.
- */
- // Note: I could create a policy template argument that allows creation of
- // lockable and non-lockable events in the case mutex is causing too much overhead.
- template <class RetType, class... Args>
- class TEvent
- {
- struct ConnectionData : BaseConnectionData
- {
- public:
- void deactivate() override
- {
- func = nullptr;
- BaseConnectionData::deactivate();
- }
- std::function<RetType(Args...)> func;
- };
- public:
- TEvent()
- :mInternalData(bs_shared_ptr_new<EventInternalData>())
- { }
- ~TEvent()
- {
- clear();
- }
- /** Register a new callback that will get notified once the event is triggered. */
- HEvent connect(std::function<RetType(Args...)> func)
- {
- RecursiveLock lock(mInternalData->mMutex);
- ConnectionData* connData = nullptr;
- if (mInternalData->mFreeConnections != nullptr)
- {
- connData = static_cast<ConnectionData*>(mInternalData->mFreeConnections);
- mInternalData->mFreeConnections = connData->next;
- new (connData)ConnectionData();
- if (connData->next != nullptr)
- connData->next->prev = nullptr;
- connData->isActive = true;
- }
- if (connData == nullptr)
- connData = bs_new<ConnectionData>();
- // If currently iterating over the connection list, delay modifying it until done
- if(mInternalData->mIsCurrentlyTriggering)
- {
- connData->prev = mInternalData->mNewConnections;
- if (mInternalData->mNewConnections != nullptr)
- mInternalData->mNewConnections->next = connData;
- mInternalData->mNewConnections = connData;
- }
- else
- {
- mInternalData->connect(connData);
- }
- connData->func = func;
- return HEvent(mInternalData, connData);
- }
- /** Trigger the event, notifying all register callback methods. */
- void operator() (Args... args)
- {
- // Increase ref count to ensure this event data isn't destroyed if one of the callbacks
- // deletes the event itself.
- SPtr<EventInternalData> internalData = mInternalData;
- RecursiveLock lock(internalData->mMutex);
- internalData->mIsCurrentlyTriggering = true;
- ConnectionData* conn = static_cast<ConnectionData*>(internalData->mConnections);
- while (conn != nullptr)
- {
- // Save next here in case the callback itself disconnects this connection
- ConnectionData* next = static_cast<ConnectionData*>(conn->next);
-
- if (conn->func != nullptr)
- conn->func(std::forward<Args>(args)...);
- conn = next;
- }
- internalData->mIsCurrentlyTriggering = false;
- // If any new connections were added during the above calls, add them to the connection list
- if(internalData->mNewConnections != nullptr)
- {
- BaseConnectionData* lastNewConnection = internalData->mNewConnections;
- while (lastNewConnection != nullptr)
- lastNewConnection = lastNewConnection->next;
- BaseConnectionData* currentConnection = lastNewConnection;
- while(currentConnection != nullptr)
- {
- BaseConnectionData* prevConnection = currentConnection->prev;
- currentConnection->next = nullptr;
- currentConnection->prev = nullptr;
- mInternalData->connect(currentConnection);
- currentConnection = prevConnection;
- }
- internalData->mNewConnections = nullptr;
- }
- }
- /** Clear all callbacks from the event. */
- void clear()
- {
- mInternalData->clear();
- }
- /**
- * Check if event has any callbacks registered.
- *
- * @note It is safe to trigger an event even if no callbacks are registered.
- */
- bool empty() const
- {
- RecursiveLock lock(mInternalData->mMutex);
- return mInternalData->mConnections == nullptr;
- }
- private:
- SPtr<EventInternalData> mInternalData;
- };
- /** @} */
- /** @} */
- /** @addtogroup General
- * @{
- */
- /************************************************************************/
- /* SPECIALIZATIONS */
- /* SO YOU MAY USE FUNCTION LIKE SYNTAX FOR DECLARING EVENT SIGNATURE */
- /************************************************************************/
-
- /** @copydoc TEvent */
- template <typename Signature>
- class Event;
- /** @copydoc TEvent */
- template <class RetType, class... Args>
- class Event<RetType(Args...) > : public TEvent <RetType, Args...>
- { };
- /** @} */
- }
|