Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

503 lines
14 KiB

  1. //========== Copyright (c) Valve Corporation, All rights reserved. ==========//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "vstdlib/ieventsystem.h"
  9. #include "tier1/generichash.h"
  10. #include "tier1/utllinkedlist.h"
  11. #include "tier0/tslist.h"
  12. #include "tier1/utltshash.h"
  13. #include "tier1/tier1.h"
  14. // NOTE: This has to be the last file included!
  15. #include "tier0/memdbgon.h"
  16. //-----------------------------------------------------------------------------
  17. // A hash of the event name
  18. //-----------------------------------------------------------------------------
  19. typedef uint32 EventNameHash_t;
  20. //-----------------------------------------------------------------------------
  21. // An event queue is a thread-safe event queue
  22. // NOTE: There are questions about whether we should have a secondary queue
  23. // similar to vgui, where if you are doing event handling, you want to
  24. // process the secondary queue prior to processing the next primary queue
  25. // Problem is w/ threadsafety: what happens if other threads which are not
  26. // on the thread processing the events posts an event?
  27. //-----------------------------------------------------------------------------
  28. class CEventQueue
  29. {
  30. public:
  31. CEventQueue();
  32. ~CEventQueue();
  33. // Posts an event
  34. void PostEvent( CFunctorCallback *pCallback, CFunctorData *pData );
  35. // Processes the event queue
  36. void ProcessEvents( );
  37. // Discards events that use a particular callback
  38. void DiscardEvents( CFunctorCallback *pCallback );
  39. // Refcount
  40. int AddRef() { return ++m_nRefCount; }
  41. int Release() { return --m_nRefCount; }
  42. int RefCount() { return m_nRefCount; }
  43. private:
  44. struct QueuedEvent_t
  45. {
  46. void *m_pTarget;
  47. CFunctorData *m_pData;
  48. CFunctorCallback *m_pCallback;
  49. };
  50. void Cleanup();
  51. CTSQueue< CFunctorCallback* > m_QueuedEventDiscards;
  52. CTSQueue< QueuedEvent_t > m_Queue;
  53. CInterlockedInt m_nRefCount;
  54. #ifdef _DEBUG
  55. bool m_bIsProcessingEvents;
  56. bool m_bIsDiscardingEvents;
  57. #endif
  58. };
  59. //-----------------------------------------------------------------------------
  60. // Constructor, destructor
  61. //-----------------------------------------------------------------------------
  62. CEventQueue::CEventQueue()
  63. {
  64. #ifdef _DEBUG
  65. m_bIsProcessingEvents = false;
  66. m_bIsDiscardingEvents = false;
  67. #endif
  68. }
  69. CEventQueue::~CEventQueue()
  70. {
  71. Assert( !m_bIsProcessingEvents );
  72. Assert( !m_bIsDiscardingEvents );
  73. Cleanup();
  74. }
  75. //-----------------------------------------------------------------------------
  76. // Cleans up the event queue
  77. //-----------------------------------------------------------------------------
  78. void CEventQueue::Cleanup()
  79. {
  80. QueuedEvent_t event;
  81. while ( m_Queue.PopItem( &event ) )
  82. {
  83. event.m_pCallback->Release();
  84. event.m_pData->Release();
  85. }
  86. }
  87. //-----------------------------------------------------------------------------
  88. // Posts an event
  89. //-----------------------------------------------------------------------------
  90. void CEventQueue::PostEvent( CFunctorCallback *pCallback, CFunctorData *pData )
  91. {
  92. QueuedEvent_t event;
  93. event.m_pCallback = pCallback;
  94. event.m_pData = pData;
  95. pCallback->AddRef();
  96. pData->AddRef();
  97. m_Queue.PushItem( event );
  98. }
  99. //-----------------------------------------------------------------------------
  100. // Discards events that use a particular callback
  101. //-----------------------------------------------------------------------------
  102. void CEventQueue::DiscardEvents( CFunctorCallback *pCallback )
  103. {
  104. Assert( !m_bIsProcessingEvents );
  105. #ifdef _DEBUG
  106. m_bIsDiscardingEvents = true;
  107. #endif
  108. m_QueuedEventDiscards.PushItem( pCallback );
  109. #ifdef _DEBUG
  110. m_bIsDiscardingEvents = false;
  111. #endif
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Processes the event queue
  115. //-----------------------------------------------------------------------------
  116. void CEventQueue::ProcessEvents( )
  117. {
  118. Assert( !m_bIsProcessingEvents );
  119. Assert( !m_bIsDiscardingEvents );
  120. #ifdef _DEBUG
  121. m_bIsProcessingEvents = true;
  122. #endif
  123. if ( m_QueuedEventDiscards.Count() == 0 )
  124. {
  125. // Dispatch all events
  126. QueuedEvent_t event;
  127. while ( m_Queue.PopItem( &event ) )
  128. {
  129. (*(event.m_pCallback))( event.m_pData );
  130. event.m_pData->Release();
  131. event.m_pCallback->Release();
  132. }
  133. }
  134. else
  135. {
  136. // Build list of callbacks which are stale and need to die
  137. CUtlVector< CFunctorCallback * > callbacks( 0, m_QueuedEventDiscards.Count() );
  138. CFunctorCallback *pCallback = NULL;
  139. while( m_QueuedEventDiscards.PopItem( &pCallback ) )
  140. {
  141. callbacks.AddToTail( pCallback );
  142. }
  143. // Only invoke events on non-stale callbacks
  144. int nCount = callbacks.Count();
  145. QueuedEvent_t event;
  146. while ( m_Queue.PopItem( &event ) )
  147. {
  148. bool bFound = false;
  149. for ( int i = 0; i < nCount; ++i )
  150. {
  151. bFound = ( event.m_pCallback == callbacks[i] );
  152. if ( bFound )
  153. break;
  154. }
  155. if ( !bFound )
  156. {
  157. (*(event.m_pCallback))( event.m_pData );
  158. }
  159. event.m_pData->Release();
  160. event.m_pCallback->Release();
  161. }
  162. }
  163. #ifdef _DEBUG
  164. m_bIsProcessingEvents = false;
  165. #endif
  166. }
  167. //-----------------------------------------------------------------------------
  168. // An event id maintains a list of all interested listeners
  169. //-----------------------------------------------------------------------------
  170. class CEventId
  171. {
  172. public:
  173. // Registers/unregisters a listener of this event
  174. void RegisterListener( CEventQueue *pEventQueue, CFunctorCallback *pCallback );
  175. void UnregisterListener( CEventQueue *pEventQueue, CFunctorCallback *pCallback );
  176. // Unregisters all listeners associated with a particular queue
  177. void UnregisterAllListeners( CEventQueue *pEventQueue );
  178. // Posts an event
  179. void PostEvent( CEventQueue *pEventQueue, const void *pListener, CFunctorData *pData );
  180. private:
  181. struct SubscribedQueue_t
  182. {
  183. CEventQueue *m_pEventQueue;
  184. CFunctorCallback *m_pCallback;
  185. };
  186. CUtlFixedLinkedList< SubscribedQueue_t > m_SubscribedQueueList;
  187. CThreadSpinRWLock m_ListenerLock;
  188. };
  189. //-----------------------------------------------------------------------------
  190. // Registers a listener of this event
  191. //-----------------------------------------------------------------------------
  192. void CEventId::RegisterListener( CEventQueue *pEventQueue, CFunctorCallback *pCallback )
  193. {
  194. // NOTE: We could probably move the write locks so they cover less code
  195. // but I'm wanting to be cautious
  196. m_ListenerLock.LockForWrite();
  197. intp i;
  198. for( i = m_SubscribedQueueList.Head(); i != m_SubscribedQueueList.InvalidIndex(); i = m_SubscribedQueueList.Next( i ) )
  199. {
  200. if ( ( m_SubscribedQueueList[i].m_pEventQueue == pEventQueue ) && m_SubscribedQueueList[i].m_pCallback->IsEqual( pCallback ) )
  201. {
  202. Warning( "Tried to install the same listener on the same event id + queue twice!\n" );
  203. break;
  204. }
  205. }
  206. if ( i == m_SubscribedQueueList.InvalidIndex() )
  207. {
  208. SubscribedQueue_t queue;
  209. queue.m_pEventQueue = pEventQueue;
  210. queue.m_pCallback = pCallback;
  211. pCallback->AddRef();
  212. pEventQueue->AddRef();
  213. m_SubscribedQueueList.AddToTail( queue );
  214. }
  215. m_ListenerLock.UnlockWrite();
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Unregisters a listener
  219. //-----------------------------------------------------------------------------
  220. void CEventId::UnregisterListener( CEventQueue *pEventQueue, CFunctorCallback *pCallback )
  221. {
  222. // NOTE: We could probably move the write locks so they cover less code
  223. // but I'm wanting to be cautious
  224. m_ListenerLock.LockForWrite();
  225. intp i;
  226. for( i = m_SubscribedQueueList.Head(); i != m_SubscribedQueueList.InvalidIndex(); i = m_SubscribedQueueList.Next( i ) )
  227. {
  228. if ( ( m_SubscribedQueueList[i].m_pEventQueue == pEventQueue ) && m_SubscribedQueueList[i].m_pCallback->IsEqual( pCallback ) )
  229. break;
  230. }
  231. if ( i != m_SubscribedQueueList.InvalidIndex() )
  232. {
  233. CFunctorCallback *pCachedCallback = m_SubscribedQueueList[ i ].m_pCallback;
  234. m_SubscribedQueueList.Remove( i );
  235. // Remove the cached callback from the queued list of events
  236. // if it has a non-zero refcount
  237. if ( pCachedCallback->Release() > 0 )
  238. {
  239. pEventQueue->DiscardEvents( pCachedCallback );
  240. }
  241. pEventQueue->Release();
  242. }
  243. m_ListenerLock.UnlockWrite();
  244. }
  245. //-----------------------------------------------------------------------------
  246. // Unregisters all listeners associated with a particular queue
  247. //-----------------------------------------------------------------------------
  248. void CEventId::UnregisterAllListeners( CEventQueue *pEventQueue )
  249. {
  250. // NOTE: We could probably move the write locks so they cover less code
  251. // but I'm wanting to be cautious
  252. m_ListenerLock.LockForWrite();
  253. intp j, next;
  254. for( j = m_SubscribedQueueList.Head(); j != m_SubscribedQueueList.InvalidIndex(); j = next )
  255. {
  256. next = m_SubscribedQueueList.Next( j );
  257. if ( m_SubscribedQueueList[j].m_pEventQueue != pEventQueue )
  258. continue;
  259. m_SubscribedQueueList[j].m_pCallback->Release();
  260. pEventQueue->Release();
  261. m_SubscribedQueueList.Remove( j );
  262. }
  263. m_ListenerLock.UnlockWrite();
  264. }
  265. //-----------------------------------------------------------------------------
  266. // Posts an event
  267. //-----------------------------------------------------------------------------
  268. void CEventId::PostEvent( CEventQueue *pEventQueue, const void *pListener, CFunctorData *pData )
  269. {
  270. m_ListenerLock.LockForRead();
  271. for( intp i = m_SubscribedQueueList.Head(); i != m_SubscribedQueueList.InvalidIndex(); i = m_SubscribedQueueList.Next( i ) )
  272. {
  273. SubscribedQueue_t &queue = m_SubscribedQueueList[i];
  274. if ( pEventQueue && queue.m_pEventQueue != pEventQueue )
  275. continue;
  276. if ( pListener && pListener != queue.m_pCallback->GetTarget() )
  277. continue;
  278. queue.m_pEventQueue->PostEvent( queue.m_pCallback, pData );
  279. }
  280. m_ListenerLock.UnlockRead();
  281. }
  282. //-----------------------------------------------------------------------------
  283. // Event system implementation
  284. //-----------------------------------------------------------------------------
  285. class CEventSystem : public CTier1AppSystem< IEventSystem >
  286. {
  287. typedef CTier1AppSystem< IEventSystem > BaseClass;
  288. // Methods of IAppSystem
  289. public:
  290. // Methods of IEventSystem
  291. public:
  292. virtual EventQueue_t CreateEventQueue();
  293. virtual void DestroyEventQueue( EventQueue_t hQueue );
  294. virtual void ProcessEvents( EventQueue_t hQueue );
  295. virtual EventId_t RegisterEvent( const char *pEventName );
  296. virtual void PostEventInternal( EventId_t nEventId, EventQueue_t hQueue, const void *pListener, CFunctorData *pData );
  297. virtual void RegisterListener( EventId_t nEventId, EventQueue_t hQueue, CFunctorCallback *pCallback );
  298. virtual void UnregisterListener( EventId_t nEventId, EventQueue_t hQueue, CFunctorCallback *pCallback );
  299. // Other public methods
  300. public:
  301. CEventSystem();
  302. virtual ~CEventSystem();
  303. private:
  304. CUtlTSHash< CEventId, 251, EventNameHash_t, CUtlTSHashGenericHash< 251, EventNameHash_t>, 8 > m_EventIds;
  305. };
  306. //-----------------------------------------------------------------------------
  307. // Globals
  308. //-----------------------------------------------------------------------------
  309. static CEventSystem s_EventSystem;
  310. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CEventSystem, IEventSystem,
  311. EVENTSYSTEM_INTERFACE_VERSION, s_EventSystem )
  312. //-----------------------------------------------------------------------------
  313. // constructor, destructor
  314. //-----------------------------------------------------------------------------
  315. CEventSystem::CEventSystem() : m_EventIds( 256 )
  316. {
  317. }
  318. CEventSystem::~CEventSystem()
  319. {
  320. }
  321. //-----------------------------------------------------------------------------
  322. // Create, destroy an event queue
  323. //-----------------------------------------------------------------------------
  324. EventQueue_t CEventSystem::CreateEventQueue()
  325. {
  326. CEventQueue *pEventQueue = new CEventQueue;
  327. return (EventQueue_t)pEventQueue;
  328. }
  329. void CEventSystem::DestroyEventQueue( EventQueue_t hQueue )
  330. {
  331. CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
  332. if ( !pEventQueue )
  333. return;
  334. if ( pEventQueue->RefCount() != 0 )
  335. {
  336. // This means it's still sitting in a listener list
  337. Warning( "Perf warning: Forgot to unregister listeners on event queue %p\n", hQueue );
  338. int nCount = m_EventIds.Count();
  339. UtlTSHashHandle_t *pHandles = (UtlTSHashHandle_t*)stackalloc( nCount * sizeof(UtlTSHashHandle_t) );
  340. nCount = m_EventIds.GetElements( 0, nCount, pHandles );
  341. for ( int i = 0; i < nCount; ++i )
  342. {
  343. m_EventIds[ pHandles[i] ].UnregisterAllListeners( pEventQueue );
  344. }
  345. }
  346. delete pEventQueue;
  347. }
  348. //-----------------------------------------------------------------------------
  349. // Registers an event
  350. //-----------------------------------------------------------------------------
  351. EventId_t CEventSystem::RegisterEvent( const char *pEventName )
  352. {
  353. EventNameHash_t hId = (EventNameHash_t)MurmurHash2( pEventName, Q_strlen(pEventName), 0xE1E47644 );
  354. CDefaultTSHashConstructor< CEventId > constructor;
  355. UtlTSHashHandle_t hList = m_EventIds.Insert( hId, &constructor );
  356. return ( EventId_t )( &m_EventIds[ hList ] );
  357. }
  358. //-----------------------------------------------------------------------------
  359. // Registers, unregisters an event listener
  360. //-----------------------------------------------------------------------------
  361. void CEventSystem::RegisterListener( EventId_t nEventId, EventQueue_t hQueue, CFunctorCallback *pCallback )
  362. {
  363. CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
  364. if ( pEventQueue )
  365. {
  366. CEventId *pEventId = ( CEventId* )nEventId;
  367. if ( pEventId )
  368. {
  369. pEventId->RegisterListener( pEventQueue, pCallback );
  370. }
  371. }
  372. pCallback->Release();
  373. }
  374. void CEventSystem::UnregisterListener( EventId_t nEventId, EventQueue_t hQueue, CFunctorCallback *pCallback )
  375. {
  376. CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
  377. if ( pEventQueue )
  378. {
  379. CEventId *pEventId = ( CEventId* )nEventId;
  380. if ( pEventId )
  381. {
  382. pEventId->UnregisterListener( pEventQueue, pCallback );
  383. }
  384. }
  385. pCallback->Release();
  386. }
  387. //-----------------------------------------------------------------------------
  388. // Posts an event to all current listeners in a single queue
  389. //-----------------------------------------------------------------------------
  390. void CEventSystem::PostEventInternal( EventId_t nEventId, EventQueue_t hQueue, const void *pListener, CFunctorData *pData )
  391. {
  392. CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
  393. CEventId *pEventId = (CEventId*)nEventId;
  394. if ( pEventId )
  395. {
  396. pEventId->PostEvent( pEventQueue, pListener, pData );
  397. }
  398. pData->Release();
  399. }
  400. //-----------------------------------------------------------------------------
  401. // Process queued events on a single queue
  402. //-----------------------------------------------------------------------------
  403. void CEventSystem::ProcessEvents( EventQueue_t hQueue )
  404. {
  405. CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
  406. if ( pEventQueue )
  407. {
  408. pEventQueue->ProcessEvents();
  409. }
  410. }