summaryrefslogtreecommitdiff
path: root/utils/vmpi/messagemgr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/vmpi/messagemgr.cpp')
-rw-r--r--utils/vmpi/messagemgr.cpp300
1 files changed, 300 insertions, 0 deletions
diff --git a/utils/vmpi/messagemgr.cpp b/utils/vmpi/messagemgr.cpp
new file mode 100644
index 0000000..35d5e53
--- /dev/null
+++ b/utils/vmpi/messagemgr.cpp
@@ -0,0 +1,300 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <windows.h>
+#include "messagemgr.h"
+#include "tcpsocket.h"
+#include "iphelpers.h"
+#include "tier0/platform.h"
+#include "threadhelpers.h"
+
+
+#define MSGMGR_LISTEN_PORT_FIRST 22512
+#define MSGMGR_LISTEN_PORT_LAST 22520
+
+
+
+#define BROADCAST_INTERVAL 2 // Broadcast our presence every N seconds.
+
+#define NUM_QUEUED_MESSAGES 200
+
+
+
+class CMessageMgr : public IMessageMgr
+{
+public:
+ CMessageMgr();
+ ~CMessageMgr();
+
+ bool Init();
+ void Term();
+
+
+// IMessageMgr overrides.
+public:
+
+ virtual void Print( const char *pMsg );
+
+
+
+private:
+
+ DWORD ThreadFn();
+ static DWORD WINAPI StaticThreadFn( LPVOID pParameter );
+
+
+private:
+
+ // Only our thread touches this, NOT the main thread.
+ CUtlLinkedList<ITCPSocket*,int> m_Sockets;
+
+ HANDLE m_hThread;
+ DWORD m_dwThreadID;
+
+ HANDLE m_hExitObj; // This is signalled when we want the thread to exit.
+ HANDLE m_hExitResponseObj; // The thread sets this when it exits.
+
+ HANDLE m_hMessageObj; // This signals to the thread that there's a message to send.
+ HANDLE m_hMessageSentObj; // This signals back to the main thread that the message was sent.
+ const char *m_pMessageText; // The text to send.
+
+ // This is only touched by the thread.
+ CUtlLinkedList<char*,int> m_MessageQ; // FIFO of NUM_QUEUED_MESSAGES.
+
+ ITCPListenSocket *m_pListenSocket;
+ int m_iListenPort;
+
+ ISocket *m_pBroadcastSocket;
+ double m_flLastBroadcast;
+
+};
+
+
+
+CMessageMgr::CMessageMgr()
+{
+ m_pBroadcastSocket = NULL;
+ m_pListenSocket = NULL;
+ m_hThread = NULL;
+ m_hExitObj = m_hExitResponseObj = m_hMessageObj = m_hMessageSentObj = NULL;
+}
+
+
+CMessageMgr::~CMessageMgr()
+{
+ Term();
+}
+
+
+bool CMessageMgr::Init()
+{
+ m_hExitObj = CreateEvent( NULL, false, false, NULL );
+ m_hExitResponseObj = CreateEvent( NULL, false, false, NULL );
+ m_hMessageObj = CreateEvent( NULL, false, false, NULL );
+ m_hMessageSentObj = CreateEvent( NULL, false, false, NULL );
+ if ( !m_hExitObj || !m_hExitResponseObj || !m_hMessageObj || !m_hMessageSentObj )
+ return false;
+
+ // Create the broadcast socket.
+ m_pBroadcastSocket = CreateIPSocket();
+ if ( !m_pBroadcastSocket )
+ return false;
+
+ if ( !m_pBroadcastSocket->BindToAny( 0 ) )
+ return false;
+
+
+ // Create the listen socket.
+ m_pListenSocket = NULL;
+ for ( m_iListenPort=MSGMGR_LISTEN_PORT_FIRST; m_iListenPort <= MSGMGR_LISTEN_PORT_LAST; m_iListenPort++ )
+ {
+ m_pListenSocket = CreateTCPListenSocket( m_iListenPort );
+ if ( m_pListenSocket )
+ break;
+ }
+ if ( !m_pListenSocket )
+ return false;
+
+
+ // Create our broadcast/connection thread.
+ m_flLastBroadcast = 0;
+ m_hThread = CreateThread(
+ NULL,
+ 0,
+ &CMessageMgr::StaticThreadFn,
+ this,
+ 0,
+ &m_dwThreadID );
+
+ if ( !m_hThread )
+ return false;
+
+ Plat_SetThreadName( m_dwThreadID, "MessageMgr" );
+ return true;
+}
+
+
+void CMessageMgr::Term()
+{
+ // Wait for the thread to exit?
+ if ( m_hThread )
+ {
+ DWORD dwExitCode = 0;
+ if ( GetExitCodeThread( m_hThread, &dwExitCode ) && dwExitCode == STILL_ACTIVE )
+ {
+ SetEvent( m_hExitObj );
+ WaitForSingleObject( m_hExitResponseObj, INFINITE );
+ }
+
+ CloseHandle( m_hThread );
+ m_hThread = NULL;
+ }
+
+ CloseHandle( m_hExitObj );
+ m_hExitObj = NULL;
+
+ CloseHandle( m_hExitResponseObj );
+ m_hExitResponseObj = NULL;
+
+ CloseHandle( m_hMessageObj );
+ m_hMessageObj = NULL;
+
+ CloseHandle( m_hMessageSentObj );
+ m_hMessageSentObj = NULL;
+
+ if ( m_pListenSocket )
+ {
+ m_pListenSocket->Release();
+ m_pListenSocket = NULL;
+ }
+
+ if ( m_pBroadcastSocket )
+ {
+ m_pBroadcastSocket->Release();
+ m_pBroadcastSocket = NULL;
+ }
+}
+
+
+void CMessageMgr::Print( const char *pMsg )
+{
+ m_pMessageText = pMsg;
+ SetEvent( m_hMessageObj );
+ WaitForSingleObject( m_hMessageSentObj, INFINITE );
+}
+
+
+DWORD CMessageMgr::ThreadFn()
+{
+ while ( 1 )
+ {
+ // Broadcast our presence?
+ double flCurTime = Plat_FloatTime();
+ if ( flCurTime - m_flLastBroadcast >= BROADCAST_INTERVAL )
+ {
+ // Broadcast our presence.
+ char msg[9];
+ msg[0] = MSGMGR_PACKETID_ANNOUNCE_PRESENCE;
+ *((int*)&msg[1]) = MSGMGR_VERSION;
+ *((int*)&msg[5]) = m_iListenPort;
+ m_pBroadcastSocket->Broadcast( msg, sizeof( msg ), MSGMGR_BROADCAST_PORT );
+
+ m_flLastBroadcast = flCurTime;
+ }
+
+
+ // Accept new connections.
+ CIPAddr addr;
+ ITCPSocket *pConn = m_pListenSocket->UpdateListen( &addr );
+ if ( pConn )
+ {
+ // Send what's in our queue.
+ FOR_EACH_LL( m_MessageQ, iQ )
+ {
+ char *pMsg = m_MessageQ[iQ];
+ int bufLen = strlen( pMsg ) + 1;
+
+ char packetID = MSGMGR_PACKETID_MSG;
+ const void *data[2] = { &packetID, pMsg };
+ int len[2] = { 1, bufLen };
+
+ // Send it out to our sockets.
+ pConn->SendChunks( data, len, 2 );
+ }
+
+ m_Sockets.AddToTail( pConn );
+ }
+
+
+ // Should we exit?
+ HANDLE handles[2] = {m_hExitObj, m_hMessageObj};
+ DWORD ret = WaitForMultipleObjects( 2, handles, FALSE, 200 );
+ if ( ret == WAIT_OBJECT_0 )
+ {
+ break;
+ }
+ else if ( ret == (WAIT_OBJECT_0+1) )
+ {
+ // Add it to the queue.
+ int index;
+ if ( m_MessageQ.Count() >= NUM_QUEUED_MESSAGES )
+ {
+ index = m_MessageQ.Tail();
+ delete m_MessageQ[index];
+ }
+ else
+ {
+ index = m_MessageQ.AddToTail();
+ }
+ int bufLen = strlen( m_pMessageText ) + 1;
+ m_MessageQ[index] = new char[ bufLen ];
+ strcpy( m_MessageQ[index], m_pMessageText );
+
+
+
+ // Ok, send out the message.
+ char packetID = MSGMGR_PACKETID_MSG;
+ const void *data[2] = { &packetID, m_pMessageText };
+ int len[2] = { 1, bufLen };
+
+ // Send it out to our sockets.
+ FOR_EACH_LL( m_Sockets, i )
+ {
+ m_Sockets[i]->SendChunks( data, len, 2 );
+ }
+
+ // Notify the main thread that we've sent it.
+ SetEvent( m_hMessageSentObj );
+ }
+ }
+
+ // Cleanup all our sockets (the main thread should never touch them).
+ FOR_EACH_LL( m_Sockets, i )
+ m_Sockets[i]->Release();
+
+ m_Sockets.Purge();
+
+ m_MessageQ.PurgeAndDeleteElements();
+
+ SetEvent( m_hExitResponseObj );
+ return 0;
+}
+
+
+DWORD CMessageMgr::StaticThreadFn( LPVOID pParameter )
+{
+ return ((CMessageMgr*)pParameter)->ThreadFn();
+}
+
+
+static CMessageMgr g_MessageMgr;
+
+IMessageMgr* GetMessageMgr()
+{
+ return &g_MessageMgr;
+}
+