uid issue
This commit is contained in:
commit
c2130ba4e9
13850 files changed
+6241419
No files matched your search
@@ -0,0 +1,503 @@
|
||||
//========== Copyright (c) Valve Corporation, All rights reserved. ==========//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "vstdlib/ieventsystem.h"
|
||||
#include "tier1/generichash.h"
|
||||
#include "tier1/utllinkedlist.h"
|
||||
#include "tier0/tslist.h"
|
||||
#include "tier1/utltshash.h"
|
||||
#include "tier1/tier1.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A hash of the event name
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef uint32 EventNameHash_t;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// An event queue is a thread-safe event queue
|
||||
// NOTE: There are questions about whether we should have a secondary queue
|
||||
// similar to vgui, where if you are doing event handling, you want to
|
||||
// process the secondary queue prior to processing the next primary queue
|
||||
// Problem is w/ threadsafety: what happens if other threads which are not
|
||||
// on the thread processing the events posts an event?
|
||||
//-----------------------------------------------------------------------------
|
||||
class CEventQueue
|
||||
{
|
||||
public:
|
||||
CEventQueue();
|
||||
~CEventQueue();
|
||||
|
||||
// Posts an event
|
||||
void PostEvent( CFunctorCallback *pCallback, CFunctorData *pData );
|
||||
|
||||
// Processes the event queue
|
||||
void ProcessEvents( );
|
||||
|
||||
// Discards events that use a particular callback
|
||||
void DiscardEvents( CFunctorCallback *pCallback );
|
||||
|
||||
// Refcount
|
||||
int AddRef() { return ++m_nRefCount; }
|
||||
int Release() { return --m_nRefCount; }
|
||||
int RefCount() { return m_nRefCount; }
|
||||
|
||||
private:
|
||||
struct QueuedEvent_t
|
||||
{
|
||||
void *m_pTarget;
|
||||
CFunctorData *m_pData;
|
||||
CFunctorCallback *m_pCallback;
|
||||
};
|
||||
|
||||
void Cleanup();
|
||||
|
||||
CTSQueue< CFunctorCallback* > m_QueuedEventDiscards;
|
||||
CTSQueue< QueuedEvent_t > m_Queue;
|
||||
CInterlockedInt m_nRefCount;
|
||||
|
||||
#ifdef _DEBUG
|
||||
bool m_bIsProcessingEvents;
|
||||
bool m_bIsDiscardingEvents;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constructor, destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CEventQueue::CEventQueue()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
m_bIsProcessingEvents = false;
|
||||
m_bIsDiscardingEvents = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
CEventQueue::~CEventQueue()
|
||||
{
|
||||
Assert( !m_bIsProcessingEvents );
|
||||
Assert( !m_bIsDiscardingEvents );
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Cleans up the event queue
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEventQueue::Cleanup()
|
||||
{
|
||||
QueuedEvent_t event;
|
||||
while ( m_Queue.PopItem( &event ) )
|
||||
{
|
||||
event.m_pCallback->Release();
|
||||
event.m_pData->Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Posts an event
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEventQueue::PostEvent( CFunctorCallback *pCallback, CFunctorData *pData )
|
||||
{
|
||||
QueuedEvent_t event;
|
||||
event.m_pCallback = pCallback;
|
||||
event.m_pData = pData;
|
||||
pCallback->AddRef();
|
||||
pData->AddRef();
|
||||
m_Queue.PushItem( event );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Discards events that use a particular callback
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEventQueue::DiscardEvents( CFunctorCallback *pCallback )
|
||||
{
|
||||
Assert( !m_bIsProcessingEvents );
|
||||
|
||||
#ifdef _DEBUG
|
||||
m_bIsDiscardingEvents = true;
|
||||
#endif
|
||||
|
||||
m_QueuedEventDiscards.PushItem( pCallback );
|
||||
|
||||
#ifdef _DEBUG
|
||||
m_bIsDiscardingEvents = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Processes the event queue
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEventQueue::ProcessEvents( )
|
||||
{
|
||||
Assert( !m_bIsProcessingEvents );
|
||||
Assert( !m_bIsDiscardingEvents );
|
||||
|
||||
#ifdef _DEBUG
|
||||
m_bIsProcessingEvents = true;
|
||||
#endif
|
||||
|
||||
if ( m_QueuedEventDiscards.Count() == 0 )
|
||||
{
|
||||
// Dispatch all events
|
||||
QueuedEvent_t event;
|
||||
while ( m_Queue.PopItem( &event ) )
|
||||
{
|
||||
(*(event.m_pCallback))( event.m_pData );
|
||||
event.m_pData->Release();
|
||||
event.m_pCallback->Release();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Build list of callbacks which are stale and need to die
|
||||
CUtlVector< CFunctorCallback * > callbacks( 0, m_QueuedEventDiscards.Count() );
|
||||
|
||||
CFunctorCallback *pCallback = NULL;
|
||||
while( m_QueuedEventDiscards.PopItem( &pCallback ) )
|
||||
{
|
||||
callbacks.AddToTail( pCallback );
|
||||
}
|
||||
|
||||
// Only invoke events on non-stale callbacks
|
||||
int nCount = callbacks.Count();
|
||||
QueuedEvent_t event;
|
||||
while ( m_Queue.PopItem( &event ) )
|
||||
{
|
||||
bool bFound = false;
|
||||
for ( int i = 0; i < nCount; ++i )
|
||||
{
|
||||
bFound = ( event.m_pCallback == callbacks[i] );
|
||||
if ( bFound )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !bFound )
|
||||
{
|
||||
(*(event.m_pCallback))( event.m_pData );
|
||||
}
|
||||
event.m_pData->Release();
|
||||
event.m_pCallback->Release();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
m_bIsProcessingEvents = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// An event id maintains a list of all interested listeners
|
||||
//-----------------------------------------------------------------------------
|
||||
class CEventId
|
||||
{
|
||||
public:
|
||||
// Registers/unregisters a listener of this event
|
||||
void RegisterListener( CEventQueue *pEventQueue, CFunctorCallback *pCallback );
|
||||
void UnregisterListener( CEventQueue *pEventQueue, CFunctorCallback *pCallback );
|
||||
|
||||
// Unregisters all listeners associated with a particular queue
|
||||
void UnregisterAllListeners( CEventQueue *pEventQueue );
|
||||
|
||||
// Posts an event
|
||||
void PostEvent( CEventQueue *pEventQueue, const void *pListener, CFunctorData *pData );
|
||||
|
||||
private:
|
||||
struct SubscribedQueue_t
|
||||
{
|
||||
CEventQueue *m_pEventQueue;
|
||||
CFunctorCallback *m_pCallback;
|
||||
};
|
||||
|
||||
CUtlFixedLinkedList< SubscribedQueue_t > m_SubscribedQueueList;
|
||||
CThreadSpinRWLock m_ListenerLock;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Registers a listener of this event
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEventId::RegisterListener( CEventQueue *pEventQueue, CFunctorCallback *pCallback )
|
||||
{
|
||||
// NOTE: We could probably move the write locks so they cover less code
|
||||
// but I'm wanting to be cautious
|
||||
m_ListenerLock.LockForWrite();
|
||||
|
||||
intp i;
|
||||
for( i = m_SubscribedQueueList.Head(); i != m_SubscribedQueueList.InvalidIndex(); i = m_SubscribedQueueList.Next( i ) )
|
||||
{
|
||||
if ( ( m_SubscribedQueueList[i].m_pEventQueue == pEventQueue ) && m_SubscribedQueueList[i].m_pCallback->IsEqual( pCallback ) )
|
||||
{
|
||||
Warning( "Tried to install the same listener on the same event id + queue twice!\n" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( i == m_SubscribedQueueList.InvalidIndex() )
|
||||
{
|
||||
SubscribedQueue_t queue;
|
||||
queue.m_pEventQueue = pEventQueue;
|
||||
queue.m_pCallback = pCallback;
|
||||
pCallback->AddRef();
|
||||
pEventQueue->AddRef();
|
||||
|
||||
m_SubscribedQueueList.AddToTail( queue );
|
||||
}
|
||||
|
||||
m_ListenerLock.UnlockWrite();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Unregisters a listener
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEventId::UnregisterListener( CEventQueue *pEventQueue, CFunctorCallback *pCallback )
|
||||
{
|
||||
// NOTE: We could probably move the write locks so they cover less code
|
||||
// but I'm wanting to be cautious
|
||||
m_ListenerLock.LockForWrite();
|
||||
|
||||
intp i;
|
||||
for( i = m_SubscribedQueueList.Head(); i != m_SubscribedQueueList.InvalidIndex(); i = m_SubscribedQueueList.Next( i ) )
|
||||
{
|
||||
if ( ( m_SubscribedQueueList[i].m_pEventQueue == pEventQueue ) && m_SubscribedQueueList[i].m_pCallback->IsEqual( pCallback ) )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( i != m_SubscribedQueueList.InvalidIndex() )
|
||||
{
|
||||
CFunctorCallback *pCachedCallback = m_SubscribedQueueList[ i ].m_pCallback;
|
||||
|
||||
m_SubscribedQueueList.Remove( i );
|
||||
|
||||
// Remove the cached callback from the queued list of events
|
||||
// if it has a non-zero refcount
|
||||
if ( pCachedCallback->Release() > 0 )
|
||||
{
|
||||
pEventQueue->DiscardEvents( pCachedCallback );
|
||||
}
|
||||
pEventQueue->Release();
|
||||
}
|
||||
|
||||
m_ListenerLock.UnlockWrite();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Unregisters all listeners associated with a particular queue
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEventId::UnregisterAllListeners( CEventQueue *pEventQueue )
|
||||
{
|
||||
// NOTE: We could probably move the write locks so they cover less code
|
||||
// but I'm wanting to be cautious
|
||||
m_ListenerLock.LockForWrite();
|
||||
|
||||
intp j, next;
|
||||
for( j = m_SubscribedQueueList.Head(); j != m_SubscribedQueueList.InvalidIndex(); j = next )
|
||||
{
|
||||
next = m_SubscribedQueueList.Next( j );
|
||||
if ( m_SubscribedQueueList[j].m_pEventQueue != pEventQueue )
|
||||
continue;
|
||||
|
||||
m_SubscribedQueueList[j].m_pCallback->Release();
|
||||
pEventQueue->Release();
|
||||
|
||||
m_SubscribedQueueList.Remove( j );
|
||||
}
|
||||
|
||||
m_ListenerLock.UnlockWrite();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Posts an event
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEventId::PostEvent( CEventQueue *pEventQueue, const void *pListener, CFunctorData *pData )
|
||||
{
|
||||
m_ListenerLock.LockForRead();
|
||||
|
||||
for( intp i = m_SubscribedQueueList.Head(); i != m_SubscribedQueueList.InvalidIndex(); i = m_SubscribedQueueList.Next( i ) )
|
||||
{
|
||||
SubscribedQueue_t &queue = m_SubscribedQueueList[i];
|
||||
if ( pEventQueue && queue.m_pEventQueue != pEventQueue )
|
||||
continue;
|
||||
|
||||
if ( pListener && pListener != queue.m_pCallback->GetTarget() )
|
||||
continue;
|
||||
|
||||
queue.m_pEventQueue->PostEvent( queue.m_pCallback, pData );
|
||||
}
|
||||
|
||||
m_ListenerLock.UnlockRead();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Event system implementation
|
||||
//-----------------------------------------------------------------------------
|
||||
class CEventSystem : public CTier1AppSystem< IEventSystem >
|
||||
{
|
||||
typedef CTier1AppSystem< IEventSystem > BaseClass;
|
||||
|
||||
// Methods of IAppSystem
|
||||
public:
|
||||
|
||||
// Methods of IEventSystem
|
||||
public:
|
||||
virtual EventQueue_t CreateEventQueue();
|
||||
virtual void DestroyEventQueue( EventQueue_t hQueue );
|
||||
virtual void ProcessEvents( EventQueue_t hQueue );
|
||||
virtual EventId_t RegisterEvent( const char *pEventName );
|
||||
virtual void PostEventInternal( EventId_t nEventId, EventQueue_t hQueue, const void *pListener, CFunctorData *pData );
|
||||
virtual void RegisterListener( EventId_t nEventId, EventQueue_t hQueue, CFunctorCallback *pCallback );
|
||||
virtual void UnregisterListener( EventId_t nEventId, EventQueue_t hQueue, CFunctorCallback *pCallback );
|
||||
|
||||
// Other public methods
|
||||
public:
|
||||
CEventSystem();
|
||||
virtual ~CEventSystem();
|
||||
|
||||
private:
|
||||
CUtlTSHash< CEventId, 251, EventNameHash_t, CUtlTSHashGenericHash< 251, EventNameHash_t>, 8 > m_EventIds;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Globals
|
||||
//-----------------------------------------------------------------------------
|
||||
static CEventSystem s_EventSystem;
|
||||
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CEventSystem, IEventSystem,
|
||||
EVENTSYSTEM_INTERFACE_VERSION, s_EventSystem )
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// constructor, destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CEventSystem::CEventSystem() : m_EventIds( 256 )
|
||||
{
|
||||
}
|
||||
|
||||
CEventSystem::~CEventSystem()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Create, destroy an event queue
|
||||
//-----------------------------------------------------------------------------
|
||||
EventQueue_t CEventSystem::CreateEventQueue()
|
||||
{
|
||||
CEventQueue *pEventQueue = new CEventQueue;
|
||||
return (EventQueue_t)pEventQueue;
|
||||
}
|
||||
|
||||
void CEventSystem::DestroyEventQueue( EventQueue_t hQueue )
|
||||
{
|
||||
CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
|
||||
if ( !pEventQueue )
|
||||
return;
|
||||
|
||||
if ( pEventQueue->RefCount() != 0 )
|
||||
{
|
||||
// This means it's still sitting in a listener list
|
||||
Warning( "Perf warning: Forgot to unregister listeners on event queue %p\n", hQueue );
|
||||
|
||||
int nCount = m_EventIds.Count();
|
||||
UtlTSHashHandle_t *pHandles = (UtlTSHashHandle_t*)stackalloc( nCount * sizeof(UtlTSHashHandle_t) );
|
||||
nCount = m_EventIds.GetElements( 0, nCount, pHandles );
|
||||
|
||||
for ( int i = 0; i < nCount; ++i )
|
||||
{
|
||||
m_EventIds[ pHandles[i] ].UnregisterAllListeners( pEventQueue );
|
||||
}
|
||||
}
|
||||
|
||||
delete pEventQueue;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Registers an event
|
||||
//-----------------------------------------------------------------------------
|
||||
EventId_t CEventSystem::RegisterEvent( const char *pEventName )
|
||||
{
|
||||
EventNameHash_t hId = (EventNameHash_t)MurmurHash2( pEventName, Q_strlen(pEventName), 0xE1E47644 );
|
||||
CDefaultTSHashConstructor< CEventId > constructor;
|
||||
UtlTSHashHandle_t hList = m_EventIds.Insert( hId, &constructor );
|
||||
return ( EventId_t )( &m_EventIds[ hList ] );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Registers, unregisters an event listener
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEventSystem::RegisterListener( EventId_t nEventId, EventQueue_t hQueue, CFunctorCallback *pCallback )
|
||||
{
|
||||
CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
|
||||
if ( pEventQueue )
|
||||
{
|
||||
CEventId *pEventId = ( CEventId* )nEventId;
|
||||
if ( pEventId )
|
||||
{
|
||||
pEventId->RegisterListener( pEventQueue, pCallback );
|
||||
}
|
||||
}
|
||||
pCallback->Release();
|
||||
}
|
||||
|
||||
void CEventSystem::UnregisterListener( EventId_t nEventId, EventQueue_t hQueue, CFunctorCallback *pCallback )
|
||||
{
|
||||
CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
|
||||
if ( pEventQueue )
|
||||
{
|
||||
CEventId *pEventId = ( CEventId* )nEventId;
|
||||
if ( pEventId )
|
||||
{
|
||||
pEventId->UnregisterListener( pEventQueue, pCallback );
|
||||
}
|
||||
}
|
||||
pCallback->Release();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Posts an event to all current listeners in a single queue
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEventSystem::PostEventInternal( EventId_t nEventId, EventQueue_t hQueue, const void *pListener, CFunctorData *pData )
|
||||
{
|
||||
CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
|
||||
CEventId *pEventId = (CEventId*)nEventId;
|
||||
if ( pEventId )
|
||||
{
|
||||
pEventId->PostEvent( pEventQueue, pListener, pData );
|
||||
}
|
||||
pData->Release();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Process queued events on a single queue
|
||||
//-----------------------------------------------------------------------------
|
||||
void CEventSystem::ProcessEvents( EventQueue_t hQueue )
|
||||
{
|
||||
CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
|
||||
if ( pEventQueue )
|
||||
{
|
||||
pEventQueue->ProcessEvents();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user