mirror of
https://github.com/celisej567/source-engine.git
synced 2026-01-05 22:09:59 +03:00
1
This commit is contained in:
173
utils/vmpi/IThreadedTCPSocket.h
Normal file
173
utils/vmpi/IThreadedTCPSocket.h
Normal file
@@ -0,0 +1,173 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef ITHREADEDTCPSOCKET_H
|
||||
#define ITHREADEDTCPSOCKET_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "iphelpers.h"
|
||||
|
||||
|
||||
class IThreadedTCPSocket;
|
||||
|
||||
|
||||
class CTCPPacket
|
||||
{
|
||||
public:
|
||||
// Access the contents of the packet.
|
||||
const char* GetData() const;
|
||||
int GetLen() const;
|
||||
|
||||
// You can attach some user data to the packet.
|
||||
int GetUserData() const;
|
||||
void SetUserData( int userData );
|
||||
|
||||
// Free resources associated with the packet.
|
||||
void Release();
|
||||
|
||||
public:
|
||||
friend class CThreadedTCPSocket;
|
||||
~CTCPPacket(); // Use Release(), not delete.
|
||||
|
||||
int m_UserData;
|
||||
int m_Len;
|
||||
char m_Data[1];
|
||||
};
|
||||
|
||||
|
||||
inline const char* CTCPPacket::GetData() const
|
||||
{
|
||||
return m_Data;
|
||||
}
|
||||
|
||||
|
||||
inline int CTCPPacket::GetLen() const
|
||||
{
|
||||
return m_Len;
|
||||
}
|
||||
|
||||
|
||||
// The application implements this to handle packets that are received.
|
||||
// Note that the implementation must be thread-safe because these functions can be called
|
||||
// from various threads.
|
||||
class ITCPSocketHandler
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
SocketError=0,
|
||||
ConnectionTimedOut
|
||||
};
|
||||
|
||||
// This is called right when the socket becomes ready to have data sent through it and
|
||||
// before OnPacketReceive is ever called.
|
||||
virtual void Init( IThreadedTCPSocket *pSocket ) = 0;
|
||||
|
||||
// This is called when a packet arrives. NOTE: you are responsible for freeing the packet
|
||||
// by calling CTCPPacket::Release() on it.
|
||||
virtual void OnPacketReceived( CTCPPacket *pPacket ) = 0;
|
||||
|
||||
// Handle errors inside the socket. After this is called, the socket is no longer alive.
|
||||
// Note: this might be called from ANY thread (the main thread, the send thread, or the receive thread).
|
||||
//
|
||||
// errorCode is one of the enums above (SocketError, ConnectionTimedOut, etc).
|
||||
virtual void OnError( int errorCode, const char *pErrorString ) = 0;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// This is the main threaded TCP socket class.
|
||||
// The way these work is that they have a thread for sending and a thread for receiving data.
|
||||
//
|
||||
// The send thread is continually pushing your data out the door.
|
||||
//
|
||||
// The receive thread is continually receiving data. When it receives data, it calls your HandlePacketFn
|
||||
// to allow the user to handle it. Be very careful in your HandlePacketFn, since it is in another thread.
|
||||
// Anything it accesses should be protected by mutexes and the like.
|
||||
//
|
||||
class IThreadedTCPSocket
|
||||
{
|
||||
public:
|
||||
// Cleanup everything and exit.
|
||||
// Note: if the receive thread is inside your HandlePacketFn returns, this function blocks until that function returns.
|
||||
virtual void Release() = 0;
|
||||
|
||||
// Returns the address of whoever you are connected to.
|
||||
virtual CIPAddr GetRemoteAddr() const = 0;
|
||||
|
||||
// Returns true if the socket is connected and ready to go. If this returns false, then the socket won't
|
||||
// send or receive data any more. It also means that your ITCPSocketHandler's OnError function has been called.
|
||||
virtual bool IsValid() = 0;
|
||||
|
||||
// Send data. Any thread can call these functions, and they don't block. They make a copy of the data, then
|
||||
// enqueue it for sending.
|
||||
virtual bool Send( const void *pData, int len ) = 0;
|
||||
virtual bool SendChunks( void const * const *pChunks, const int *pChunkLengths, int nChunks ) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Use these to get incoming connections.
|
||||
class ITCPConnectSocket
|
||||
{
|
||||
public:
|
||||
// Call this to stop listening for connections and delete the object.
|
||||
virtual void Release() = 0;
|
||||
|
||||
// Keep calling this as long as you want to wait for connections.
|
||||
//
|
||||
// If it returns true and pSocket is NULL, it means it hasn't connected yet.
|
||||
// If it returns true and pSocket is non-NULL, then it has connected.
|
||||
// If it returns false, then the connection attempt failed and all further Update() calls will return false.
|
||||
virtual bool Update( IThreadedTCPSocket **pSocket, unsigned long milliseconds=0 ) = 0;
|
||||
};
|
||||
|
||||
|
||||
// This class is implemented by the app and passed into CreateListener. When the listener makes
|
||||
// a new connection, it calls CreateNewHandler() to have the app create something that will handle
|
||||
// the received packets and errors for the new socket.
|
||||
class IHandlerCreator
|
||||
{
|
||||
public:
|
||||
// This function must return a valid value.
|
||||
virtual ITCPSocketHandler* CreateNewHandler() = 0;
|
||||
};
|
||||
|
||||
|
||||
// Use this to listen for TCP connections. The ITCPConnectSocket will keep returning connections
|
||||
// until you call Release().
|
||||
ITCPConnectSocket* ThreadedTCP_CreateListener(
|
||||
IHandlerCreator *pHandlerCreator, // This handles messages from the socket.
|
||||
const unsigned short port, // Listen on this port.
|
||||
int nQueueLength = 5 // How many connections
|
||||
);
|
||||
|
||||
|
||||
// Use this to connect to a remote process. After Update() returns a non-NULL value, you should
|
||||
// call Release() on the ITCPConnectSocket because it won't ever return another connection.
|
||||
ITCPConnectSocket* ThreadedTCP_CreateConnector(
|
||||
const CIPAddr &addr, // Who to connect to.
|
||||
const CIPAddr &localAddr, // Local address to bind to. Leave uninitialized (pass in CIPAddr()) and it will
|
||||
// an interface and a port for you. You can also just fill in the port, and it will
|
||||
// use that port and choose an interface for you.
|
||||
IHandlerCreator *pHandlerCreator// If it connects, it asks this thing to make a handler for the connection.
|
||||
);
|
||||
|
||||
|
||||
// Enable or disable timeouts.
|
||||
void ThreadedTCP_EnableTimeouts( bool bEnable );
|
||||
|
||||
// This should be called at init time. If set to true, it'll set the send and recv threads to low priority.
|
||||
// (Default is true).
|
||||
void ThreadedTCP_SetTCPSocketThreadPriorities( bool bSetTCPSocketThreadPriorities );
|
||||
|
||||
|
||||
#endif // ITHREADEDTCPSOCKET_H
|
||||
1085
utils/vmpi/ThreadedTCPSocket.cpp
Normal file
1085
utils/vmpi/ThreadedTCPSocket.cpp
Normal file
File diff suppressed because it is too large
Load Diff
344
utils/vmpi/ThreadedTCPSocketEmu.cpp
Normal file
344
utils/vmpi/ThreadedTCPSocketEmu.cpp
Normal file
@@ -0,0 +1,344 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include <windows.h>
|
||||
#include "tcpsocket.h"
|
||||
#include "IThreadedTCPSocket.h"
|
||||
#include "ThreadedTCPSocketEmu.h"
|
||||
#include "ThreadHelpers.h"
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------- //
|
||||
// CThreadedTCPSocketEmu. This uses IThreadedTCPSocket to emulate the polling-type interface
|
||||
// in ITCPSocket.
|
||||
// ---------------------------------------------------------------------------------------- //
|
||||
|
||||
// This class uses the IThreadedTCPSocket interface to emulate the old ITCPSocket.
|
||||
class CThreadedTCPSocketEmu : public ITCPSocket, public ITCPSocketHandler, public IHandlerCreator
|
||||
{
|
||||
public:
|
||||
|
||||
CThreadedTCPSocketEmu()
|
||||
{
|
||||
m_pSocket = NULL;
|
||||
m_LocalPort = 0xFFFF;
|
||||
m_pConnectSocket = NULL;
|
||||
m_RecvPacketsEvent.Init( false, false );
|
||||
m_bError = false;
|
||||
}
|
||||
|
||||
virtual ~CThreadedTCPSocketEmu()
|
||||
{
|
||||
Term();
|
||||
}
|
||||
|
||||
void Init( IThreadedTCPSocket *pSocket )
|
||||
{
|
||||
m_pSocket = pSocket;
|
||||
}
|
||||
|
||||
void Term()
|
||||
{
|
||||
if ( m_pSocket )
|
||||
{
|
||||
m_pSocket->Release();
|
||||
m_pSocket = NULL;
|
||||
}
|
||||
|
||||
if ( m_pConnectSocket )
|
||||
{
|
||||
m_pConnectSocket->Release();
|
||||
m_pConnectSocket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ITCPSocketHandler implementation.
|
||||
private:
|
||||
|
||||
|
||||
virtual void OnPacketReceived( CTCPPacket *pPacket )
|
||||
{
|
||||
CCriticalSectionLock csLock( &m_RecvPacketsCS );
|
||||
csLock.Lock();
|
||||
|
||||
m_RecvPackets.AddToTail( pPacket );
|
||||
m_RecvPacketsEvent.SetEvent();
|
||||
}
|
||||
|
||||
|
||||
virtual void OnError( int errorCode, const char *pErrorString )
|
||||
{
|
||||
CCriticalSectionLock csLock( &m_ErrorStringCS );
|
||||
csLock.Lock();
|
||||
|
||||
m_ErrorString.CopyArray( pErrorString, strlen( pErrorString ) + 1 );
|
||||
m_bError = true;
|
||||
}
|
||||
|
||||
|
||||
// IHandlerCreator implementation.
|
||||
public:
|
||||
|
||||
// This is used for connecting.
|
||||
virtual ITCPSocketHandler* CreateNewHandler()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ITCPSocket implementation.
|
||||
public:
|
||||
|
||||
virtual void Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual bool BindToAny( const unsigned short port )
|
||||
{
|
||||
m_LocalPort = port;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool BeginConnect( const CIPAddr &addr )
|
||||
{
|
||||
// They should have "bound" to a port before trying to connect.
|
||||
Assert( m_LocalPort != 0xFFFF );
|
||||
|
||||
if ( m_pConnectSocket )
|
||||
m_pConnectSocket->Release();
|
||||
|
||||
m_pConnectSocket = ThreadedTCP_CreateConnector(
|
||||
addr,
|
||||
CIPAddr( 0, 0, 0, 0, m_LocalPort ),
|
||||
this );
|
||||
|
||||
return m_pConnectSocket != 0;
|
||||
}
|
||||
|
||||
virtual bool UpdateConnect()
|
||||
{
|
||||
Assert( !m_pSocket );
|
||||
if ( !m_pConnectSocket )
|
||||
return false;
|
||||
|
||||
if ( m_pConnectSocket->Update( &m_pSocket ) )
|
||||
{
|
||||
if ( m_pSocket )
|
||||
{
|
||||
// Ok, we're connected now.
|
||||
m_pConnectSocket->Release();
|
||||
m_pConnectSocket = NULL;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( false );
|
||||
m_pConnectSocket->Release();
|
||||
m_pConnectSocket = NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool IsConnected()
|
||||
{
|
||||
if ( m_bError )
|
||||
{
|
||||
Term();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_pSocket != NULL;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void GetDisconnectReason( CUtlVector<char> &reason )
|
||||
{
|
||||
CCriticalSectionLock csLock( &m_ErrorStringCS );
|
||||
csLock.Lock();
|
||||
|
||||
reason = m_ErrorString;
|
||||
}
|
||||
|
||||
virtual bool Send( const void *pData, int size )
|
||||
{
|
||||
Assert( m_pSocket );
|
||||
if ( !m_pSocket )
|
||||
return false;
|
||||
|
||||
return m_pSocket->Send( pData, size );
|
||||
}
|
||||
|
||||
virtual bool SendChunks( void const * const *pChunks, const int *pChunkLengths, int nChunks )
|
||||
{
|
||||
Assert( m_pSocket );
|
||||
if ( !m_pSocket || !m_pSocket->IsValid() )
|
||||
return false;
|
||||
|
||||
return m_pSocket->SendChunks( pChunks, pChunkLengths, nChunks );
|
||||
}
|
||||
|
||||
virtual bool Recv( CUtlVector<unsigned char> &data, double flTimeout )
|
||||
{
|
||||
// Use our m_RecvPacketsEvent event to determine if there is data to receive yet.
|
||||
DWORD nMilliseconds = (DWORD)( flTimeout * 1000.0f );
|
||||
DWORD ret = WaitForSingleObject( m_RecvPacketsEvent.GetEventHandle(), nMilliseconds );
|
||||
if ( ret == WAIT_OBJECT_0 )
|
||||
{
|
||||
// Ok, there's a packet.
|
||||
CCriticalSectionLock csLock( &m_RecvPacketsCS );
|
||||
csLock.Lock();
|
||||
|
||||
Assert( m_RecvPackets.Count() > 0 );
|
||||
|
||||
int iHead = m_RecvPackets.Head();
|
||||
CTCPPacket *pPacket = m_RecvPackets[ iHead ];
|
||||
|
||||
data.CopyArray( (const unsigned char*)pPacket->GetData(), pPacket->GetLen() );
|
||||
|
||||
pPacket->Release();
|
||||
m_RecvPackets.Remove( iHead );
|
||||
|
||||
// Re-set the event if there are more packets left to receive.
|
||||
if ( m_RecvPackets.Count() > 0 )
|
||||
{
|
||||
m_RecvPacketsEvent.SetEvent();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
IThreadedTCPSocket *m_pSocket;
|
||||
|
||||
unsigned short m_LocalPort; // The port we bind to when we want to connect.
|
||||
ITCPConnectSocket *m_pConnectSocket;
|
||||
|
||||
|
||||
// All the received data is stored in here.
|
||||
CEvent m_RecvPacketsEvent;
|
||||
CCriticalSection m_RecvPacketsCS;
|
||||
CUtlLinkedList<CTCPPacket*, int> m_RecvPackets;
|
||||
|
||||
CCriticalSection m_ErrorStringCS;
|
||||
CUtlVector<char> m_ErrorString;
|
||||
bool m_bError; // Set to true when there's an error. Next chance we get in the main thread, we'll close the socket.
|
||||
};
|
||||
|
||||
|
||||
ITCPSocket* CreateTCPSocketEmu()
|
||||
{
|
||||
return new CThreadedTCPSocketEmu;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------- //
|
||||
// CThreadedTCPListenSocketEmu implementation.
|
||||
// ---------------------------------------------------------------------------------------- //
|
||||
|
||||
class CThreadedTCPListenSocketEmu : public ITCPListenSocket, public IHandlerCreator
|
||||
{
|
||||
public:
|
||||
CThreadedTCPListenSocketEmu()
|
||||
{
|
||||
m_pListener = NULL;
|
||||
m_pLastCreatedSocket = NULL;
|
||||
}
|
||||
|
||||
virtual ~CThreadedTCPListenSocketEmu()
|
||||
{
|
||||
if ( m_pListener )
|
||||
m_pListener->Release();
|
||||
}
|
||||
|
||||
bool StartListening( const unsigned short port, int nQueueLength )
|
||||
{
|
||||
m_pListener = ThreadedTCP_CreateListener(
|
||||
this,
|
||||
port,
|
||||
nQueueLength );
|
||||
|
||||
return m_pListener != 0;
|
||||
}
|
||||
|
||||
|
||||
// ITCPListenSocket implementation.
|
||||
private:
|
||||
|
||||
virtual void Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual ITCPSocket* UpdateListen( CIPAddr *pAddr )
|
||||
{
|
||||
if ( !m_pListener )
|
||||
return NULL;
|
||||
|
||||
IThreadedTCPSocket *pSocket;
|
||||
if ( m_pListener->Update( &pSocket ) && pSocket )
|
||||
{
|
||||
*pAddr = pSocket->GetRemoteAddr();
|
||||
|
||||
// This is pretty hacky, but this stuff is just around for test code.
|
||||
CThreadedTCPSocketEmu *pLast = m_pLastCreatedSocket;
|
||||
pLast->Init( pSocket );
|
||||
m_pLastCreatedSocket = NULL;
|
||||
return pLast;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// IHandlerCreator implementation.
|
||||
private:
|
||||
|
||||
virtual ITCPSocketHandler* CreateNewHandler()
|
||||
{
|
||||
m_pLastCreatedSocket = new CThreadedTCPSocketEmu;
|
||||
return m_pLastCreatedSocket;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
ITCPConnectSocket *m_pListener;
|
||||
CThreadedTCPSocketEmu *m_pLastCreatedSocket;
|
||||
};
|
||||
|
||||
ITCPListenSocket* CreateTCPListenSocketEmu( const unsigned short port, int nQueueLength )
|
||||
{
|
||||
CThreadedTCPListenSocketEmu *pSocket = new CThreadedTCPListenSocketEmu;
|
||||
if ( pSocket->StartListening( port, nQueueLength ) )
|
||||
{
|
||||
return pSocket;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pSocket;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
26
utils/vmpi/ThreadedTCPSocketEmu.h
Normal file
26
utils/vmpi/ThreadedTCPSocketEmu.h
Normal file
@@ -0,0 +1,26 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef THREADEDTCPSOCKETEMU_H
|
||||
#define THREADEDTCPSOCKETEMU_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "tcpsocket.h"
|
||||
|
||||
|
||||
// This creates a class that's based on IThreadedTCPSocket, but emulates the old ITCPSocket interface.
|
||||
// This is used for stress-testing IThreadedTCPSocket.
|
||||
ITCPSocket* CreateTCPSocketEmu();
|
||||
|
||||
|
||||
ITCPListenSocket* CreateTCPListenSocketEmu( const unsigned short port, int nQueueLength = -1 );
|
||||
|
||||
|
||||
#endif // THREADEDTCPSOCKETEMU_H
|
||||
15
utils/vmpi/WaitAndRestart/StdAfx.cpp
Normal file
15
utils/vmpi/WaitAndRestart/StdAfx.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// WaitAndRestart.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
32
utils/vmpi/WaitAndRestart/StdAfx.h
Normal file
32
utils/vmpi/WaitAndRestart/StdAfx.h
Normal file
@@ -0,0 +1,32 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#if !defined(AFX_STDAFX_H__6E874DA8_5D18_47D5_B557_3E07B6171907__INCLUDED_)
|
||||
#define AFX_STDAFX_H__6E874DA8_5D18_47D5_B557_3E07B6171907__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <conio.h>
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
|
||||
//{{AFX_INSERT_LOCATION}}
|
||||
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||
|
||||
#endif // !defined(AFX_STDAFX_H__6E874DA8_5D18_47D5_B557_3E07B6171907__INCLUDED_)
|
||||
166
utils/vmpi/WaitAndRestart/WaitAndRestart.cpp
Normal file
166
utils/vmpi/WaitAndRestart/WaitAndRestart.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// WaitAndRestart.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "vmpi_defs.h"
|
||||
|
||||
|
||||
void PrintLog( const char *pMsg, ... )
|
||||
{
|
||||
#ifdef VMPI_SERVICE_LOGS
|
||||
char str[4096];
|
||||
va_list marker;
|
||||
|
||||
va_start( marker, pMsg );
|
||||
_vsnprintf( str, sizeof( str ), pMsg, marker );
|
||||
va_end( marker );
|
||||
|
||||
printf( "%s", str );
|
||||
|
||||
static FILE *fp = fopen( "c:\\vmpi_WaitAndRestart.log", "wt" );
|
||||
if ( fp )
|
||||
{
|
||||
fprintf( fp, "%s", str );
|
||||
fflush( fp );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
char* GetLastErrorString()
|
||||
{
|
||||
static char err[2048];
|
||||
|
||||
LPVOID lpMsgBuf;
|
||||
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL );
|
||||
strncpy( err, (char*)lpMsgBuf, sizeof( err ) );
|
||||
LocalFree( lpMsgBuf );
|
||||
|
||||
err[ sizeof( err ) - 1 ] = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
Sleep(5000);
|
||||
if ( argc < 4 )
|
||||
{
|
||||
PrintLog( "WaitAndRestart <seconds to wait> <working directory> command line...\n" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
PrintLog( "WaitAndRestart <seconds to wait> <working directory> command line...\n" );
|
||||
|
||||
|
||||
const char *pTimeToWait = argv[1];
|
||||
const char *pWorkingDir = argv[2];
|
||||
|
||||
// If a * precedes the time-to-wait arg, then it's a process ID and we wait for that process to exit.
|
||||
if ( pTimeToWait[0] == '*' )
|
||||
{
|
||||
++pTimeToWait;
|
||||
DWORD dwProcessId;
|
||||
sscanf( pTimeToWait, "%lu", &dwProcessId );
|
||||
|
||||
PrintLog( "Waiting for process %lu to exit. Press a key to cancel...\n", dwProcessId );
|
||||
|
||||
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | SYNCHRONIZE, false, dwProcessId );
|
||||
if ( hProcess )
|
||||
{
|
||||
while ( 1 )
|
||||
{
|
||||
DWORD val = WaitForSingleObject( hProcess, 100 );
|
||||
if ( val == WAIT_OBJECT_0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if ( val == WAIT_ABANDONED )
|
||||
{
|
||||
PrintLog( "Got WAIT_ABANDONED (error). Waiting 5 seconds, then continuing.\n" );
|
||||
Sleep( 5000 );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( kbhit() )
|
||||
return 2;
|
||||
}
|
||||
PrintLog( "Process %lu terminated. Continuing.\n", dwProcessId );
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintLog( "Process %lu not running. Continuing.\n", dwProcessId );
|
||||
}
|
||||
|
||||
CloseHandle( hProcess );
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD timeToWait = (DWORD)atoi( argv[1] );
|
||||
|
||||
PrintLog( "\n\nWaiting for %d seconds to launch ' ", timeToWait );
|
||||
PrintLog( "%s> ", pWorkingDir );
|
||||
for ( int i=3; i < argc; i++ )
|
||||
{
|
||||
PrintLog( "%s ", argv[i] );
|
||||
}
|
||||
PrintLog( "'\n\nPress a key to cancel... " );
|
||||
|
||||
DWORD startTime = GetTickCount();
|
||||
while ( GetTickCount() - startTime < (timeToWait*1000) )
|
||||
{
|
||||
if ( kbhit() )
|
||||
return 2;
|
||||
|
||||
Sleep( 100 );
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, launch it!
|
||||
char commandLine[1024] = {0};
|
||||
for ( int i=3; i < argc; i++ )
|
||||
{
|
||||
Q_strncat( commandLine, "\"", sizeof( commandLine ), COPY_ALL_CHARACTERS );
|
||||
Q_strncat( commandLine, argv[i], sizeof( commandLine ), COPY_ALL_CHARACTERS );
|
||||
Q_strncat( commandLine, "\" ", sizeof( commandLine ), COPY_ALL_CHARACTERS );
|
||||
}
|
||||
|
||||
STARTUPINFO si;
|
||||
memset( &si, 0, sizeof( si ) );
|
||||
si.cb = sizeof( si );
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
memset( &pi, 0, sizeof( pi ) );
|
||||
|
||||
if ( CreateProcess(
|
||||
NULL,
|
||||
commandLine,
|
||||
NULL, // security
|
||||
NULL,
|
||||
FALSE,
|
||||
0, // flags
|
||||
NULL, // environment
|
||||
pWorkingDir, // current directory
|
||||
&si,
|
||||
&pi ) )
|
||||
{
|
||||
PrintLog( "Process started.\n" );
|
||||
CloseHandle( pi.hThread ); // We don't care what the process does.
|
||||
CloseHandle( pi.hProcess );
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintLog( "CreateProcess error!\n%s", GetLastErrorString() );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
32
utils/vmpi/WaitAndRestart/waitandrestart.vpc
Normal file
32
utils/vmpi/WaitAndRestart/waitandrestart.vpc
Normal file
@@ -0,0 +1,32 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// WAITANDRESTART.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR "..\..\.."
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
|
||||
$Macro OUTBINNAME "WaitAndRestart"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_exe_con_win32_base.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE,..\"
|
||||
$PreprocessorDefinitions "$BASE;PROTECTED_THINGS_DISABLE"
|
||||
}
|
||||
}
|
||||
|
||||
$Project "WaitAndRestart"
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$File "WaitAndRestart.cpp"
|
||||
}
|
||||
|
||||
$Folder "Header Files"
|
||||
{
|
||||
}
|
||||
}
|
||||
BIN
utils/vmpi/ZLib.lib
Normal file
BIN
utils/vmpi/ZLib.lib
Normal file
Binary file not shown.
318
utils/vmpi/ZLib/deflate.h
Normal file
318
utils/vmpi/ZLib/deflate.h
Normal file
@@ -0,0 +1,318 @@
|
||||
/* deflate.h -- internal compression state
|
||||
* Copyright (C) 1995-1998 Jean-loup Gailly
|
||||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||||
*/
|
||||
|
||||
/* WARNING: this file should *not* be used by applications. It is
|
||||
part of the implementation of the compression library and is
|
||||
subject to change. Applications should only use zlib.h.
|
||||
*/
|
||||
|
||||
/* @(#) $Id$ */
|
||||
|
||||
#ifndef _DEFLATE_H
|
||||
#define _DEFLATE_H
|
||||
|
||||
#include "zutil.h"
|
||||
|
||||
/* ===========================================================================
|
||||
* Internal compression state.
|
||||
*/
|
||||
|
||||
#define LENGTH_CODES 29
|
||||
/* number of length codes, not counting the special END_BLOCK code */
|
||||
|
||||
#define LITERALS 256
|
||||
/* number of literal bytes 0..255 */
|
||||
|
||||
#define L_CODES (LITERALS+1+LENGTH_CODES)
|
||||
/* number of Literal or Length codes, including the END_BLOCK code */
|
||||
|
||||
#define D_CODES 30
|
||||
/* number of distance codes */
|
||||
|
||||
#define BL_CODES 19
|
||||
/* number of codes used to transfer the bit lengths */
|
||||
|
||||
#define HEAP_SIZE (2*L_CODES+1)
|
||||
/* maximum heap size */
|
||||
|
||||
#define MAX_BITS 15
|
||||
/* All codes must not exceed MAX_BITS bits */
|
||||
|
||||
#define INIT_STATE 42
|
||||
#define BUSY_STATE 113
|
||||
#define FINISH_STATE 666
|
||||
/* Stream status */
|
||||
|
||||
|
||||
/* Data structure describing a single value and its code string. */
|
||||
typedef struct ct_data_s {
|
||||
union {
|
||||
ush freq; /* frequency count */
|
||||
ush code; /* bit string */
|
||||
} fc;
|
||||
union {
|
||||
ush dad; /* father node in Huffman tree */
|
||||
ush len; /* length of bit string */
|
||||
} dl;
|
||||
} FAR ct_data;
|
||||
|
||||
#define Freq fc.freq
|
||||
#define Code fc.code
|
||||
#define Dad dl.dad
|
||||
#define Len dl.len
|
||||
|
||||
typedef struct static_tree_desc_s static_tree_desc;
|
||||
|
||||
typedef struct tree_desc_s {
|
||||
ct_data *dyn_tree; /* the dynamic tree */
|
||||
int max_code; /* largest code with non zero frequency */
|
||||
static_tree_desc *stat_desc; /* the corresponding static tree */
|
||||
} FAR tree_desc;
|
||||
|
||||
typedef ush Pos;
|
||||
typedef Pos FAR Posf;
|
||||
typedef unsigned IPos;
|
||||
|
||||
/* A Pos is an index in the character window. We use short instead of int to
|
||||
* save space in the various tables. IPos is used only for parameter passing.
|
||||
*/
|
||||
|
||||
typedef struct internal_state {
|
||||
z_streamp strm; /* pointer back to this zlib stream */
|
||||
int status; /* as the name implies */
|
||||
Bytef *pending_buf; /* output still pending */
|
||||
ulg pending_buf_size; /* size of pending_buf */
|
||||
Bytef *pending_out; /* next pending byte to output to the stream */
|
||||
int pending; /* nb of bytes in the pending buffer */
|
||||
int noheader; /* suppress zlib header and adler32 */
|
||||
Byte data_type; /* UNKNOWN, BINARY or ASCII */
|
||||
Byte method; /* STORED (for zip only) or DEFLATED */
|
||||
int last_flush; /* value of flush param for previous deflate call */
|
||||
|
||||
/* used by deflate.c: */
|
||||
|
||||
uInt w_size; /* LZ77 window size (32K by default) */
|
||||
uInt w_bits; /* log2(w_size) (8..16) */
|
||||
uInt w_mask; /* w_size - 1 */
|
||||
|
||||
Bytef *window;
|
||||
/* Sliding window. Input bytes are read into the second half of the window,
|
||||
* and move to the first half later to keep a dictionary of at least wSize
|
||||
* bytes. With this organization, matches are limited to a distance of
|
||||
* wSize-MAX_MATCH bytes, but this ensures that IO is always
|
||||
* performed with a length multiple of the block size. Also, it limits
|
||||
* the window size to 64K, which is quite useful on MSDOS.
|
||||
* To do: use the user input buffer as sliding window.
|
||||
*/
|
||||
|
||||
ulg window_size;
|
||||
/* Actual size of window: 2*wSize, except when the user input buffer
|
||||
* is directly used as sliding window.
|
||||
*/
|
||||
|
||||
Posf *prev;
|
||||
/* Link to older string with same hash index. To limit the size of this
|
||||
* array to 64K, this link is maintained only for the last 32K strings.
|
||||
* An index in this array is thus a window index modulo 32K.
|
||||
*/
|
||||
|
||||
Posf *head; /* Heads of the hash chains or NIL. */
|
||||
|
||||
uInt ins_h; /* hash index of string to be inserted */
|
||||
uInt hash_size; /* number of elements in hash table */
|
||||
uInt hash_bits; /* log2(hash_size) */
|
||||
uInt hash_mask; /* hash_size-1 */
|
||||
|
||||
uInt hash_shift;
|
||||
/* Number of bits by which ins_h must be shifted at each input
|
||||
* step. It must be such that after MIN_MATCH steps, the oldest
|
||||
* byte no longer takes part in the hash key, that is:
|
||||
* hash_shift * MIN_MATCH >= hash_bits
|
||||
*/
|
||||
|
||||
long block_start;
|
||||
/* Window position at the beginning of the current output block. Gets
|
||||
* negative when the window is moved backwards.
|
||||
*/
|
||||
|
||||
uInt match_length; /* length of best match */
|
||||
IPos prev_match; /* previous match */
|
||||
int match_available; /* set if previous match exists */
|
||||
uInt strstart; /* start of string to insert */
|
||||
uInt match_start; /* start of matching string */
|
||||
uInt lookahead; /* number of valid bytes ahead in window */
|
||||
|
||||
uInt prev_length;
|
||||
/* Length of the best match at previous step. Matches not greater than this
|
||||
* are discarded. This is used in the lazy match evaluation.
|
||||
*/
|
||||
|
||||
uInt max_chain_length;
|
||||
/* To speed up deflation, hash chains are never searched beyond this
|
||||
* length. A higher limit improves compression ratio but degrades the
|
||||
* speed.
|
||||
*/
|
||||
|
||||
uInt max_lazy_match;
|
||||
/* Attempt to find a better match only when the current match is strictly
|
||||
* smaller than this value. This mechanism is used only for compression
|
||||
* levels >= 4.
|
||||
*/
|
||||
# define max_insert_length max_lazy_match
|
||||
/* Insert new strings in the hash table only if the match length is not
|
||||
* greater than this length. This saves time but degrades compression.
|
||||
* max_insert_length is used only for compression levels <= 3.
|
||||
*/
|
||||
|
||||
int level; /* compression level (1..9) */
|
||||
int strategy; /* favor or force Huffman coding*/
|
||||
|
||||
uInt good_match;
|
||||
/* Use a faster search when the previous match is longer than this */
|
||||
|
||||
int nice_match; /* Stop searching when current match exceeds this */
|
||||
|
||||
/* used by trees.c: */
|
||||
/* Didn't use ct_data typedef below to supress compiler warning */
|
||||
struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
|
||||
struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
|
||||
struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
|
||||
|
||||
struct tree_desc_s l_desc; /* desc. for literal tree */
|
||||
struct tree_desc_s d_desc; /* desc. for distance tree */
|
||||
struct tree_desc_s bl_desc; /* desc. for bit length tree */
|
||||
|
||||
ush bl_count[MAX_BITS+1];
|
||||
/* number of codes at each bit length for an optimal tree */
|
||||
|
||||
int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
|
||||
int heap_len; /* number of elements in the heap */
|
||||
int heap_max; /* element of largest frequency */
|
||||
/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
|
||||
* The same heap array is used to build all trees.
|
||||
*/
|
||||
|
||||
uch depth[2*L_CODES+1];
|
||||
/* Depth of each subtree used as tie breaker for trees of equal frequency
|
||||
*/
|
||||
|
||||
uchf *l_buf; /* buffer for literals or lengths */
|
||||
|
||||
uInt lit_bufsize;
|
||||
/* Size of match buffer for literals/lengths. There are 4 reasons for
|
||||
* limiting lit_bufsize to 64K:
|
||||
* - frequencies can be kept in 16 bit counters
|
||||
* - if compression is not successful for the first block, all input
|
||||
* data is still in the window so we can still emit a stored block even
|
||||
* when input comes from standard input. (This can also be done for
|
||||
* all blocks if lit_bufsize is not greater than 32K.)
|
||||
* - if compression is not successful for a file smaller than 64K, we can
|
||||
* even emit a stored file instead of a stored block (saving 5 bytes).
|
||||
* This is applicable only for zip (not gzip or zlib).
|
||||
* - creating new Huffman trees less frequently may not provide fast
|
||||
* adaptation to changes in the input data statistics. (Take for
|
||||
* example a binary file with poorly compressible code followed by
|
||||
* a highly compressible string table.) Smaller buffer sizes give
|
||||
* fast adaptation but have of course the overhead of transmitting
|
||||
* trees more frequently.
|
||||
* - I can't count above 4
|
||||
*/
|
||||
|
||||
uInt last_lit; /* running index in l_buf */
|
||||
|
||||
ushf *d_buf;
|
||||
/* Buffer for distances. To simplify the code, d_buf and l_buf have
|
||||
* the same number of elements. To use different lengths, an extra flag
|
||||
* array would be necessary.
|
||||
*/
|
||||
|
||||
ulg opt_len; /* bit length of current block with optimal trees */
|
||||
ulg static_len; /* bit length of current block with static trees */
|
||||
uInt matches; /* number of string matches in current block */
|
||||
int last_eob_len; /* bit length of EOB code for last block */
|
||||
|
||||
#ifdef DEBUG
|
||||
ulg compressed_len; /* total bit length of compressed file mod 2^32 */
|
||||
ulg bits_sent; /* bit length of compressed data sent mod 2^32 */
|
||||
#endif
|
||||
|
||||
ush bi_buf;
|
||||
/* Output buffer. bits are inserted starting at the bottom (least
|
||||
* significant bits).
|
||||
*/
|
||||
int bi_valid;
|
||||
/* Number of valid bits in bi_buf. All bits above the last valid bit
|
||||
* are always zero.
|
||||
*/
|
||||
|
||||
} FAR deflate_state;
|
||||
|
||||
/* Output a byte on the stream.
|
||||
* IN assertion: there is enough room in pending_buf.
|
||||
*/
|
||||
#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
|
||||
|
||||
|
||||
#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
|
||||
/* Minimum amount of lookahead, except at the end of the input file.
|
||||
* See deflate.c for comments about the MIN_MATCH+1.
|
||||
*/
|
||||
|
||||
#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
|
||||
/* In order to simplify the code, particularly on 16 bit machines, match
|
||||
* distances are limited to MAX_DIST instead of WSIZE.
|
||||
*/
|
||||
|
||||
/* in trees.c */
|
||||
void _tr_init OF((deflate_state *s));
|
||||
int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
|
||||
void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len,
|
||||
int eof));
|
||||
void _tr_align OF((deflate_state *s));
|
||||
void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
|
||||
int eof));
|
||||
|
||||
#define d_code(dist) \
|
||||
((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
|
||||
/* Mapping from a distance to a distance code. dist is the distance - 1 and
|
||||
* must not have side effects. _dist_code[256] and _dist_code[257] are never
|
||||
* used.
|
||||
*/
|
||||
|
||||
#ifndef DEBUG
|
||||
/* Inline versions of _tr_tally for speed: */
|
||||
|
||||
#if defined(GEN_TREES_H) || !defined(STDC)
|
||||
extern uch _length_code[];
|
||||
extern uch _dist_code[];
|
||||
#else
|
||||
extern const uch _length_code[];
|
||||
extern const uch _dist_code[];
|
||||
#endif
|
||||
|
||||
# define _tr_tally_lit(s, c, flush) \
|
||||
{ uch cc = (c); \
|
||||
s->d_buf[s->last_lit] = 0; \
|
||||
s->l_buf[s->last_lit++] = cc; \
|
||||
s->dyn_ltree[cc].Freq++; \
|
||||
flush = (s->last_lit == s->lit_bufsize-1); \
|
||||
}
|
||||
# define _tr_tally_dist(s, distance, length, flush) \
|
||||
{ uch len = (length); \
|
||||
ush dist = (distance); \
|
||||
s->d_buf[s->last_lit] = dist; \
|
||||
s->l_buf[s->last_lit++] = len; \
|
||||
dist--; \
|
||||
s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
|
||||
s->dyn_dtree[d_code(dist)].Freq++; \
|
||||
flush = (s->last_lit == s->lit_bufsize-1); \
|
||||
}
|
||||
#else
|
||||
# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
|
||||
# define _tr_tally_dist(s, distance, length, flush) \
|
||||
flush = _tr_tally(s, distance, length)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
39
utils/vmpi/ZLib/infblock.h
Normal file
39
utils/vmpi/ZLib/infblock.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* infblock.h -- header to use infblock.c
|
||||
* Copyright (C) 1995-1998 Mark Adler
|
||||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||||
*/
|
||||
|
||||
/* WARNING: this file should *not* be used by applications. It is
|
||||
part of the implementation of the compression library and is
|
||||
subject to change. Applications should only use zlib.h.
|
||||
*/
|
||||
|
||||
struct inflate_blocks_state;
|
||||
typedef struct inflate_blocks_state FAR inflate_blocks_statef;
|
||||
|
||||
extern inflate_blocks_statef * inflate_blocks_new OF((
|
||||
z_streamp z,
|
||||
check_func c, /* check function */
|
||||
uInt w)); /* window size */
|
||||
|
||||
extern int inflate_blocks OF((
|
||||
inflate_blocks_statef *,
|
||||
z_streamp ,
|
||||
int)); /* initial return code */
|
||||
|
||||
extern void inflate_blocks_reset OF((
|
||||
inflate_blocks_statef *,
|
||||
z_streamp ,
|
||||
uLongf *)); /* check value on output */
|
||||
|
||||
extern int inflate_blocks_free OF((
|
||||
inflate_blocks_statef *,
|
||||
z_streamp));
|
||||
|
||||
extern void inflate_set_dictionary OF((
|
||||
inflate_blocks_statef *s,
|
||||
const Bytef *d, /* dictionary */
|
||||
uInt n)); /* dictionary length */
|
||||
|
||||
extern int inflate_blocks_sync_point OF((
|
||||
inflate_blocks_statef *s));
|
||||
27
utils/vmpi/ZLib/infcodes.h
Normal file
27
utils/vmpi/ZLib/infcodes.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* infcodes.h -- header to use infcodes.c
|
||||
* Copyright (C) 1995-1998 Mark Adler
|
||||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||||
*/
|
||||
|
||||
/* WARNING: this file should *not* be used by applications. It is
|
||||
part of the implementation of the compression library and is
|
||||
subject to change. Applications should only use zlib.h.
|
||||
*/
|
||||
|
||||
struct inflate_codes_state;
|
||||
typedef struct inflate_codes_state FAR inflate_codes_statef;
|
||||
|
||||
extern inflate_codes_statef *inflate_codes_new OF((
|
||||
uInt, uInt,
|
||||
inflate_huft *, inflate_huft *,
|
||||
z_streamp ));
|
||||
|
||||
extern int inflate_codes OF((
|
||||
inflate_blocks_statef *,
|
||||
z_streamp ,
|
||||
int));
|
||||
|
||||
extern void inflate_codes_free OF((
|
||||
inflate_codes_statef *,
|
||||
z_streamp ));
|
||||
|
||||
17
utils/vmpi/ZLib/inffast.h
Normal file
17
utils/vmpi/ZLib/inffast.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/* inffast.h -- header to use inffast.c
|
||||
* Copyright (C) 1995-1998 Mark Adler
|
||||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||||
*/
|
||||
|
||||
/* WARNING: this file should *not* be used by applications. It is
|
||||
part of the implementation of the compression library and is
|
||||
subject to change. Applications should only use zlib.h.
|
||||
*/
|
||||
|
||||
extern int inflate_fast OF((
|
||||
uInt,
|
||||
uInt,
|
||||
inflate_huft *,
|
||||
inflate_huft *,
|
||||
inflate_blocks_statef *,
|
||||
z_streamp ));
|
||||
151
utils/vmpi/ZLib/inffixed.h
Normal file
151
utils/vmpi/ZLib/inffixed.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/* inffixed.h -- table for decoding fixed codes
|
||||
* Generated automatically by the maketree.c program
|
||||
*/
|
||||
|
||||
/* WARNING: this file should *not* be used by applications. It is
|
||||
part of the implementation of the compression library and is
|
||||
subject to change. Applications should only use zlib.h.
|
||||
*/
|
||||
|
||||
local uInt fixed_bl = 9;
|
||||
local uInt fixed_bd = 5;
|
||||
local inflate_huft fixed_tl[] = {
|
||||
{{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
|
||||
{{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192},
|
||||
{{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160},
|
||||
{{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224},
|
||||
{{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144},
|
||||
{{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208},
|
||||
{{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176},
|
||||
{{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240},
|
||||
{{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
|
||||
{{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200},
|
||||
{{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168},
|
||||
{{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232},
|
||||
{{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152},
|
||||
{{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216},
|
||||
{{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184},
|
||||
{{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248},
|
||||
{{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
|
||||
{{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196},
|
||||
{{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164},
|
||||
{{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228},
|
||||
{{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148},
|
||||
{{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212},
|
||||
{{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180},
|
||||
{{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244},
|
||||
{{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
|
||||
{{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204},
|
||||
{{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172},
|
||||
{{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236},
|
||||
{{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156},
|
||||
{{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220},
|
||||
{{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188},
|
||||
{{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252},
|
||||
{{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
|
||||
{{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194},
|
||||
{{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162},
|
||||
{{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226},
|
||||
{{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146},
|
||||
{{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210},
|
||||
{{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178},
|
||||
{{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242},
|
||||
{{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
|
||||
{{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202},
|
||||
{{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170},
|
||||
{{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234},
|
||||
{{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154},
|
||||
{{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218},
|
||||
{{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186},
|
||||
{{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250},
|
||||
{{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
|
||||
{{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198},
|
||||
{{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166},
|
||||
{{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230},
|
||||
{{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150},
|
||||
{{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214},
|
||||
{{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182},
|
||||
{{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246},
|
||||
{{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
|
||||
{{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206},
|
||||
{{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174},
|
||||
{{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238},
|
||||
{{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158},
|
||||
{{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222},
|
||||
{{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190},
|
||||
{{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254},
|
||||
{{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
|
||||
{{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193},
|
||||
{{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161},
|
||||
{{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225},
|
||||
{{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145},
|
||||
{{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209},
|
||||
{{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177},
|
||||
{{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241},
|
||||
{{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
|
||||
{{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201},
|
||||
{{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169},
|
||||
{{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233},
|
||||
{{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153},
|
||||
{{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217},
|
||||
{{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185},
|
||||
{{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249},
|
||||
{{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
|
||||
{{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197},
|
||||
{{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165},
|
||||
{{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229},
|
||||
{{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149},
|
||||
{{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213},
|
||||
{{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181},
|
||||
{{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245},
|
||||
{{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
|
||||
{{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205},
|
||||
{{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173},
|
||||
{{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237},
|
||||
{{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157},
|
||||
{{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221},
|
||||
{{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189},
|
||||
{{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253},
|
||||
{{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
|
||||
{{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195},
|
||||
{{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163},
|
||||
{{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227},
|
||||
{{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147},
|
||||
{{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211},
|
||||
{{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179},
|
||||
{{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243},
|
||||
{{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
|
||||
{{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203},
|
||||
{{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171},
|
||||
{{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235},
|
||||
{{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155},
|
||||
{{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219},
|
||||
{{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187},
|
||||
{{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251},
|
||||
{{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
|
||||
{{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199},
|
||||
{{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167},
|
||||
{{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231},
|
||||
{{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151},
|
||||
{{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215},
|
||||
{{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183},
|
||||
{{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247},
|
||||
{{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
|
||||
{{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207},
|
||||
{{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175},
|
||||
{{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239},
|
||||
{{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159},
|
||||
{{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223},
|
||||
{{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191},
|
||||
{{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255}
|
||||
};
|
||||
local inflate_huft fixed_td[] = {
|
||||
{{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097},
|
||||
{{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385},
|
||||
{{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193},
|
||||
{{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577},
|
||||
{{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145},
|
||||
{{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577},
|
||||
{{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289},
|
||||
{{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577}
|
||||
};
|
||||
58
utils/vmpi/ZLib/inftrees.h
Normal file
58
utils/vmpi/ZLib/inftrees.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* inftrees.h -- header to use inftrees.c
|
||||
* Copyright (C) 1995-1998 Mark Adler
|
||||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||||
*/
|
||||
|
||||
/* WARNING: this file should *not* be used by applications. It is
|
||||
part of the implementation of the compression library and is
|
||||
subject to change. Applications should only use zlib.h.
|
||||
*/
|
||||
|
||||
/* Huffman code lookup table entry--this entry is four bytes for machines
|
||||
that have 16-bit pointers (e.g. PC's in the small or medium model). */
|
||||
|
||||
typedef struct inflate_huft_s FAR inflate_huft;
|
||||
|
||||
struct inflate_huft_s {
|
||||
union {
|
||||
struct {
|
||||
Byte Exop; /* number of extra bits or operation */
|
||||
Byte Bits; /* number of bits in this code or subcode */
|
||||
} what;
|
||||
uInt pad; /* pad structure to a power of 2 (4 bytes for */
|
||||
} word; /* 16-bit, 8 bytes for 32-bit int's) */
|
||||
uInt base; /* literal, length base, distance base,
|
||||
or table offset */
|
||||
};
|
||||
|
||||
/* Maximum size of dynamic tree. The maximum found in a long but non-
|
||||
exhaustive search was 1004 huft structures (850 for length/literals
|
||||
and 154 for distances, the latter actually the result of an
|
||||
exhaustive search). The actual maximum is not known, but the
|
||||
value below is more than safe. */
|
||||
#define MANY 1440
|
||||
|
||||
extern int inflate_trees_bits OF((
|
||||
uIntf *, /* 19 code lengths */
|
||||
uIntf *, /* bits tree desired/actual depth */
|
||||
inflate_huft * FAR *, /* bits tree result */
|
||||
inflate_huft *, /* space for trees */
|
||||
z_streamp)); /* for messages */
|
||||
|
||||
extern int inflate_trees_dynamic OF((
|
||||
uInt, /* number of literal/length codes */
|
||||
uInt, /* number of distance codes */
|
||||
uIntf *, /* that many (total) code lengths */
|
||||
uIntf *, /* literal desired/actual bit depth */
|
||||
uIntf *, /* distance desired/actual bit depth */
|
||||
inflate_huft * FAR *, /* literal/length tree result */
|
||||
inflate_huft * FAR *, /* distance tree result */
|
||||
inflate_huft *, /* space for trees */
|
||||
z_streamp)); /* for messages */
|
||||
|
||||
extern int inflate_trees_fixed OF((
|
||||
uIntf *, /* literal desired/actual bit depth */
|
||||
uIntf *, /* distance desired/actual bit depth */
|
||||
inflate_huft * FAR *, /* literal/length tree result */
|
||||
inflate_huft * FAR *, /* distance tree result */
|
||||
z_streamp)); /* for memory allocation */
|
||||
98
utils/vmpi/ZLib/infutil.h
Normal file
98
utils/vmpi/ZLib/infutil.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* infutil.h -- types and macros common to blocks and codes
|
||||
* Copyright (C) 1995-1998 Mark Adler
|
||||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||||
*/
|
||||
|
||||
/* WARNING: this file should *not* be used by applications. It is
|
||||
part of the implementation of the compression library and is
|
||||
subject to change. Applications should only use zlib.h.
|
||||
*/
|
||||
|
||||
#ifndef _INFUTIL_H
|
||||
#define _INFUTIL_H
|
||||
|
||||
typedef enum {
|
||||
TYPE, /* get type bits (3, including end bit) */
|
||||
LENS, /* get lengths for stored */
|
||||
STORED, /* processing stored block */
|
||||
TABLE, /* get table lengths */
|
||||
BTREE, /* get bit lengths tree for a dynamic block */
|
||||
DTREE, /* get length, distance trees for a dynamic block */
|
||||
CODES, /* processing fixed or dynamic block */
|
||||
DRY, /* output remaining window bytes */
|
||||
DONE, /* finished last block, done */
|
||||
BAD} /* got a data error--stuck here */
|
||||
inflate_block_mode;
|
||||
|
||||
/* inflate blocks semi-private state */
|
||||
struct inflate_blocks_state {
|
||||
|
||||
/* mode */
|
||||
inflate_block_mode mode; /* current inflate_block mode */
|
||||
|
||||
/* mode dependent information */
|
||||
union {
|
||||
uInt left; /* if STORED, bytes left to copy */
|
||||
struct {
|
||||
uInt table; /* table lengths (14 bits) */
|
||||
uInt index; /* index into blens (or border) */
|
||||
uIntf *blens; /* bit lengths of codes */
|
||||
uInt bb; /* bit length tree depth */
|
||||
inflate_huft *tb; /* bit length decoding tree */
|
||||
} trees; /* if DTREE, decoding info for trees */
|
||||
struct {
|
||||
inflate_codes_statef
|
||||
*codes;
|
||||
} decode; /* if CODES, current state */
|
||||
} sub; /* submode */
|
||||
uInt last; /* true if this block is the last block */
|
||||
|
||||
/* mode independent information */
|
||||
uInt bitk; /* bits in bit buffer */
|
||||
uLong bitb; /* bit buffer */
|
||||
inflate_huft *hufts; /* single malloc for tree space */
|
||||
Bytef *window; /* sliding window */
|
||||
Bytef *end; /* one byte after sliding window */
|
||||
Bytef *read; /* window read pointer */
|
||||
Bytef *write; /* window write pointer */
|
||||
check_func checkfn; /* check function */
|
||||
uLong check; /* check on output */
|
||||
|
||||
};
|
||||
|
||||
|
||||
/* defines for inflate input/output */
|
||||
/* update pointers and return */
|
||||
#define UPDBITS {s->bitb=b;s->bitk=k;}
|
||||
#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
|
||||
#define UPDOUT {s->write=q;}
|
||||
#define UPDATE {UPDBITS UPDIN UPDOUT}
|
||||
#define LEAVE {UPDATE return inflate_flush(s,z,r);}
|
||||
/* get bytes and bits */
|
||||
#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
|
||||
#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
|
||||
#define NEXTBYTE (n--,*p++)
|
||||
#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
|
||||
#define DUMPBITS(j) {b>>=(j);k-=(j);}
|
||||
/* output bytes */
|
||||
#define WAVAIL (uInt)(q<s->read?s->read-q-1:s->end-q)
|
||||
#define LOADOUT {q=s->write;m=(uInt)WAVAIL;}
|
||||
#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}}
|
||||
#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
|
||||
#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
|
||||
#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
|
||||
/* load local pointers */
|
||||
#define LOAD {LOADIN LOADOUT}
|
||||
|
||||
/* masks for lower bits (size given to avoid silly warnings with Visual C++) */
|
||||
extern uInt inflate_mask[17];
|
||||
|
||||
/* copy as much as possible from the sliding window to the output area */
|
||||
extern int inflate_flush OF((
|
||||
inflate_blocks_statef *,
|
||||
z_streamp ,
|
||||
int));
|
||||
|
||||
struct internal_state {int dummy;}; /* for buggy compilers */
|
||||
|
||||
#endif
|
||||
128
utils/vmpi/ZLib/trees.h
Normal file
128
utils/vmpi/ZLib/trees.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/* header created automatically with -DGEN_TREES_H */
|
||||
|
||||
local const ct_data static_ltree[L_CODES+2] = {
|
||||
{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}},
|
||||
{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}},
|
||||
{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}},
|
||||
{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}},
|
||||
{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}},
|
||||
{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}},
|
||||
{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}},
|
||||
{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}},
|
||||
{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}},
|
||||
{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}},
|
||||
{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}},
|
||||
{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}},
|
||||
{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}},
|
||||
{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}},
|
||||
{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}},
|
||||
{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}},
|
||||
{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}},
|
||||
{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}},
|
||||
{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}},
|
||||
{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}},
|
||||
{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}},
|
||||
{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}},
|
||||
{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}},
|
||||
{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}},
|
||||
{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}},
|
||||
{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}},
|
||||
{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}},
|
||||
{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}},
|
||||
{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}},
|
||||
{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}},
|
||||
{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}},
|
||||
{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}},
|
||||
{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}},
|
||||
{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}},
|
||||
{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}},
|
||||
{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}},
|
||||
{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}},
|
||||
{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}},
|
||||
{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}},
|
||||
{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}},
|
||||
{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}},
|
||||
{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}},
|
||||
{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}},
|
||||
{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}},
|
||||
{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}},
|
||||
{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}},
|
||||
{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}},
|
||||
{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}},
|
||||
{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}},
|
||||
{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}},
|
||||
{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}},
|
||||
{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}},
|
||||
{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}},
|
||||
{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}},
|
||||
{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}},
|
||||
{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}},
|
||||
{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}},
|
||||
{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}}
|
||||
};
|
||||
|
||||
local const ct_data static_dtree[D_CODES] = {
|
||||
{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
|
||||
{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
|
||||
{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
|
||||
{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
|
||||
{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
|
||||
{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
|
||||
};
|
||||
|
||||
const uch _dist_code[DIST_CODE_LEN] = {
|
||||
0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
|
||||
8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||
10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
|
||||
11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
|
||||
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
|
||||
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
|
||||
13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17,
|
||||
18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||
27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||
28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||||
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||||
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
|
||||
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
|
||||
};
|
||||
|
||||
const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12,
|
||||
13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
|
||||
19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
|
||||
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||
23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
|
||||
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
|
||||
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
|
||||
};
|
||||
|
||||
local const int base_length[LENGTH_CODES] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
|
||||
64, 80, 96, 112, 128, 160, 192, 224, 0
|
||||
};
|
||||
|
||||
local const int base_dist[D_CODES] = {
|
||||
0, 1, 2, 3, 4, 6, 8, 12, 16, 24,
|
||||
32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
|
||||
1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
|
||||
};
|
||||
|
||||
279
utils/vmpi/ZLib/zconf.h
Normal file
279
utils/vmpi/ZLib/zconf.h
Normal file
@@ -0,0 +1,279 @@
|
||||
/* zconf.h -- configuration of the zlib compression library
|
||||
* Copyright (C) 1995-1998 Jean-loup Gailly.
|
||||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||||
*/
|
||||
|
||||
/* @(#) $Id$ */
|
||||
|
||||
#ifndef _ZCONF_H
|
||||
#define _ZCONF_H
|
||||
|
||||
/*
|
||||
* If you *really* need a unique prefix for all types and library functions,
|
||||
* compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
|
||||
*/
|
||||
#ifdef Z_PREFIX
|
||||
# define deflateInit_ z_deflateInit_
|
||||
# define deflate z_deflate
|
||||
# define deflateEnd z_deflateEnd
|
||||
# define inflateInit_ z_inflateInit_
|
||||
# define inflate z_inflate
|
||||
# define inflateEnd z_inflateEnd
|
||||
# define deflateInit2_ z_deflateInit2_
|
||||
# define deflateSetDictionary z_deflateSetDictionary
|
||||
# define deflateCopy z_deflateCopy
|
||||
# define deflateReset z_deflateReset
|
||||
# define deflateParams z_deflateParams
|
||||
# define inflateInit2_ z_inflateInit2_
|
||||
# define inflateSetDictionary z_inflateSetDictionary
|
||||
# define inflateSync z_inflateSync
|
||||
# define inflateSyncPoint z_inflateSyncPoint
|
||||
# define inflateReset z_inflateReset
|
||||
# define compress z_compress
|
||||
# define compress2 z_compress2
|
||||
# define uncompress z_uncompress
|
||||
# define adler32 z_adler32
|
||||
# define crc32 z_crc32
|
||||
# define get_crc_table z_get_crc_table
|
||||
|
||||
# define Byte z_Byte
|
||||
# define uInt z_uInt
|
||||
# define uLong z_uLong
|
||||
# define Bytef z_Bytef
|
||||
# define charf z_charf
|
||||
# define intf z_intf
|
||||
# define uIntf z_uIntf
|
||||
# define uLongf z_uLongf
|
||||
# define voidpf z_voidpf
|
||||
# define voidp z_voidp
|
||||
#endif
|
||||
|
||||
#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
|
||||
# define WIN32
|
||||
#endif
|
||||
#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386)
|
||||
# ifndef __32BIT__
|
||||
# define __32BIT__
|
||||
# endif
|
||||
#endif
|
||||
#if defined(__MSDOS__) && !defined(MSDOS)
|
||||
# define MSDOS
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Compile with -DMAXSEG_64K if the alloc function cannot allocate more
|
||||
* than 64k bytes at a time (needed on systems with 16-bit int).
|
||||
*/
|
||||
#if defined(MSDOS) && !defined(__32BIT__)
|
||||
# define MAXSEG_64K
|
||||
#endif
|
||||
#ifdef MSDOS
|
||||
# define UNALIGNED_OK
|
||||
#endif
|
||||
|
||||
#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC)
|
||||
# define STDC
|
||||
#endif
|
||||
#if defined(__STDC__) || defined(__cplusplus) || defined(__OS2__)
|
||||
# ifndef STDC
|
||||
# define STDC
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef STDC
|
||||
# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
|
||||
# define const
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Some Mac compilers merge all .h files incorrectly: */
|
||||
#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__)
|
||||
# define NO_DUMMY_DECL
|
||||
#endif
|
||||
|
||||
/* Old Borland C incorrectly complains about missing returns: */
|
||||
#if defined(__BORLANDC__) && (__BORLANDC__ < 0x500)
|
||||
# define NEED_DUMMY_RETURN
|
||||
#endif
|
||||
|
||||
|
||||
/* Maximum value for memLevel in deflateInit2 */
|
||||
#ifndef MAX_MEM_LEVEL
|
||||
# ifdef MAXSEG_64K
|
||||
# define MAX_MEM_LEVEL 8
|
||||
# else
|
||||
# define MAX_MEM_LEVEL 9
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Maximum value for windowBits in deflateInit2 and inflateInit2.
|
||||
* WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
|
||||
* created by gzip. (Files created by minigzip can still be extracted by
|
||||
* gzip.)
|
||||
*/
|
||||
#ifndef MAX_WBITS
|
||||
# define MAX_WBITS 15 /* 32K LZ77 window */
|
||||
#endif
|
||||
|
||||
/* The memory requirements for deflate are (in bytes):
|
||||
(1 << (windowBits+2)) + (1 << (memLevel+9))
|
||||
that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
|
||||
plus a few kilobytes for small objects. For example, if you want to reduce
|
||||
the default memory requirements from 256K to 128K, compile with
|
||||
make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
|
||||
Of course this will generally degrade compression (there's no free lunch).
|
||||
|
||||
The memory requirements for inflate are (in bytes) 1 << windowBits
|
||||
that is, 32K for windowBits=15 (default value) plus a few kilobytes
|
||||
for small objects.
|
||||
*/
|
||||
|
||||
/* Type declarations */
|
||||
|
||||
#ifndef OF /* function prototypes */
|
||||
# ifdef STDC
|
||||
# define OF(args) args
|
||||
# else
|
||||
# define OF(args) ()
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* The following definitions for FAR are needed only for MSDOS mixed
|
||||
* model programming (small or medium model with some far allocations).
|
||||
* This was tested only with MSC; for other MSDOS compilers you may have
|
||||
* to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
|
||||
* just define FAR to be empty.
|
||||
*/
|
||||
#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__)
|
||||
/* MSC small or medium model */
|
||||
# define SMALL_MEDIUM
|
||||
# ifdef _MSC_VER
|
||||
# define FAR _far
|
||||
# else
|
||||
# define FAR far
|
||||
# endif
|
||||
#endif
|
||||
#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__))
|
||||
# ifndef __32BIT__
|
||||
# define SMALL_MEDIUM
|
||||
# define FAR _far
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Compile with -DZLIB_DLL for Windows DLL support */
|
||||
#if defined(ZLIB_DLL)
|
||||
# if defined(_WINDOWS) || defined(WINDOWS)
|
||||
# ifdef FAR
|
||||
# undef FAR
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# define ZEXPORT WINAPI
|
||||
# ifdef WIN32
|
||||
# define ZEXPORTVA WINAPIV
|
||||
# else
|
||||
# define ZEXPORTVA FAR _cdecl _export
|
||||
# endif
|
||||
# endif
|
||||
# if defined (__BORLANDC__)
|
||||
# if (__BORLANDC__ >= 0x0500) && defined (WIN32)
|
||||
# include <windows.h>
|
||||
# define ZEXPORT __declspec(dllexport) WINAPI
|
||||
# define ZEXPORTRVA __declspec(dllexport) WINAPIV
|
||||
# else
|
||||
# if defined (_Windows) && defined (__DLL__)
|
||||
# define ZEXPORT _export
|
||||
# define ZEXPORTVA _export
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__BEOS__)
|
||||
# if defined (ZLIB_DLL)
|
||||
# define ZEXTERN extern __declspec(dllexport)
|
||||
# else
|
||||
# define ZEXTERN extern __declspec(dllimport)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef ZEXPORT
|
||||
# define ZEXPORT
|
||||
#endif
|
||||
#ifndef ZEXPORTVA
|
||||
# define ZEXPORTVA
|
||||
#endif
|
||||
#ifndef ZEXTERN
|
||||
# define ZEXTERN extern
|
||||
#endif
|
||||
|
||||
#ifndef FAR
|
||||
# define FAR
|
||||
#endif
|
||||
|
||||
#if !defined(MACOS) && !defined(TARGET_OS_MAC)
|
||||
typedef unsigned char Byte; /* 8 bits */
|
||||
#endif
|
||||
typedef unsigned int uInt; /* 16 bits or more */
|
||||
typedef unsigned long uLong; /* 32 bits or more */
|
||||
|
||||
#ifdef SMALL_MEDIUM
|
||||
/* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
|
||||
# define Bytef Byte FAR
|
||||
#else
|
||||
typedef Byte FAR Bytef;
|
||||
#endif
|
||||
typedef char FAR charf;
|
||||
typedef int FAR intf;
|
||||
typedef uInt FAR uIntf;
|
||||
typedef uLong FAR uLongf;
|
||||
|
||||
#ifdef STDC
|
||||
typedef void FAR *voidpf;
|
||||
typedef void *voidp;
|
||||
#else
|
||||
typedef Byte FAR *voidpf;
|
||||
typedef Byte *voidp;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <sys/types.h> /* for off_t */
|
||||
# include <unistd.h> /* for SEEK_* and off_t */
|
||||
# define z_off_t off_t
|
||||
#endif
|
||||
#ifndef SEEK_SET
|
||||
# define SEEK_SET 0 /* Seek from beginning of file. */
|
||||
# define SEEK_CUR 1 /* Seek from current position. */
|
||||
# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
|
||||
#endif
|
||||
#ifndef z_off_t
|
||||
# define z_off_t long
|
||||
#endif
|
||||
|
||||
/* MVS linker does not support external names larger than 8 bytes */
|
||||
#if defined(__MVS__)
|
||||
# pragma map(deflateInit_,"DEIN")
|
||||
# pragma map(deflateInit2_,"DEIN2")
|
||||
# pragma map(deflateEnd,"DEEND")
|
||||
# pragma map(inflateInit_,"ININ")
|
||||
# pragma map(inflateInit2_,"ININ2")
|
||||
# pragma map(inflateEnd,"INEND")
|
||||
# pragma map(inflateSync,"INSY")
|
||||
# pragma map(inflateSetDictionary,"INSEDI")
|
||||
# pragma map(inflate_blocks,"INBL")
|
||||
# pragma map(inflate_blocks_new,"INBLNE")
|
||||
# pragma map(inflate_blocks_free,"INBLFR")
|
||||
# pragma map(inflate_blocks_reset,"INBLRE")
|
||||
# pragma map(inflate_codes_free,"INCOFR")
|
||||
# pragma map(inflate_codes,"INCO")
|
||||
# pragma map(inflate_fast,"INFA")
|
||||
# pragma map(inflate_flush,"INFLU")
|
||||
# pragma map(inflate_mask,"INMA")
|
||||
# pragma map(inflate_set_dictionary,"INSEDI2")
|
||||
# pragma map(inflate_copyright,"INCOPY")
|
||||
# pragma map(inflate_trees_bits,"INTRBI")
|
||||
# pragma map(inflate_trees_dynamic,"INTRDY")
|
||||
# pragma map(inflate_trees_fixed,"INTRFI")
|
||||
# pragma map(inflate_trees_free,"INTRFR")
|
||||
#endif
|
||||
|
||||
#endif /* _ZCONF_H */
|
||||
893
utils/vmpi/ZLib/zlib.h
Normal file
893
utils/vmpi/ZLib/zlib.h
Normal file
@@ -0,0 +1,893 @@
|
||||
/* zlib.h -- interface of the 'zlib' general purpose compression library
|
||||
version 1.1.3, July 9th, 1998
|
||||
|
||||
Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Jean-loup Gailly Mark Adler
|
||||
jloup@gzip.org madler@alumni.caltech.edu
|
||||
|
||||
|
||||
The data format used by the zlib library is described by RFCs (Request for
|
||||
Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
|
||||
(zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
|
||||
*/
|
||||
|
||||
#ifndef _ZLIB_H
|
||||
#define _ZLIB_H
|
||||
|
||||
#include "zconf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ZLIB_VERSION "1.1.3"
|
||||
|
||||
/*
|
||||
The 'zlib' compression library provides in-memory compression and
|
||||
decompression functions, including integrity checks of the uncompressed
|
||||
data. This version of the library supports only one compression method
|
||||
(deflation) but other algorithms will be added later and will have the same
|
||||
stream interface.
|
||||
|
||||
Compression can be done in a single step if the buffers are large
|
||||
enough (for example if an input file is mmap'ed), or can be done by
|
||||
repeated calls of the compression function. In the latter case, the
|
||||
application must provide more input and/or consume the output
|
||||
(providing more output space) before each call.
|
||||
|
||||
The library also supports reading and writing files in gzip (.gz) format
|
||||
with an interface similar to that of stdio.
|
||||
|
||||
The library does not install any signal handler. The decoder checks
|
||||
the consistency of the compressed data, so the library should never
|
||||
crash even in case of corrupted input.
|
||||
*/
|
||||
|
||||
typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
|
||||
typedef void (*free_func) OF((voidpf opaque, voidpf address));
|
||||
|
||||
struct internal_state;
|
||||
|
||||
typedef struct z_stream_s {
|
||||
Bytef *next_in; /* next input byte */
|
||||
uInt avail_in; /* number of bytes available at next_in */
|
||||
uLong total_in; /* total nb of input bytes read so far */
|
||||
|
||||
Bytef *next_out; /* next output byte should be put there */
|
||||
uInt avail_out; /* remaining free space at next_out */
|
||||
uLong total_out; /* total nb of bytes output so far */
|
||||
|
||||
char *msg; /* last error message, NULL if no error */
|
||||
struct internal_state FAR *state; /* not visible by applications */
|
||||
|
||||
alloc_func zalloc; /* used to allocate the internal state */
|
||||
free_func zfree; /* used to free the internal state */
|
||||
voidpf opaque; /* private data object passed to zalloc and zfree */
|
||||
|
||||
int data_type; /* best guess about the data type: ascii or binary */
|
||||
uLong adler; /* adler32 value of the uncompressed data */
|
||||
uLong reserved; /* reserved for future use */
|
||||
} z_stream;
|
||||
|
||||
typedef z_stream FAR *z_streamp;
|
||||
|
||||
/*
|
||||
The application must update next_in and avail_in when avail_in has
|
||||
dropped to zero. It must update next_out and avail_out when avail_out
|
||||
has dropped to zero. The application must initialize zalloc, zfree and
|
||||
opaque before calling the init function. All other fields are set by the
|
||||
compression library and must not be updated by the application.
|
||||
|
||||
The opaque value provided by the application will be passed as the first
|
||||
parameter for calls of zalloc and zfree. This can be useful for custom
|
||||
memory management. The compression library attaches no meaning to the
|
||||
opaque value.
|
||||
|
||||
zalloc must return Z_NULL if there is not enough memory for the object.
|
||||
If zlib is used in a multi-threaded application, zalloc and zfree must be
|
||||
thread safe.
|
||||
|
||||
On 16-bit systems, the functions zalloc and zfree must be able to allocate
|
||||
exactly 65536 bytes, but will not be required to allocate more than this
|
||||
if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
|
||||
pointers returned by zalloc for objects of exactly 65536 bytes *must*
|
||||
have their offset normalized to zero. The default allocation function
|
||||
provided by this library ensures this (see zutil.c). To reduce memory
|
||||
requirements and avoid any allocation of 64K objects, at the expense of
|
||||
compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
|
||||
|
||||
The fields total_in and total_out can be used for statistics or
|
||||
progress reports. After compression, total_in holds the total size of
|
||||
the uncompressed data and may be saved for use in the decompressor
|
||||
(particularly if the decompressor wants to decompress everything in
|
||||
a single step).
|
||||
*/
|
||||
|
||||
/* constants */
|
||||
|
||||
#define Z_NO_FLUSH 0
|
||||
#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
|
||||
#define Z_SYNC_FLUSH 2
|
||||
#define Z_FULL_FLUSH 3
|
||||
#define Z_FINISH 4
|
||||
/* Allowed flush values; see deflate() below for details */
|
||||
|
||||
#define Z_OK 0
|
||||
#define Z_STREAM_END 1
|
||||
#define Z_NEED_DICT 2
|
||||
#define Z_ERRNO (-1)
|
||||
#define Z_STREAM_ERROR (-2)
|
||||
#define Z_DATA_ERROR (-3)
|
||||
#define Z_MEM_ERROR (-4)
|
||||
#define Z_BUF_ERROR (-5)
|
||||
#define Z_VERSION_ERROR (-6)
|
||||
/* Return codes for the compression/decompression functions. Negative
|
||||
* values are errors, positive values are used for special but normal events.
|
||||
*/
|
||||
|
||||
#define Z_NO_COMPRESSION 0
|
||||
#define Z_BEST_SPEED 1
|
||||
#define Z_BEST_COMPRESSION 9
|
||||
#define Z_DEFAULT_COMPRESSION (-1)
|
||||
/* compression levels */
|
||||
|
||||
#define Z_FILTERED 1
|
||||
#define Z_HUFFMAN_ONLY 2
|
||||
#define Z_DEFAULT_STRATEGY 0
|
||||
/* compression strategy; see deflateInit2() below for details */
|
||||
|
||||
#define Z_BINARY 0
|
||||
#define Z_ASCII 1
|
||||
#define Z_UNKNOWN 2
|
||||
/* Possible values of the data_type field */
|
||||
|
||||
#define Z_DEFLATED 8
|
||||
/* The deflate compression method (the only one supported in this version) */
|
||||
|
||||
#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
|
||||
|
||||
#define zlib_version zlibVersion()
|
||||
/* for compatibility with versions < 1.0.2 */
|
||||
|
||||
/* basic functions */
|
||||
|
||||
ZEXTERN const char * ZEXPORT zlibVersion OF((void));
|
||||
/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
|
||||
If the first character differs, the library code actually used is
|
||||
not compatible with the zlib.h header file used by the application.
|
||||
This check is automatically made by deflateInit and inflateInit.
|
||||
*/
|
||||
|
||||
/*
|
||||
ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
|
||||
|
||||
Initializes the internal stream state for compression. The fields
|
||||
zalloc, zfree and opaque must be initialized before by the caller.
|
||||
If zalloc and zfree are set to Z_NULL, deflateInit updates them to
|
||||
use default allocation functions.
|
||||
|
||||
The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
|
||||
1 gives best speed, 9 gives best compression, 0 gives no compression at
|
||||
all (the input data is simply copied a block at a time).
|
||||
Z_DEFAULT_COMPRESSION requests a default compromise between speed and
|
||||
compression (currently equivalent to level 6).
|
||||
|
||||
deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
|
||||
enough memory, Z_STREAM_ERROR if level is not a valid compression level,
|
||||
Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
|
||||
with the version assumed by the caller (ZLIB_VERSION).
|
||||
msg is set to null if there is no error message. deflateInit does not
|
||||
perform any compression: this will be done by deflate().
|
||||
*/
|
||||
|
||||
|
||||
ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
|
||||
/*
|
||||
deflate compresses as much data as possible, and stops when the input
|
||||
buffer becomes empty or the output buffer becomes full. It may introduce some
|
||||
output latency (reading input without producing any output) except when
|
||||
forced to flush.
|
||||
|
||||
The detailed semantics are as follows. deflate performs one or both of the
|
||||
following actions:
|
||||
|
||||
- Compress more input starting at next_in and update next_in and avail_in
|
||||
accordingly. If not all input can be processed (because there is not
|
||||
enough room in the output buffer), next_in and avail_in are updated and
|
||||
processing will resume at this point for the next call of deflate().
|
||||
|
||||
- Provide more output starting at next_out and update next_out and avail_out
|
||||
accordingly. This action is forced if the parameter flush is non zero.
|
||||
Forcing flush frequently degrades the compression ratio, so this parameter
|
||||
should be set only when necessary (in interactive applications).
|
||||
Some output may be provided even if flush is not set.
|
||||
|
||||
Before the call of deflate(), the application should ensure that at least
|
||||
one of the actions is possible, by providing more input and/or consuming
|
||||
more output, and updating avail_in or avail_out accordingly; avail_out
|
||||
should never be zero before the call. The application can consume the
|
||||
compressed output when it wants, for example when the output buffer is full
|
||||
(avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
|
||||
and with zero avail_out, it must be called again after making room in the
|
||||
output buffer because there might be more output pending.
|
||||
|
||||
If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
|
||||
flushed to the output buffer and the output is aligned on a byte boundary, so
|
||||
that the decompressor can get all input data available so far. (In particular
|
||||
avail_in is zero after the call if enough output space has been provided
|
||||
before the call.) Flushing may degrade compression for some compression
|
||||
algorithms and so it should be used only when necessary.
|
||||
|
||||
If flush is set to Z_FULL_FLUSH, all output is flushed as with
|
||||
Z_SYNC_FLUSH, and the compression state is reset so that decompression can
|
||||
restart from this point if previous compressed data has been damaged or if
|
||||
random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
|
||||
the compression.
|
||||
|
||||
If deflate returns with avail_out == 0, this function must be called again
|
||||
with the same value of the flush parameter and more output space (updated
|
||||
avail_out), until the flush is complete (deflate returns with non-zero
|
||||
avail_out).
|
||||
|
||||
If the parameter flush is set to Z_FINISH, pending input is processed,
|
||||
pending output is flushed and deflate returns with Z_STREAM_END if there
|
||||
was enough output space; if deflate returns with Z_OK, this function must be
|
||||
called again with Z_FINISH and more output space (updated avail_out) but no
|
||||
more input data, until it returns with Z_STREAM_END or an error. After
|
||||
deflate has returned Z_STREAM_END, the only possible operations on the
|
||||
stream are deflateReset or deflateEnd.
|
||||
|
||||
Z_FINISH can be used immediately after deflateInit if all the compression
|
||||
is to be done in a single step. In this case, avail_out must be at least
|
||||
0.1% larger than avail_in plus 12 bytes. If deflate does not return
|
||||
Z_STREAM_END, then it must be called again as described above.
|
||||
|
||||
deflate() sets strm->adler to the adler32 checksum of all input read
|
||||
so far (that is, total_in bytes).
|
||||
|
||||
deflate() may update data_type if it can make a good guess about
|
||||
the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
|
||||
binary. This field is only for information purposes and does not affect
|
||||
the compression algorithm in any manner.
|
||||
|
||||
deflate() returns Z_OK if some progress has been made (more input
|
||||
processed or more output produced), Z_STREAM_END if all input has been
|
||||
consumed and all output has been produced (only when flush is set to
|
||||
Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
|
||||
if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
|
||||
(for example avail_in or avail_out was zero).
|
||||
*/
|
||||
|
||||
|
||||
ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
|
||||
/*
|
||||
All dynamically allocated data structures for this stream are freed.
|
||||
This function discards any unprocessed input and does not flush any
|
||||
pending output.
|
||||
|
||||
deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
|
||||
stream state was inconsistent, Z_DATA_ERROR if the stream was freed
|
||||
prematurely (some input or output was discarded). In the error case,
|
||||
msg may be set but then points to a static string (which must not be
|
||||
deallocated).
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
|
||||
|
||||
Initializes the internal stream state for decompression. The fields
|
||||
next_in, avail_in, zalloc, zfree and opaque must be initialized before by
|
||||
the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
|
||||
value depends on the compression method), inflateInit determines the
|
||||
compression method from the zlib header and allocates all data structures
|
||||
accordingly; otherwise the allocation will be deferred to the first call of
|
||||
inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to
|
||||
use default allocation functions.
|
||||
|
||||
inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
|
||||
memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
|
||||
version assumed by the caller. msg is set to null if there is no error
|
||||
message. inflateInit does not perform any decompression apart from reading
|
||||
the zlib header if present: this will be done by inflate(). (So next_in and
|
||||
avail_in may be modified, but next_out and avail_out are unchanged.)
|
||||
*/
|
||||
|
||||
|
||||
ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
|
||||
/*
|
||||
inflate decompresses as much data as possible, and stops when the input
|
||||
buffer becomes empty or the output buffer becomes full. It may some
|
||||
introduce some output latency (reading input without producing any output)
|
||||
except when forced to flush.
|
||||
|
||||
The detailed semantics are as follows. inflate performs one or both of the
|
||||
following actions:
|
||||
|
||||
- Decompress more input starting at next_in and update next_in and avail_in
|
||||
accordingly. If not all input can be processed (because there is not
|
||||
enough room in the output buffer), next_in is updated and processing
|
||||
will resume at this point for the next call of inflate().
|
||||
|
||||
- Provide more output starting at next_out and update next_out and avail_out
|
||||
accordingly. inflate() provides as much output as possible, until there
|
||||
is no more input data or no more space in the output buffer (see below
|
||||
about the flush parameter).
|
||||
|
||||
Before the call of inflate(), the application should ensure that at least
|
||||
one of the actions is possible, by providing more input and/or consuming
|
||||
more output, and updating the next_* and avail_* values accordingly.
|
||||
The application can consume the uncompressed output when it wants, for
|
||||
example when the output buffer is full (avail_out == 0), or after each
|
||||
call of inflate(). If inflate returns Z_OK and with zero avail_out, it
|
||||
must be called again after making room in the output buffer because there
|
||||
might be more output pending.
|
||||
|
||||
If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much
|
||||
output as possible to the output buffer. The flushing behavior of inflate is
|
||||
not specified for values of the flush parameter other than Z_SYNC_FLUSH
|
||||
and Z_FINISH, but the current implementation actually flushes as much output
|
||||
as possible anyway.
|
||||
|
||||
inflate() should normally be called until it returns Z_STREAM_END or an
|
||||
error. However if all decompression is to be performed in a single step
|
||||
(a single call of inflate), the parameter flush should be set to
|
||||
Z_FINISH. In this case all pending input is processed and all pending
|
||||
output is flushed; avail_out must be large enough to hold all the
|
||||
uncompressed data. (The size of the uncompressed data may have been saved
|
||||
by the compressor for this purpose.) The next operation on this stream must
|
||||
be inflateEnd to deallocate the decompression state. The use of Z_FINISH
|
||||
is never required, but can be used to inform inflate that a faster routine
|
||||
may be used for the single inflate() call.
|
||||
|
||||
If a preset dictionary is needed at this point (see inflateSetDictionary
|
||||
below), inflate sets strm-adler to the adler32 checksum of the
|
||||
dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise
|
||||
it sets strm->adler to the adler32 checksum of all output produced
|
||||
so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or
|
||||
an error code as described below. At the end of the stream, inflate()
|
||||
checks that its computed adler32 checksum is equal to that saved by the
|
||||
compressor and returns Z_STREAM_END only if the checksum is correct.
|
||||
|
||||
inflate() returns Z_OK if some progress has been made (more input processed
|
||||
or more output produced), Z_STREAM_END if the end of the compressed data has
|
||||
been reached and all uncompressed output has been produced, Z_NEED_DICT if a
|
||||
preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
|
||||
corrupted (input stream not conforming to the zlib format or incorrect
|
||||
adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent
|
||||
(for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not
|
||||
enough memory, Z_BUF_ERROR if no progress is possible or if there was not
|
||||
enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR
|
||||
case, the application may then call inflateSync to look for a good
|
||||
compression block.
|
||||
*/
|
||||
|
||||
|
||||
ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
|
||||
/*
|
||||
All dynamically allocated data structures for this stream are freed.
|
||||
This function discards any unprocessed input and does not flush any
|
||||
pending output.
|
||||
|
||||
inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
|
||||
was inconsistent. In the error case, msg may be set but then points to a
|
||||
static string (which must not be deallocated).
|
||||
*/
|
||||
|
||||
/* Advanced functions */
|
||||
|
||||
/*
|
||||
The following functions are needed only in some special applications.
|
||||
*/
|
||||
|
||||
/*
|
||||
ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
|
||||
int level,
|
||||
int method,
|
||||
int windowBits,
|
||||
int memLevel,
|
||||
int strategy));
|
||||
|
||||
This is another version of deflateInit with more compression options. The
|
||||
fields next_in, zalloc, zfree and opaque must be initialized before by
|
||||
the caller.
|
||||
|
||||
The method parameter is the compression method. It must be Z_DEFLATED in
|
||||
this version of the library.
|
||||
|
||||
The windowBits parameter is the base two logarithm of the window size
|
||||
(the size of the history buffer). It should be in the range 8..15 for this
|
||||
version of the library. Larger values of this parameter result in better
|
||||
compression at the expense of memory usage. The default value is 15 if
|
||||
deflateInit is used instead.
|
||||
|
||||
The memLevel parameter specifies how much memory should be allocated
|
||||
for the internal compression state. memLevel=1 uses minimum memory but
|
||||
is slow and reduces compression ratio; memLevel=9 uses maximum memory
|
||||
for optimal speed. The default value is 8. See zconf.h for total memory
|
||||
usage as a function of windowBits and memLevel.
|
||||
|
||||
The strategy parameter is used to tune the compression algorithm. Use the
|
||||
value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
|
||||
filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no
|
||||
string match). Filtered data consists mostly of small values with a
|
||||
somewhat random distribution. In this case, the compression algorithm is
|
||||
tuned to compress them better. The effect of Z_FILTERED is to force more
|
||||
Huffman coding and less string matching; it is somewhat intermediate
|
||||
between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects
|
||||
the compression ratio but not the correctness of the compressed output even
|
||||
if it is not set appropriately.
|
||||
|
||||
deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
|
||||
memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
|
||||
method). msg is set to null if there is no error message. deflateInit2 does
|
||||
not perform any compression: this will be done by deflate().
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
|
||||
const Bytef *dictionary,
|
||||
uInt dictLength));
|
||||
/*
|
||||
Initializes the compression dictionary from the given byte sequence
|
||||
without producing any compressed output. This function must be called
|
||||
immediately after deflateInit, deflateInit2 or deflateReset, before any
|
||||
call of deflate. The compressor and decompressor must use exactly the same
|
||||
dictionary (see inflateSetDictionary).
|
||||
|
||||
The dictionary should consist of strings (byte sequences) that are likely
|
||||
to be encountered later in the data to be compressed, with the most commonly
|
||||
used strings preferably put towards the end of the dictionary. Using a
|
||||
dictionary is most useful when the data to be compressed is short and can be
|
||||
predicted with good accuracy; the data can then be compressed better than
|
||||
with the default empty dictionary.
|
||||
|
||||
Depending on the size of the compression data structures selected by
|
||||
deflateInit or deflateInit2, a part of the dictionary may in effect be
|
||||
discarded, for example if the dictionary is larger than the window size in
|
||||
deflate or deflate2. Thus the strings most likely to be useful should be
|
||||
put at the end of the dictionary, not at the front.
|
||||
|
||||
Upon return of this function, strm->adler is set to the Adler32 value
|
||||
of the dictionary; the decompressor may later use this value to determine
|
||||
which dictionary has been used by the compressor. (The Adler32 value
|
||||
applies to the whole dictionary even if only a subset of the dictionary is
|
||||
actually used by the compressor.)
|
||||
|
||||
deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
|
||||
parameter is invalid (such as NULL dictionary) or the stream state is
|
||||
inconsistent (for example if deflate has already been called for this stream
|
||||
or if the compression method is bsort). deflateSetDictionary does not
|
||||
perform any compression: this will be done by deflate().
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
|
||||
z_streamp source));
|
||||
/*
|
||||
Sets the destination stream as a complete copy of the source stream.
|
||||
|
||||
This function can be useful when several compression strategies will be
|
||||
tried, for example when there are several ways of pre-processing the input
|
||||
data with a filter. The streams that will be discarded should then be freed
|
||||
by calling deflateEnd. Note that deflateCopy duplicates the internal
|
||||
compression state which can be quite large, so this strategy is slow and
|
||||
can consume lots of memory.
|
||||
|
||||
deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
|
||||
enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
|
||||
(such as zalloc being NULL). msg is left unchanged in both source and
|
||||
destination.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
|
||||
/*
|
||||
This function is equivalent to deflateEnd followed by deflateInit,
|
||||
but does not free and reallocate all the internal compression state.
|
||||
The stream will keep the same compression level and any other attributes
|
||||
that may have been set by deflateInit2.
|
||||
|
||||
deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
|
||||
stream state was inconsistent (such as zalloc or state being NULL).
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
|
||||
int level,
|
||||
int strategy));
|
||||
/*
|
||||
Dynamically update the compression level and compression strategy. The
|
||||
interpretation of level and strategy is as in deflateInit2. This can be
|
||||
used to switch between compression and straight copy of the input data, or
|
||||
to switch to a different kind of input data requiring a different
|
||||
strategy. If the compression level is changed, the input available so far
|
||||
is compressed with the old level (and may be flushed); the new level will
|
||||
take effect only at the next call of deflate().
|
||||
|
||||
Before the call of deflateParams, the stream state must be set as for
|
||||
a call of deflate(), since the currently available input may have to
|
||||
be compressed and flushed. In particular, strm->avail_out must be non-zero.
|
||||
|
||||
deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
|
||||
stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
|
||||
if strm->avail_out was zero.
|
||||
*/
|
||||
|
||||
/*
|
||||
ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
|
||||
int windowBits));
|
||||
|
||||
This is another version of inflateInit with an extra parameter. The
|
||||
fields next_in, avail_in, zalloc, zfree and opaque must be initialized
|
||||
before by the caller.
|
||||
|
||||
The windowBits parameter is the base two logarithm of the maximum window
|
||||
size (the size of the history buffer). It should be in the range 8..15 for
|
||||
this version of the library. The default value is 15 if inflateInit is used
|
||||
instead. If a compressed stream with a larger window size is given as
|
||||
input, inflate() will return with the error code Z_DATA_ERROR instead of
|
||||
trying to allocate a larger window.
|
||||
|
||||
inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
|
||||
memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative
|
||||
memLevel). msg is set to null if there is no error message. inflateInit2
|
||||
does not perform any decompression apart from reading the zlib header if
|
||||
present: this will be done by inflate(). (So next_in and avail_in may be
|
||||
modified, but next_out and avail_out are unchanged.)
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
|
||||
const Bytef *dictionary,
|
||||
uInt dictLength));
|
||||
/*
|
||||
Initializes the decompression dictionary from the given uncompressed byte
|
||||
sequence. This function must be called immediately after a call of inflate
|
||||
if this call returned Z_NEED_DICT. The dictionary chosen by the compressor
|
||||
can be determined from the Adler32 value returned by this call of
|
||||
inflate. The compressor and decompressor must use exactly the same
|
||||
dictionary (see deflateSetDictionary).
|
||||
|
||||
inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
|
||||
parameter is invalid (such as NULL dictionary) or the stream state is
|
||||
inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
|
||||
expected one (incorrect Adler32 value). inflateSetDictionary does not
|
||||
perform any decompression: this will be done by subsequent calls of
|
||||
inflate().
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
|
||||
/*
|
||||
Skips invalid compressed data until a full flush point (see above the
|
||||
description of deflate with Z_FULL_FLUSH) can be found, or until all
|
||||
available input is skipped. No output is provided.
|
||||
|
||||
inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
|
||||
if no more input was provided, Z_DATA_ERROR if no flush point has been found,
|
||||
or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
|
||||
case, the application may save the current current value of total_in which
|
||||
indicates where valid compressed data was found. In the error case, the
|
||||
application may repeatedly call inflateSync, providing more input each time,
|
||||
until success or end of the input data.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
|
||||
/*
|
||||
This function is equivalent to inflateEnd followed by inflateInit,
|
||||
but does not free and reallocate all the internal decompression state.
|
||||
The stream will keep attributes that may have been set by inflateInit2.
|
||||
|
||||
inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
|
||||
stream state was inconsistent (such as zalloc or state being NULL).
|
||||
*/
|
||||
|
||||
|
||||
/* utility functions */
|
||||
|
||||
/*
|
||||
The following utility functions are implemented on top of the
|
||||
basic stream-oriented functions. To simplify the interface, some
|
||||
default options are assumed (compression level and memory usage,
|
||||
standard memory allocation functions). The source code of these
|
||||
utility functions can easily be modified if you need special options.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
|
||||
const Bytef *source, uLong sourceLen));
|
||||
/*
|
||||
Compresses the source buffer into the destination buffer. sourceLen is
|
||||
the byte length of the source buffer. Upon entry, destLen is the total
|
||||
size of the destination buffer, which must be at least 0.1% larger than
|
||||
sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the
|
||||
compressed buffer.
|
||||
This function can be used to compress a whole file at once if the
|
||||
input file is mmap'ed.
|
||||
compress returns Z_OK if success, Z_MEM_ERROR if there was not
|
||||
enough memory, Z_BUF_ERROR if there was not enough room in the output
|
||||
buffer.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
|
||||
const Bytef *source, uLong sourceLen,
|
||||
int level));
|
||||
/*
|
||||
Compresses the source buffer into the destination buffer. The level
|
||||
parameter has the same meaning as in deflateInit. sourceLen is the byte
|
||||
length of the source buffer. Upon entry, destLen is the total size of the
|
||||
destination buffer, which must be at least 0.1% larger than sourceLen plus
|
||||
12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
|
||||
|
||||
compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
|
||||
memory, Z_BUF_ERROR if there was not enough room in the output buffer,
|
||||
Z_STREAM_ERROR if the level parameter is invalid.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
|
||||
const Bytef *source, uLong sourceLen));
|
||||
/*
|
||||
Decompresses the source buffer into the destination buffer. sourceLen is
|
||||
the byte length of the source buffer. Upon entry, destLen is the total
|
||||
size of the destination buffer, which must be large enough to hold the
|
||||
entire uncompressed data. (The size of the uncompressed data must have
|
||||
been saved previously by the compressor and transmitted to the decompressor
|
||||
by some mechanism outside the scope of this compression library.)
|
||||
Upon exit, destLen is the actual size of the compressed buffer.
|
||||
This function can be used to decompress a whole file at once if the
|
||||
input file is mmap'ed.
|
||||
|
||||
uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
|
||||
enough memory, Z_BUF_ERROR if there was not enough room in the output
|
||||
buffer, or Z_DATA_ERROR if the input data was corrupted.
|
||||
*/
|
||||
|
||||
|
||||
typedef voidp gzFile;
|
||||
|
||||
ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
|
||||
/*
|
||||
Opens a gzip (.gz) file for reading or writing. The mode parameter
|
||||
is as in fopen ("rb" or "wb") but can also include a compression level
|
||||
("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
|
||||
Huffman only compression as in "wb1h". (See the description
|
||||
of deflateInit2 for more information about the strategy parameter.)
|
||||
|
||||
gzopen can be used to read a file which is not in gzip format; in this
|
||||
case gzread will directly read from the file without decompression.
|
||||
|
||||
gzopen returns NULL if the file could not be opened or if there was
|
||||
insufficient memory to allocate the (de)compression state; errno
|
||||
can be checked to distinguish the two cases (if errno is zero, the
|
||||
zlib error is Z_MEM_ERROR). */
|
||||
|
||||
ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
|
||||
/*
|
||||
gzdopen() associates a gzFile with the file descriptor fd. File
|
||||
descriptors are obtained from calls like open, dup, creat, pipe or
|
||||
fileno (in the file has been previously opened with fopen).
|
||||
The mode parameter is as in gzopen.
|
||||
The next call of gzclose on the returned gzFile will also close the
|
||||
file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
|
||||
descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
|
||||
gzdopen returns NULL if there was insufficient memory to allocate
|
||||
the (de)compression state.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
|
||||
/*
|
||||
Dynamically update the compression level or strategy. See the description
|
||||
of deflateInit2 for the meaning of these parameters.
|
||||
gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
|
||||
opened for writing.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
|
||||
/*
|
||||
Reads the given number of uncompressed bytes from the compressed file.
|
||||
If the input file was not in gzip format, gzread copies the given number
|
||||
of bytes into the buffer.
|
||||
gzread returns the number of uncompressed bytes actually read (0 for
|
||||
end of file, -1 for error). */
|
||||
|
||||
ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
|
||||
const voidp buf, unsigned len));
|
||||
/*
|
||||
Writes the given number of uncompressed bytes into the compressed file.
|
||||
gzwrite returns the number of uncompressed bytes actually written
|
||||
(0 in case of error).
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...));
|
||||
/*
|
||||
Converts, formats, and writes the args to the compressed file under
|
||||
control of the format string, as in fprintf. gzprintf returns the number of
|
||||
uncompressed bytes actually written (0 in case of error).
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
|
||||
/*
|
||||
Writes the given null-terminated string to the compressed file, excluding
|
||||
the terminating null character.
|
||||
gzputs returns the number of characters written, or -1 in case of error.
|
||||
*/
|
||||
|
||||
ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
|
||||
/*
|
||||
Reads bytes from the compressed file until len-1 characters are read, or
|
||||
a newline character is read and transferred to buf, or an end-of-file
|
||||
condition is encountered. The string is then terminated with a null
|
||||
character.
|
||||
gzgets returns buf, or Z_NULL in case of error.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
|
||||
/*
|
||||
Writes c, converted to an unsigned char, into the compressed file.
|
||||
gzputc returns the value that was written, or -1 in case of error.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
|
||||
/*
|
||||
Reads one byte from the compressed file. gzgetc returns this byte
|
||||
or -1 in case of end of file or error.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
|
||||
/*
|
||||
Flushes all pending output into the compressed file. The parameter
|
||||
flush is as in the deflate() function. The return value is the zlib
|
||||
error number (see function gzerror below). gzflush returns Z_OK if
|
||||
the flush parameter is Z_FINISH and all output could be flushed.
|
||||
gzflush should be called only when strictly necessary because it can
|
||||
degrade compression.
|
||||
*/
|
||||
|
||||
ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
|
||||
z_off_t offset, int whence));
|
||||
/*
|
||||
Sets the starting position for the next gzread or gzwrite on the
|
||||
given compressed file. The offset represents a number of bytes in the
|
||||
uncompressed data stream. The whence parameter is defined as in lseek(2);
|
||||
the value SEEK_END is not supported.
|
||||
If the file is opened for reading, this function is emulated but can be
|
||||
extremely slow. If the file is opened for writing, only forward seeks are
|
||||
supported; gzseek then compresses a sequence of zeroes up to the new
|
||||
starting position.
|
||||
|
||||
gzseek returns the resulting offset location as measured in bytes from
|
||||
the beginning of the uncompressed stream, or -1 in case of error, in
|
||||
particular if the file is opened for writing and the new starting position
|
||||
would be before the current position.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
|
||||
/*
|
||||
Rewinds the given file. This function is supported only for reading.
|
||||
|
||||
gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
|
||||
*/
|
||||
|
||||
ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
|
||||
/*
|
||||
Returns the starting position for the next gzread or gzwrite on the
|
||||
given compressed file. This position represents a number of bytes in the
|
||||
uncompressed data stream.
|
||||
|
||||
gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzeof OF((gzFile file));
|
||||
/*
|
||||
Returns 1 when EOF has previously been detected reading the given
|
||||
input stream, otherwise zero.
|
||||
*/
|
||||
|
||||
ZEXTERN int ZEXPORT gzclose OF((gzFile file));
|
||||
/*
|
||||
Flushes all pending output if necessary, closes the compressed file
|
||||
and deallocates all the (de)compression state. The return value is the zlib
|
||||
error number (see function gzerror below).
|
||||
*/
|
||||
|
||||
ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
|
||||
/*
|
||||
Returns the error message for the last error which occurred on the
|
||||
given compressed file. errnum is set to zlib error number. If an
|
||||
error occurred in the file system and not in the compression library,
|
||||
errnum is set to Z_ERRNO and the application may consult errno
|
||||
to get the exact error code.
|
||||
*/
|
||||
|
||||
/* checksum functions */
|
||||
|
||||
/*
|
||||
These functions are not related to compression but are exported
|
||||
anyway because they might be useful in applications using the
|
||||
compression library.
|
||||
*/
|
||||
|
||||
ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
|
||||
|
||||
/*
|
||||
Update a running Adler-32 checksum with the bytes buf[0..len-1] and
|
||||
return the updated checksum. If buf is NULL, this function returns
|
||||
the required initial value for the checksum.
|
||||
An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
|
||||
much faster. Usage example:
|
||||
|
||||
uLong adler = adler32(0L, Z_NULL, 0);
|
||||
|
||||
while (read_buffer(buffer, length) != EOF) {
|
||||
adler = adler32(adler, buffer, length);
|
||||
}
|
||||
if (adler != original_adler) error();
|
||||
*/
|
||||
|
||||
ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
|
||||
/*
|
||||
Update a running crc with the bytes buf[0..len-1] and return the updated
|
||||
crc. If buf is NULL, this function returns the required initial value
|
||||
for the crc. Pre- and post-conditioning (one's complement) is performed
|
||||
within this function so it shouldn't be done by the application.
|
||||
Usage example:
|
||||
|
||||
uLong crc = crc32(0L, Z_NULL, 0);
|
||||
|
||||
while (read_buffer(buffer, length) != EOF) {
|
||||
crc = crc32(crc, buffer, length);
|
||||
}
|
||||
if (crc != original_crc) error();
|
||||
*/
|
||||
|
||||
|
||||
/* various hacks, don't look :) */
|
||||
|
||||
/* deflateInit and inflateInit are macros to allow checking the zlib version
|
||||
* and the compiler's view of z_stream:
|
||||
*/
|
||||
ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
|
||||
const char *version, int stream_size));
|
||||
ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
|
||||
const char *version, int stream_size));
|
||||
ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
|
||||
int windowBits, int memLevel,
|
||||
int strategy, const char *version,
|
||||
int stream_size));
|
||||
ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
|
||||
const char *version, int stream_size));
|
||||
#define deflateInit(strm, level) \
|
||||
deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
|
||||
#define inflateInit(strm) \
|
||||
inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
|
||||
#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
|
||||
deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
|
||||
(strategy), ZLIB_VERSION, sizeof(z_stream))
|
||||
#define inflateInit2(strm, windowBits) \
|
||||
inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
|
||||
|
||||
|
||||
#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL)
|
||||
struct internal_state {int dummy;}; /* hack for buggy compilers */
|
||||
#endif
|
||||
|
||||
ZEXTERN const char * ZEXPORT zError OF((int err));
|
||||
ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z));
|
||||
ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ZLIB_H */
|
||||
220
utils/vmpi/ZLib/zutil.h
Normal file
220
utils/vmpi/ZLib/zutil.h
Normal file
@@ -0,0 +1,220 @@
|
||||
/* zutil.h -- internal interface and configuration of the compression library
|
||||
* Copyright (C) 1995-1998 Jean-loup Gailly.
|
||||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||||
*/
|
||||
|
||||
/* WARNING: this file should *not* be used by applications. It is
|
||||
part of the implementation of the compression library and is
|
||||
subject to change. Applications should only use zlib.h.
|
||||
*/
|
||||
|
||||
/* @(#) $Id$ */
|
||||
|
||||
#ifndef _Z_UTIL_H
|
||||
#define _Z_UTIL_H
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
#ifdef STDC
|
||||
# include <stddef.h>
|
||||
# include <string.h>
|
||||
# include <stdlib.h>
|
||||
#endif
|
||||
#ifdef NO_ERRNO_H
|
||||
extern int errno;
|
||||
#else
|
||||
# include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifndef local
|
||||
# define local static
|
||||
#endif
|
||||
/* compile with -Dlocal if your debugger can't find static symbols */
|
||||
|
||||
typedef unsigned char uch;
|
||||
typedef uch FAR uchf;
|
||||
typedef unsigned short ush;
|
||||
typedef ush FAR ushf;
|
||||
typedef unsigned long ulg;
|
||||
|
||||
extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */
|
||||
/* (size given to avoid silly warnings with Visual C++) */
|
||||
|
||||
#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
|
||||
|
||||
#define ERR_RETURN(strm,err) \
|
||||
return (strm->msg = (char*)ERR_MSG(err), (err))
|
||||
/* To be used only when the state is known to be valid */
|
||||
|
||||
/* common constants */
|
||||
|
||||
#ifndef DEF_WBITS
|
||||
# define DEF_WBITS MAX_WBITS
|
||||
#endif
|
||||
/* default windowBits for decompression. MAX_WBITS is for compression only */
|
||||
|
||||
#if MAX_MEM_LEVEL >= 8
|
||||
# define DEF_MEM_LEVEL 8
|
||||
#else
|
||||
# define DEF_MEM_LEVEL MAX_MEM_LEVEL
|
||||
#endif
|
||||
/* default memLevel */
|
||||
|
||||
#define STORED_BLOCK 0
|
||||
#define STATIC_TREES 1
|
||||
#define DYN_TREES 2
|
||||
/* The three kinds of block type */
|
||||
|
||||
#define MIN_MATCH 3
|
||||
#define MAX_MATCH 258
|
||||
/* The minimum and maximum match lengths */
|
||||
|
||||
#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
|
||||
|
||||
/* target dependencies */
|
||||
|
||||
#ifdef MSDOS
|
||||
# define OS_CODE 0x00
|
||||
# if defined(__TURBOC__) || defined(__BORLANDC__)
|
||||
# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
|
||||
/* Allow compilation with ANSI keywords only enabled */
|
||||
void _Cdecl farfree( void *block );
|
||||
void *_Cdecl farmalloc( unsigned long nbytes );
|
||||
# else
|
||||
# include <alloc.h>
|
||||
# endif
|
||||
# else /* MSC or DJGPP */
|
||||
# include <malloc.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef OS2
|
||||
# define OS_CODE 0x06
|
||||
#endif
|
||||
|
||||
#ifdef WIN32 /* Window 95 & Windows NT */
|
||||
# define OS_CODE 0x0b
|
||||
#endif
|
||||
|
||||
#if defined(VAXC) || defined(VMS)
|
||||
# define OS_CODE 0x02
|
||||
# define F_OPEN(name, mode) \
|
||||
fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
|
||||
#endif
|
||||
|
||||
#ifdef AMIGA
|
||||
# define OS_CODE 0x01
|
||||
#endif
|
||||
|
||||
#if defined(ATARI) || defined(atarist)
|
||||
# define OS_CODE 0x05
|
||||
#endif
|
||||
|
||||
#if defined(MACOS) || defined(TARGET_OS_MAC)
|
||||
# define OS_CODE 0x07
|
||||
# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
|
||||
# include <unix.h> /* for fdopen */
|
||||
# else
|
||||
# ifndef fdopen
|
||||
# define fdopen(fd,mode) NULL /* No fdopen() */
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __50SERIES /* Prime/PRIMOS */
|
||||
# define OS_CODE 0x0F
|
||||
#endif
|
||||
|
||||
#ifdef TOPS20
|
||||
# define OS_CODE 0x0a
|
||||
#endif
|
||||
|
||||
#if defined(_BEOS_) || defined(RISCOS)
|
||||
# define fdopen(fd,mode) NULL /* No fdopen() */
|
||||
#endif
|
||||
|
||||
#if (defined(_MSC_VER) && (_MSC_VER > 600))
|
||||
# define fdopen(fd,type) _fdopen(fd,type)
|
||||
#endif
|
||||
|
||||
|
||||
/* Common defaults */
|
||||
|
||||
#ifndef OS_CODE
|
||||
# define OS_CODE 0x03 /* assume Unix */
|
||||
#endif
|
||||
|
||||
#ifndef F_OPEN
|
||||
# define F_OPEN(name, mode) fopen((name), (mode))
|
||||
#endif
|
||||
|
||||
/* functions */
|
||||
|
||||
#ifdef HAVE_STRERROR
|
||||
extern char *strerror OF((int));
|
||||
# define zstrerror(errnum) strerror(errnum)
|
||||
#else
|
||||
# define zstrerror(errnum) ""
|
||||
#endif
|
||||
|
||||
#if defined(pyr)
|
||||
# define NO_MEMCPY
|
||||
#endif
|
||||
#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
|
||||
/* Use our own functions for small and medium model with MSC <= 5.0.
|
||||
* You may have to use the same strategy for Borland C (untested).
|
||||
* The __SC__ check is for Symantec.
|
||||
*/
|
||||
# define NO_MEMCPY
|
||||
#endif
|
||||
#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
|
||||
# define HAVE_MEMCPY
|
||||
#endif
|
||||
#ifdef HAVE_MEMCPY
|
||||
# ifdef SMALL_MEDIUM /* MSDOS small or medium model */
|
||||
# define zmemcpy _fmemcpy
|
||||
# define zmemcmp _fmemcmp
|
||||
# define zmemzero(dest, len) _fmemset(dest, 0, len)
|
||||
# else
|
||||
# define zmemcpy memcpy
|
||||
# define zmemcmp memcmp
|
||||
# define zmemzero(dest, len) memset(dest, 0, len)
|
||||
# endif
|
||||
#else
|
||||
extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
|
||||
extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
|
||||
extern void zmemzero OF((Bytef* dest, uInt len));
|
||||
#endif
|
||||
|
||||
/* Diagnostic functions */
|
||||
#ifdef DEBUG
|
||||
# include <stdio.h>
|
||||
extern int z_verbose;
|
||||
extern void z_error OF((char *m));
|
||||
# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
|
||||
# define Trace(x) {if (z_verbose>=0) fprintf x ;}
|
||||
# define Tracev(x) {if (z_verbose>0) fprintf x ;}
|
||||
# define Tracevv(x) {if (z_verbose>1) fprintf x ;}
|
||||
# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
|
||||
# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
|
||||
#else
|
||||
# define Assert(cond,msg)
|
||||
# define Trace(x)
|
||||
# define Tracev(x)
|
||||
# define Tracevv(x)
|
||||
# define Tracec(c,x)
|
||||
# define Tracecv(c,x)
|
||||
#endif
|
||||
|
||||
|
||||
typedef uLong (ZEXPORT *check_func) OF((uLong check, const Bytef *buf,
|
||||
uInt len));
|
||||
voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
|
||||
void zcfree OF((voidpf opaque, voidpf ptr));
|
||||
|
||||
#define ZALLOC(strm, items, size) \
|
||||
(*((strm)->zalloc))((strm)->opaque, (items), (size))
|
||||
#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
|
||||
#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
|
||||
|
||||
#endif /* _Z_UTIL_H */
|
||||
49
utils/vmpi/ichannel.h
Normal file
49
utils/vmpi/ichannel.h
Normal file
@@ -0,0 +1,49 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef ICHANNEL_H
|
||||
#define ICHANNEL_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "tier1/utlvector.h"
|
||||
|
||||
|
||||
class IChannel
|
||||
{
|
||||
public:
|
||||
// Note: this also releases any channels contained inside. So if you make a reliable
|
||||
// channel that contains an unreliable channel and release the reliable one,
|
||||
// it will automatically release the unreliable one it contains.
|
||||
virtual void Release() = 0;
|
||||
|
||||
// Send data to the destination.
|
||||
virtual bool Send( const void *pData, int len ) = 0;
|
||||
|
||||
// This version puts all the chunks into one packet and ships it off.
|
||||
virtual bool SendChunks( void const * const *pChunks, const int *pChunkLengths, int nChunks ) = 0;
|
||||
|
||||
// Check for any packets coming in from the destination.
|
||||
// Returns false if no packet was received.
|
||||
//
|
||||
// flTimeout can be used to make it wait for data.
|
||||
//
|
||||
// Note: this is most efficient if you keep the buffer around between calls so it only
|
||||
// reallocates it when it needs more space.
|
||||
virtual bool Recv( CUtlVector<unsigned char> &data, double flTimeout=0 ) = 0;
|
||||
|
||||
// Returns false if the connection has been broken.
|
||||
virtual bool IsConnected() = 0;
|
||||
|
||||
// If IsConnected returns false, you can call this to find out why the socket got disconnected.
|
||||
virtual void GetDisconnectReason( CUtlVector<char> &reason ) = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // ICHANNEL_H
|
||||
46
utils/vmpi/idle_dialog.cpp
Normal file
46
utils/vmpi/idle_dialog.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "idle_dialog.h"
|
||||
|
||||
|
||||
#define WM_STARTIDLE (WM_USER + 565)
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(CIdleDialog, CDialog)
|
||||
//{{AFX_MSG_MAP(CVMPIBrowserDlg)
|
||||
ON_MESSAGE(WM_STARTIDLE, OnStartIdle)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
|
||||
CIdleDialog::CIdleDialog( int id, CWnd *pParent )
|
||||
: CDialog( id, pParent )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CIdleDialog::StartIdleProcessing( DWORD msInterval )
|
||||
{
|
||||
m_cWinIdle.StartIdle( GetSafeHwnd(), WM_STARTIDLE, 0, 0, msInterval );
|
||||
m_cWinIdle.NextIdle();
|
||||
}
|
||||
|
||||
|
||||
LONG CIdleDialog::OnStartIdle( UINT, LONG )
|
||||
{
|
||||
MSG msg;
|
||||
|
||||
if ( !PeekMessage( &msg, GetSafeHwnd(), 0,0, PM_NOREMOVE ) )
|
||||
OnIdle();
|
||||
|
||||
m_cWinIdle.NextIdle();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
48
utils/vmpi/idle_dialog.h
Normal file
48
utils/vmpi/idle_dialog.h
Normal file
@@ -0,0 +1,48 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef IDLE_DIALOG_H
|
||||
#define IDLE_DIALOG_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "win_idle.h"
|
||||
|
||||
|
||||
//
|
||||
// This is a base class that provides in-thread idle processing.
|
||||
//
|
||||
// To use it:
|
||||
// 1. derive from it
|
||||
// 2. Change your message map to point at this class instead of CDialog
|
||||
// 3. Call StartIdleProcessing to begin receiving idle calls.
|
||||
// 4. Override OnIdle().
|
||||
//
|
||||
|
||||
class CIdleDialog : public CDialog
|
||||
{
|
||||
public:
|
||||
|
||||
CIdleDialog( int id, CWnd *pParent );
|
||||
|
||||
// Call this to start the idle processing.
|
||||
void StartIdleProcessing( DWORD msInterval );
|
||||
|
||||
virtual void OnIdle() = 0;
|
||||
|
||||
|
||||
private:
|
||||
DECLARE_MESSAGE_MAP()
|
||||
afx_msg LONG OnStartIdle(UINT, LONG);
|
||||
|
||||
CWinIdle m_cWinIdle;
|
||||
};
|
||||
|
||||
|
||||
#endif // IDLE_DIALOG_H
|
||||
115
utils/vmpi/imysqlwrapper.h
Normal file
115
utils/vmpi/imysqlwrapper.h
Normal file
@@ -0,0 +1,115 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MYSQL_WRAPPER_H
|
||||
#define MYSQL_WRAPPER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "utlvector.h"
|
||||
#include "interface.h"
|
||||
|
||||
|
||||
class IMySQLRowSet;
|
||||
|
||||
|
||||
class CColumnValue
|
||||
{
|
||||
public:
|
||||
|
||||
CColumnValue( IMySQLRowSet *pSQL, int iColumn );
|
||||
|
||||
const char* String();
|
||||
long Int32();
|
||||
|
||||
private:
|
||||
IMySQLRowSet *m_pSQL;
|
||||
int m_iColumn;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class IMySQLRowSet
|
||||
{
|
||||
public:
|
||||
virtual void Release() = 0;
|
||||
|
||||
// Get the number of columns in the data returned from the last query (if it was a select statement).
|
||||
virtual int NumFields() = 0;
|
||||
|
||||
// Get the name of each column returned by the last query.
|
||||
virtual const char* GetFieldName( int iColumn ) = 0;
|
||||
|
||||
// Call this in a loop until it returns false to iterate over all rows the query returned.
|
||||
virtual bool NextRow() = 0;
|
||||
|
||||
// You can call this to start iterating over the result set from the start again.
|
||||
// Note: after calling this, you have to call NextRow() to actually get the first row's value ready.
|
||||
virtual bool SeekToFirstRow() = 0;
|
||||
|
||||
virtual CColumnValue GetColumnValue( int iColumn ) = 0;
|
||||
virtual CColumnValue GetColumnValue( const char *pColumnName ) = 0;
|
||||
|
||||
virtual const char* GetColumnValue_String( int iColumn ) = 0;
|
||||
virtual long GetColumnValue_Int( int iColumn ) = 0;
|
||||
|
||||
// You can call this to get the index of a column for faster lookups with GetColumnValue( int ).
|
||||
// Returns -1 if the column can't be found.
|
||||
virtual int GetColumnIndex( const char *pColumnName ) = 0;
|
||||
};
|
||||
|
||||
|
||||
class IMySQL : public IMySQLRowSet
|
||||
{
|
||||
public:
|
||||
virtual bool InitMySQL( const char *pDBName, const char *pHostName="", const char *pUserName="", const char *pPassword="" ) = 0;
|
||||
virtual void Release() = 0;
|
||||
|
||||
// These execute SQL commands. They return 0 if the query was successful.
|
||||
virtual int Execute( const char *pString ) = 0;
|
||||
|
||||
// This reads in all of the data in the last row set you queried with Execute and builds a separate
|
||||
// copy. This is useful in some of the VMPI tools to have a thread repeatedly execute a slow query, then
|
||||
// store off the results for the main thread to parse.
|
||||
virtual IMySQLRowSet* DuplicateRowSet() = 0;
|
||||
|
||||
// If you just inserted rows into a table with an AUTO_INCREMENT column,
|
||||
// then this returns the (unique) value of that column.
|
||||
virtual unsigned long InsertID() = 0;
|
||||
|
||||
// Returns the last error message, if an error took place
|
||||
virtual const char * GetLastError() = 0;
|
||||
};
|
||||
|
||||
|
||||
#define MYSQL_WRAPPER_VERSION_NAME "MySQLWrapper001"
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------ //
|
||||
// Inlines.
|
||||
// ------------------------------------------------------------------------------------------------ //
|
||||
|
||||
inline CColumnValue::CColumnValue( IMySQLRowSet *pSQL, int iColumn )
|
||||
{
|
||||
m_pSQL = pSQL;
|
||||
m_iColumn = iColumn;
|
||||
}
|
||||
|
||||
inline const char* CColumnValue::String()
|
||||
{
|
||||
return m_pSQL->GetColumnValue_String( m_iColumn );
|
||||
}
|
||||
|
||||
inline long CColumnValue::Int32()
|
||||
{
|
||||
return m_pSQL->GetColumnValue_Int( m_iColumn );
|
||||
}
|
||||
|
||||
|
||||
#endif // MYSQL_WRAPPER_H
|
||||
610
utils/vmpi/iphelpers.cpp
Normal file
610
utils/vmpi/iphelpers.cpp
Normal file
@@ -0,0 +1,610 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#pragma warning (disable:4127)
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#pragma warning (default:4127)
|
||||
|
||||
#include "iphelpers.h"
|
||||
#include "basetypes.h"
|
||||
#include <assert.h>
|
||||
#include "utllinkedlist.h"
|
||||
#include "utlvector.h"
|
||||
#include "tier1/strtools.h"
|
||||
|
||||
|
||||
// This automatically calls WSAStartup for the app at startup.
|
||||
class CIPStarter
|
||||
{
|
||||
public:
|
||||
CIPStarter()
|
||||
{
|
||||
WSADATA wsaData;
|
||||
WSAStartup( WINSOCK_VERSION, &wsaData );
|
||||
}
|
||||
};
|
||||
static CIPStarter g_Starter;
|
||||
|
||||
|
||||
unsigned long SampleMilliseconds()
|
||||
{
|
||||
CCycleCount cnt;
|
||||
cnt.Sample();
|
||||
return cnt.GetMilliseconds();
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------ //
|
||||
// CChunkWalker.
|
||||
// ------------------------------------------------------------------------------------------ //
|
||||
|
||||
CChunkWalker::CChunkWalker( void const * const *pChunks, const int *pChunkLengths, int nChunks )
|
||||
{
|
||||
m_TotalLength = 0;
|
||||
for ( int i=0; i < nChunks; i++ )
|
||||
m_TotalLength += pChunkLengths[i];
|
||||
|
||||
m_iCurChunk = 0;
|
||||
m_iCurChunkPos = 0;
|
||||
m_pChunks = pChunks;
|
||||
m_pChunkLengths = pChunkLengths;
|
||||
m_nChunks = nChunks;
|
||||
}
|
||||
|
||||
int CChunkWalker::GetTotalLength() const
|
||||
{
|
||||
return m_TotalLength;
|
||||
}
|
||||
|
||||
void CChunkWalker::CopyTo( void *pOut, int nBytes )
|
||||
{
|
||||
unsigned char *pOutPos = (unsigned char*)pOut;
|
||||
|
||||
int nBytesLeft = nBytes;
|
||||
while ( nBytesLeft > 0 )
|
||||
{
|
||||
int toCopy = nBytesLeft;
|
||||
int curChunkLen = m_pChunkLengths[m_iCurChunk];
|
||||
|
||||
int amtLeft = curChunkLen - m_iCurChunkPos;
|
||||
if ( nBytesLeft > amtLeft )
|
||||
{
|
||||
toCopy = amtLeft;
|
||||
}
|
||||
|
||||
unsigned char *pCurChunkData = (unsigned char*)m_pChunks[m_iCurChunk];
|
||||
memcpy( pOutPos, &pCurChunkData[m_iCurChunkPos], toCopy );
|
||||
nBytesLeft -= toCopy;
|
||||
pOutPos += toCopy;
|
||||
|
||||
// Slide up to the next chunk if we're done with the one we're on.
|
||||
m_iCurChunkPos += toCopy;
|
||||
assert( m_iCurChunkPos <= curChunkLen );
|
||||
if ( m_iCurChunkPos == curChunkLen )
|
||||
{
|
||||
++m_iCurChunk;
|
||||
m_iCurChunkPos = 0;
|
||||
if ( m_iCurChunk == m_nChunks )
|
||||
{
|
||||
assert( nBytesLeft == 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------ //
|
||||
// CWaitTimer
|
||||
// ------------------------------------------------------------------------------------------ //
|
||||
|
||||
bool g_bForceWaitTimers = false;
|
||||
|
||||
CWaitTimer::CWaitTimer( double flSeconds )
|
||||
{
|
||||
m_StartTime = SampleMilliseconds();
|
||||
m_WaitMS = (unsigned long)( flSeconds * 1000.0 );
|
||||
}
|
||||
|
||||
|
||||
bool CWaitTimer::ShouldKeepWaiting()
|
||||
{
|
||||
if ( m_WaitMS == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ( SampleMilliseconds() - m_StartTime ) <= m_WaitMS || g_bForceWaitTimers;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------ //
|
||||
// CIPAddr.
|
||||
// ------------------------------------------------------------------------------------------ //
|
||||
|
||||
CIPAddr::CIPAddr()
|
||||
{
|
||||
Init( 0, 0, 0, 0, 0 );
|
||||
}
|
||||
|
||||
|
||||
CIPAddr::CIPAddr( const int inputIP[4], const int inputPort )
|
||||
{
|
||||
Init( inputIP[0], inputIP[1], inputIP[2], inputIP[3], inputPort );
|
||||
}
|
||||
|
||||
|
||||
CIPAddr::CIPAddr( int ip0, int ip1, int ip2, int ip3, int ipPort )
|
||||
{
|
||||
Init( ip0, ip1, ip2, ip3, ipPort );
|
||||
}
|
||||
|
||||
|
||||
void CIPAddr::Init( int ip0, int ip1, int ip2, int ip3, int ipPort )
|
||||
{
|
||||
ip[0] = (unsigned char)ip0;
|
||||
ip[1] = (unsigned char)ip1;
|
||||
ip[2] = (unsigned char)ip2;
|
||||
ip[3] = (unsigned char)ip3;
|
||||
port = (unsigned short)ipPort;
|
||||
}
|
||||
|
||||
bool CIPAddr::operator==( const CIPAddr &o ) const
|
||||
{
|
||||
return ip[0] == o.ip[0] && ip[1] == o.ip[1] && ip[2] == o.ip[2] && ip[3] == o.ip[3] && port == o.port;
|
||||
}
|
||||
|
||||
|
||||
bool CIPAddr::operator!=( const CIPAddr &o ) const
|
||||
{
|
||||
return !( *this == o );
|
||||
}
|
||||
|
||||
|
||||
void CIPAddr::SetupLocal( int inPort )
|
||||
{
|
||||
ip[0] = 0x7f;
|
||||
ip[1] = 0;
|
||||
ip[2] = 0;
|
||||
ip[3] = 1;
|
||||
port = inPort;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------ //
|
||||
// Static helpers.
|
||||
// ------------------------------------------------------------------------------------------ //
|
||||
|
||||
static double IP_FloatTime()
|
||||
{
|
||||
CCycleCount cnt;
|
||||
cnt.Sample();
|
||||
return cnt.GetSeconds();
|
||||
}
|
||||
|
||||
TIMEVAL SetupTimeVal( double flTimeout )
|
||||
{
|
||||
TIMEVAL timeVal;
|
||||
timeVal.tv_sec = (long)flTimeout;
|
||||
timeVal.tv_usec = (long)( (flTimeout - (long)flTimeout) * 1000.0 );
|
||||
return timeVal;
|
||||
}
|
||||
|
||||
// Convert a CIPAddr to a sockaddr_in.
|
||||
void IPAddrToInAddr( const CIPAddr *pIn, in_addr *pOut )
|
||||
{
|
||||
u_char *p = (u_char*)pOut;
|
||||
p[0] = pIn->ip[0];
|
||||
p[1] = pIn->ip[1];
|
||||
p[2] = pIn->ip[2];
|
||||
p[3] = pIn->ip[3];
|
||||
}
|
||||
|
||||
// Convert a CIPAddr to a sockaddr_in.
|
||||
void IPAddrToSockAddr( const CIPAddr *pIn, struct sockaddr_in *pOut )
|
||||
{
|
||||
memset( pOut, 0, sizeof(*pOut) );
|
||||
pOut->sin_family = AF_INET;
|
||||
pOut->sin_port = htons( pIn->port );
|
||||
|
||||
IPAddrToInAddr( pIn, &pOut->sin_addr );
|
||||
}
|
||||
|
||||
// Convert a CIPAddr to a sockaddr_in.
|
||||
void SockAddrToIPAddr( const struct sockaddr_in *pIn, CIPAddr *pOut )
|
||||
{
|
||||
const u_char *p = (const u_char*)&pIn->sin_addr;
|
||||
pOut->ip[0] = p[0];
|
||||
pOut->ip[1] = p[1];
|
||||
pOut->ip[2] = p[2];
|
||||
pOut->ip[3] = p[3];
|
||||
pOut->port = ntohs( pIn->sin_port );
|
||||
}
|
||||
|
||||
|
||||
class CIPSocket : public ISocket
|
||||
{
|
||||
public:
|
||||
CIPSocket()
|
||||
{
|
||||
m_Socket = INVALID_SOCKET;
|
||||
m_bSetupToBroadcast = false;
|
||||
}
|
||||
|
||||
virtual ~CIPSocket()
|
||||
{
|
||||
Term();
|
||||
}
|
||||
|
||||
|
||||
// ISocket implementation.
|
||||
public:
|
||||
|
||||
virtual void Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
||||
virtual bool CreateSocket()
|
||||
{
|
||||
// Clear any old socket we had around.
|
||||
Term();
|
||||
|
||||
// Create a socket to send and receive through.
|
||||
SOCKET sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_IP );
|
||||
if ( sock == INVALID_SOCKET )
|
||||
{
|
||||
Assert( false );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Nonblocking please..
|
||||
int status;
|
||||
DWORD val = 1;
|
||||
status = ioctlsocket( sock, FIONBIO, &val );
|
||||
if ( status != 0 )
|
||||
{
|
||||
assert( false );
|
||||
closesocket( sock );
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Socket = sock;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Called after we have a socket.
|
||||
virtual bool BindPart2( const CIPAddr *pAddr )
|
||||
{
|
||||
Assert( m_Socket != INVALID_SOCKET );
|
||||
|
||||
// bind to it!
|
||||
sockaddr_in addr;
|
||||
IPAddrToSockAddr( pAddr, &addr );
|
||||
|
||||
int status = bind( m_Socket, (sockaddr*)&addr, sizeof(addr) );
|
||||
if ( status == 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Term();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual bool Bind( const CIPAddr *pAddr )
|
||||
{
|
||||
if ( !CreateSocket() )
|
||||
return false;
|
||||
|
||||
return BindPart2( pAddr );
|
||||
}
|
||||
|
||||
virtual bool BindToAny( const unsigned short port )
|
||||
{
|
||||
// (INADDR_ANY)
|
||||
CIPAddr addr;
|
||||
addr.ip[0] = addr.ip[1] = addr.ip[2] = addr.ip[3] = 0;
|
||||
addr.port = port;
|
||||
return Bind( &addr );
|
||||
}
|
||||
|
||||
virtual bool ListenToMulticastStream( const CIPAddr &addr, const CIPAddr &localInterface )
|
||||
{
|
||||
ip_mreq mr;
|
||||
IPAddrToInAddr( &addr, &mr.imr_multiaddr );
|
||||
IPAddrToInAddr( &localInterface, &mr.imr_interface );
|
||||
|
||||
// This helps a lot if the stream is sending really fast.
|
||||
int rcvBuf = 1024*1024*2;
|
||||
setsockopt( m_Socket, SOL_SOCKET, SO_RCVBUF, (char*)&rcvBuf, sizeof( rcvBuf ) );
|
||||
|
||||
if ( setsockopt( m_Socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mr, sizeof( mr ) ) == 0 )
|
||||
{
|
||||
// Remember this so we do IP_DEL_MEMBERSHIP on shutdown.
|
||||
m_bMulticastGroupMembership = true;
|
||||
m_MulticastGroupMREQ = mr;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool Broadcast( const void *pData, const int len, const unsigned short port )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
|
||||
// Make sure we're setup to broadcast.
|
||||
if ( !m_bSetupToBroadcast )
|
||||
{
|
||||
BOOL bBroadcast = true;
|
||||
if ( setsockopt( m_Socket, SOL_SOCKET, SO_BROADCAST, (char*)&bBroadcast, sizeof( bBroadcast ) ) != 0 )
|
||||
{
|
||||
assert( false );
|
||||
return false;
|
||||
}
|
||||
|
||||
m_bSetupToBroadcast = true;
|
||||
}
|
||||
|
||||
CIPAddr addr;
|
||||
addr.ip[0] = addr.ip[1] = addr.ip[2] = addr.ip[3] = 0xFF;
|
||||
addr.port = port;
|
||||
return SendTo( &addr, pData, len );
|
||||
}
|
||||
|
||||
virtual bool SendTo( const CIPAddr *pAddr, const void *pData, const int len )
|
||||
{
|
||||
return SendChunksTo( pAddr, &pData, &len, 1 );
|
||||
}
|
||||
|
||||
virtual bool SendChunksTo( const CIPAddr *pAddr, void const * const *pChunks, const int *pChunkLengths, int nChunks )
|
||||
{
|
||||
WSABUF bufs[32];
|
||||
if ( nChunks > 32 )
|
||||
{
|
||||
Error( "CIPSocket::SendChunksTo: too many chunks (%d).", nChunks );
|
||||
}
|
||||
|
||||
int nTotalBytes = 0;
|
||||
for ( int i=0; i < nChunks; i++ )
|
||||
{
|
||||
bufs[i].len = pChunkLengths[i];
|
||||
bufs[i].buf = (char*)pChunks[i];
|
||||
nTotalBytes += pChunkLengths[i];
|
||||
}
|
||||
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
|
||||
// Translate the address.
|
||||
sockaddr_in addr;
|
||||
IPAddrToSockAddr( pAddr, &addr );
|
||||
|
||||
DWORD dwNumBytesSent = 0;
|
||||
DWORD ret = WSASendTo(
|
||||
m_Socket,
|
||||
bufs,
|
||||
nChunks,
|
||||
&dwNumBytesSent,
|
||||
0,
|
||||
(sockaddr*)&addr,
|
||||
sizeof( addr ),
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
return ret == 0 && (int)dwNumBytesSent == nTotalBytes;
|
||||
}
|
||||
|
||||
virtual int RecvFrom( void *pData, int maxDataLen, CIPAddr *pFrom )
|
||||
{
|
||||
assert( m_Socket != INVALID_SOCKET );
|
||||
|
||||
fd_set readSet;
|
||||
readSet.fd_count = 1;
|
||||
readSet.fd_array[0] = m_Socket;
|
||||
|
||||
TIMEVAL timeVal = SetupTimeVal( 0 );
|
||||
|
||||
// See if it has a packet waiting.
|
||||
int status = select( 0, &readSet, NULL, NULL, &timeVal );
|
||||
if ( status == 0 || status == SOCKET_ERROR )
|
||||
return -1;
|
||||
|
||||
// Get the data.
|
||||
sockaddr_in sender;
|
||||
int fromSize = sizeof( sockaddr_in );
|
||||
status = recvfrom( m_Socket, (char*)pData, maxDataLen, 0, (struct sockaddr*)&sender, &fromSize );
|
||||
if ( status == 0 || status == SOCKET_ERROR )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( pFrom )
|
||||
{
|
||||
SockAddrToIPAddr( &sender, pFrom );
|
||||
}
|
||||
|
||||
m_flLastRecvTime = IP_FloatTime();
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
virtual double GetRecvTimeout()
|
||||
{
|
||||
return IP_FloatTime() - m_flLastRecvTime;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void Term()
|
||||
{
|
||||
if ( m_Socket != INVALID_SOCKET )
|
||||
{
|
||||
if ( m_bMulticastGroupMembership )
|
||||
{
|
||||
// Undo our multicast group membership.
|
||||
setsockopt( m_Socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&m_MulticastGroupMREQ, sizeof( m_MulticastGroupMREQ ) );
|
||||
}
|
||||
|
||||
closesocket( m_Socket );
|
||||
m_Socket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
m_bSetupToBroadcast = false;
|
||||
m_bMulticastGroupMembership = false;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
SOCKET m_Socket;
|
||||
|
||||
bool m_bMulticastGroupMembership; // Did we join a multicast group?
|
||||
ip_mreq m_MulticastGroupMREQ;
|
||||
|
||||
bool m_bSetupToBroadcast;
|
||||
double m_flLastRecvTime;
|
||||
bool m_bListenSocket;
|
||||
};
|
||||
|
||||
|
||||
|
||||
ISocket* CreateIPSocket()
|
||||
{
|
||||
return new CIPSocket;
|
||||
}
|
||||
|
||||
|
||||
ISocket* CreateMulticastListenSocket(
|
||||
const CIPAddr &addr,
|
||||
const CIPAddr &localInterface )
|
||||
{
|
||||
CIPSocket *pSocket = new CIPSocket;
|
||||
|
||||
CIPAddr bindAddr = localInterface;
|
||||
bindAddr.port = addr.port;
|
||||
|
||||
if ( pSocket->Bind( &bindAddr ) &&
|
||||
pSocket->ListenToMulticastStream( addr, localInterface )
|
||||
)
|
||||
{
|
||||
return pSocket;
|
||||
}
|
||||
else
|
||||
{
|
||||
pSocket->Release();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ConvertStringToIPAddr( const char *pStr, CIPAddr *pOut )
|
||||
{
|
||||
char ipStr[512];
|
||||
|
||||
const char *pColon = strchr( pStr, ':' );
|
||||
if ( pColon )
|
||||
{
|
||||
int toCopy = pColon - pStr;
|
||||
if ( toCopy < 2 || toCopy > sizeof(ipStr)-1 )
|
||||
{
|
||||
assert( false );
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy( ipStr, pStr, toCopy );
|
||||
ipStr[toCopy] = 0;
|
||||
|
||||
pOut->port = (unsigned short)atoi( pColon+1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy( ipStr, pStr, sizeof( ipStr ) );
|
||||
ipStr[ sizeof(ipStr)-1 ] = 0;
|
||||
}
|
||||
|
||||
if ( ipStr[0] >= '0' && ipStr[0] <= '9' )
|
||||
{
|
||||
// It's numbers.
|
||||
int ip[4];
|
||||
sscanf( ipStr, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3] );
|
||||
pOut->ip[0] = (unsigned char)ip[0];
|
||||
pOut->ip[1] = (unsigned char)ip[1];
|
||||
pOut->ip[2] = (unsigned char)ip[2];
|
||||
pOut->ip[3] = (unsigned char)ip[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's a text string.
|
||||
struct hostent *pHost = gethostbyname( ipStr );
|
||||
if( !pHost )
|
||||
return false;
|
||||
|
||||
pOut->ip[0] = pHost->h_addr_list[0][0];
|
||||
pOut->ip[1] = pHost->h_addr_list[0][1];
|
||||
pOut->ip[2] = pHost->h_addr_list[0][2];
|
||||
pOut->ip[3] = pHost->h_addr_list[0][3];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ConvertIPAddrToString( const CIPAddr *pIn, char *pOut, int outLen )
|
||||
{
|
||||
in_addr addr;
|
||||
addr.S_un.S_un_b.s_b1 = pIn->ip[0];
|
||||
addr.S_un.S_un_b.s_b2 = pIn->ip[1];
|
||||
addr.S_un.S_un_b.s_b3 = pIn->ip[2];
|
||||
addr.S_un.S_un_b.s_b4 = pIn->ip[3];
|
||||
|
||||
HOSTENT *pEnt = gethostbyaddr( (char*)&addr, sizeof( addr ), AF_INET );
|
||||
if ( pEnt )
|
||||
{
|
||||
Q_strncpy( pOut, pEnt->h_name, outLen );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IP_GetLastErrorString( char *pStr, int maxLen )
|
||||
{
|
||||
char *lpMsgBuf;
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
||||
(LPTSTR) &lpMsgBuf,
|
||||
0,
|
||||
NULL
|
||||
);
|
||||
|
||||
Q_strncpy( pStr, lpMsgBuf, maxLen );
|
||||
LocalFree( lpMsgBuf );
|
||||
}
|
||||
|
||||
162
utils/vmpi/iphelpers.h
Normal file
162
utils/vmpi/iphelpers.h
Normal file
@@ -0,0 +1,162 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef IPHELPERS_H
|
||||
#define IPHELPERS_H
|
||||
|
||||
|
||||
#include "ichannel.h"
|
||||
|
||||
|
||||
// Loops that poll sockets should Sleep for this amount of time between iterations
|
||||
// so they don't hog all the CPU.
|
||||
#define LOOP_POLL_INTERVAL 5
|
||||
|
||||
|
||||
// Useful for putting the arguments into a printf statement.
|
||||
#define EXPAND_ADDR( x ) (x).ip[0], (x).ip[1], (x).ip[2], (x).ip[3], (x).port
|
||||
|
||||
|
||||
// This is a simple wrapper layer for UDP sockets.
|
||||
class CIPAddr
|
||||
{
|
||||
public:
|
||||
CIPAddr();
|
||||
CIPAddr( const int inputIP[4], const int inputPort );
|
||||
CIPAddr( int ip0, int ip1, int ip2, int ip3, int ipPort );
|
||||
|
||||
void Init( int ip0, int ip1, int ip2, int ip3, int ipPort );
|
||||
bool operator==( const CIPAddr &o ) const;
|
||||
bool operator!=( const CIPAddr &o ) const;
|
||||
|
||||
// Setup to send to the local machine on the specified port.
|
||||
void SetupLocal( int inPort );
|
||||
|
||||
public:
|
||||
|
||||
unsigned char ip[4];
|
||||
unsigned short port;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// The chunk walker provides an easy way to copy data out of the chunks as though it were a
|
||||
// single contiguous chunk of memory.s
|
||||
class CChunkWalker
|
||||
{
|
||||
public:
|
||||
CChunkWalker( void const * const *pChunks, const int *pChunkLengths, int nChunks );
|
||||
|
||||
int GetTotalLength() const;
|
||||
void CopyTo( void *pOut, int nBytes );
|
||||
|
||||
private:
|
||||
|
||||
void const * const *m_pChunks;
|
||||
const int *m_pChunkLengths;
|
||||
int m_nChunks;
|
||||
|
||||
int m_iCurChunk;
|
||||
int m_iCurChunkPos;
|
||||
|
||||
int m_TotalLength;
|
||||
};
|
||||
|
||||
|
||||
// This class makes loop that wait on something look nicer. ALL loops using this class
|
||||
// should follow this pattern, or they can wind up with unforeseen delays that add a whole
|
||||
// lot of lag.
|
||||
//
|
||||
// CWaitTimer waitTimer( 5.0 );
|
||||
// while ( 1 )
|
||||
// {
|
||||
// do your thing here like Recv() from a socket.
|
||||
//
|
||||
// if ( waitTimer.ShouldKeepWaiting() )
|
||||
// Sleep() for some time interval like 5ms so you don't hog the CPU
|
||||
// else
|
||||
// BREAK HERE
|
||||
// }
|
||||
class CWaitTimer
|
||||
{
|
||||
public:
|
||||
CWaitTimer( double flSeconds );
|
||||
|
||||
bool ShouldKeepWaiting();
|
||||
|
||||
private:
|
||||
unsigned long m_StartTime;
|
||||
unsigned long m_WaitMS;
|
||||
};
|
||||
|
||||
|
||||
// Helper function to get time in milliseconds.
|
||||
unsigned long SampleMilliseconds();
|
||||
|
||||
|
||||
class ISocket
|
||||
{
|
||||
public:
|
||||
|
||||
// Call this when you're done.
|
||||
virtual void Release() = 0;
|
||||
|
||||
|
||||
// Bind the socket so you can send and receive with it.
|
||||
// If you bind to port 0, then the system will select the port for you.
|
||||
virtual bool Bind( const CIPAddr *pAddr ) = 0;
|
||||
virtual bool BindToAny( const unsigned short port ) = 0;
|
||||
|
||||
|
||||
// Broadcast some data.
|
||||
virtual bool Broadcast( const void *pData, const int len, const unsigned short port ) = 0;
|
||||
|
||||
// Send a packet.
|
||||
virtual bool SendTo( const CIPAddr *pAddr, const void *pData, const int len ) = 0;
|
||||
virtual bool SendChunksTo( const CIPAddr *pAddr, void const * const *pChunks, const int *pChunkLengths, int nChunks ) = 0;
|
||||
|
||||
// Receive a packet. Returns the length received or -1 if no data came in.
|
||||
// If pFrom is set, then it is filled in with the sender's IP address.
|
||||
virtual int RecvFrom( void *pData, int maxDataLen, CIPAddr *pFrom ) = 0;
|
||||
|
||||
// How long has it been since we successfully received a packet?
|
||||
virtual double GetRecvTimeout() = 0;
|
||||
};
|
||||
|
||||
// Create a connectionless socket that you can send packets out of.
|
||||
ISocket* CreateIPSocket();
|
||||
|
||||
// This sets up the socket to receive multicast data on the specified group.
|
||||
// By default, localInterface is INADDR_ANY, but if you want to specify a specific interface
|
||||
// the data should come in through, you can.
|
||||
ISocket* CreateMulticastListenSocket(
|
||||
const CIPAddr &addr,
|
||||
const CIPAddr &localInterface = CIPAddr()
|
||||
);
|
||||
|
||||
|
||||
// Setup a CIPAddr from the string. The string can be a dotted IP address or
|
||||
// a hostname, and it can be followed by a colon and a port number like "1.2.3.4:3443"
|
||||
// or "myhostname.valvesoftware.com:2342".
|
||||
//
|
||||
// Note: if the string does not contain a port, then pOut->port will be left alone.
|
||||
bool ConvertStringToIPAddr( const char *pStr, CIPAddr *pOut );
|
||||
|
||||
// Do a DNS lookup on the IP.
|
||||
// You can optionally get a service name back too.
|
||||
bool ConvertIPAddrToString( const CIPAddr *pIn, char *pOut, int outLen );
|
||||
|
||||
|
||||
void IP_GetLastErrorString( char *pStr, int maxLen );
|
||||
|
||||
void SockAddrToIPAddr( const struct sockaddr_in *pIn, CIPAddr *pOut );
|
||||
void IPAddrToSockAddr( const CIPAddr *pIn, struct sockaddr_in *pOut );
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
98
utils/vmpi/loopback_channel.cpp
Normal file
98
utils/vmpi/loopback_channel.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "loopback_channel.h"
|
||||
#include "utllinkedlist.h"
|
||||
#include "iphelpers.h"
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// CLoopbackChannel.
|
||||
// -------------------------------------------------------------------------------- //
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int m_Len;
|
||||
unsigned char m_Data[1];
|
||||
} LoopbackMsg_t;
|
||||
|
||||
|
||||
class CLoopbackChannel : public IChannel
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~CLoopbackChannel()
|
||||
{
|
||||
FOR_EACH_LL( m_Messages, i )
|
||||
{
|
||||
free( m_Messages[i] );
|
||||
}
|
||||
m_Messages.Purge();
|
||||
}
|
||||
|
||||
virtual void Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual bool Send( const void *pData, int len )
|
||||
{
|
||||
const void *pChunks[1] = { pData };
|
||||
int chunkLengths[1] = { len };
|
||||
return SendChunks( pChunks, chunkLengths, 1 );
|
||||
}
|
||||
|
||||
virtual bool SendChunks( void const * const *pChunks, const int *pChunkLengths, int nChunks )
|
||||
{
|
||||
CChunkWalker walker( pChunks, pChunkLengths, nChunks );
|
||||
|
||||
LoopbackMsg_t *pMsg = (LoopbackMsg_t*)malloc( sizeof( LoopbackMsg_t ) - 1 + walker.GetTotalLength() );
|
||||
walker.CopyTo( pMsg->m_Data, walker.GetTotalLength() );
|
||||
pMsg->m_Len = walker.GetTotalLength();
|
||||
m_Messages.AddToTail( pMsg );
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool Recv( CUtlVector<unsigned char> &data, double flTimeout )
|
||||
{
|
||||
int iNext = m_Messages.Head();
|
||||
if ( iNext == m_Messages.InvalidIndex() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
LoopbackMsg_t *pMsg = m_Messages[iNext];
|
||||
|
||||
data.CopyArray( pMsg->m_Data, pMsg->m_Len );
|
||||
|
||||
free( pMsg );
|
||||
m_Messages.Remove( iNext );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool IsConnected()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void GetDisconnectReason( CUtlVector<char> &reason )
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
CUtlLinkedList<LoopbackMsg_t*,int> m_Messages; // FIFO for messages we've sent.
|
||||
};
|
||||
|
||||
|
||||
IChannel* CreateLoopbackChannel()
|
||||
{
|
||||
return new CLoopbackChannel;
|
||||
}
|
||||
|
||||
22
utils/vmpi/loopback_channel.h
Normal file
22
utils/vmpi/loopback_channel.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef LOOPBACK_CHANNEL_H
|
||||
#define LOOPBACK_CHANNEL_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "ichannel.h"
|
||||
|
||||
|
||||
// Loopback sockets receive the same data they send.
|
||||
IChannel* CreateLoopbackChannel();
|
||||
|
||||
|
||||
#endif // LOOPBACK_CHANNEL_H
|
||||
300
utils/vmpi/messagemgr.cpp
Normal file
300
utils/vmpi/messagemgr.cpp
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
39
utils/vmpi/messagemgr.h
Normal file
39
utils/vmpi/messagemgr.h
Normal file
@@ -0,0 +1,39 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MESSAGEMGR_H
|
||||
#define MESSAGEMGR_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#define MSGMGR_VERSION 52314
|
||||
#define MSGMGR_BROADCAST_PORT 22511
|
||||
|
||||
#define MSGMGR_PACKETID_MSG 0
|
||||
#define MSGMGR_PACKETID_ANNOUNCE_PRESENCE 1 // followed by version # and port
|
||||
|
||||
|
||||
// IMessageMgr provides a simple interface apps can use to generate output. Apps
|
||||
// on the network can connect to the messagemgr to get its output and display it.
|
||||
class IMessageMgr
|
||||
{
|
||||
public:
|
||||
virtual bool Init() = 0;
|
||||
virtual void Term() = 0;
|
||||
|
||||
virtual void Print( const char *pMsg ) = 0;
|
||||
};
|
||||
|
||||
|
||||
// Get the message manager. It's a global singleton so this will always
|
||||
// return the same value (null if the manager can't initialize).
|
||||
IMessageMgr* GetMessageMgr();
|
||||
|
||||
|
||||
#endif // MESSAGEMGR_H
|
||||
279
utils/vmpi/messbuf.cpp
Normal file
279
utils/vmpi/messbuf.cpp
Normal file
@@ -0,0 +1,279 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
//
|
||||
// MessageBuffer - handy for serializing/unserializing
|
||||
// structures to be sent as messages
|
||||
// dal - 9/2002
|
||||
//
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "messbuf.h"
|
||||
#include "tier1/strtools.h"
|
||||
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
MessageBuffer::MessageBuffer()
|
||||
{
|
||||
size = DEFAULT_MESSAGE_BUFFER_SIZE;
|
||||
data = (char *) malloc(size);
|
||||
len = 0;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
MessageBuffer::MessageBuffer(int minsize)
|
||||
{
|
||||
size = minsize;
|
||||
data = (char *) malloc(size);
|
||||
len = 0;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
MessageBuffer::~MessageBuffer()
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
int
|
||||
MessageBuffer::getSize()
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
int
|
||||
MessageBuffer::getLen()
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
int
|
||||
MessageBuffer::setLen(int nlen)
|
||||
{
|
||||
if (nlen < 0) return -1;
|
||||
if (nlen > size) {
|
||||
resize(nlen);
|
||||
}
|
||||
|
||||
int res = len;
|
||||
len = nlen;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
int
|
||||
MessageBuffer::getOffset()
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
int
|
||||
MessageBuffer::setOffset(int noffset)
|
||||
{
|
||||
if (noffset < 0 || noffset > len) return -1;
|
||||
int res = offset;
|
||||
offset = noffset;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
int
|
||||
MessageBuffer::write(void const * p, int bytes)
|
||||
{
|
||||
if (bytes + len > size) {
|
||||
resize(bytes + len);
|
||||
}
|
||||
memcpy(data + len, p, bytes);
|
||||
int res = len;
|
||||
len += bytes;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
int
|
||||
MessageBuffer::update(int loc, void const * p, int bytes)
|
||||
{
|
||||
if (loc + bytes > size) {
|
||||
resize(loc + bytes);
|
||||
}
|
||||
memcpy(data + loc, p, bytes);
|
||||
|
||||
if (len < loc + bytes) {
|
||||
len = loc + bytes;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
int
|
||||
MessageBuffer::extract(int loc, void * p, int bytes)
|
||||
{
|
||||
if (loc + bytes > len) return -1;
|
||||
memcpy(p, data + loc, bytes);
|
||||
|
||||
return loc + bytes;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
int
|
||||
MessageBuffer::read(void * p, int bytes)
|
||||
{
|
||||
if (offset + bytes > len) return -1;
|
||||
memcpy(p, data + offset, bytes);
|
||||
|
||||
offset += bytes;
|
||||
return offset;
|
||||
}
|
||||
|
||||
int MessageBuffer::WriteString( const char *pString )
|
||||
{
|
||||
return write( pString, V_strlen( pString ) + 1 );
|
||||
}
|
||||
|
||||
int MessageBuffer::ReadString( char *pOut, int bufferLength )
|
||||
{
|
||||
int nChars = 0;
|
||||
while ( 1 )
|
||||
{
|
||||
char ch;
|
||||
if ( read( &ch, sizeof( ch ) ) == -1 )
|
||||
{
|
||||
pOut[0] = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( ch == 0 || nChars >= (bufferLength-1) )
|
||||
break;
|
||||
|
||||
pOut[nChars] = ch;
|
||||
++nChars;
|
||||
}
|
||||
pOut[nChars] = 0;
|
||||
return nChars + 1;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
void
|
||||
MessageBuffer::clear()
|
||||
{
|
||||
memset(data, 0, size);
|
||||
offset = 0;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
void
|
||||
MessageBuffer::clear(int minsize)
|
||||
{
|
||||
if (minsize > size) {
|
||||
resize(minsize);
|
||||
}
|
||||
memset(data, 0, size);
|
||||
offset = 0;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
void
|
||||
MessageBuffer::reset(int minsize)
|
||||
{
|
||||
if (minsize > size) {
|
||||
resize(minsize);
|
||||
}
|
||||
len = 0;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
//
|
||||
void
|
||||
MessageBuffer::resize(int minsize)
|
||||
{
|
||||
if (minsize < size) return;
|
||||
|
||||
if (size * 2 > minsize) minsize = size * 2;
|
||||
|
||||
char * odata = data;
|
||||
data = (char *) malloc(minsize);
|
||||
memcpy(data, odata, len);
|
||||
size = minsize;
|
||||
free(odata);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////
|
||||
//
|
||||
//
|
||||
void
|
||||
MessageBuffer::print(FILE * ofile, int num)
|
||||
{
|
||||
fprintf(ofile, "Len: %d Offset: %d Size: %d\n", len, offset, size);
|
||||
if (num > size) num = size;
|
||||
for (int i=0; i<num; ++i) {
|
||||
fprintf(ofile, "%02x ", (unsigned char) data[i]);
|
||||
}
|
||||
fprintf(ofile, "\n");
|
||||
}
|
||||
52
utils/vmpi/messbuf.h
Normal file
52
utils/vmpi/messbuf.h
Normal file
@@ -0,0 +1,52 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
//
|
||||
// MessageBuffer - handy for packing and upacking
|
||||
// structures to be sent as messages
|
||||
//
|
||||
#ifndef _MESSAGEBUFFER
|
||||
#define _MESSAGEBUFFER
|
||||
|
||||
#include <stdio.h>
|
||||
#define DEFAULT_MESSAGE_BUFFER_SIZE 2048
|
||||
|
||||
class MessageBuffer {
|
||||
public:
|
||||
char * data;
|
||||
|
||||
MessageBuffer();
|
||||
MessageBuffer(int size);
|
||||
~MessageBuffer();
|
||||
|
||||
int getSize();
|
||||
int getLen();
|
||||
int setLen(int len);
|
||||
int getOffset();
|
||||
int setOffset(int offset);
|
||||
|
||||
int write(void const * p, int bytes);
|
||||
int update(int loc, void const * p, int bytes);
|
||||
int extract(int loc, void * p, int bytes);
|
||||
int read(void * p, int bytes);
|
||||
|
||||
int WriteString( const char *pString );
|
||||
int ReadString( char *pOut, int bufferLength );
|
||||
|
||||
void clear();
|
||||
void clear(int minsize);
|
||||
void reset(int minsize);
|
||||
void print(FILE * ofile, int num);
|
||||
|
||||
private:
|
||||
void resize(int minsize);
|
||||
int size;
|
||||
int offset;
|
||||
int len;
|
||||
};
|
||||
|
||||
#endif
|
||||
275
utils/vmpi/mysql_async.cpp
Normal file
275
utils/vmpi/mysql_async.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include <windows.h>
|
||||
#include "imysqlwrapper.h"
|
||||
#include "mysql_async.h"
|
||||
#include "utllinkedlist.h"
|
||||
|
||||
|
||||
static char* CopyString( const char *pStr )
|
||||
{
|
||||
char *pRet = new char[ strlen( pStr ) + 1 ];
|
||||
strcpy( pRet, pStr );
|
||||
return pRet;
|
||||
}
|
||||
|
||||
|
||||
class CMySQLAsync : public IMySQLAsync
|
||||
{
|
||||
public:
|
||||
|
||||
CMySQLAsync()
|
||||
{
|
||||
m_hThread = NULL;
|
||||
m_pSQL = NULL;
|
||||
|
||||
m_hExitEvent = CreateEvent( NULL, true, false, NULL ); // Use manual reset because we want it to cascade out without
|
||||
// resetting the event if it gets set.
|
||||
m_hPendingQueryEvent = CreateEvent( NULL, false, false, NULL );
|
||||
m_hQueryResultsEvent = CreateEvent( NULL, false, false, NULL );
|
||||
|
||||
InitializeCriticalSection( &m_ExecuteQueryCS );
|
||||
InitializeCriticalSection( &m_PendingQueryCS );
|
||||
}
|
||||
|
||||
~CMySQLAsync()
|
||||
{
|
||||
Term();
|
||||
|
||||
CloseHandle( m_hExitEvent );
|
||||
CloseHandle( m_hPendingQueryEvent );
|
||||
CloseHandle( m_hQueryResultsEvent );
|
||||
|
||||
DeleteCriticalSection( &m_ExecuteQueryCS );
|
||||
DeleteCriticalSection( &m_PendingQueryCS );
|
||||
}
|
||||
|
||||
virtual void Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual IMySQLRowSet* ExecuteBlocking( const char *pStr )
|
||||
{
|
||||
IMySQLRowSet *pRet;
|
||||
|
||||
EnterCriticalSection( &m_ExecuteQueryCS );
|
||||
m_pSQL->Execute( pStr );
|
||||
pRet = m_pSQL->DuplicateRowSet();
|
||||
LeaveCriticalSection( &m_ExecuteQueryCS );
|
||||
|
||||
return pRet;
|
||||
}
|
||||
|
||||
virtual void Execute( const char *pStr, void *pUserData )
|
||||
{
|
||||
EnterCriticalSection( &m_PendingQueryCS );
|
||||
|
||||
CPendingQuery query;
|
||||
query.m_pStr = CopyString( pStr );
|
||||
query.m_pUserData = pUserData;
|
||||
query.m_Timer.Start();
|
||||
|
||||
m_PendingQueries.AddToTail( query );
|
||||
SetEvent( m_hPendingQueryEvent );
|
||||
|
||||
LeaveCriticalSection( &m_PendingQueryCS );
|
||||
}
|
||||
|
||||
virtual bool GetNextResults( CQueryResults &results )
|
||||
{
|
||||
results.m_pResults = NULL;
|
||||
|
||||
if ( WaitForSingleObject( m_hQueryResultsEvent, 0 ) == WAIT_OBJECT_0 )
|
||||
{
|
||||
EnterCriticalSection( &m_PendingQueryCS );
|
||||
|
||||
Assert( m_QueryResults.Count() > 0 );
|
||||
int iHead = m_QueryResults.Head();
|
||||
results = m_QueryResults[iHead];
|
||||
m_QueryResults.Remove( iHead );
|
||||
|
||||
if ( m_QueryResults.Count() > 0 )
|
||||
SetEvent( m_hQueryResultsEvent );
|
||||
|
||||
LeaveCriticalSection( &m_PendingQueryCS );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Init( IMySQL *pSQL )
|
||||
{
|
||||
Term();
|
||||
|
||||
DWORD dwThreadID;
|
||||
m_hThread = CreateThread( NULL, 0, &CMySQLAsync::StaticThreadFn, this, 0, &dwThreadID );
|
||||
if ( m_hThread )
|
||||
{
|
||||
m_pSQL = pSQL;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Term()
|
||||
{
|
||||
// Stop the thread.
|
||||
if ( m_hThread )
|
||||
{
|
||||
// Delete all our queries.
|
||||
SetEvent( m_hExitEvent );
|
||||
WaitForSingleObject( m_hThread, INFINITE );
|
||||
CloseHandle( m_hThread );
|
||||
m_hThread = NULL;
|
||||
}
|
||||
|
||||
// Delete leftover queries.
|
||||
FOR_EACH_LL( m_PendingQueries, iPendingQuery )
|
||||
{
|
||||
delete [] m_PendingQueries[iPendingQuery].m_pStr;
|
||||
}
|
||||
m_PendingQueries.Purge();
|
||||
|
||||
FOR_EACH_LL( m_QueryResults, i )
|
||||
{
|
||||
m_QueryResults[i].m_pResults->Release();
|
||||
}
|
||||
m_QueryResults.Purge();
|
||||
|
||||
if ( m_pSQL )
|
||||
{
|
||||
m_pSQL->Release();
|
||||
m_pSQL = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
DWORD ThreadFn()
|
||||
{
|
||||
HANDLE hEvents[2] = { m_hExitEvent, m_hPendingQueryEvent };
|
||||
|
||||
//
|
||||
while ( 1 )
|
||||
{
|
||||
int ret = WaitForMultipleObjects( ARRAYSIZE( hEvents ), hEvents, false, INFINITE );
|
||||
if ( ret == WAIT_OBJECT_0 )
|
||||
break;
|
||||
|
||||
if ( ret == WAIT_OBJECT_0+1 )
|
||||
{
|
||||
// A new string has been queued up for us to execute.
|
||||
EnterCriticalSection( &m_PendingQueryCS );
|
||||
|
||||
Assert( m_PendingQueries.Count() > 0 );
|
||||
int iHead = m_PendingQueries.Head();
|
||||
|
||||
CPendingQuery pending = m_PendingQueries[iHead];
|
||||
m_PendingQueries.Remove( iHead );
|
||||
|
||||
// Set the pending query event if there are more queries waiting to run.
|
||||
if ( m_PendingQueries.Count() > 0 )
|
||||
SetEvent( m_hPendingQueryEvent );
|
||||
|
||||
LeaveCriticalSection( &m_PendingQueryCS );
|
||||
|
||||
|
||||
// Run the query.
|
||||
EnterCriticalSection( &m_ExecuteQueryCS );
|
||||
|
||||
CQueryResults results;
|
||||
results.m_pResults = NULL;
|
||||
results.m_pUserData = pending.m_pUserData;
|
||||
results.m_ExecuteTime.Init();
|
||||
pending.m_Timer.End();
|
||||
results.m_QueueTime = pending.m_Timer.GetDuration();
|
||||
|
||||
CFastTimer executeTimer;
|
||||
executeTimer.Start();
|
||||
|
||||
if ( m_pSQL->Execute( pending.m_pStr ) == 0 )
|
||||
{
|
||||
executeTimer.End();
|
||||
results.m_ExecuteTime = executeTimer.GetDuration();
|
||||
results.m_pResults = m_pSQL->DuplicateRowSet();
|
||||
}
|
||||
|
||||
delete pending.m_pStr;
|
||||
|
||||
LeaveCriticalSection( &m_ExecuteQueryCS );
|
||||
|
||||
|
||||
// Store the results.
|
||||
EnterCriticalSection( &m_PendingQueryCS );
|
||||
|
||||
m_QueryResults.AddToTail( results );
|
||||
SetEvent( m_hQueryResultsEvent );
|
||||
|
||||
LeaveCriticalSection( &m_PendingQueryCS );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD WINAPI StaticThreadFn( LPVOID lpParameter )
|
||||
{
|
||||
return ((CMySQLAsync*)lpParameter)->ThreadFn();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
HANDLE m_hThread;
|
||||
HANDLE m_hExitEvent;
|
||||
HANDLE m_hPendingQueryEvent; // Signaled when a new query is added.
|
||||
HANDLE m_hQueryResultsEvent;
|
||||
|
||||
IMySQL *m_pSQL;
|
||||
|
||||
CRITICAL_SECTION m_PendingQueryCS;
|
||||
CRITICAL_SECTION m_ExecuteQueryCS;
|
||||
|
||||
|
||||
// Outgoing query results. New ones are added to the tail.
|
||||
CUtlLinkedList<CQueryResults, int> m_QueryResults;
|
||||
|
||||
|
||||
// New ones added to the tail.
|
||||
class CPendingQuery
|
||||
{
|
||||
public:
|
||||
char *m_pStr;
|
||||
void *m_pUserData;
|
||||
CFastTimer m_Timer; // Times how long this query is in the queue.
|
||||
};
|
||||
|
||||
CUtlLinkedList<CPendingQuery,int> m_PendingQueries;
|
||||
};
|
||||
|
||||
|
||||
IMySQLAsync* CreateMySQLAsync( IMySQL *pSQL )
|
||||
{
|
||||
CMySQLAsync *pRet = new CMySQLAsync;
|
||||
if ( pRet->Init( pSQL ) )
|
||||
{
|
||||
return pRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pRet;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
56
utils/vmpi/mysql_async.h
Normal file
56
utils/vmpi/mysql_async.h
Normal file
@@ -0,0 +1,56 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MYSQL_ASYNC_H
|
||||
#define MYSQL_ASYNC_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "tier0/fasttimer.h"
|
||||
|
||||
|
||||
class IMySQL;
|
||||
|
||||
|
||||
class CQueryResults
|
||||
{
|
||||
public:
|
||||
IMySQLRowSet *m_pResults;
|
||||
void *m_pUserData; // This is the value passed to Execute.
|
||||
CCycleCount m_ExecuteTime; // How long it took to execute this query in MySQL.
|
||||
CCycleCount m_QueueTime; // How long it spent in the queue.
|
||||
};
|
||||
|
||||
|
||||
// This provides a way to do asynchronous MySQL queries. They are executed in a thread,
|
||||
// then you can pop the results off as they arrive.
|
||||
class IMySQLAsync
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void Release() = 0;
|
||||
|
||||
// After finishing the current query, if there is one, this immediately executes
|
||||
// the specified query and returns its results.
|
||||
virtual IMySQLRowSet* ExecuteBlocking( const char *pStr ) = 0;
|
||||
|
||||
// Queue up a query.
|
||||
virtual void Execute( const char *pStr, void *pUserData ) = 0;
|
||||
|
||||
// Poll this to pick up results from Execute().
|
||||
// NOTE: if this returns true, but pResults is set to NULL, then that query had an error.
|
||||
virtual bool GetNextResults( CQueryResults &results ) = 0;
|
||||
};
|
||||
|
||||
// Create an async mysql interface. Note: if this call returns a non-null value,
|
||||
// then after this call, you do NOT own pSQL anymore and you shouldn't ever call
|
||||
// a function in it again.
|
||||
IMySQLAsync* CreateMySQLAsync( IMySQL *pSQL );
|
||||
|
||||
|
||||
#endif // MYSQL_ASYNC_H
|
||||
104
utils/vmpi/mysql_wrapper.h
Normal file
104
utils/vmpi/mysql_wrapper.h
Normal file
@@ -0,0 +1,104 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MYSQL_WRAPPER_H
|
||||
#define MYSQL_WRAPPER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "utlvector.h"
|
||||
|
||||
|
||||
class IMySQL;
|
||||
|
||||
|
||||
// This is a helper class to build queries like the stream IO.
|
||||
class CMySQLQuery
|
||||
{
|
||||
friend class CMySQL;
|
||||
|
||||
public:
|
||||
// This is like a sprintf, but it will grow the string as necessary.
|
||||
void Format( PRINTF_FORMAT_STRING const char *pFormat, ... );
|
||||
|
||||
private:
|
||||
CUtlVector<char> m_QueryText;
|
||||
};
|
||||
|
||||
|
||||
class CColumnValue
|
||||
{
|
||||
public:
|
||||
|
||||
CColumnValue( CMySQL *pSQL, int iColumn );
|
||||
|
||||
const char* String();
|
||||
long Int32();
|
||||
unsigned long UInt32();
|
||||
|
||||
private:
|
||||
CMySQL *m_pSQL;
|
||||
int m_iColumn;
|
||||
};
|
||||
|
||||
|
||||
class IMySQL
|
||||
{
|
||||
public:
|
||||
virtual void Release() = 0;
|
||||
|
||||
// These execute SQL commands. They return 0 if the query was successful.
|
||||
virtual int Execute( const char *pString ) = 0;
|
||||
virtual int Execute( CMySQLQuery &query ) = 0;
|
||||
|
||||
// If you just inserted rows into a table with an AUTO_INCREMENT column,
|
||||
// then this returns the (unique) value of that column.
|
||||
virtual unsigned long InsertID() = 0;
|
||||
|
||||
// If you just executed a select statement, then you can use these functions to
|
||||
// iterate over the result set.
|
||||
|
||||
// Get the number of columns in the data returned from the last query (if it was a select statement).
|
||||
virtual int NumFields() = 0;
|
||||
|
||||
// Get the name of each column returned by the last query.
|
||||
virtual const char* GetFieldName( int iColumn ) = 0;
|
||||
|
||||
// Call this in a loop until it returns false to iterate over all rows the query returned.
|
||||
virtual bool NextRow() = 0;
|
||||
|
||||
// You can call this to start iterating over the result set from the start again.
|
||||
// Note: after calling this, you have to call NextRow() to actually get the first row's value ready.
|
||||
virtual bool SeekToFirstRow() = 0;
|
||||
|
||||
virtual CColumnValue GetColumnValue( int iColumn ) = 0;
|
||||
virtual CColumnValue GetColumnValue( const char *pColumnName ) = 0;
|
||||
|
||||
// You can call this to get the index of a column for faster lookups with GetColumnValue( int ).
|
||||
// Returns -1 if the column can't be found.
|
||||
virtual int GetColumnIndex( const char *pColumnName ) = 0;
|
||||
};
|
||||
|
||||
|
||||
IMySQL* InitMySQL( const char *pDBName, const char *pHostName="", const char *pUserName="", const char *pPassword="" );
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------ //
|
||||
// Inlines.
|
||||
// ------------------------------------------------------------------------------------------------ //
|
||||
|
||||
inline CColumnValue::CColumnValue( CMySQL *pSQL, int iColumn )
|
||||
{
|
||||
m_pSQL = pSQL;
|
||||
m_iColumn = iColumn;
|
||||
}
|
||||
|
||||
|
||||
#endif // MYSQL_WRAPPER_H
|
||||
208
utils/vmpi/net_view_thread.cpp
Normal file
208
utils/vmpi/net_view_thread.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "net_view_thread.h"
|
||||
|
||||
|
||||
char* CopyAlloc( const char *pStr )
|
||||
{
|
||||
char *pRet = new char[ strlen( pStr ) + 1];
|
||||
strcpy( pRet, pStr );
|
||||
return pRet;
|
||||
}
|
||||
|
||||
|
||||
CNetViewThread::CNetViewThread()
|
||||
{
|
||||
m_hThread = NULL;
|
||||
m_hThreadExitEvent = NULL;
|
||||
InitializeCriticalSection( &m_ComputerNamesCS );
|
||||
}
|
||||
|
||||
|
||||
CNetViewThread::~CNetViewThread()
|
||||
{
|
||||
Term();
|
||||
DeleteCriticalSection( &m_ComputerNamesCS );
|
||||
}
|
||||
|
||||
|
||||
void CNetViewThread::Init()
|
||||
{
|
||||
Term();
|
||||
|
||||
m_hThreadExitEvent = CreateEvent( NULL, false, false, NULL );
|
||||
|
||||
DWORD dwThreadID = 0;
|
||||
m_hThread = CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
&CNetViewThread::StaticThreadFn,
|
||||
this,
|
||||
0,
|
||||
&dwThreadID );
|
||||
}
|
||||
|
||||
|
||||
void CNetViewThread::Term()
|
||||
{
|
||||
if ( m_hThread )
|
||||
{
|
||||
SetEvent( m_hThreadExitEvent );
|
||||
WaitForSingleObject( m_hThread, INFINITE );
|
||||
CloseHandle( m_hThread );
|
||||
m_hThread = NULL;
|
||||
}
|
||||
|
||||
if ( m_hThreadExitEvent )
|
||||
{
|
||||
CloseHandle( m_hThreadExitEvent );
|
||||
m_hThreadExitEvent = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CNetViewThread::GetComputerNames( CUtlVector<char*> &computerNames )
|
||||
{
|
||||
EnterCriticalSection( &m_ComputerNamesCS );
|
||||
|
||||
computerNames.Purge();
|
||||
for ( int i=0; i < m_ComputerNames.Count(); i++ )
|
||||
{
|
||||
computerNames.AddToTail( CopyAlloc( m_ComputerNames[i] ) );
|
||||
}
|
||||
|
||||
LeaveCriticalSection( &m_ComputerNamesCS );
|
||||
}
|
||||
|
||||
|
||||
void CNetViewThread::UpdateServicesFromNetView()
|
||||
{
|
||||
HANDLE hChildStdoutRd, hChildStdoutWr;
|
||||
|
||||
// Set the bInheritHandle flag so pipe handles are inherited.
|
||||
SECURITY_ATTRIBUTES saAttr;
|
||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
saAttr.bInheritHandle = TRUE;
|
||||
saAttr.lpSecurityDescriptor = NULL;
|
||||
|
||||
if( CreatePipe( &hChildStdoutRd, &hChildStdoutWr, &saAttr, 0 ) )
|
||||
{
|
||||
STARTUPINFO si;
|
||||
memset(&si, 0, sizeof si);
|
||||
si.cb = sizeof(si);
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
si.hStdOutput = hChildStdoutWr;
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
if( CreateProcess(
|
||||
NULL,
|
||||
"net view",
|
||||
NULL, // lpProcessAttributes
|
||||
NULL, // lpThreadAttributes
|
||||
TRUE, // bInheritHandls
|
||||
DETACHED_PROCESS, // dwCreationFlags
|
||||
NULL, // lpEnvironment
|
||||
NULL, // lpCurrentDirectory
|
||||
&si, // lpStartupInfo
|
||||
&pi // lpProcessInformation
|
||||
) )
|
||||
{
|
||||
// read from pipe..
|
||||
#define BUFFER_SIZE 8192
|
||||
char buffer[BUFFER_SIZE];
|
||||
BOOL bDone = FALSE;
|
||||
CUtlVector<char> totalBuffer;
|
||||
|
||||
while(1)
|
||||
{
|
||||
DWORD dwCount = 0;
|
||||
DWORD dwRead = 0;
|
||||
|
||||
// read from input handle
|
||||
PeekNamedPipe(hChildStdoutRd, NULL, NULL, NULL, &dwCount, NULL);
|
||||
if (dwCount)
|
||||
{
|
||||
dwCount = min (dwCount, (DWORD)BUFFER_SIZE - 1);
|
||||
ReadFile(hChildStdoutRd, buffer, dwCount, &dwRead, NULL);
|
||||
}
|
||||
if(dwRead)
|
||||
{
|
||||
buffer[dwRead] = 0;
|
||||
totalBuffer.AddMultipleToTail( dwRead, buffer );
|
||||
}
|
||||
// check process termination
|
||||
else if( WaitForSingleObject( pi.hProcess, 1000 ) != WAIT_TIMEOUT )
|
||||
{
|
||||
if ( bDone )
|
||||
break;
|
||||
|
||||
bDone = TRUE; // next time we get it
|
||||
}
|
||||
}
|
||||
|
||||
// Now parse the output.
|
||||
totalBuffer.AddToTail( 0 );
|
||||
ParseComputerNames( totalBuffer.Base() );
|
||||
}
|
||||
|
||||
CloseHandle( hChildStdoutRd );
|
||||
CloseHandle( hChildStdoutWr );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CNetViewThread::ParseComputerNames( const char *pNetViewOutput )
|
||||
{
|
||||
EnterCriticalSection( &m_ComputerNamesCS );
|
||||
|
||||
m_ComputerNames.PurgeAndDeleteElements();
|
||||
|
||||
const char *pCur = pNetViewOutput;
|
||||
while ( *pCur != 0 )
|
||||
{
|
||||
// If we get a \\, then it's a computer name followed by whitespace.
|
||||
if ( pCur[0] == '\\' && pCur[1] == '\\' )
|
||||
{
|
||||
char curComputerName[512];
|
||||
char *pOutPos = curComputerName;
|
||||
|
||||
pCur += 2;
|
||||
while ( *pCur && !V_isspace( *pCur ) && (pOutPos-curComputerName < 510) )
|
||||
{
|
||||
*pOutPos++ = *pCur++;
|
||||
}
|
||||
*pOutPos = 0;
|
||||
|
||||
m_ComputerNames.AddToTail( CopyAlloc( curComputerName ) );
|
||||
}
|
||||
++pCur;
|
||||
}
|
||||
|
||||
LeaveCriticalSection( &m_ComputerNamesCS );
|
||||
}
|
||||
|
||||
|
||||
DWORD CNetViewThread::ThreadFn()
|
||||
{
|
||||
// Update the services list every 30 seconds.
|
||||
do
|
||||
{
|
||||
UpdateServicesFromNetView();
|
||||
} while ( WaitForSingleObject( m_hThreadExitEvent, 30000 ) != WAIT_OBJECT_0 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
DWORD CNetViewThread::StaticThreadFn( LPVOID lpParameter )
|
||||
{
|
||||
return ((CNetViewThread*)lpParameter)->ThreadFn();
|
||||
}
|
||||
|
||||
|
||||
45
utils/vmpi/net_view_thread.h
Normal file
45
utils/vmpi/net_view_thread.h
Normal file
@@ -0,0 +1,45 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef NET_VIEW_THREAD_H
|
||||
#define NET_VIEW_THREAD_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "utlvector.h"
|
||||
|
||||
|
||||
class CNetViewThread
|
||||
{
|
||||
public:
|
||||
CNetViewThread();
|
||||
~CNetViewThread();
|
||||
|
||||
// This creates the thread that periodically checks "net view" to get the current list of
|
||||
// machines out on the network.
|
||||
void Init();
|
||||
void Term();
|
||||
|
||||
void GetComputerNames( CUtlVector<char*> &computerNames );
|
||||
|
||||
private:
|
||||
|
||||
void UpdateServicesFromNetView();
|
||||
void ParseComputerNames( const char *pNetViewOutput );
|
||||
|
||||
DWORD ThreadFn();
|
||||
static DWORD WINAPI StaticThreadFn( LPVOID lpParameter );
|
||||
|
||||
CUtlVector<char*> m_ComputerNames;
|
||||
HANDLE m_hThread;
|
||||
HANDLE m_hThreadExitEvent;
|
||||
CRITICAL_SECTION m_ComputerNamesCS;
|
||||
};
|
||||
|
||||
|
||||
#endif // NET_VIEW_THREAD_H
|
||||
1178
utils/vmpi/tcpsocket.cpp
Normal file
1178
utils/vmpi/tcpsocket.cpp
Normal file
File diff suppressed because it is too large
Load Diff
84
utils/vmpi/tcpsocket.h
Normal file
84
utils/vmpi/tcpsocket.h
Normal file
@@ -0,0 +1,84 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef ITCPSOCKET_H
|
||||
#define ITCPSOCKET_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "ichannel.h"
|
||||
#include "iphelpers.h"
|
||||
|
||||
|
||||
class ITCPSocket : public IChannel
|
||||
{
|
||||
public:
|
||||
virtual void Release() = 0;
|
||||
|
||||
// Bind to the specified port on any address this host has. Note that the address the
|
||||
// socket winds up with (and getsockname() returns) isn't decided until you send a packet.
|
||||
virtual bool BindToAny( const unsigned short port ) = 0;
|
||||
|
||||
|
||||
// Use these to connect to a remote listening socket without blocking.
|
||||
// Call BeginConnect, then call UpdateConnect until it returns true.
|
||||
virtual bool BeginConnect( const CIPAddr &addr ) = 0;
|
||||
virtual bool UpdateConnect() = 0;
|
||||
|
||||
|
||||
// Connection state.
|
||||
virtual bool IsConnected() = 0;
|
||||
|
||||
// If IsConnected returns false, you can call this to find out why the socket got disconnected.
|
||||
virtual void GetDisconnectReason( CUtlVector<char> &reason ) = 0;
|
||||
|
||||
|
||||
// Send data. Returns true if successful.
|
||||
//
|
||||
// Note: TCP likes to clump your packets together in one, so the data multiple send() calls will
|
||||
// get concatenated and returned in one recv() call. ITCPSocket FIXES this behavior so your recv()
|
||||
// calls match your send() calls.
|
||||
//
|
||||
virtual bool Send( const void *pData, int size ) = 0;
|
||||
|
||||
// Receive data. Returns the number of bytes received.
|
||||
// This will wait as long as flTimeout for something to come in.
|
||||
// Returns false if no data was waiting.
|
||||
virtual bool Recv( CUtlVector<unsigned char> &data, double flTimeout=0 ) = 0;
|
||||
};
|
||||
|
||||
|
||||
// Use these to get incoming connections.
|
||||
class ITCPListenSocket
|
||||
{
|
||||
public:
|
||||
// Call this to stop listening for connections and delete the object.
|
||||
virtual void Release() = 0;
|
||||
|
||||
// Keep calling this as long as you want to wait for connections.
|
||||
virtual ITCPSocket* UpdateListen( CIPAddr *pAddr ) = 0; // pAddr is set to the remote process's address.
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// Use these to create the interfaces.
|
||||
ITCPSocket* CreateTCPSocket();
|
||||
|
||||
// Create a socket to listen with. nQueueLength specifies how many connections to enqueue.
|
||||
// When the queue runs out, connections can take a little longer to make.
|
||||
ITCPListenSocket* CreateTCPListenSocket( const unsigned short port, int nQueueLength = -1 );
|
||||
|
||||
|
||||
// By default, timeouts are on. It's helpful to turn them off during debugging.
|
||||
void TCPSocket_EnableTimeout( bool bEnable );
|
||||
|
||||
|
||||
|
||||
#endif // ITCPSOCKET_H
|
||||
48
utils/vmpi/tcpsocket_helpers.cpp
Normal file
48
utils/vmpi/tcpsocket_helpers.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include <windows.h>
|
||||
#include "tcpsocket_helpers.h"
|
||||
|
||||
|
||||
// This connects to an ISocket listening with Listen().
|
||||
bool TCPSocket_Connect( ITCPSocket *pSocket, const CIPAddr *pAddr, double flTimeout )
|
||||
{
|
||||
pSocket->BeginConnect( *pAddr );
|
||||
|
||||
CWaitTimer waitTimer( flTimeout );
|
||||
while ( 1 )
|
||||
{
|
||||
if ( pSocket->UpdateConnect() )
|
||||
return true;
|
||||
|
||||
if ( waitTimer.ShouldKeepWaiting() )
|
||||
Sleep( 10 );
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
ITCPSocket* TCPSocket_ListenForOneConnection( ITCPListenSocket *pSocket, CIPAddr *pAddr, double flTimeout )
|
||||
{
|
||||
CWaitTimer waitTimer( flTimeout );
|
||||
while ( 1 )
|
||||
{
|
||||
ITCPSocket *pRet = pSocket->UpdateListen( pAddr );
|
||||
if ( pRet )
|
||||
return pRet;
|
||||
|
||||
if ( waitTimer.ShouldKeepWaiting() )
|
||||
Sleep( 10 );
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
21
utils/vmpi/tcpsocket_helpers.h
Normal file
21
utils/vmpi/tcpsocket_helpers.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef TCPSOCKET_HELPERS_H
|
||||
#define TCPSOCKET_HELPERS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "tcpsocket.h"
|
||||
|
||||
|
||||
bool TCPSocket_Connect( ITCPSocket *pSocket, const CIPAddr *pAddr, double flTimeout );
|
||||
ITCPSocket* TCPSocket_ListenForOneConnection( ITCPListenSocket *pSocket, CIPAddr *pAddr, double flTimeout );
|
||||
|
||||
|
||||
#endif // TCPSOCKET_HELPERS_H
|
||||
22
utils/vmpi/testapps/MessageWatch/MessageRecvMgr.h
Normal file
22
utils/vmpi/testapps/MessageWatch/MessageRecvMgr.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MESSAGERECVMGR_H
|
||||
#define MESSAGERECVMGR_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
class IMessageRecvMgr
|
||||
{
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // MESSAGERECVMGR_H
|
||||
77
utils/vmpi/testapps/MessageWatch/MessageWatch.cpp
Normal file
77
utils/vmpi/testapps/MessageWatch/MessageWatch.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// MessageWatch.cpp : Defines the class behaviors for the application.
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "MessageWatch.h"
|
||||
#include "MessageWatchDlg.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define new DEBUG_NEW
|
||||
#undef THIS_FILE
|
||||
static char THIS_FILE[] = __FILE__;
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CMessageWatchApp
|
||||
|
||||
BEGIN_MESSAGE_MAP(CMessageWatchApp, CWinApp)
|
||||
//{{AFX_MSG_MAP(CMessageWatchApp)
|
||||
// NOTE - the ClassWizard will add and remove mapping macros here.
|
||||
// DO NOT EDIT what you see in these blocks of generated code!
|
||||
//}}AFX_MSG
|
||||
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CMessageWatchApp construction
|
||||
|
||||
CMessageWatchApp::CMessageWatchApp()
|
||||
{
|
||||
// TODO: add construction code here,
|
||||
// Place all significant initialization in InitInstance
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// The one and only CMessageWatchApp object
|
||||
|
||||
CMessageWatchApp theApp;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CMessageWatchApp initialization
|
||||
|
||||
BOOL CMessageWatchApp::InitInstance()
|
||||
{
|
||||
// Standard initialization
|
||||
// If you are not using these features and wish to reduce the size
|
||||
// of your final executable, you should remove from the following
|
||||
// the specific initialization routines you do not need.
|
||||
|
||||
#ifdef _AFXDLL
|
||||
Enable3dControls(); // Call this when using MFC in a shared DLL
|
||||
#else
|
||||
Enable3dControlsStatic(); // Call this when linking to MFC statically
|
||||
#endif
|
||||
|
||||
CMessageWatchDlg dlg;
|
||||
m_pMainWnd = &dlg;
|
||||
int nResponse = dlg.DoModal();
|
||||
if (nResponse == IDOK)
|
||||
{
|
||||
// TODO: Place code here to handle when the dialog is
|
||||
// dismissed with OK
|
||||
}
|
||||
else if (nResponse == IDCANCEL)
|
||||
{
|
||||
// TODO: Place code here to handle when the dialog is
|
||||
// dismissed with Cancel
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
56
utils/vmpi/testapps/MessageWatch/MessageWatch.h
Normal file
56
utils/vmpi/testapps/MessageWatch/MessageWatch.h
Normal file
@@ -0,0 +1,56 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// MessageWatch.h : main header file for the MESSAGEWATCH application
|
||||
//
|
||||
|
||||
#if !defined(AFX_MESSAGEWATCH_H__72A09EC9_2B19_4AC5_A281_5FAD41F6DFCA__INCLUDED_)
|
||||
#define AFX_MESSAGEWATCH_H__72A09EC9_2B19_4AC5_A281_5FAD41F6DFCA__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
#ifndef __AFXWIN_H__
|
||||
#error include 'stdafx.h' before including this file for PCH
|
||||
#endif
|
||||
|
||||
#include "resource.h" // main symbols
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CMessageWatchApp:
|
||||
// See MessageWatch.cpp for the implementation of this class
|
||||
//
|
||||
|
||||
class CMessageWatchApp : public CWinApp
|
||||
{
|
||||
public:
|
||||
CMessageWatchApp();
|
||||
|
||||
// Overrides
|
||||
// ClassWizard generated virtual function overrides
|
||||
//{{AFX_VIRTUAL(CMessageWatchApp)
|
||||
public:
|
||||
virtual BOOL InitInstance();
|
||||
//}}AFX_VIRTUAL
|
||||
|
||||
// Implementation
|
||||
|
||||
//{{AFX_MSG(CMessageWatchApp)
|
||||
// NOTE - the ClassWizard will add and remove member functions here.
|
||||
// DO NOT EDIT what you see in these blocks of generated code !
|
||||
//}}AFX_MSG
|
||||
DECLARE_MESSAGE_MAP()
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//{{AFX_INSERT_LOCATION}}
|
||||
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||
|
||||
#endif // !defined(AFX_MESSAGEWATCH_H__72A09EC9_2B19_4AC5_A281_5FAD41F6DFCA__INCLUDED_)
|
||||
194
utils/vmpi/testapps/MessageWatch/MessageWatch.rc
Normal file
194
utils/vmpi/testapps/MessageWatch/MessageWatch.rc
Normal file
@@ -0,0 +1,194 @@
|
||||
//Microsoft Developer Studio generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "afxres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"#include ""afxres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"#define _AFX_NO_SPLITTER_RESOURCES\r\n"
|
||||
"#define _AFX_NO_OLE_RESOURCES\r\n"
|
||||
"#define _AFX_NO_TRACKER_RESOURCES\r\n"
|
||||
"#define _AFX_NO_PROPERTY_RESOURCES\r\n"
|
||||
"\r\n"
|
||||
"#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
|
||||
"#ifdef _WIN32\r\n"
|
||||
"LANGUAGE 9, 1\r\n"
|
||||
"#pragma code_page(1252)\r\n"
|
||||
"#endif //_WIN32\r\n"
|
||||
"#include ""res\\MessageWatch.rc2"" // non-Microsoft Visual C++ edited resources\r\n"
|
||||
"#include ""afxres.rc"" // Standard components\r\n"
|
||||
"#endif\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDR_MAINFRAME ICON DISCARDABLE "res\\MessageWatch.ico"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_MESSAGEWATCH_DIALOG DIALOGEX 0, 0, 232, 147
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
|
||||
EXSTYLE WS_EX_APPWINDOW
|
||||
CAPTION "MessageWatch"
|
||||
FONT 8, "MS Sans Serif"
|
||||
BEGIN
|
||||
PUSHBUTTON "Quit",IDCANCEL,175,126,50,14
|
||||
LISTBOX IDC_MACHINES,7,7,218,114,LBS_SORT | LBS_NOINTEGRALHEIGHT |
|
||||
WS_VSCROLL | WS_TABSTOP
|
||||
PUSHBUTTON "&Show All",IDSHOWALL,7,126,50,14
|
||||
PUSHBUTTON "&Hide All",IDHIDEALL,91,126,50,14
|
||||
END
|
||||
|
||||
IDD_OUTPUT DIALOG DISCARDABLE 0, 0, 320, 225
|
||||
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Output"
|
||||
FONT 8, "MS Sans Serif"
|
||||
BEGIN
|
||||
EDITTEXT IDC_DEBUG_OUTPUT,7,7,306,211,ES_MULTILINE | ES_READONLY |
|
||||
WS_VSCROLL
|
||||
END
|
||||
|
||||
|
||||
#ifndef _MAC
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,0,1
|
||||
PRODUCTVERSION 1,0,0,1
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904B0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "\0"
|
||||
VALUE "FileDescription", "MessageWatch MFC Application\0"
|
||||
VALUE "FileVersion", "1, 0, 0, 1\0"
|
||||
VALUE "InternalName", "MessageWatch\0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2002\0"
|
||||
VALUE "LegalTrademarks", "\0"
|
||||
VALUE "OriginalFilename", "MessageWatch.EXE\0"
|
||||
VALUE "ProductName", "MessageWatch Application\0"
|
||||
VALUE "ProductVersion", "1, 0, 0, 1\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // !_MAC
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO DISCARDABLE
|
||||
BEGIN
|
||||
IDD_MESSAGEWATCH_DIALOG, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 225
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 140
|
||||
END
|
||||
|
||||
IDD_OUTPUT, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 313
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 218
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
#define _AFX_NO_SPLITTER_RESOURCES
|
||||
#define _AFX_NO_OLE_RESOURCES
|
||||
#define _AFX_NO_TRACKER_RESOURCES
|
||||
#define _AFX_NO_PROPERTY_RESOURCES
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE 9, 1
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
#include "res\MessageWatch.rc2" // non-Microsoft Visual C++ edited resources
|
||||
#include "afxres.rc" // Standard components
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
282
utils/vmpi/testapps/MessageWatch/MessageWatch.vcproj
Normal file
282
utils/vmpi/testapps/MessageWatch/MessageWatch.vcproj
Normal file
@@ -0,0 +1,282 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="7.10"
|
||||
Name="MessageWatch"
|
||||
ProjectGUID="{52DE0F9C-D5D8-4C31-A6EB-6841285BF1CB}"
|
||||
SccProjectName=""
|
||||
SccAuxPath=""
|
||||
SccLocalPath=""
|
||||
SccProvider=""
|
||||
Keyword="MFCProj">
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"/>
|
||||
</Platforms>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory=".\Debug"
|
||||
IntermediateDirectory=".\Debug"
|
||||
ConfigurationType="1"
|
||||
UseOfMFC="2"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\common,..\..\..\public,..\..\..\public\tier1,..\..\..\common,.."
|
||||
PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;PROTECTED_THINGS_DISABLE"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
PrecompiledHeaderFile=".\Debug/MessageWatch.pch"
|
||||
AssemblerListingLocation=".\Debug/"
|
||||
ObjectFile=".\Debug/"
|
||||
ProgramDataBaseFileName=".\Debug/"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="4"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
CommandLine="copy "$(TargetPath)" ..\..\..\..\game\bin
|
||||
"
|
||||
Outputs="..\..\..\..\game\bin\messagewatch.exe"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib"
|
||||
OutputFile=".\Debug/MessageWatch.exe"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
GenerateDebugInformation="TRUE"
|
||||
ProgramDatabaseFile=".\Debug/MessageWatch.pdb"
|
||||
SubSystem="2"
|
||||
TargetMachine="1"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
MkTypLibCompatible="TRUE"
|
||||
SuppressStartupBanner="TRUE"
|
||||
TargetEnvironment="1"
|
||||
TypeLibraryName=".\Debug/MessageWatch.tlb"
|
||||
HeaderFileName=""/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory=".\Release"
|
||||
IntermediateDirectory=".\Release"
|
||||
ConfigurationType="1"
|
||||
UseOfMFC="2"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="1"
|
||||
AdditionalIncludeDirectories="..\..\common,..\..\..\public,..\..\..\public\tier1,..\..\..\common,.."
|
||||
PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;PROTECTED_THINGS_DISABLE"
|
||||
StringPooling="TRUE"
|
||||
RuntimeLibrary="2"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
PrecompiledHeaderFile=".\Release/MessageWatch.pch"
|
||||
AssemblerListingLocation=".\Release/"
|
||||
ObjectFile=".\Release/"
|
||||
ProgramDataBaseFileName=".\Release/"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
CommandLine="copy "$(TargetPath)" ..\..\..\..\game\bin
|
||||
"
|
||||
Outputs="..\..\..\..\game\bin\messagewatch.exe"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib"
|
||||
OutputFile=".\Release/MessageWatch.exe"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
ProgramDatabaseFile=".\Release/MessageWatch.pdb"
|
||||
SubSystem="2"
|
||||
TargetMachine="1"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
PreprocessorDefinitions="NDEBUG"
|
||||
MkTypLibCompatible="TRUE"
|
||||
SuppressStartupBanner="TRUE"
|
||||
TargetEnvironment="1"
|
||||
TypeLibraryName=".\Release/MessageWatch.tlb"
|
||||
HeaderFileName=""/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="NDEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
|
||||
<File
|
||||
RelativePath="..\..\common\consolewnd.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="MessageWatch.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="MessageWatch.rc">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="MessageWatchDlg.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="StdAfx.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="win_idle.cpp">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl">
|
||||
<File
|
||||
RelativePath="..\..\common\consolewnd.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\common\iphelpers.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\common\messagemgr.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="MessageWatch.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="MessageWatchDlg.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="Resource.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="StdAfx.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\common\tcpsocket.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\common\threadhelpers.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="win_idle.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
|
||||
<File
|
||||
RelativePath="res\MessageWatch.ico">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="res\MessageWatch.rc2">
|
||||
</File>
|
||||
</Filter>
|
||||
<File
|
||||
RelativePath="ReadMe.txt">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\tier0.lib">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\vmpi.lib">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\vstdlib.lib">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
324
utils/vmpi/testapps/MessageWatch/MessageWatchDlg.cpp
Normal file
324
utils/vmpi/testapps/MessageWatch/MessageWatchDlg.cpp
Normal file
@@ -0,0 +1,324 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// MessageWatchDlg.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "MessageWatch.h"
|
||||
#include "MessageWatchDlg.h"
|
||||
#include "messagemgr.h"
|
||||
#include "tier1/strtools.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define new DEBUG_NEW
|
||||
#undef THIS_FILE
|
||||
static char THIS_FILE[] = __FILE__;
|
||||
#endif
|
||||
|
||||
|
||||
#define WM_STARTIDLE (WM_USER + 565)
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------- //
|
||||
// CSender.
|
||||
// --------------------------------------------------------------------------- //
|
||||
|
||||
CSender::CSender()
|
||||
{
|
||||
m_pSocket = NULL;
|
||||
m_pConsoleWnd = NULL;
|
||||
}
|
||||
|
||||
CSender::~CSender()
|
||||
{
|
||||
if ( m_pSocket )
|
||||
m_pSocket->Release();
|
||||
|
||||
if ( m_pConsoleWnd )
|
||||
m_pConsoleWnd->Release();
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CMessageWatchDlg dialog
|
||||
|
||||
CMessageWatchDlg::CMessageWatchDlg(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(CMessageWatchDlg::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(CMessageWatchDlg)
|
||||
// NOTE: the ClassWizard will add member initialization here
|
||||
//}}AFX_DATA_INIT
|
||||
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
|
||||
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
|
||||
|
||||
m_pListenSocket = NULL;
|
||||
}
|
||||
|
||||
CMessageWatchDlg::~CMessageWatchDlg()
|
||||
{
|
||||
// destroy the sender objects.
|
||||
|
||||
if ( m_pListenSocket )
|
||||
m_pListenSocket->Release();
|
||||
}
|
||||
|
||||
void CMessageWatchDlg::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(CMessageWatchDlg)
|
||||
DDX_Control(pDX, IDC_MACHINES, m_Machines);
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(CMessageWatchDlg, CDialog)
|
||||
//{{AFX_MSG_MAP(CMessageWatchDlg)
|
||||
ON_MESSAGE(WM_STARTIDLE, OnStartIdle)
|
||||
ON_WM_PAINT()
|
||||
ON_WM_QUERYDRAGICON()
|
||||
ON_LBN_DBLCLK(IDC_MACHINES, OnDblclkMachines)
|
||||
ON_BN_CLICKED(IDSHOWALL, OnShowall)
|
||||
ON_BN_CLICKED(IDHIDEALL, OnHideall)
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CMessageWatchDlg message handlers
|
||||
|
||||
BOOL CMessageWatchDlg::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
// Set the icon for this dialog. The framework does this automatically
|
||||
// when the application's main window is not a dialog
|
||||
SetIcon(m_hIcon, TRUE); // Set big icon
|
||||
SetIcon(m_hIcon, FALSE); // Set small icon
|
||||
|
||||
// Setup our listen socket and thread.
|
||||
m_pListenSocket = CreateIPSocket();
|
||||
m_pListenSocket->BindToAny( MSGMGR_BROADCAST_PORT );
|
||||
|
||||
m_cWinIdle.StartIdle( GetSafeHwnd(), WM_STARTIDLE, 0, 0, 100 );
|
||||
m_cWinIdle.NextIdle();
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
}
|
||||
|
||||
|
||||
LONG CMessageWatchDlg::OnStartIdle( UINT, LONG )
|
||||
{
|
||||
MSG msg;
|
||||
if (!PeekMessage(&msg, GetSafeHwnd(), 0,0, PM_NOREMOVE))
|
||||
OnIdle();
|
||||
m_cWinIdle.NextIdle();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void CMessageWatchDlg::OnIdle()
|
||||
{
|
||||
// Kill dead connections.
|
||||
int iNext;
|
||||
for ( int iSender=m_Senders.Head(); iSender != m_Senders.InvalidIndex(); iSender = iNext )
|
||||
{
|
||||
iNext = m_Senders.Next( iSender );
|
||||
|
||||
CSender *pSender = m_Senders[iSender];
|
||||
if ( pSender->m_pSocket && !pSender->m_pSocket->IsConnected() )
|
||||
{
|
||||
// Just release the socket so the text stays there.
|
||||
pSender->m_pSocket->Release();
|
||||
pSender->m_pSocket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for new connections.
|
||||
while ( 1 )
|
||||
{
|
||||
CIPAddr ipFrom;
|
||||
char data[16];
|
||||
int len = m_pListenSocket->RecvFrom( data, sizeof( data ), &ipFrom );
|
||||
if ( len == -1 )
|
||||
break;
|
||||
|
||||
if ( data[0] == MSGMGR_PACKETID_ANNOUNCE_PRESENCE &&
|
||||
*((int*)&data[1]) == MSGMGR_VERSION )
|
||||
{
|
||||
int iPort = *((int*)&data[5]);
|
||||
|
||||
// See if we have a machine with this info yet.
|
||||
CIPAddr connectAddr = ipFrom;
|
||||
connectAddr.port = iPort;
|
||||
|
||||
// NOTE: we'll accept connections from machines we were connected to earlier but
|
||||
// lost the connection to.
|
||||
CSender *pSender = FindSenderByAddr( ipFrom.ip );
|
||||
if ( !pSender || !pSender->m_pSocket )
|
||||
{
|
||||
// 'nitiate the connection.
|
||||
ITCPSocket *pNew = CreateTCPSocket();
|
||||
if ( pNew->BindToAny( 0 ) && TCPSocket_Connect( pNew, &connectAddr, 1000 ) )
|
||||
{
|
||||
char nameStr[256];
|
||||
char title[512];
|
||||
if ( !ConvertIPAddrToString( &ipFrom, nameStr, sizeof( nameStr ) ) )
|
||||
Q_snprintf( nameStr, sizeof( nameStr ), "%d.%d.%d.%d", ipFrom.ip[0], ipFrom.ip[1], ipFrom.ip[2], ipFrom.ip[3] );
|
||||
|
||||
Q_snprintf( title, sizeof( title ), "%s:%d", nameStr, iPort );
|
||||
|
||||
// If the sender didn't exist yet, add a new one.
|
||||
if ( !pSender )
|
||||
{
|
||||
pSender = new CSender;
|
||||
|
||||
IConsoleWnd *pWnd = CreateConsoleWnd(
|
||||
AfxGetInstanceHandle(),
|
||||
IDD_OUTPUT,
|
||||
IDC_DEBUG_OUTPUT,
|
||||
false
|
||||
);
|
||||
|
||||
pSender->m_pConsoleWnd = pWnd;
|
||||
pWnd->SetTitle( title );
|
||||
|
||||
Q_strncpy( pSender->m_Name, title, sizeof( pSender->m_Name ) );
|
||||
m_Senders.AddToTail( pSender );
|
||||
m_Machines.AddString( pSender->m_Name );
|
||||
}
|
||||
|
||||
pSender->m_Addr = connectAddr;
|
||||
pSender->m_pSocket = pNew;
|
||||
}
|
||||
else
|
||||
{
|
||||
pNew->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Read input from our current connections.
|
||||
FOR_EACH_LL( m_Senders, i )
|
||||
{
|
||||
CSender *pSender = m_Senders[i];
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if ( !pSender->m_pSocket )
|
||||
break;
|
||||
|
||||
CUtlVector<unsigned char> data;
|
||||
if ( !pSender->m_pSocket->Recv( data ) )
|
||||
break;
|
||||
|
||||
if ( data[0] == MSGMGR_PACKETID_MSG )
|
||||
{
|
||||
char *pMsg = (char*)&data[1];
|
||||
pSender->m_pConsoleWnd->PrintToConsole( pMsg );
|
||||
OutputDebugString( pMsg );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CMessageWatchDlg::OnDestroy()
|
||||
{
|
||||
// Stop the idling thread
|
||||
m_cWinIdle.EndIdle();
|
||||
CDialog::OnDestroy();
|
||||
}
|
||||
|
||||
|
||||
CSender* CMessageWatchDlg::FindSenderByAddr( const unsigned char ip[4] )
|
||||
{
|
||||
FOR_EACH_LL( m_Senders, i )
|
||||
{
|
||||
if ( memcmp( m_Senders[i]->m_Addr.ip, ip, 4 ) == 0 )
|
||||
return m_Senders[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
CSender* CMessageWatchDlg::FindSenderByName( const char *pName )
|
||||
{
|
||||
FOR_EACH_LL( m_Senders, i )
|
||||
{
|
||||
if ( stricmp( pName, m_Senders[i]->m_Name ) == 0 )
|
||||
return m_Senders[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// If you add a minimize button to your dialog, you will need the code below
|
||||
// to draw the icon. For MFC applications using the document/view model,
|
||||
// this is automatically done for you by the framework.
|
||||
|
||||
void CMessageWatchDlg::OnPaint()
|
||||
{
|
||||
if (IsIconic())
|
||||
{
|
||||
CPaintDC dc(this); // device context for painting
|
||||
|
||||
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
|
||||
|
||||
// Center icon in client rectangle
|
||||
int cxIcon = GetSystemMetrics(SM_CXICON);
|
||||
int cyIcon = GetSystemMetrics(SM_CYICON);
|
||||
CRect rect;
|
||||
GetClientRect(&rect);
|
||||
int x = (rect.Width() - cxIcon + 1) / 2;
|
||||
int y = (rect.Height() - cyIcon + 1) / 2;
|
||||
|
||||
// Draw the icon
|
||||
dc.DrawIcon(x, y, m_hIcon);
|
||||
}
|
||||
else
|
||||
{
|
||||
CDialog::OnPaint();
|
||||
}
|
||||
}
|
||||
|
||||
// The system calls this to obtain the cursor to display while the user drags
|
||||
// the minimized window.
|
||||
HCURSOR CMessageWatchDlg::OnQueryDragIcon()
|
||||
{
|
||||
return (HCURSOR) m_hIcon;
|
||||
}
|
||||
|
||||
void CMessageWatchDlg::OnDblclkMachines()
|
||||
{
|
||||
int index = m_Machines.GetCurSel();
|
||||
if ( index != LB_ERR )
|
||||
{
|
||||
CString str;
|
||||
m_Machines.GetText( index, str );
|
||||
|
||||
CSender *pSender = FindSenderByName( str );
|
||||
if ( pSender )
|
||||
pSender->m_pConsoleWnd->SetVisible( true );
|
||||
}
|
||||
}
|
||||
|
||||
void CMessageWatchDlg::OnShowall()
|
||||
{
|
||||
FOR_EACH_LL( m_Senders, i )
|
||||
{
|
||||
m_Senders[i]->m_pConsoleWnd->SetVisible( true );
|
||||
}
|
||||
}
|
||||
|
||||
void CMessageWatchDlg::OnHideall()
|
||||
{
|
||||
FOR_EACH_LL( m_Senders, i )
|
||||
{
|
||||
m_Senders[i]->m_pConsoleWnd->SetVisible( false );
|
||||
}
|
||||
}
|
||||
100
utils/vmpi/testapps/MessageWatch/MessageWatchDlg.h
Normal file
100
utils/vmpi/testapps/MessageWatch/MessageWatchDlg.h
Normal file
@@ -0,0 +1,100 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// MessageWatchDlg.h : header file
|
||||
//
|
||||
|
||||
#if !defined(AFX_MESSAGEWATCHDLG_H__AB9CEAF4_0166_4CCA_9DEC_77C0918F78C4__INCLUDED_)
|
||||
#define AFX_MESSAGEWATCHDLG_H__AB9CEAF4_0166_4CCA_9DEC_77C0918F78C4__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
|
||||
#include "iphelpers.h"
|
||||
#include "tcpsocket.h"
|
||||
#include "threadhelpers.h"
|
||||
#include "consolewnd.h"
|
||||
#include "win_idle.h"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CMessageWatchDlg dialog
|
||||
|
||||
class CSender
|
||||
{
|
||||
public:
|
||||
CSender();
|
||||
~CSender();
|
||||
|
||||
public:
|
||||
|
||||
CIPAddr m_Addr;
|
||||
ITCPSocket *m_pSocket;
|
||||
IConsoleWnd *m_pConsoleWnd;
|
||||
char m_Name[128];
|
||||
};
|
||||
|
||||
class CMessageWatchDlg : public CDialog
|
||||
{
|
||||
// Construction
|
||||
public:
|
||||
CMessageWatchDlg(CWnd* pParent = NULL); // standard constructor
|
||||
~CMessageWatchDlg();
|
||||
|
||||
|
||||
// Listen for broadcasts on this socket.
|
||||
ISocket *m_pListenSocket;
|
||||
|
||||
// Connections we've made.
|
||||
CUtlLinkedList<CSender*,int> m_Senders;
|
||||
|
||||
CCriticalSection m_SocketsCS;
|
||||
CWinIdle m_cWinIdle;
|
||||
|
||||
|
||||
CSender* FindSenderByAddr( const unsigned char ip[4] );
|
||||
CSender* FindSenderByName( const char *pName );
|
||||
|
||||
|
||||
// Dialog Data
|
||||
//{{AFX_DATA(CMessageWatchDlg)
|
||||
enum { IDD = IDD_MESSAGEWATCH_DIALOG };
|
||||
CListBox m_Machines;
|
||||
//}}AFX_DATA
|
||||
|
||||
// ClassWizard generated virtual function overrides
|
||||
//{{AFX_VIRTUAL(CMessageWatchDlg)
|
||||
protected:
|
||||
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
|
||||
//}}AFX_VIRTUAL
|
||||
|
||||
// Implementation
|
||||
protected:
|
||||
HICON m_hIcon;
|
||||
|
||||
void OnIdle();
|
||||
|
||||
// Generated message map functions
|
||||
//{{AFX_MSG(CMessageWatchDlg)
|
||||
afx_msg void OnDestroy();
|
||||
afx_msg LONG OnStartIdle(UINT, LONG);
|
||||
virtual BOOL OnInitDialog();
|
||||
afx_msg void OnPaint();
|
||||
afx_msg HCURSOR OnQueryDragIcon();
|
||||
afx_msg void OnDblclkMachines();
|
||||
afx_msg void OnShowall();
|
||||
afx_msg void OnHideall();
|
||||
//}}AFX_MSG
|
||||
DECLARE_MESSAGE_MAP()
|
||||
};
|
||||
|
||||
//{{AFX_INSERT_LOCATION}}
|
||||
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||
|
||||
#endif // !defined(AFX_MESSAGEWATCHDLG_H__AB9CEAF4_0166_4CCA_9DEC_77C0918F78C4__INCLUDED_)
|
||||
15
utils/vmpi/testapps/MessageWatch/StdAfx.cpp
Normal file
15
utils/vmpi/testapps/MessageWatch/StdAfx.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// MessageWatch.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
|
||||
|
||||
33
utils/vmpi/testapps/MessageWatch/StdAfx.h
Normal file
33
utils/vmpi/testapps/MessageWatch/StdAfx.h
Normal file
@@ -0,0 +1,33 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#if !defined(AFX_STDAFX_H__70653A1B_FB34_4AD9_861C_580071240D6F__INCLUDED_)
|
||||
#define AFX_STDAFX_H__70653A1B_FB34_4AD9_861C_580071240D6F__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
|
||||
|
||||
#include <afxwin.h> // MFC core and standard components
|
||||
#include <afxext.h> // MFC extensions
|
||||
#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
|
||||
#ifndef _AFX_NO_AFXCMN_SUPPORT
|
||||
#include <afxcmn.h> // MFC support for Windows Common Controls
|
||||
#endif // _AFX_NO_AFXCMN_SUPPORT
|
||||
|
||||
|
||||
//{{AFX_INSERT_LOCATION}}
|
||||
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||
|
||||
#endif // !defined(AFX_STDAFX_H__70653A1B_FB34_4AD9_861C_580071240D6F__INCLUDED_)
|
||||
BIN
utils/vmpi/testapps/MessageWatch/res/MessageWatch.ico
Normal file
BIN
utils/vmpi/testapps/MessageWatch/res/MessageWatch.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 766 B |
13
utils/vmpi/testapps/MessageWatch/res/MessageWatch.rc2
Normal file
13
utils/vmpi/testapps/MessageWatch/res/MessageWatch.rc2
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// MESSAGEWATCH.RC2 - resources Microsoft Visual C++ does not edit directly
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#error this file is not editable by Microsoft Visual C++
|
||||
#endif //APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Add manually edited resources here...
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
29
utils/vmpi/testapps/MessageWatch/resource.h
Normal file
29
utils/vmpi/testapps/MessageWatch/resource.h
Normal file
@@ -0,0 +1,29 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Developer Studio generated include file.
|
||||
// Used by MessageWatch.rc
|
||||
//
|
||||
#define IDSHOWALL 3
|
||||
#define IDHIDEALL 4
|
||||
#define IDD_MESSAGEWATCH_DIALOG 102
|
||||
#define IDR_MAINFRAME 128
|
||||
#define IDD_OUTPUT 129
|
||||
#define IDC_MACHINES 1000
|
||||
#define IDC_DEBUG_OUTPUT 1000
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 129
|
||||
#define _APS_NEXT_COMMAND_VALUE 32771
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
123
utils/vmpi/testapps/MessageWatch/win_idle.cpp
Normal file
123
utils/vmpi/testapps/MessageWatch/win_idle.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// Class for sending idle messages to a window
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "win_idle.h"
|
||||
|
||||
// Stub function to get into the object's main thread loop
|
||||
DWORD WINAPI CWinIdle::ThreadStub(LPVOID pIdle)
|
||||
{
|
||||
return ((CWinIdle *)pIdle)->RunIdle();
|
||||
}
|
||||
|
||||
CWinIdle::CWinIdle() :
|
||||
m_hIdleThread(NULL),
|
||||
m_hIdleEvent(NULL),
|
||||
m_hStopEvent(NULL),
|
||||
m_hWnd(0),
|
||||
m_uMsg(0),
|
||||
m_dwDelay(0)
|
||||
{
|
||||
}
|
||||
|
||||
CWinIdle::~CWinIdle()
|
||||
{
|
||||
if (m_hIdleThread)
|
||||
OutputDebugString("!!CWinIdle Warning!! Idle thread not shut down!\n");
|
||||
}
|
||||
|
||||
DWORD CWinIdle::RunIdle()
|
||||
{
|
||||
// Set up an event list
|
||||
HANDLE aEvents[2];
|
||||
|
||||
aEvents[0] = m_hStopEvent;
|
||||
aEvents[1] = m_hIdleEvent;
|
||||
|
||||
// Wait for a stop or idle event
|
||||
while (WaitForMultipleObjects(2, aEvents, FALSE, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
// Send an idle message
|
||||
PostMessage(m_hWnd, m_uMsg, m_wParam, m_lParam);
|
||||
// Wait for a bit...
|
||||
Sleep(m_dwDelay);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL CWinIdle::StartIdle(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam, DWORD dwDelay)
|
||||
{
|
||||
// Make sure it's not already running
|
||||
if (m_hIdleThread)
|
||||
return FALSE;
|
||||
|
||||
// Make sure they send in a valid handle..
|
||||
if (!hWnd)
|
||||
return FALSE;
|
||||
|
||||
// Create the events
|
||||
m_hIdleEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
m_hStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
// Make sure the events got created
|
||||
if ((!m_hIdleEvent) || (!m_hStopEvent))
|
||||
return FALSE;
|
||||
|
||||
// Create the thread
|
||||
DWORD dwThreadID;
|
||||
m_hIdleThread = CreateThread(NULL, 0, CWinIdle::ThreadStub, (void *)this, 0, &dwThreadID);
|
||||
|
||||
if (m_hIdleThread)
|
||||
{
|
||||
SetThreadPriority(m_hIdleThread, THREAD_PRIORITY_IDLE);
|
||||
|
||||
m_hWnd = hWnd;
|
||||
m_uMsg = uMessage;
|
||||
m_wParam = wParam;
|
||||
m_lParam = lParam;
|
||||
|
||||
m_dwDelay = dwDelay;
|
||||
}
|
||||
|
||||
return m_hIdleThread != 0;
|
||||
}
|
||||
|
||||
BOOL CWinIdle::EndIdle()
|
||||
{
|
||||
// Make sure it's running
|
||||
if (!m_hIdleThread)
|
||||
return FALSE;
|
||||
|
||||
// Stop the idle thread
|
||||
SetEvent(m_hStopEvent);
|
||||
WaitForSingleObject(m_hIdleThread, INFINITE);
|
||||
CloseHandle(m_hIdleThread);
|
||||
|
||||
// Get rid of the event objects
|
||||
CloseHandle(m_hIdleEvent);
|
||||
CloseHandle(m_hStopEvent);
|
||||
|
||||
// Set everything back to 0
|
||||
m_hIdleEvent = 0;
|
||||
m_hStopEvent = 0;
|
||||
m_hIdleThread = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CWinIdle::NextIdle()
|
||||
{
|
||||
// Make sure the thread's running
|
||||
if (!m_hIdleThread)
|
||||
return;
|
||||
|
||||
// Signal an idle message
|
||||
SetEvent(m_hIdleEvent);
|
||||
}
|
||||
78
utils/vmpi/testapps/MessageWatch/win_idle.h
Normal file
78
utils/vmpi/testapps/MessageWatch/win_idle.h
Normal file
@@ -0,0 +1,78 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// WinIdle.h - Defines a class for sending idle messages to a window from a secondary thread
|
||||
|
||||
#ifndef __WINIDLE_H__
|
||||
#define __WINIDLE_H__
|
||||
|
||||
|
||||
class CWinIdle
|
||||
{
|
||||
protected:
|
||||
HANDLE m_hIdleEvent, m_hStopEvent;
|
||||
|
||||
HWND m_hWnd;
|
||||
UINT m_uMsg;
|
||||
WPARAM m_wParam;
|
||||
LPARAM m_lParam;
|
||||
|
||||
DWORD m_dwDelay;
|
||||
|
||||
HANDLE m_hIdleThread;
|
||||
|
||||
// The thread calling stub
|
||||
static DWORD WINAPI ThreadStub(LPVOID pIdle);
|
||||
// The actual idle loop
|
||||
virtual DWORD RunIdle();
|
||||
|
||||
public:
|
||||
CWinIdle();
|
||||
virtual ~CWinIdle();
|
||||
|
||||
inline DWORD GetDelay() {return m_dwDelay;}
|
||||
inline void SetDelay(DWORD delay) {m_dwDelay = delay;}
|
||||
|
||||
// Member access
|
||||
virtual HANDLE GetThreadHandle() const { return m_hIdleThread; };
|
||||
|
||||
// Start idling, and define the message and window to use
|
||||
// Returns TRUE on success
|
||||
virtual BOOL StartIdle(HWND hWnd, UINT uMessage, WPARAM wParam = 0, LPARAM lParam = 0, DWORD dwDelay = 0);
|
||||
// Stop idling
|
||||
// Returns TRUE on success
|
||||
virtual BOOL EndIdle();
|
||||
// Notify the idle process that the message was received.
|
||||
// Note : If this function is not called, the idle thread will not send any messages
|
||||
virtual void NextIdle();
|
||||
};
|
||||
|
||||
|
||||
// Used to slow down the idle thread while dialogs are up.
|
||||
class IdleChanger
|
||||
{
|
||||
public:
|
||||
IdleChanger(CWinIdle *pIdle, DWORD msDelay)
|
||||
{
|
||||
m_pIdle = pIdle;
|
||||
m_OldDelay = pIdle->GetDelay();
|
||||
pIdle->SetDelay(msDelay);
|
||||
}
|
||||
|
||||
~IdleChanger()
|
||||
{
|
||||
m_pIdle->SetDelay(m_OldDelay);
|
||||
}
|
||||
|
||||
CWinIdle *m_pIdle;
|
||||
DWORD m_OldDelay;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //__WINIDLE_H__
|
||||
|
||||
15
utils/vmpi/testapps/ThreadedTCPSocketTest/StdAfx.cpp
Normal file
15
utils/vmpi/testapps/ThreadedTCPSocketTest/StdAfx.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// ThreadedTCPSocketTest.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
31
utils/vmpi/testapps/ThreadedTCPSocketTest/StdAfx.h
Normal file
31
utils/vmpi/testapps/ThreadedTCPSocketTest/StdAfx.h
Normal file
@@ -0,0 +1,31 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#if !defined(AFX_STDAFX_H__AA10D99C_786F_4324_86C6_4D7CDE546561__INCLUDED_)
|
||||
#define AFX_STDAFX_H__AA10D99C_786F_4324_86C6_4D7CDE546561__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <conio.h>
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
|
||||
//{{AFX_INSERT_LOCATION}}
|
||||
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||
|
||||
#endif // !defined(AFX_STDAFX_H__AA10D99C_786F_4324_86C6_4D7CDE546561__INCLUDED_)
|
||||
@@ -0,0 +1,198 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// ThreadedTCPSocketTest.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "IThreadedTCPSocket.h"
|
||||
#include "threadhelpers.h"
|
||||
#include "vstdlib/random.h"
|
||||
|
||||
|
||||
CCriticalSection g_MsgCS;
|
||||
|
||||
|
||||
IThreadedTCPSocket *g_pClientSocket = NULL;
|
||||
IThreadedTCPSocket *g_pServerSocket = NULL;
|
||||
|
||||
CEvent g_ClientPacketEvent;
|
||||
CUtlVector<char> g_ClientPacket;
|
||||
|
||||
|
||||
|
||||
SpewRetval_t MySpewFunc( SpewType_t type, char const *pMsg )
|
||||
{
|
||||
CCriticalSectionLock csLock( &g_MsgCS );
|
||||
csLock.Lock();
|
||||
|
||||
printf( "%s", pMsg );
|
||||
OutputDebugString( pMsg );
|
||||
|
||||
csLock.Unlock();
|
||||
|
||||
if( type == SPEW_ASSERT )
|
||||
return SPEW_DEBUGGER;
|
||||
else if( type == SPEW_ERROR )
|
||||
return SPEW_ABORT;
|
||||
else
|
||||
return SPEW_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
class CHandler_Server : public ITCPSocketHandler
|
||||
{
|
||||
public:
|
||||
virtual void Init( IThreadedTCPSocket *pSocket )
|
||||
{
|
||||
}
|
||||
|
||||
virtual void OnPacketReceived( CTCPPacket *pPacket )
|
||||
{
|
||||
// Echo the data back.
|
||||
g_pServerSocket->Send( pPacket->GetData(), pPacket->GetLen() );
|
||||
pPacket->Release();
|
||||
}
|
||||
|
||||
virtual void OnError( int errorCode, const char *pErrorString )
|
||||
{
|
||||
Msg( "Server error: %s\n", pErrorString );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CHandler_Client : public ITCPSocketHandler
|
||||
{
|
||||
public:
|
||||
virtual void Init( IThreadedTCPSocket *pSocket )
|
||||
{
|
||||
}
|
||||
|
||||
virtual void OnPacketReceived( CTCPPacket *pPacket )
|
||||
{
|
||||
if ( g_ClientPacket.Count() < pPacket->GetLen() )
|
||||
g_ClientPacket.SetSize( pPacket->GetLen() );
|
||||
|
||||
memcpy( g_ClientPacket.Base(), pPacket->GetData(), pPacket->GetLen() );
|
||||
g_ClientPacketEvent.SetEvent();
|
||||
pPacket->Release();
|
||||
}
|
||||
|
||||
virtual void OnError( int errorCode, const char *pErrorString )
|
||||
{
|
||||
Msg( "Client error: %s\n", pErrorString );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CHandlerCreator_Server : public IHandlerCreator
|
||||
{
|
||||
public:
|
||||
virtual ITCPSocketHandler* CreateNewHandler()
|
||||
{
|
||||
return new CHandler_Server;
|
||||
}
|
||||
};
|
||||
|
||||
class CHandlerCreator_Client : public IHandlerCreator
|
||||
{
|
||||
public:
|
||||
virtual ITCPSocketHandler* CreateNewHandler()
|
||||
{
|
||||
return new CHandler_Client;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
SpewOutputFunc( MySpewFunc );
|
||||
|
||||
// Figure out a random port to use.
|
||||
CCycleCount cnt;
|
||||
cnt.Sample();
|
||||
CUniformRandomStream randomStream;
|
||||
randomStream.SetSeed( cnt.GetMicroseconds() );
|
||||
int iPort = randomStream.RandomInt( 20000, 30000 );
|
||||
|
||||
|
||||
g_ClientPacketEvent.Init( false, false );
|
||||
|
||||
|
||||
// Setup the "server".
|
||||
CHandlerCreator_Server serverHandler;
|
||||
CIPAddr addr( 127, 0, 0, 1, iPort );
|
||||
|
||||
ITCPConnectSocket *pListener = ThreadedTCP_CreateListener(
|
||||
&serverHandler,
|
||||
(unsigned short)iPort );
|
||||
|
||||
|
||||
// Setup the "client".
|
||||
CHandlerCreator_Client clientCreator;
|
||||
ITCPConnectSocket *pConnector = ThreadedTCP_CreateConnector(
|
||||
CIPAddr( 127, 0, 0, 1, iPort ),
|
||||
CIPAddr(),
|
||||
&clientCreator );
|
||||
|
||||
|
||||
// Wait for them to connect.
|
||||
while ( !g_pClientSocket )
|
||||
{
|
||||
if ( !pConnector->Update( &g_pClientSocket ) )
|
||||
{
|
||||
Error( "Error in client connector!\n" );
|
||||
}
|
||||
}
|
||||
pConnector->Release();
|
||||
|
||||
|
||||
while ( !g_pServerSocket )
|
||||
{
|
||||
if ( !pListener->Update( &g_pServerSocket ) )
|
||||
Error( "Error in server connector!\n" );
|
||||
}
|
||||
pListener->Release();
|
||||
|
||||
|
||||
// Send some data.
|
||||
__int64 totalBytes = 0;
|
||||
CCycleCount startTime;
|
||||
int iPacket = 1;
|
||||
|
||||
startTime.Sample();
|
||||
CUtlVector<char> buf;
|
||||
|
||||
while ( (GetAsyncKeyState( VK_SHIFT ) & 0x8000) == 0 )
|
||||
{
|
||||
int size = randomStream.RandomInt( 1024*0, 1024*320 );
|
||||
if ( buf.Count() < size )
|
||||
buf.SetSize( size );
|
||||
|
||||
if ( g_pClientSocket->Send( buf.Base(), size ) )
|
||||
{
|
||||
// Server receives the data and echoes it back. Verify that the data is good.
|
||||
WaitForSingleObject( g_ClientPacketEvent.GetEventHandle(), INFINITE );
|
||||
Assert( memcmp( g_ClientPacket.Base(), buf.Base(), size ) == 0 );
|
||||
|
||||
totalBytes += size;
|
||||
CCycleCount curTime, elapsed;
|
||||
curTime.Sample();
|
||||
CCycleCount::Sub( curTime, startTime, elapsed );
|
||||
double flSeconds = elapsed.GetSeconds();
|
||||
Msg( "Packet %d, %d bytes, %dk/sec\n", iPacket++, size, (int)(((totalBytes+511)/1024) / flSeconds) );
|
||||
}
|
||||
}
|
||||
|
||||
g_pClientSocket->Release();
|
||||
g_pServerSocket->Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="7.10"
|
||||
Name="ThreadedTCPSocketTest"
|
||||
ProjectGUID="{6973F221-D381-4569-901F-6D5311FB4CD5}"
|
||||
SccProjectName=""
|
||||
SccAuxPath=""
|
||||
SccLocalPath=""
|
||||
SccProvider="">
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"/>
|
||||
</Platforms>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory=".\Debug"
|
||||
IntermediateDirectory=".\Debug"
|
||||
ConfigurationType="1"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..,..\..\..\public,..\..\..\public\tier1"
|
||||
PreprocessorDefinitions="_DEBUG;WIN32;_CONSOLE;PROTECTED_THINGS_DISABLE"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
PrecompiledHeaderFile=".\Debug/ThreadedTCPSocketTest.pch"
|
||||
AssemblerListingLocation=".\Debug/"
|
||||
ObjectFile=".\Debug/"
|
||||
ProgramDataBaseFileName=".\Debug/"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="4"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib odbc32.lib odbccp32.lib"
|
||||
OutputFile=".\Debug/ThreadedTCPSocketTest.exe"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
GenerateDebugInformation="TRUE"
|
||||
ProgramDatabaseFile=".\Debug/ThreadedTCPSocketTest.pdb"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TypeLibraryName=".\Debug/ThreadedTCPSocketTest.tlb"
|
||||
HeaderFileName=""/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory=".\Release"
|
||||
IntermediateDirectory=".\Release"
|
||||
ConfigurationType="1"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="1"
|
||||
AdditionalIncludeDirectories="..,..\..\..\public,..\..\..\public\tier1"
|
||||
PreprocessorDefinitions="NDEBUG;WIN32;_CONSOLE;PROTECTED_THINGS_DISABLE"
|
||||
StringPooling="TRUE"
|
||||
RuntimeLibrary="0"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
PrecompiledHeaderFile=".\Release/ThreadedTCPSocketTest.pch"
|
||||
AssemblerListingLocation=".\Release/"
|
||||
ObjectFile=".\Release/"
|
||||
ProgramDataBaseFileName=".\Release/"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib odbc32.lib odbccp32.lib"
|
||||
OutputFile=".\Release/ThreadedTCPSocketTest.exe"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
GenerateDebugInformation="TRUE"
|
||||
ProgramDatabaseFile=".\Release/ThreadedTCPSocketTest.pdb"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TypeLibraryName=".\Release/ThreadedTCPSocketTest.tlb"
|
||||
HeaderFileName=""/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="NDEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
|
||||
<File
|
||||
RelativePath="StdAfx.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\ThreadedTCPSocket.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\ThreadedTCPSocketEmu.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ThreadedTCPSocketTest.cpp">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl">
|
||||
<File
|
||||
RelativePath="..\IThreadedTCPSocket.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="StdAfx.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\ThreadedTCPSocketEmu.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
|
||||
</Filter>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\dbg.lib">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\platform.lib">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="ReadMe.txt">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\vmpi.lib">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\vstdlib.lib">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
15
utils/vmpi/testapps/pingpong/StdAfx.cpp
Normal file
15
utils/vmpi/testapps/pingpong/StdAfx.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// pingpong.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
29
utils/vmpi/testapps/pingpong/StdAfx.h
Normal file
29
utils/vmpi/testapps/pingpong/StdAfx.h
Normal file
@@ -0,0 +1,29 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#if !defined(AFX_STDAFX_H__13A3CFA6_6BF8_45A8_A2CD_91444DCFF7C0__INCLUDED_)
|
||||
#define AFX_STDAFX_H__13A3CFA6_6BF8_45A8_A2CD_91444DCFF7C0__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
|
||||
//{{AFX_INSERT_LOCATION}}
|
||||
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||
|
||||
#endif // !defined(AFX_STDAFX_H__13A3CFA6_6BF8_45A8_A2CD_91444DCFF7C0__INCLUDED_)
|
||||
308
utils/vmpi/testapps/pingpong/pingpong.cpp
Normal file
308
utils/vmpi/testapps/pingpong/pingpong.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// pingpong.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "tcpsocket.h"
|
||||
#include "tier0/fasttimer.h"
|
||||
#include "vmpi.h"
|
||||
#include "tcpsocket_helpers.h"
|
||||
|
||||
//#define USE_MPI
|
||||
|
||||
|
||||
#if defined( USE_MPI )
|
||||
#include "mpi/mpi.h"
|
||||
#include "vmpi.h"
|
||||
#include "tier1/bitbuf.h"
|
||||
|
||||
int myProcId = -1;
|
||||
#else
|
||||
IChannel *g_pSocket = NULL;
|
||||
int g_iPortNum = 27141;
|
||||
#endif
|
||||
|
||||
|
||||
int PrintUsage()
|
||||
{
|
||||
printf( "pingpong <-server or -client ip>\n" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void DoClientConnect( const char *pIP )
|
||||
{
|
||||
#if defined( USE_MPI )
|
||||
int argc = 1;
|
||||
char *testargv[1] = { "-nounc" };
|
||||
char **argv = testargv;
|
||||
if ( MPI_Init( &argc, &argv ) )
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
MPI_Comm_rank( MPI_COMM_WORLD, &myProcId );
|
||||
|
||||
int nProcs;
|
||||
MPI_Comm_size( MPI_COMM_WORLD, &nProcs );
|
||||
if ( nProcs != 2 )
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
#else
|
||||
// Try to connect, or listen.
|
||||
ITCPSocket *pTCPSocket = CreateTCPSocket();
|
||||
if ( !pTCPSocket->BindToAny( 0 ) )
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
|
||||
CIPAddr addr;
|
||||
if ( !ConvertStringToIPAddr( pIP, &addr ) )
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
|
||||
addr.port = g_iPortNum;
|
||||
printf( "Client connecting to %d.%d.%d.%d:%d\n", addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], addr.port );
|
||||
if ( !TCPSocket_Connect( pTCPSocket, &addr, 50000 ) )
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
|
||||
printf( "Client connected...\n ");
|
||||
g_pSocket = pTCPSocket;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void DoServerConnect()
|
||||
{
|
||||
#if defined( USE_MPI )
|
||||
ISocket *pSocket = CreateIPSocket();
|
||||
if ( !pSocket )
|
||||
{
|
||||
printf( "Error creating a socket.\n" );
|
||||
assert( false );
|
||||
return;
|
||||
}
|
||||
else if ( !pSocket->BindToAny( VMPI_SERVICE_PORT ) )
|
||||
{
|
||||
printf( "Error binding a socket to port %d.\n", VMPI_SERVICE_PORT );
|
||||
assert( false );
|
||||
return;
|
||||
}
|
||||
|
||||
printf( "Waiting for jobs...\n" );
|
||||
while ( 1 )
|
||||
{
|
||||
// Any incoming packets?
|
||||
char data[2048];
|
||||
CIPAddr ipFrom;
|
||||
int len = pSocket->RecvFrom( data, sizeof( data ), &ipFrom );
|
||||
if ( len > 3 )
|
||||
{
|
||||
bf_read buf( data, len );
|
||||
if ( buf.ReadByte() == VMPI_PROTOCOL_VERSION )
|
||||
{
|
||||
if ( buf.ReadByte() == VMPI_LOOKING_FOR_WORKERS )
|
||||
{
|
||||
// Read the listen port.
|
||||
int iListenPort = buf.ReadLong();
|
||||
|
||||
static char ipString[128];
|
||||
_snprintf( ipString, sizeof( ipString ), "%d.%d.%d.%d:%d", ipFrom.ip[0], ipFrom.ip[1], ipFrom.ip[2], ipFrom.ip[3], iListenPort );
|
||||
|
||||
int argc = 3;
|
||||
char *testargv[3];
|
||||
testargv[0] = "<supposedly the executable name!>";
|
||||
testargv[1] = "-mpi_worker";
|
||||
testargv[2] = ipString;
|
||||
|
||||
char **argv = testargv;
|
||||
if ( MPI_Init( &argc, &argv ) )
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
MPI_Comm_rank( MPI_COMM_WORLD, &myProcId );
|
||||
|
||||
int nProcs;
|
||||
MPI_Comm_size( MPI_COMM_WORLD, &nProcs );
|
||||
if ( nProcs != 2 )
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sleep( 100 );
|
||||
}
|
||||
|
||||
pSocket->Release();
|
||||
#else
|
||||
// Try to connect, or listen.
|
||||
ITCPListenSocket *pListen = CreateTCPListenSocket( g_iPortNum );
|
||||
if ( !pListen )
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
|
||||
printf( "Server listening...\n" );
|
||||
|
||||
CIPAddr addr;
|
||||
ITCPSocket *pTCPSocket = TCPSocket_ListenForOneConnection( pListen, &addr, 50000 );
|
||||
if ( !pTCPSocket )
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
pListen->Release();
|
||||
|
||||
printf( "Server connected...\n ");
|
||||
g_pSocket = pTCPSocket;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void SendData( const void *pBuf, int size )
|
||||
{
|
||||
#if defined( USE_MPI )
|
||||
MPI_Send( (void*)pBuf, size, MPI_BYTE, !myProcId, 0, MPI_COMM_WORLD );
|
||||
#else
|
||||
g_pSocket->Send( pBuf, size );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void RecvData( CUtlVector<unsigned char> &recvBuf )
|
||||
{
|
||||
#if defined( USE_MPI )
|
||||
MPI_Status stat;
|
||||
MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &stat);
|
||||
|
||||
recvBuf.SetCount( stat.count );
|
||||
MPI_Recv( recvBuf.Base(), stat.count, MPI_BYTE, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &stat);
|
||||
#else
|
||||
if ( !g_pSocket->Recv( recvBuf, 50000 ) )
|
||||
{
|
||||
g_pSocket->Release();
|
||||
g_pSocket = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
if ( argc < 2 )
|
||||
{
|
||||
return PrintUsage();
|
||||
}
|
||||
|
||||
const char *pClientOrServer = argv[1];
|
||||
const char *pIP = NULL;
|
||||
|
||||
bool bClient = false;
|
||||
if ( stricmp( pClientOrServer, "-client" ) == 0 )
|
||||
{
|
||||
if ( argc < 3 )
|
||||
{
|
||||
return PrintUsage();
|
||||
}
|
||||
|
||||
bClient = true;
|
||||
pIP = argv[2];
|
||||
}
|
||||
|
||||
CUtlVector<unsigned char> recvBuf;
|
||||
if ( bClient )
|
||||
{
|
||||
DoClientConnect( pIP );
|
||||
|
||||
// Ok, now start blasting packets of different sizes and measure how long it takes to get an ack back.
|
||||
int nIterations = 30;
|
||||
|
||||
for ( int size=350; size <= 350000; size += 512 )
|
||||
{
|
||||
CUtlVector<unsigned char> buf;
|
||||
buf.SetCount( size );
|
||||
|
||||
double flTotalRoundTripTime = 0;
|
||||
|
||||
CFastTimer throughputTimer;
|
||||
throughputTimer.Start();
|
||||
|
||||
for ( int i=0; i < nIterations; i++ )
|
||||
{
|
||||
for ( int z=0; z < size; z++ )
|
||||
buf[z] = (char)rand();
|
||||
|
||||
SendData( buf.Base(), buf.Count() );
|
||||
|
||||
CFastTimer timer;
|
||||
timer.Start();
|
||||
RecvData( recvBuf );
|
||||
timer.End();
|
||||
|
||||
|
||||
// Make sure we got the same data back.
|
||||
assert( recvBuf.Count() == buf.Count() );
|
||||
for ( z=0; z < size; z++ )
|
||||
{
|
||||
assert( recvBuf[z] == buf[z] );
|
||||
}
|
||||
|
||||
|
||||
//if ( i % 100 == 0 )
|
||||
// printf( "%05d\n", i );
|
||||
printf( "%d\n", i );
|
||||
flTotalRoundTripTime += timer.GetDuration().GetMillisecondsF();
|
||||
}
|
||||
throughputTimer.End();
|
||||
double flTotalSeconds = throughputTimer.GetDuration().GetSeconds();
|
||||
|
||||
double flAvgRoundTripTime = flTotalRoundTripTime / nIterations;
|
||||
printf( "%d: %.2f ms per roundtrip (%d bytes/sec) sec: %.2f megs: %.2f\n",
|
||||
size,
|
||||
flAvgRoundTripTime,
|
||||
(int)((size*nIterations)/flTotalSeconds),
|
||||
flTotalSeconds,
|
||||
(double)(size*nIterations) / (1024*1024) );
|
||||
}
|
||||
|
||||
// Send an 'end' message to the server.
|
||||
int val = -1;
|
||||
SendData( &val, sizeof( val ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wait for a connection.
|
||||
DoServerConnect();
|
||||
|
||||
// Wait for packets and ack them.
|
||||
while ( 1 )
|
||||
{
|
||||
RecvData( recvBuf );
|
||||
if ( !g_pSocket )
|
||||
break;
|
||||
|
||||
if ( recvBuf.Count() < 4 )
|
||||
{
|
||||
assert( false );
|
||||
}
|
||||
|
||||
SendData( recvBuf.Base(), recvBuf.Count() );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
245
utils/vmpi/testapps/pingpong/pingpong.vcproj
Normal file
245
utils/vmpi/testapps/pingpong/pingpong.vcproj
Normal file
@@ -0,0 +1,245 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="7.10"
|
||||
Name="pingpong"
|
||||
ProjectGUID="{5E114A75-BC7C-4E3A-895D-47C097AFF02B}"
|
||||
SccProjectName=""
|
||||
SccAuxPath=""
|
||||
SccLocalPath=""
|
||||
SccProvider="">
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"/>
|
||||
</Platforms>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory=".\Release"
|
||||
IntermediateDirectory=".\Release"
|
||||
ConfigurationType="1"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="1"
|
||||
AdditionalIncludeDirectories="..\..\..\public,..\..\common,..\..\vmpi"
|
||||
PreprocessorDefinitions="NDEBUG;WIN32;_CONSOLE;PROTECTED_THINGS_DISABLE"
|
||||
StringPooling="TRUE"
|
||||
RuntimeLibrary="0"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
PrecompiledHeaderFile=".\Release/pingpong.pch"
|
||||
AssemblerListingLocation=".\Release/"
|
||||
ObjectFile=".\Release/"
|
||||
ProgramDataBaseFileName=".\Release/"
|
||||
WarningLevel="4"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib odbc32.lib odbccp32.lib"
|
||||
OutputFile=".\Release/pingpong.exe"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
IgnoreAllDefaultLibraries="FALSE"
|
||||
IgnoreDefaultLibraryNames="libcmtd.lib"
|
||||
GenerateDebugInformation="TRUE"
|
||||
ProgramDatabaseFile=".\Release/pingpong.pdb"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TypeLibraryName=".\Release/pingpong.tlb"
|
||||
HeaderFileName=""/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="NDEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory=".\Debug"
|
||||
IntermediateDirectory=".\Debug"
|
||||
ConfigurationType="1"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\..\public,..\..\common,..\..\vmpi"
|
||||
PreprocessorDefinitions="_DEBUG;WIN32;_CONSOLE;PROTECTED_THINGS_DISABLE"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
PrecompiledHeaderFile=".\Debug/pingpong.pch"
|
||||
AssemblerListingLocation=".\Debug/"
|
||||
ObjectFile=".\Debug/"
|
||||
ProgramDataBaseFileName=".\Debug/"
|
||||
WarningLevel="4"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="4"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib odbc32.lib odbccp32.lib"
|
||||
OutputFile=".\Debug/pingpong.exe"
|
||||
LinkIncremental="2"
|
||||
SuppressStartupBanner="TRUE"
|
||||
IgnoreDefaultLibraryNames="libcmt.lib"
|
||||
GenerateDebugInformation="TRUE"
|
||||
ProgramDatabaseFile=".\Debug/pingpong.pdb"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TypeLibraryName=".\Debug/pingpong.tlb"
|
||||
HeaderFileName=""/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
|
||||
<File
|
||||
RelativePath="..\..\..\public\tier0\memoverride.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="pingpong.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="StdAfx.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\tcpsocket.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\tcpsocket_helpers.cpp">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl">
|
||||
<File
|
||||
RelativePath="..\..\..\public\tier1\bitbuf.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\public\platform\fasttimer.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\common\ichannel.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\common\iphelpers.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\common\loopback_channel.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="StdAfx.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\common\tcpsocket.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\public\tier1\utlvector.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\common\vmpi.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
|
||||
</Filter>
|
||||
<File
|
||||
RelativePath="ReadMe.txt">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\tier0.lib">
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\tier1.lib">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\vmpi.lib">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\vstdlib.lib">
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
15
utils/vmpi/testapps/socket_stresstest/StdAfx.cpp
Normal file
15
utils/vmpi/testapps/socket_stresstest/StdAfx.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// socket_stresstest.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
33
utils/vmpi/testapps/socket_stresstest/StdAfx.h
Normal file
33
utils/vmpi/testapps/socket_stresstest/StdAfx.h
Normal file
@@ -0,0 +1,33 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#if !defined(AFX_STDAFX_H__7E382B26_1CB4_461A_8087_762358153941__INCLUDED_)
|
||||
#define AFX_STDAFX_H__7E382B26_1CB4_461A_8087_762358153941__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include "tcpsocket.h"
|
||||
#include <conio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
|
||||
//{{AFX_INSERT_LOCATION}}
|
||||
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||
|
||||
#endif // !defined(AFX_STDAFX_H__7E382B26_1CB4_461A_8087_762358153941__INCLUDED_)
|
||||
274
utils/vmpi/testapps/socket_stresstest/socket_stresstest.cpp
Normal file
274
utils/vmpi/testapps/socket_stresstest/socket_stresstest.cpp
Normal file
@@ -0,0 +1,274 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// socket_stresstest.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "utllinkedlist.h"
|
||||
|
||||
|
||||
class CSocketInfo
|
||||
{
|
||||
public:
|
||||
|
||||
bool IsValid()
|
||||
{
|
||||
return m_pSocket != 0;
|
||||
}
|
||||
|
||||
void Term();
|
||||
|
||||
void ThreadFn();
|
||||
|
||||
|
||||
|
||||
public:
|
||||
ITCPSocket *m_pSocket;
|
||||
int m_iListenPort;
|
||||
|
||||
DWORD m_CreateTime; // When this socket was created.
|
||||
DWORD m_ExpireTime;
|
||||
};
|
||||
|
||||
|
||||
|
||||
CSocketInfo g_Infos[132];
|
||||
CRITICAL_SECTION g_CS, g_PrintCS;
|
||||
HANDLE g_hThreads[ ARRAYSIZE( g_Infos ) ];
|
||||
bool g_bShouldExit = false;
|
||||
CUtlLinkedList<int,int> g_ListenPorts;
|
||||
|
||||
|
||||
SpewRetval_t StressTestSpew( SpewType_t type, char const *pMsg )
|
||||
{
|
||||
EnterCriticalSection( &g_PrintCS );
|
||||
printf( "%s", pMsg );
|
||||
LeaveCriticalSection( &g_PrintCS );
|
||||
|
||||
if( type == SPEW_ASSERT )
|
||||
return SPEW_DEBUGGER;
|
||||
else if( type == SPEW_ERROR )
|
||||
return SPEW_ABORT;
|
||||
else
|
||||
return SPEW_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
void CSocketInfo::Term()
|
||||
{
|
||||
if ( m_pSocket )
|
||||
{
|
||||
m_pSocket->Release();
|
||||
m_pSocket = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CSocketInfo* FindOldestSocketInfo( CSocketInfo *pInfos, int nInfos )
|
||||
{
|
||||
int iOldest = 0;
|
||||
DWORD oldestTime = 0xFFFFFFFF;
|
||||
for ( int i=0; i < nInfos; i++ )
|
||||
{
|
||||
if ( !pInfos[i].IsValid() )
|
||||
return &pInfos[i];
|
||||
|
||||
if ( pInfos[i].m_CreateTime < oldestTime )
|
||||
{
|
||||
oldestTime = pInfos[i].m_CreateTime;
|
||||
iOldest = i;
|
||||
}
|
||||
}
|
||||
return &pInfos[iOldest];
|
||||
}
|
||||
|
||||
|
||||
int g_iNextExpire = -1;
|
||||
|
||||
void CSocketInfo::ThreadFn()
|
||||
{
|
||||
int iInfo = this - g_Infos;
|
||||
|
||||
while ( !g_bShouldExit )
|
||||
{
|
||||
DWORD curTime = GetTickCount();
|
||||
|
||||
// Break the connection after a certain amount of time.
|
||||
if ( m_pSocket && curTime >= m_ExpireTime )
|
||||
{
|
||||
Term();
|
||||
Msg( "%02d: expire.\n", iInfo, m_iListenPort );
|
||||
}
|
||||
|
||||
if ( m_pSocket )
|
||||
{
|
||||
EnterCriticalSection( &g_CS );
|
||||
if ( g_iNextExpire == -1 )
|
||||
{
|
||||
g_iNextExpire = iInfo;
|
||||
LeaveCriticalSection( &g_CS );
|
||||
|
||||
Msg( "%02d: forcing an expire.\n", iInfo, m_iListenPort );
|
||||
Sleep( 16000 );
|
||||
|
||||
EnterCriticalSection( &g_CS );
|
||||
g_iNextExpire = -1;
|
||||
}
|
||||
LeaveCriticalSection( &g_CS );
|
||||
|
||||
if ( m_pSocket->IsConnected() )
|
||||
{
|
||||
// Receive whatever data it has waiting for it.
|
||||
CUtlVector<unsigned char> data;
|
||||
while ( m_pSocket->Recv( data ) )
|
||||
{
|
||||
Msg( "%02d: recv %d.\n", iInfo, data.Count() );
|
||||
}
|
||||
|
||||
// Send some data.
|
||||
int size = rand() % 8192;
|
||||
data.SetSize( size );
|
||||
m_pSocket->Send( data.Base(), data.Count() );
|
||||
//Msg( "%02d: send %d.\n", iInfo, data.Count() );
|
||||
}
|
||||
else
|
||||
{
|
||||
Term();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not initialized.. either listen or connect.
|
||||
int iConnectPort = -1;
|
||||
if ( rand() > VALVE_RAND_MAX/2 )
|
||||
{
|
||||
if ( rand() % 100 < 50 )
|
||||
Sleep( 500 );
|
||||
|
||||
EnterCriticalSection( &g_CS );
|
||||
int iHead = g_ListenPorts.Head();
|
||||
if ( iHead != g_ListenPorts.InvalidIndex() )
|
||||
iConnectPort = g_ListenPorts[iHead];
|
||||
LeaveCriticalSection( &g_CS );
|
||||
}
|
||||
|
||||
if ( iConnectPort != -1 )
|
||||
{
|
||||
CIPAddr addr( 127, 0, 0, 1, iConnectPort );
|
||||
|
||||
m_pSocket = CreateTCPSocket();
|
||||
m_pSocket->BindToAny( 0 );
|
||||
m_CreateTime = curTime;
|
||||
m_ExpireTime = curTime + rand() % 5000;
|
||||
if ( !TCPSocket_Connect( m_pSocket, &addr, 3.0f ) )
|
||||
{
|
||||
Term();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int iTry=0; iTry < 32; iTry++ )
|
||||
{
|
||||
m_iListenPort = 100 + rand() % (VALVE_RAND_MAX/2);
|
||||
ITCPListenSocket *pListenSocket = CreateTCPListenSocket( m_iListenPort );
|
||||
if ( pListenSocket )
|
||||
{
|
||||
Msg( "%02d: listen on %d.\n", iInfo, m_iListenPort );
|
||||
|
||||
// Add us to the list of ports to connect to.
|
||||
EnterCriticalSection( &g_CS );
|
||||
g_ListenPorts.AddToTail( m_iListenPort );
|
||||
LeaveCriticalSection( &g_CS );
|
||||
|
||||
// Listen for a connection.
|
||||
CIPAddr connectedAddr;
|
||||
m_pSocket = TCPSocket_ListenForOneConnection( pListenSocket, &connectedAddr, 4.0 );
|
||||
|
||||
// Remove us from the list of ports to connect to.
|
||||
EnterCriticalSection( &g_CS );
|
||||
g_ListenPorts.Remove( g_ListenPorts.Find( m_iListenPort ) );
|
||||
LeaveCriticalSection( &g_CS );
|
||||
|
||||
pListenSocket->Release();
|
||||
|
||||
if ( m_pSocket )
|
||||
{
|
||||
Msg( "%02d: listen found connection.\n", iInfo );
|
||||
m_CreateTime = curTime;
|
||||
m_ExpireTime = curTime + rand() % 5000;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sleep( 1 );
|
||||
}
|
||||
|
||||
g_hThreads[iInfo] = 0;
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI ThreadFn( LPVOID lpParameter )
|
||||
{
|
||||
CSocketInfo *pInfo = (CSocketInfo*)lpParameter;
|
||||
pInfo->ThreadFn();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void AllocError( unsigned long size )
|
||||
{
|
||||
Assert( false );
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
memset( g_Infos, 0, sizeof( g_Infos ) );
|
||||
memset( g_hThreads, 0, sizeof( g_hThreads ) );
|
||||
|
||||
InitializeCriticalSection( &g_CS );
|
||||
InitializeCriticalSection( &g_PrintCS );
|
||||
|
||||
SpewOutputFunc( StressTestSpew );
|
||||
Plat_SetAllocErrorFn( AllocError );
|
||||
|
||||
SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS );
|
||||
|
||||
for ( int i=0; i < ARRAYSIZE( g_Infos ); i++ )
|
||||
{
|
||||
DWORD dwThreadID = 0;
|
||||
g_hThreads[i] = CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
ThreadFn,
|
||||
&g_Infos[i],
|
||||
0,
|
||||
&dwThreadID );
|
||||
}
|
||||
|
||||
|
||||
while ( !kbhit() )
|
||||
{
|
||||
}
|
||||
|
||||
g_bShouldExit = true;
|
||||
|
||||
HANDLE hZeroArray[ ARRAYSIZE( g_Infos ) ];
|
||||
memset( hZeroArray, 0, sizeof( hZeroArray ) );
|
||||
|
||||
while ( memcmp( hZeroArray, g_hThreads, sizeof( hZeroArray ) ) != 0 )
|
||||
{
|
||||
Sleep( 10 );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
210
utils/vmpi/testapps/socket_stresstest/socket_stresstest.vcproj
Normal file
210
utils/vmpi/testapps/socket_stresstest/socket_stresstest.vcproj
Normal file
@@ -0,0 +1,210 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="7.10"
|
||||
Name="socket_stresstest"
|
||||
ProjectGUID="{46DE8944-1A71-4A43-98FE-7A96CB8E5596}"
|
||||
SccProjectName=""
|
||||
SccAuxPath=""
|
||||
SccLocalPath=""
|
||||
SccProvider="">
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"/>
|
||||
</Platforms>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory=".\Debug"
|
||||
IntermediateDirectory=".\Debug"
|
||||
ConfigurationType="1"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..,..\..\common,..\..\..\public,..\..\..\public\tier1"
|
||||
PreprocessorDefinitions="_DEBUG;WIN32;_CONSOLE;PROTECTED_THINGS_DISABLE"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
PrecompiledHeaderFile=".\Debug/socket_stresstest.pch"
|
||||
AssemblerListingLocation=".\Debug/"
|
||||
ObjectFile=".\Debug/"
|
||||
ProgramDataBaseFileName=".\Debug/"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="4"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib odbc32.lib odbccp32.lib"
|
||||
OutputFile=".\Debug/socket_stresstest.exe"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
GenerateDebugInformation="TRUE"
|
||||
ProgramDatabaseFile=".\Debug/socket_stresstest.pdb"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TypeLibraryName=".\Debug/socket_stresstest.tlb"
|
||||
HeaderFileName=""/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory=".\Release"
|
||||
IntermediateDirectory=".\Release"
|
||||
ConfigurationType="1"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="1"
|
||||
AdditionalIncludeDirectories="..,..\..\common,..\..\..\public,..\..\..\public\tier1"
|
||||
PreprocessorDefinitions="NDEBUG;WIN32;_CONSOLE;PROTECTED_THINGS_DISABLE"
|
||||
StringPooling="TRUE"
|
||||
RuntimeLibrary="0"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
PrecompiledHeaderFile=".\Release/socket_stresstest.pch"
|
||||
AssemblerListingLocation=".\Release/"
|
||||
ObjectFile=".\Release/"
|
||||
ProgramDataBaseFileName=".\Release/"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib odbc32.lib odbccp32.lib"
|
||||
OutputFile=".\Release/socket_stresstest.exe"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
ProgramDatabaseFile=".\Release/socket_stresstest.pdb"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TypeLibraryName=".\Release/socket_stresstest.tlb"
|
||||
HeaderFileName=""/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="NDEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
|
||||
<File
|
||||
RelativePath="..\iphelpers.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="socket_stresstest.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="StdAfx.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\tcpsocket.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\threadhelpers.cpp">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl">
|
||||
<File
|
||||
RelativePath="StdAfx.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
|
||||
</Filter>
|
||||
<File
|
||||
RelativePath="ReadMe.txt">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\tier0.lib">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\vstdlib.lib">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
15
utils/vmpi/testapps/vmpi_launch/StdAfx.cpp
Normal file
15
utils/vmpi/testapps/vmpi_launch/StdAfx.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// vmpi_launch.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
30
utils/vmpi/testapps/vmpi_launch/StdAfx.h
Normal file
30
utils/vmpi/testapps/vmpi_launch/StdAfx.h
Normal file
@@ -0,0 +1,30 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#if !defined(AFX_STDAFX_H__00616BF6_B7E2_4D94_8DC8_3F85BEBD1834__INCLUDED_)
|
||||
#define AFX_STDAFX_H__00616BF6_B7E2_4D94_8DC8_3F85BEBD1834__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
|
||||
//{{AFX_INSERT_LOCATION}}
|
||||
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||
|
||||
#endif // !defined(AFX_STDAFX_H__00616BF6_B7E2_4D94_8DC8_3F85BEBD1834__INCLUDED_)
|
||||
256
utils/vmpi/testapps/vmpi_launch/vmpi_launch.cpp
Normal file
256
utils/vmpi/testapps/vmpi_launch/vmpi_launch.cpp
Normal file
@@ -0,0 +1,256 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// vmpi_launch.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "iphelpers.h"
|
||||
#include "bitbuf.h"
|
||||
#include "vmpi.h"
|
||||
|
||||
bool g_bBroadcast = false;
|
||||
|
||||
int PrintUsage()
|
||||
{
|
||||
printf( "vmpi_launch -machine <remote machine> -priority <priority> [-mpi_pw <password>] -command \"command line...\"\n" );
|
||||
printf( "-command must be the last switch..\n" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int GetCurMicrosecondsAndSleep( int sleepLen )
|
||||
{
|
||||
Sleep( sleepLen );
|
||||
|
||||
int retVal;
|
||||
__asm
|
||||
{
|
||||
rdtsc
|
||||
mov retVal, eax
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
const char* FindArg( int argc, char **argv, const char *pName, const char *pDefault )
|
||||
{
|
||||
for ( int i=0; i < argc; i++ )
|
||||
{
|
||||
if ( stricmp( argv[i], pName ) == 0 )
|
||||
{
|
||||
if ( (i+1) < argc )
|
||||
return argv[i+1];
|
||||
else
|
||||
return pDefault;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int ParseArgs( int argc, char **argv, CIPAddr &remoteIP, int &iPriority, int &iFirstArg )
|
||||
{
|
||||
if ( FindArg( argc, argv, "-broadcast", "1" ) )
|
||||
g_bBroadcast = true;
|
||||
|
||||
if ( g_bBroadcast == false )
|
||||
{
|
||||
const char *pRemoteIPStr = FindArg( argc, argv, "-machine", NULL );
|
||||
if ( !pRemoteIPStr || !ConvertStringToIPAddr( pRemoteIPStr, &remoteIP ) )
|
||||
{
|
||||
printf( "%s is not a valid machine name or IP address.\n", pRemoteIPStr );
|
||||
return PrintUsage();
|
||||
}
|
||||
}
|
||||
|
||||
iPriority = 0;
|
||||
const char *pPriorityStr = FindArg( argc, argv, "-priority", NULL );
|
||||
if ( pPriorityStr )
|
||||
iPriority = atoi( pPriorityStr );
|
||||
|
||||
if ( iPriority < 0 || iPriority > 1000 )
|
||||
{
|
||||
printf( "%s is not a valid priority.\n", pPriorityStr );
|
||||
return PrintUsage();
|
||||
}
|
||||
|
||||
const char *pCommand = FindArg( argc, argv, "-command", NULL );
|
||||
if ( !pCommand )
|
||||
{
|
||||
return PrintUsage();
|
||||
}
|
||||
for ( iFirstArg=1; iFirstArg < argc; iFirstArg++ )
|
||||
{
|
||||
if ( argv[iFirstArg] == pCommand )
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void SendJobRequest(
|
||||
ISocket *pSocket,
|
||||
int argc,
|
||||
char **argv,
|
||||
CIPAddr &remoteIP,
|
||||
int &iPriority,
|
||||
int &iFirstArg,
|
||||
int jobID[4] )
|
||||
{
|
||||
// Build the packet to send out the job.
|
||||
char packetData[4096];
|
||||
bf_write packetBuf;
|
||||
|
||||
// Come up with a unique job ID.
|
||||
jobID[0] = GetCurMicrosecondsAndSleep( 1 );
|
||||
jobID[1] = GetCurMicrosecondsAndSleep( 1 );
|
||||
jobID[2] = GetCurMicrosecondsAndSleep( 1 );
|
||||
jobID[3] = GetCurMicrosecondsAndSleep( 1 );
|
||||
|
||||
|
||||
// Broadcast out to tell all the machines we want workers.
|
||||
packetBuf.StartWriting( packetData, sizeof( packetData ) );
|
||||
packetBuf.WriteByte( VMPI_PROTOCOL_VERSION );
|
||||
|
||||
const char *pPassword = FindArg( argc, argv, "-mpi_pw", "" );
|
||||
packetBuf.WriteString( pPassword );
|
||||
|
||||
packetBuf.WriteByte( VMPI_LOOKING_FOR_WORKERS );
|
||||
|
||||
packetBuf.WriteShort( 0 ); // Tell the port that we're listening on.
|
||||
// In this case, there is no VMPI master waiting for the app to connect, so
|
||||
// this parameter doesn't matter.
|
||||
packetBuf.WriteShort( iPriority );
|
||||
|
||||
packetBuf.WriteLong( jobID[0] );
|
||||
packetBuf.WriteLong( jobID[1] );
|
||||
packetBuf.WriteLong( jobID[2] );
|
||||
packetBuf.WriteLong( jobID[3] );
|
||||
packetBuf.WriteWord( argc-iFirstArg ); // 1 command line argument..
|
||||
|
||||
// Write the alternate exe name.
|
||||
for ( int iArg=iFirstArg; iArg < argc; iArg++ )
|
||||
packetBuf.WriteString( argv[iArg] );
|
||||
|
||||
for ( int iBroadcastPort=VMPI_SERVICE_PORT; iBroadcastPort <= VMPI_LAST_SERVICE_PORT; iBroadcastPort++ )
|
||||
{
|
||||
remoteIP.port = iBroadcastPort;
|
||||
|
||||
if ( g_bBroadcast == false )
|
||||
pSocket->SendTo( &remoteIP, packetBuf.GetBasePointer(), packetBuf.GetNumBytesWritten() );
|
||||
else
|
||||
pSocket->Broadcast( packetBuf.GetBasePointer(), packetBuf.GetNumBytesWritten(), iBroadcastPort );
|
||||
}
|
||||
|
||||
if ( g_bBroadcast == false )
|
||||
printf( "Sent command, waiting for reply...\n" );
|
||||
else
|
||||
printf( "Sent command\n" );
|
||||
}
|
||||
|
||||
|
||||
bool WaitForJobStart( ISocket *pSocket, const CIPAddr &remoteIP, const int jobID[4] )
|
||||
{
|
||||
while ( 1 )
|
||||
{
|
||||
CIPAddr senderAddr;
|
||||
char data[4096];
|
||||
int len = -1;
|
||||
|
||||
if ( g_bBroadcast == false )
|
||||
pSocket->RecvFrom( data, sizeof( data ), &senderAddr );
|
||||
else
|
||||
pSocket->RecvFrom( data, sizeof( data ), NULL );
|
||||
|
||||
if ( len == 19 &&
|
||||
memcmp( senderAddr.ip, remoteIP.ip, sizeof( senderAddr.ip ) ) == 0 &&
|
||||
data[1] == VMPI_NOTIFY_START_STATUS &&
|
||||
memcmp( &data[2], jobID, 16 ) == 0 )
|
||||
{
|
||||
if ( data[18] == 0 )
|
||||
{
|
||||
// Wasn't able to run.
|
||||
printf( "Wasn't able to run on target machine.\n" );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ok, the process is running now.
|
||||
printf( "Process running, waiting for completion...\n" );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Sleep( 100 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WaitForJobEnd( ISocket *pSocket, const CIPAddr &remoteIP, const int jobID[4] )
|
||||
{
|
||||
while ( 1 )
|
||||
{
|
||||
CIPAddr senderAddr;
|
||||
char data[4096];
|
||||
int len = pSocket->RecvFrom( data, sizeof( data ), &senderAddr );
|
||||
if ( len == 18 &&
|
||||
memcmp( senderAddr.ip, remoteIP.ip, sizeof( senderAddr.ip ) ) == 0 &&
|
||||
data[1] == VMPI_NOTIFY_END_STATUS &&
|
||||
memcmp( &data[2], jobID, 16 ) == 0 )
|
||||
{
|
||||
int ret = *((int*)&data[2]);
|
||||
printf( "Finished [%d].\n", ret );
|
||||
break;
|
||||
}
|
||||
|
||||
Sleep( 100 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if ( argc < 4 )
|
||||
{
|
||||
return PrintUsage();
|
||||
}
|
||||
|
||||
|
||||
// Parse the command line.
|
||||
CIPAddr remoteIP;
|
||||
int iFirstArg, iPriority;
|
||||
int jobID[4];
|
||||
|
||||
int ret = ParseArgs( argc, argv, remoteIP, iPriority, iFirstArg );
|
||||
if ( ret != 0 )
|
||||
return ret;
|
||||
|
||||
// Now send the command to the vmpi service on that machine.
|
||||
ISocket *pSocket = CreateIPSocket();
|
||||
if ( !pSocket->BindToAny( 0 ) )
|
||||
{
|
||||
printf( "Error binding a socket.\n" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
SendJobRequest( pSocket, argc, argv, remoteIP, iPriority, iFirstArg, jobID );
|
||||
|
||||
// Wait for a reply, positive or negative.
|
||||
if ( g_bBroadcast == false )
|
||||
{
|
||||
if ( !WaitForJobStart( pSocket, remoteIP, jobID ) )
|
||||
return 2;
|
||||
|
||||
WaitForJobEnd( pSocket, remoteIP, jobID );
|
||||
}
|
||||
|
||||
pSocket->Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
38
utils/vmpi/testapps/vmpi_launch/vmpi_launch.vpc
Normal file
38
utils/vmpi/testapps/vmpi_launch/vmpi_launch.vpc
Normal file
@@ -0,0 +1,38 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// VMPI_LAUNCH.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR "..\..\..\.."
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_exe_con_win32_base.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE,.\,..\.."
|
||||
}
|
||||
|
||||
$Linker
|
||||
{
|
||||
$AdditionalDependencies "ws2_32.lib odbc32.lib odbccp32.lib"
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Vmpi_launch"
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$File "..\..\iphelpers.cpp"
|
||||
$File "StdAfx.cpp"
|
||||
$File "vmpi_launch.cpp"
|
||||
}
|
||||
|
||||
$Folder "Header Files"
|
||||
{
|
||||
$File "StdAfx.h"
|
||||
}
|
||||
}
|
||||
15
utils/vmpi/testapps/vmpi_ping/StdAfx.cpp
Normal file
15
utils/vmpi/testapps/vmpi_ping/StdAfx.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// vmpi_ping.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
30
utils/vmpi/testapps/vmpi_ping/StdAfx.h
Normal file
30
utils/vmpi/testapps/vmpi_ping/StdAfx.h
Normal file
@@ -0,0 +1,30 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#if !defined(AFX_STDAFX_H__04B9E767_FE9B_4F2A_84A3_D6B85737214E__INCLUDED_)
|
||||
#define AFX_STDAFX_H__04B9E767_FE9B_4F2A_84A3_D6B85737214E__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
|
||||
//{{AFX_INSERT_LOCATION}}
|
||||
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||
|
||||
#endif // !defined(AFX_STDAFX_H__04B9E767_FE9B_4F2A_84A3_D6B85737214E__INCLUDED_)
|
||||
196
utils/vmpi/testapps/vmpi_ping/vmpi_ping.cpp
Normal file
196
utils/vmpi/testapps/vmpi_ping/vmpi_ping.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// vmpi_ping.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "iphelpers.h"
|
||||
#include "vmpi.h"
|
||||
#include "tier0/platform.h"
|
||||
#include "bitbuf.h"
|
||||
#include <conio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
const char* FindArg( int argc, char **argv, const char *pName, const char *pDefault = "" )
|
||||
{
|
||||
for ( int i=0; i < argc; i++ )
|
||||
{
|
||||
if ( stricmp( argv[i], pName ) == 0 )
|
||||
{
|
||||
if ( (i+1) < argc )
|
||||
return argv[i+1];
|
||||
else
|
||||
return pDefault;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
CUtlVector<CIPAddr> addrs;
|
||||
|
||||
printf( "\n" );
|
||||
printf( "vmpi_ping <option>\n" );
|
||||
printf( "option can be:\n" );
|
||||
printf( " -stop .. stop any VMPI services\n" );
|
||||
printf( " -kill .. kill any processes being run by VMPI\n" );
|
||||
printf( " -patch <timeout> .. stops VMPI services for <timeout> seconds\n" );
|
||||
printf( " -mpi_pw <password> .. only talk to services with the specified password\n" );
|
||||
printf( " -dns .. enable DNS lookups (slows the listing down)\n" );
|
||||
printf( " -ShowConsole .. show the console window\n" );
|
||||
printf( " -HideConsole .. hide the console window\n" );
|
||||
|
||||
//Scary to show these to users...
|
||||
//printf( " -ShowCache .. show the cache directory and its capacity\n" );
|
||||
//printf( " -FlushCache .. flush the cache of ALL VMPI services\n" );
|
||||
|
||||
printf( "\n" );
|
||||
|
||||
|
||||
ISocket *pSocket = CreateIPSocket();
|
||||
if ( !pSocket->BindToAny( 0 ) )
|
||||
{
|
||||
printf( "Error binding to a port!\n" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *pPassword = FindArg( argc, argv, "-mpi_pw" );
|
||||
|
||||
|
||||
// Figure out which action they want to take.
|
||||
int timeout = 0;
|
||||
char cRequest = VMPI_PING_REQUEST;
|
||||
if ( FindArg( argc, argv, "-Stop" ) )
|
||||
{
|
||||
cRequest = VMPI_STOP_SERVICE;
|
||||
}
|
||||
else if ( FindArg( argc, argv, "-Kill" ) )
|
||||
{
|
||||
cRequest = VMPI_KILL_PROCESS;
|
||||
}
|
||||
/*
|
||||
else if ( FindArg( argc, argv, "-ShowConsole" ) )
|
||||
{
|
||||
cRequest = VMPI_SHOW_CONSOLE_WINDOW;
|
||||
}
|
||||
else if ( FindArg( argc, argv, "-HideConsole" ) )
|
||||
{
|
||||
cRequest = VMPI_HIDE_CONSOLE_WINDOW;
|
||||
}
|
||||
*/
|
||||
else if ( FindArg( argc, argv, "-ShowCache" ) )
|
||||
{
|
||||
cRequest = VMPI_GET_CACHE_INFO;
|
||||
}
|
||||
else if ( FindArg( argc, argv, "-FlushCache" ) )
|
||||
{
|
||||
cRequest = VMPI_FLUSH_CACHE;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *pTimeout = FindArg( argc, argv, "-patch", "60" );
|
||||
if ( pTimeout )
|
||||
{
|
||||
if ( isdigit( pTimeout[0] ) )
|
||||
{
|
||||
cRequest = VMPI_SERVICE_PATCH;
|
||||
timeout = atoi( pTimeout );
|
||||
printf( "Patching with timeout of %d seconds.\n", timeout );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "-patch requires a timeout parameter.\n" );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int nMachines = 0;
|
||||
printf( "Pinging VMPI Services... press a key to stop.\n\n" );
|
||||
while ( !kbhit() )
|
||||
{
|
||||
for ( int i=VMPI_SERVICE_PORT; i <= VMPI_LAST_SERVICE_PORT; i++ )
|
||||
{
|
||||
unsigned char data[256];
|
||||
bf_write buf( data, sizeof( data ) );
|
||||
buf.WriteByte( VMPI_PROTOCOL_VERSION );
|
||||
buf.WriteString( pPassword );
|
||||
buf.WriteByte( cRequest );
|
||||
|
||||
if ( cRequest == VMPI_SERVICE_PATCH )
|
||||
buf.WriteLong( timeout );
|
||||
|
||||
pSocket->Broadcast( data, buf.GetNumBytesWritten(), i );
|
||||
}
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
CIPAddr ipFrom;
|
||||
char in[256];
|
||||
int len = pSocket->RecvFrom( in, sizeof( in ), &ipFrom );
|
||||
if ( len == -1 )
|
||||
break;
|
||||
|
||||
if ( len >= 2 &&
|
||||
in[0] == VMPI_PROTOCOL_VERSION &&
|
||||
in[1] == VMPI_PING_RESPONSE &&
|
||||
addrs.Find( ipFrom ) == -1 )
|
||||
{
|
||||
char *pStateString = "(unknown)";
|
||||
if ( len >= 3 )
|
||||
{
|
||||
if ( in[2] )
|
||||
pStateString = "(running)";
|
||||
else
|
||||
pStateString = "(idle) ";
|
||||
}
|
||||
|
||||
++nMachines;
|
||||
char nameStr[256];
|
||||
|
||||
if ( FindArg( argc, argv, "-dns" ) && ConvertIPAddrToString( &ipFrom, nameStr, sizeof( nameStr ) ) )
|
||||
{
|
||||
printf( "%02d. %s - %s:%d (%d.%d.%d.%d)",
|
||||
nMachines, pStateString, nameStr, ipFrom.port,
|
||||
ipFrom.ip[0], ipFrom.ip[1], ipFrom.ip[2], ipFrom.ip[3] );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "%02d. %s - %d.%d.%d.%d:%d",
|
||||
nMachines, pStateString, ipFrom.ip[0], ipFrom.ip[1], ipFrom.ip[2], ipFrom.ip[3], ipFrom.port );
|
||||
}
|
||||
|
||||
if ( cRequest == VMPI_GET_CACHE_INFO )
|
||||
{
|
||||
// Next var is a 64-bit int with the size of the cache.
|
||||
char *pCur = &in[3];
|
||||
__int64 cacheSize = *((__int64*)pCur);
|
||||
pCur += sizeof( __int64 );
|
||||
|
||||
char *pCacheDir = pCur;
|
||||
|
||||
__int64 nMegs = cacheSize / (1024*1024);
|
||||
printf( "\n\tCache dir: %s, size: %d megs", pCur, nMegs );
|
||||
}
|
||||
|
||||
printf( "\n" );
|
||||
|
||||
addrs.AddToTail( ipFrom );
|
||||
}
|
||||
}
|
||||
|
||||
Sleep( 1000 );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
249
utils/vmpi/testapps/vmpi_ping/vmpi_ping.vcproj
Normal file
249
utils/vmpi/testapps/vmpi_ping/vmpi_ping.vcproj
Normal file
@@ -0,0 +1,249 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="7.10"
|
||||
Name="vmpi_ping"
|
||||
ProjectGUID="{D6F83A58-89A2-4F57-9761-32208BD95E83}"
|
||||
SccProjectName=""
|
||||
SccAuxPath=""
|
||||
SccLocalPath=""
|
||||
SccProvider="">
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"/>
|
||||
</Platforms>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory=".\Debug"
|
||||
IntermediateDirectory=".\Debug"
|
||||
ConfigurationType="1"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories="..\..\common,..\..\..\common,..\..\..\public,..\..\..\public\tier1,.."
|
||||
PreprocessorDefinitions="_DEBUG;WIN32;_CONSOLE;PROTECTED_THINGS_DISABLE"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="5"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
PrecompiledHeaderFile=".\Debug/vmpi_ping.pch"
|
||||
AssemblerListingLocation=".\Debug/"
|
||||
ObjectFile=".\Debug/"
|
||||
ProgramDataBaseFileName=".\Debug/"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="4"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
CommandLine="if exist ..\..\..\..\game\bin\vmpi_ping.exe attrib -r ..\..\..\..\game\bin\vmpi_ping.exe
|
||||
copy "$(TargetPath)" ..\..\..\..\game\bin
|
||||
"
|
||||
Outputs="..\..\..\..\game\bin\vmpi_ping.exe"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib odbc32.lib odbccp32.lib"
|
||||
OutputFile=".\Debug/vmpi_ping.exe"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
GenerateDebugInformation="TRUE"
|
||||
ProgramDatabaseFile=".\Debug/vmpi_ping.pdb"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TypeLibraryName=".\Debug/vmpi_ping.tlb"
|
||||
HeaderFileName=""/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory=".\Release"
|
||||
IntermediateDirectory=".\Release"
|
||||
ConfigurationType="1"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE"
|
||||
CharacterSet="2">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="1"
|
||||
AdditionalIncludeDirectories="..\..\common,..\..\..\common,..\..\..\public,..\..\..\public\tier1,.."
|
||||
PreprocessorDefinitions="NDEBUG;WIN32;_CONSOLE;PROTECTED_THINGS_DISABLE"
|
||||
StringPooling="TRUE"
|
||||
RuntimeLibrary="4"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
UsePrecompiledHeader="3"
|
||||
PrecompiledHeaderThrough="stdafx.h"
|
||||
PrecompiledHeaderFile=".\Release/vmpi_ping.pch"
|
||||
AssemblerListingLocation=".\Release/"
|
||||
ObjectFile=".\Release/"
|
||||
ProgramDataBaseFileName=".\Release/"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
CompileAs="0"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
CommandLine="if exist ..\..\..\..\game\bin\vmpi_ping.exe attrib -r ..\..\..\..\game\bin\vmpi_ping.exe
|
||||
copy "$(TargetPath)" ..\..\..\..\game\bin
|
||||
"
|
||||
Outputs="..\..\..\..\game\bin\vmpi_ping.exe"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="ws2_32.lib odbc32.lib odbccp32.lib"
|
||||
OutputFile=".\Release/vmpi_ping.exe"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
ProgramDatabaseFile=".\Release/vmpi_ping.pdb"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TypeLibraryName=".\Release/vmpi_ping.tlb"
|
||||
HeaderFileName=""/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="NDEBUG"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
<Tool
|
||||
Name="VCManagedWrapperGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
|
||||
<File
|
||||
RelativePath="..\iphelpers.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\public\tier0\memoverride.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="StdAfx.cpp">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="1"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="vmpi_ping.cpp">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl">
|
||||
<File
|
||||
RelativePath="..\..\common\iphelpers.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="StdAfx.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
|
||||
</Filter>
|
||||
<File
|
||||
RelativePath="ReadMe.txt">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\tier0.lib">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\tier1.lib">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\lib\public\vstdlib.lib">
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32">
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
Description=""
|
||||
CommandLine=""/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
155
utils/vmpi/threadhelpers.cpp
Normal file
155
utils/vmpi/threadhelpers.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include <windows.h>
|
||||
#include "threadhelpers.h"
|
||||
#include "tier0/dbg.h"
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// CCriticalSection implementation.
|
||||
// -------------------------------------------------------------------------------- //
|
||||
|
||||
CCriticalSection::CCriticalSection()
|
||||
{
|
||||
Assert( sizeof( CRITICAL_SECTION ) == SIZEOF_CS );
|
||||
|
||||
#if defined( _DEBUG )
|
||||
InitializeCriticalSection( (CRITICAL_SECTION*)&m_DeadlockProtect );
|
||||
#endif
|
||||
|
||||
InitializeCriticalSection( (CRITICAL_SECTION*)&m_CS );
|
||||
}
|
||||
|
||||
|
||||
CCriticalSection::~CCriticalSection()
|
||||
{
|
||||
DeleteCriticalSection( (CRITICAL_SECTION*)&m_CS );
|
||||
|
||||
#if defined( _DEBUG )
|
||||
DeleteCriticalSection( (CRITICAL_SECTION*)&m_DeadlockProtect );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CCriticalSection::Lock()
|
||||
{
|
||||
#if defined( _DEBUG )
|
||||
// Check if this one is already locked.
|
||||
DWORD id = GetCurrentThreadId();
|
||||
EnterCriticalSection( (CRITICAL_SECTION*)&m_DeadlockProtect );
|
||||
Assert( m_Locks.Find( id ) == m_Locks.InvalidIndex() );
|
||||
m_Locks.AddToTail( id );
|
||||
LeaveCriticalSection( (CRITICAL_SECTION*)&m_DeadlockProtect );
|
||||
#endif
|
||||
|
||||
EnterCriticalSection( (CRITICAL_SECTION*)&m_CS );
|
||||
}
|
||||
|
||||
|
||||
void CCriticalSection::Unlock()
|
||||
{
|
||||
#if defined( _DEBUG )
|
||||
// Check if this one is already locked.
|
||||
DWORD id = GetCurrentThreadId();
|
||||
EnterCriticalSection( (CRITICAL_SECTION*)&m_DeadlockProtect );
|
||||
int index = m_Locks.Find( id );
|
||||
Assert( index != m_Locks.InvalidIndex() );
|
||||
m_Locks.Remove( index );
|
||||
LeaveCriticalSection( (CRITICAL_SECTION*)&m_DeadlockProtect );
|
||||
#endif
|
||||
|
||||
LeaveCriticalSection( (CRITICAL_SECTION*)&m_CS );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// CCriticalSectionLock implementation.
|
||||
// -------------------------------------------------------------------------------- //
|
||||
|
||||
CCriticalSectionLock::CCriticalSectionLock( CCriticalSection *pCS )
|
||||
{
|
||||
m_pCS = pCS;
|
||||
m_bLocked = false;
|
||||
}
|
||||
|
||||
|
||||
CCriticalSectionLock::~CCriticalSectionLock()
|
||||
{
|
||||
if ( m_bLocked )
|
||||
m_pCS->Unlock();
|
||||
}
|
||||
|
||||
|
||||
void CCriticalSectionLock::Lock()
|
||||
{
|
||||
Assert( !m_bLocked );
|
||||
m_bLocked = true;
|
||||
m_pCS->Lock();
|
||||
}
|
||||
|
||||
|
||||
void CCriticalSectionLock::Unlock()
|
||||
{
|
||||
Assert( m_bLocked );
|
||||
m_bLocked = false;
|
||||
m_pCS->Unlock();
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// CEvent implementation.
|
||||
// -------------------------------------------------------------------------------- //
|
||||
|
||||
CEvent::CEvent()
|
||||
{
|
||||
m_hEvent = NULL;
|
||||
}
|
||||
|
||||
CEvent::~CEvent()
|
||||
{
|
||||
Term();
|
||||
}
|
||||
|
||||
bool CEvent::Init( bool bManualReset, bool bInitialState )
|
||||
{
|
||||
Term();
|
||||
|
||||
m_hEvent = (void*)CreateEvent( NULL, bManualReset, bInitialState, NULL );
|
||||
return (m_hEvent != NULL);
|
||||
}
|
||||
|
||||
void CEvent::Term()
|
||||
{
|
||||
if ( m_hEvent )
|
||||
{
|
||||
CloseHandle( (HANDLE)m_hEvent );
|
||||
m_hEvent = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void* CEvent::GetEventHandle() const
|
||||
{
|
||||
Assert( m_hEvent );
|
||||
return m_hEvent;
|
||||
}
|
||||
|
||||
bool CEvent::SetEvent()
|
||||
{
|
||||
Assert( m_hEvent );
|
||||
return ::SetEvent( (HANDLE)m_hEvent ) != 0;
|
||||
}
|
||||
|
||||
bool CEvent::ResetEvent()
|
||||
{
|
||||
Assert( m_hEvent );
|
||||
return ::ResetEvent( (HANDLE)m_hEvent ) != 0;
|
||||
}
|
||||
|
||||
|
||||
110
utils/vmpi/threadhelpers.h
Normal file
110
utils/vmpi/threadhelpers.h
Normal file
@@ -0,0 +1,110 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef THREADHELPERS_H
|
||||
#define THREADHELPERS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "tier1/utllinkedlist.h"
|
||||
|
||||
|
||||
#define SIZEOF_CS 24 // sizeof( CRITICAL_SECTION )
|
||||
|
||||
|
||||
class CCriticalSection
|
||||
{
|
||||
public:
|
||||
CCriticalSection();
|
||||
~CCriticalSection();
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
friend class CCriticalSectionLock;
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
|
||||
|
||||
public:
|
||||
char m_CS[SIZEOF_CS];
|
||||
|
||||
// Used to protect against deadlock in debug mode.
|
||||
//#if defined( _DEBUG )
|
||||
CUtlLinkedList<unsigned long,int> m_Locks;
|
||||
char m_DeadlockProtect[SIZEOF_CS];
|
||||
//#endif
|
||||
};
|
||||
|
||||
|
||||
// Use this to lock a critical section.
|
||||
class CCriticalSectionLock
|
||||
{
|
||||
public:
|
||||
CCriticalSectionLock( CCriticalSection *pCS );
|
||||
~CCriticalSectionLock();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
|
||||
private:
|
||||
CCriticalSection *m_pCS;
|
||||
bool m_bLocked;
|
||||
};
|
||||
|
||||
|
||||
template< class T >
|
||||
class CCriticalSectionData : private CCriticalSection
|
||||
{
|
||||
public:
|
||||
// You only have access to the data between Lock() and Unlock().
|
||||
T* Lock()
|
||||
{
|
||||
CCriticalSection::Lock();
|
||||
return &m_Data;
|
||||
}
|
||||
|
||||
void Unlock()
|
||||
{
|
||||
CCriticalSection::Unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
T m_Data;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------ //
|
||||
// CEvent.
|
||||
// ------------------------------------------------------------------------------------------------ //
|
||||
class CEvent
|
||||
{
|
||||
public:
|
||||
CEvent();
|
||||
~CEvent();
|
||||
|
||||
bool Init( bool bManualReset, bool bInitialState );
|
||||
void Term();
|
||||
|
||||
void* GetEventHandle() const;
|
||||
|
||||
// Signal the event.
|
||||
bool SetEvent();
|
||||
|
||||
// Unset the event's signalled status.
|
||||
bool ResetEvent();
|
||||
|
||||
|
||||
private:
|
||||
void *m_hEvent;
|
||||
};
|
||||
|
||||
|
||||
#endif // THREADHELPERS_H
|
||||
2478
utils/vmpi/vmpi.cpp
Normal file
2478
utils/vmpi/vmpi.cpp
Normal file
File diff suppressed because it is too large
Load Diff
217
utils/vmpi/vmpi.h
Normal file
217
utils/vmpi/vmpi.h
Normal file
@@ -0,0 +1,217 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef VMPI_H
|
||||
#define VMPI_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "vmpi_defs.h"
|
||||
#include "messbuf.h"
|
||||
#include "iphelpers.h"
|
||||
|
||||
|
||||
// These are called to handle incoming messages.
|
||||
// Return true if you handled the message and false otherwise.
|
||||
// Note: the first byte in each message is the packet ID.
|
||||
typedef bool (*VMPIDispatchFn)( MessageBuffer *pBuf, int iSource, int iPacketID );
|
||||
|
||||
typedef void (*VMPI_Disconnect_Handler)( int procID, const char *pReason );
|
||||
|
||||
|
||||
// Which machine is the master.
|
||||
#define VMPI_MASTER_ID 0
|
||||
|
||||
#define VMPI_SEND_TO_ALL -2
|
||||
#define VMPI_PERSISTENT -3 // If this is set as the destination for a packet, it is sent to all
|
||||
// workers, and also to new workers that connect.
|
||||
|
||||
#define MAX_VMPI_PACKET_IDS 32
|
||||
|
||||
|
||||
#define VMPI_TIMEOUT_INFINITE 0xFFFFFFFF
|
||||
|
||||
|
||||
// Instantiate one of these to register a dispatch.
|
||||
class CDispatchReg
|
||||
{
|
||||
public:
|
||||
CDispatchReg( int iPacketID, VMPIDispatchFn fn );
|
||||
};
|
||||
|
||||
|
||||
// Enums for all the command line parameters.
|
||||
#define VMPI_PARAM_SDK_HIDDEN 0x0001 // Hidden in SDK mode.
|
||||
|
||||
#define VMPI_PARAM( paramName, paramFlags, helpText ) paramName,
|
||||
enum EVMPICmdLineParam
|
||||
{
|
||||
k_eVMPICmdLineParam_FirstParam=0,
|
||||
k_eVMPICmdLineParam_VMPIParam,
|
||||
#include "vmpi_parameters.h"
|
||||
k_eVMPICmdLineParam_LastParam
|
||||
};
|
||||
#undef VMPI_PARAM
|
||||
|
||||
|
||||
// Shared by all the tools.
|
||||
extern bool g_bUseMPI;
|
||||
extern bool g_bMPIMaster; // Set to true if we're the master in a VMPI session.
|
||||
extern int g_iVMPIVerboseLevel; // Higher numbers make it spit out more data.
|
||||
|
||||
extern bool g_bMPI_Stats; // Send stats to the MySQL database?
|
||||
extern bool g_bMPI_StatsTextOutput; // Send text output in the stats?
|
||||
|
||||
// These can be watched or modified to check bandwidth statistics.
|
||||
extern int g_nBytesSent;
|
||||
extern int g_nMessagesSent;
|
||||
extern int g_nBytesReceived;
|
||||
extern int g_nMessagesReceived;
|
||||
|
||||
extern int g_nMulticastBytesSent;
|
||||
extern int g_nMulticastBytesReceived;
|
||||
|
||||
extern int g_nMaxWorkerCount;
|
||||
|
||||
|
||||
enum VMPIRunMode
|
||||
{
|
||||
VMPI_RUN_NETWORKED,
|
||||
VMPI_RUN_LOCAL // Just make a local process and have it do the work.
|
||||
};
|
||||
|
||||
|
||||
enum VMPIFileSystemMode
|
||||
{
|
||||
VMPI_FILESYSTEM_MULTICAST, // Multicast out, find workers, have them do work.
|
||||
VMPI_FILESYSTEM_BROADCAST, // Broadcast out, find workers, have them do work.
|
||||
VMPI_FILESYSTEM_TCP // TCP filesystem.
|
||||
};
|
||||
|
||||
|
||||
// If this precedes the dependency filename, then it will transfer all the files in the specified directory.
|
||||
#define VMPI_DEPENDENCY_DIRECTORY_TOKEN '*'
|
||||
|
||||
|
||||
// It's good to specify a disconnect handler here immediately. If you don't have a handler
|
||||
// and the master disconnects, you'll lockup forever inside a dispatch loop because you
|
||||
// never handled the master disconnecting.
|
||||
//
|
||||
// Note: runMode is only relevant for the VMPI master. The worker always connects to the master
|
||||
// the same way.
|
||||
bool VMPI_Init(
|
||||
int &argc,
|
||||
char **&argv,
|
||||
const char *pDependencyFilename,
|
||||
VMPI_Disconnect_Handler handler = NULL,
|
||||
VMPIRunMode runMode = VMPI_RUN_NETWORKED, // Networked or local?,
|
||||
bool bConnectingAsService = false
|
||||
);
|
||||
|
||||
// Used when hosting a patch.
|
||||
void VMPI_Init_PatchMaster( int argc, char **argv );
|
||||
|
||||
void VMPI_Finalize();
|
||||
|
||||
VMPIRunMode VMPI_GetRunMode();
|
||||
VMPIFileSystemMode VMPI_GetFileSystemMode();
|
||||
|
||||
// Note: this number can change on the master.
|
||||
int VMPI_GetCurrentNumberOfConnections();
|
||||
|
||||
|
||||
// Dispatch messages until it gets one with the specified packet ID.
|
||||
// If subPacketID is not set to -1, then the second byte must match that as well.
|
||||
//
|
||||
// Note: this WILL dispatch packets with matching packet IDs and give them a chance to handle packets first.
|
||||
//
|
||||
// If bWait is true, then this function either succeeds or Error() is called. If it's false, then if the first available message
|
||||
// is handled by a dispatch, this function returns false.
|
||||
bool VMPI_DispatchUntil( MessageBuffer *pBuf, int *pSource, int packetID, int subPacketID = -1, bool bWait = true );
|
||||
|
||||
// This waits for the next message and dispatches it.
|
||||
// You can specify a timeout in milliseconds. If the timeout expires, the function returns false.
|
||||
bool VMPI_DispatchNextMessage( unsigned long timeout=VMPI_TIMEOUT_INFINITE );
|
||||
|
||||
// This should be called periodically in modal loops that don't call other VMPI functions. This will
|
||||
// check for disconnected sockets and call disconnect handlers so the app can error out if
|
||||
// it loses all of its connections.
|
||||
//
|
||||
// This can be used in place of a Sleep() call by specifying a timeout value.
|
||||
void VMPI_HandleSocketErrors( unsigned long timeout=0 );
|
||||
|
||||
|
||||
|
||||
enum VMPISendFlags
|
||||
{
|
||||
k_eVMPISendFlags_GroupPackets = 0x0001
|
||||
};
|
||||
|
||||
// Use these to send data to one of the machines.
|
||||
// If iDest is VMPI_SEND_TO_ALL, then the message goes to all the machines.
|
||||
// Flags is a combination of the VMPISendFlags enums.
|
||||
bool VMPI_SendData( void *pData, int nBytes, int iDest, int fVMPISendFlags=0 );
|
||||
bool VMPI_SendChunks( void const * const *pChunks, const int *pChunkLengths, int nChunks, int iDest, int fVMPISendFlags=0 );
|
||||
bool VMPI_Send2Chunks( const void *pChunk1, int chunk1Len, const void *pChunk2, int chunk2Len, int iDest, int fVMPISendFlags=0 ); // for convenience..
|
||||
bool VMPI_Send3Chunks( const void *pChunk1, int chunk1Len, const void *pChunk2, int chunk2Len, const void *pChunk3, int chunk3Len, int iDest, int fVMPISendFlags=0 );
|
||||
|
||||
// Flush any groups that were queued with k_eVMPISendFlags_GroupPackets.
|
||||
// If msInterval is > 0, then it will check a timer and only flush that often (so you can call this a lot, and have it check).
|
||||
void VMPI_FlushGroupedPackets( unsigned long msInterval=0 );
|
||||
|
||||
// This registers a function that gets called when a connection is terminated ungracefully.
|
||||
void VMPI_AddDisconnectHandler( VMPI_Disconnect_Handler handler );
|
||||
|
||||
// Returns false if the process has disconnected ungracefully (disconnect handlers
|
||||
// would have been called for it too).
|
||||
bool VMPI_IsProcConnected( int procID );
|
||||
|
||||
// Returns true if the process is just a service (in which case it should only get file IO traffic).
|
||||
bool VMPI_IsProcAService( int procID );
|
||||
|
||||
// Simple wrapper for Sleep() so people can avoid including windows.h
|
||||
void VMPI_Sleep( unsigned long ms );
|
||||
|
||||
// VMPI sends machine names around first thing.
|
||||
const char* VMPI_GetLocalMachineName();
|
||||
const char* VMPI_GetMachineName( int iProc );
|
||||
bool VMPI_HasMachineNameBeenSet( int iProc );
|
||||
|
||||
// Returns 0xFFFFFFFF if the ID hasn't been set.
|
||||
unsigned long VMPI_GetJobWorkerID( int iProc );
|
||||
void VMPI_SetJobWorkerID( int iProc, unsigned long jobWorkerID );
|
||||
|
||||
// Search a command line to find arguments. Looks for pName, and if it finds it, returns the
|
||||
// argument following it. If pName is the last argument, it returns pDefault. If it doesn't
|
||||
// find pName, returns NULL.
|
||||
const char* VMPI_FindArg( int argc, char **argv, const char *pName, const char *pDefault = "" );
|
||||
|
||||
// (Threadsafe) get and set the current stage. This info winds up in the VMPI database.
|
||||
void VMPI_GetCurrentStage( char *pOut, int strLen );
|
||||
void VMPI_SetCurrentStage( const char *pCurStage );
|
||||
|
||||
// VMPI is always broadcasting this job in the background.
|
||||
// This changes the password to 'debugworker' and allows more workers in.
|
||||
// This can be used if workers are dying on certain work units. Then a programmer
|
||||
// can run vmpi_service with -superdebug and debug the whole thing.
|
||||
void VMPI_InviteDebugWorkers();
|
||||
|
||||
bool VMPI_IsSDKMode();
|
||||
|
||||
// Lookup a command line parameter string.
|
||||
const char* VMPI_GetParamString( EVMPICmdLineParam eParam );
|
||||
int VMPI_GetParamFlags( EVMPICmdLineParam eParam );
|
||||
const char* VMPI_GetParamHelpString( EVMPICmdLineParam eParam );
|
||||
bool VMPI_IsParamUsed( EVMPICmdLineParam eParam ); // Returns true if the specified parameter is on the command line.
|
||||
|
||||
// Can be called from error handlers and if -mpi_Restart is used, it'll automatically restart the process.
|
||||
bool VMPI_HandleAutoRestart();
|
||||
|
||||
|
||||
#endif // VMPI_H
|
||||
62
utils/vmpi/vmpi.vpc
Normal file
62
utils/vmpi/vmpi.vpc
Normal file
@@ -0,0 +1,62 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// VMPI.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR "..\.."
|
||||
$Include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE,..\common,zlib,.\"
|
||||
$PreprocessorDefinitions "$BASE;PROTECTED_THINGS_DISABLE;MPI"
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Vmpi"
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$File "$SRCDIR\public\filesystem_init.cpp"
|
||||
$File "..\common\filesystem_tools.cpp"
|
||||
$File "iphelpers.cpp"
|
||||
$File "loopback_channel.cpp"
|
||||
$File "messbuf.cpp"
|
||||
$File "ThreadedTCPSocket.cpp"
|
||||
$File "ThreadedTCPSocketEmu.cpp"
|
||||
$File "threadhelpers.cpp"
|
||||
$File "vmpi.cpp"
|
||||
$File "vmpi_distribute_tracker.cpp"
|
||||
$File "vmpi_distribute_work.cpp"
|
||||
$File "vmpi_distribute_work_sdk.cpp"
|
||||
$File "vmpi_distribute_work_default.cpp"
|
||||
$File "vmpi_filesystem.cpp"
|
||||
$File "vmpi_filesystem_internal.h"
|
||||
$File "vmpi_filesystem_master.cpp"
|
||||
$File "vmpi_filesystem_worker.cpp"
|
||||
}
|
||||
|
||||
$Folder "Header Files"
|
||||
{
|
||||
$File "$SRCDIR\public\tier1\bitbuf.h"
|
||||
$File "ichannel.h"
|
||||
$File "iphelpers.h"
|
||||
$File "IThreadedTCPSocket.h"
|
||||
$File "loopback_channel.h"
|
||||
$File "messbuf.h"
|
||||
$File "tcpsocket.h"
|
||||
$File "ThreadedTCPSocketEmu.h"
|
||||
$File "threadhelpers.h"
|
||||
$File "vmpi.h"
|
||||
$File "vmpi_defs.h"
|
||||
$File "vmpi_filesystem.h"
|
||||
}
|
||||
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$File "ZLib.lib"
|
||||
}
|
||||
}
|
||||
43
utils/vmpi/vmpi_browser_helpers.cpp
Normal file
43
utils/vmpi/vmpi_browser_helpers.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "tier1/strtools.h"
|
||||
#include "vmpi_browser_helpers.h"
|
||||
|
||||
|
||||
|
||||
void FormatTimeString( unsigned long nInputSeconds, char *timeStr, int outLen )
|
||||
{
|
||||
// Make a string to say how long the thing has been running.
|
||||
unsigned long minutes = nInputSeconds / 60;
|
||||
unsigned long nSeconds = nInputSeconds - minutes * 60;
|
||||
|
||||
unsigned long hours = minutes / 60;
|
||||
minutes -= hours * 60;
|
||||
|
||||
unsigned long days = hours / 24;
|
||||
hours -= days * 24;
|
||||
|
||||
if ( days && hours )
|
||||
{
|
||||
Q_snprintf( timeStr, outLen, "%dd %dh %dm", days, hours, minutes );
|
||||
}
|
||||
else if ( hours )
|
||||
{
|
||||
Q_snprintf( timeStr, outLen, "%dh %dm", hours, minutes );
|
||||
}
|
||||
else if ( minutes )
|
||||
{
|
||||
Q_snprintf( timeStr, outLen, "%dm %ds", minutes, nSeconds );
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_snprintf( timeStr, outLen, "%d seconds", nSeconds );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
utils/vmpi/vmpi_browser_helpers.h
Normal file
18
utils/vmpi/vmpi_browser_helpers.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef VMPI_BROWSER_HELPERS_H
|
||||
#define VMPI_BROWSER_HELPERS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
void FormatTimeString( unsigned long nSeconds, char *pOut, int outLen );
|
||||
|
||||
|
||||
#endif // VMPI_BROWSER_HELPERS_H
|
||||
147
utils/vmpi/vmpi_defs.h
Normal file
147
utils/vmpi/vmpi_defs.h
Normal file
@@ -0,0 +1,147 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef VMPI_DEFS_H
|
||||
#define VMPI_DEFS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
// This goes in front of all packets.
|
||||
#define VMPI_PROTOCOL_VERSION 5
|
||||
|
||||
// This represents the protocol between the service and its UI.
|
||||
#define VMPI_SERVICE_UI_PROTOCOL_VERSION 1
|
||||
|
||||
// NOTE: the service version (embedded in vmpi_service.exe as a resource) is the version
|
||||
// that is used to apply patches.
|
||||
#define VMPI_SERVICE_IDS_VERSION_STRING 102 // This matches IDS_VERSION_STRING in vmpi_service.exe.
|
||||
|
||||
// Known packet IDs in various systems.
|
||||
#define VMPI_PACKETID_FILESYSTEM 0 // The file system reserves this packet ID.
|
||||
// All application traffic must set its first byte to something other
|
||||
// than this value.
|
||||
#define VMPI_SHARED_PACKET_ID 10
|
||||
|
||||
|
||||
// Turn this on, and the various service apps will log stuff.
|
||||
//#define VMPI_SERVICE_LOGS
|
||||
|
||||
|
||||
// This value is put in the RunningTimeMS until the job is finished. This is how
|
||||
// the job_search app knows if a job never finished.
|
||||
#define RUNNINGTIME_MS_SENTINEL 0xFEDCBAFD
|
||||
|
||||
|
||||
|
||||
#define VMPI_SERVICE_NAME_INTERNAL "VMPI"
|
||||
#define VMPI_SERVICE_NAME "Valve MPI Service"
|
||||
|
||||
// Stuff in the registry goes under here (in HKEY_LOCAL_MACHINE).
|
||||
#define VMPI_SERVICE_KEY "Software\\Valve\\VMPI"
|
||||
#define SERVICE_INSTALL_LOCATION_KEY "InstallLocation"
|
||||
|
||||
// The VMPI service listens on one of these ports to talk to the UI.
|
||||
#define VMPI_SERVICE_FIRST_UI_PORT 23300
|
||||
#define VMPI_SERVICE_LAST_UI_PORT 23310
|
||||
|
||||
// Port numbers that the master will use to broadcast unless -mpi_port is used.
|
||||
#define VMPI_MASTER_FIRST_PORT 23311
|
||||
#define VMPI_MASTER_LAST_PORT 23330
|
||||
|
||||
|
||||
// Packet IDs for vmpi_service to talk to UI clients.
|
||||
#define VMPI_SERVICE_TO_UI_CONSOLE_TEXT 0 // Print some text to the UI's console.
|
||||
#define VMPI_SERVICE_TO_UI_STATE 1 // Updates state reflecting whether it's idle, busy, etc.
|
||||
#define VMPI_SERVICE_TO_UI_PATCHING 2 // Updates state reflecting whether it's idle, busy, etc.
|
||||
#define VMPI_SERVICE_TO_UI_EXIT 3 // Updates state reflecting whether it's idle, busy, etc.
|
||||
|
||||
// Application state.. these are communicated between the service and the UI.
|
||||
enum
|
||||
{
|
||||
VMPI_SERVICE_STATE_IDLE=0,
|
||||
VMPI_SERVICE_STATE_BUSY,
|
||||
VMPI_SERVICE_STATE_DISABLED
|
||||
};
|
||||
#define VMPI_SERVICE_DISABLE 2 // Stop waiting for jobs..
|
||||
#define VMPI_SERVICE_ENABLE 3
|
||||
#define VMPI_SERVICE_UPDATE_PASSWORD 4 // New password.
|
||||
#define VMPI_SERVICE_EXIT 5 // User chose "exit" from the menu. Kill the service.
|
||||
#define VMPI_SERVICE_SKIP_CSX_JOBS 6
|
||||
#define VMPI_SERVICE_SCREENSAVER_MODE 7
|
||||
|
||||
|
||||
// The worker service waits on this range of ports.
|
||||
#define VMPI_SERVICE_PORT 23397
|
||||
#define VMPI_LAST_SERVICE_PORT (VMPI_SERVICE_PORT + 15)
|
||||
|
||||
|
||||
#define VMPI_WORKER_PORT_FIRST 22340
|
||||
#define VMPI_WORKER_PORT_LAST 22350
|
||||
|
||||
// The VMPI service downloader is still a worker but it uses this port range so the
|
||||
// master knows it's just downloading the exes.
|
||||
#define VMPI_SERVICE_DOWNLOADER_PORT_FIRST 22351
|
||||
#define VMPI_SERVICE_DOWNLOADER_PORT_LAST 22360
|
||||
|
||||
// Give it a small range so they can have multiple masters running.
|
||||
#define VMPI_MASTER_PORT_FIRST 21140
|
||||
#define VMPI_MASTER_PORT_LAST 21145
|
||||
#define VMPI_MASTER_FILESYSTEM_BROADCAST_PORT 21146
|
||||
|
||||
|
||||
|
||||
|
||||
// Protocol.
|
||||
|
||||
// The message format is:
|
||||
// - VMPI_PROTOCOL_VERSION
|
||||
// - null-terminated password string (or VMPI_PASSWORD_OVERRIDE followed by a zero to process it regardless of pw).
|
||||
// - packet ID
|
||||
// - payload
|
||||
|
||||
|
||||
#define VMPI_PASSWORD_OVERRIDE -111
|
||||
|
||||
|
||||
#define VMPI_MESSAGE_BASE 71
|
||||
|
||||
|
||||
// This is the broadcast message from the main (rank 0) process looking for workers.
|
||||
#define VMPI_LOOKING_FOR_WORKERS (VMPI_MESSAGE_BASE+0)
|
||||
|
||||
// This is so an app can find out what machines are running the service.
|
||||
#define VMPI_PING_REQUEST (VMPI_MESSAGE_BASE+2)
|
||||
#define VMPI_PING_RESPONSE (VMPI_MESSAGE_BASE+3)
|
||||
|
||||
// This tells the service to quit.
|
||||
#define VMPI_STOP_SERVICE (VMPI_MESSAGE_BASE+6)
|
||||
|
||||
// This tells the service to kill any process it has running.
|
||||
#define VMPI_KILL_PROCESS (VMPI_MESSAGE_BASE+7)
|
||||
|
||||
// This tells the service to patch itself.
|
||||
#define VMPI_SERVICE_PATCH (VMPI_MESSAGE_BASE+8)
|
||||
|
||||
// Sent back to the master via UDP to tell it if its job has started and ended.
|
||||
#define VMPI_NOTIFY_START_STATUS (VMPI_MESSAGE_BASE+9)
|
||||
#define VMPI_NOTIFY_END_STATUS (VMPI_MESSAGE_BASE+10)
|
||||
|
||||
#define VMPI_FORCE_PASSWORD_CHANGE (VMPI_MESSAGE_BASE+11)
|
||||
|
||||
|
||||
// These states are sent from the service to the services browser.
|
||||
#define VMPI_STATE_IDLE 0
|
||||
#define VMPI_STATE_BUSY 1
|
||||
#define VMPI_STATE_PATCHING 2
|
||||
#define VMPI_STATE_DISABLED 3
|
||||
#define VMPI_STATE_SCREENSAVER_DISABLED 4
|
||||
#define VMPI_STATE_DOWNLOADING 5
|
||||
|
||||
|
||||
#endif // VMPI_DEFS_H
|
||||
13
utils/vmpi/vmpi_dispatch.cpp
Normal file
13
utils/vmpi/vmpi_dispatch.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "vmpi_dispatch.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "mpi/mpi.h"
|
||||
#include "vmpi.h"
|
||||
|
||||
|
||||
15
utils/vmpi/vmpi_dispatch.h
Normal file
15
utils/vmpi/vmpi_dispatch.h
Normal file
@@ -0,0 +1,15 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef VMPI_DISPATCH_H
|
||||
#define VMPI_DISPATCH_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#endif // VMPI_DISPATCH_H
|
||||
579
utils/vmpi/vmpi_distribute_tracker.cpp
Normal file
579
utils/vmpi/vmpi_distribute_tracker.cpp
Normal file
@@ -0,0 +1,579 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include <windows.h>
|
||||
#include <conio.h>
|
||||
#include <io.h>
|
||||
#include "vmpi.h"
|
||||
#include "vmpi_distribute_work.h"
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "utlvector.h"
|
||||
#include "utllinkedlist.h"
|
||||
|
||||
|
||||
#define EVENT_TYPE_SEND_WORK_UNIT 0
|
||||
#define EVENT_TYPE_WU_STARTED 1
|
||||
#define EVENT_TYPE_WU_COMPLETED 2
|
||||
|
||||
class CWorkUnitEvent
|
||||
{
|
||||
public:
|
||||
int m_iEventType; // EVENT_TYPE_ define.
|
||||
int m_iWorker;
|
||||
double m_flTime;
|
||||
};
|
||||
|
||||
|
||||
class CWorkUnit
|
||||
{
|
||||
public:
|
||||
CWorkUnit()
|
||||
{
|
||||
m_iWorkerCompleted = -1;
|
||||
}
|
||||
|
||||
int m_iWorkerCompleted; // Which worker completed this work unit (-1 if not done yet).
|
||||
|
||||
CUtlVector<CWorkUnitEvent> m_Events;
|
||||
};
|
||||
|
||||
|
||||
static CUtlVector<CWorkUnit> g_WorkUnits;
|
||||
static double g_flJobStartTime;
|
||||
static bool g_bTrackWorkUnitEvents = false;
|
||||
|
||||
|
||||
static int CountActiveWorkUnits()
|
||||
{
|
||||
int nActive = 0;
|
||||
for ( int i=0; i < g_WorkUnits.Count(); i++ )
|
||||
{
|
||||
if ( g_WorkUnits[i].m_iWorkerCompleted == -1 )
|
||||
++nActive;
|
||||
}
|
||||
return nActive;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------ //
|
||||
// Graphical functions.
|
||||
// ------------------------------------------------------------------------ //
|
||||
|
||||
static bool g_bUseGraphics = false;
|
||||
static HWND g_hWnd = 0;
|
||||
|
||||
static int g_LastSizeX = 600, g_LastSizeY = 600;
|
||||
|
||||
static COLORREF g_StateColors[] =
|
||||
{
|
||||
RGB(50,50,50),
|
||||
RGB(100,0,0),
|
||||
RGB(150,0,0),
|
||||
RGB(0,155,0),
|
||||
RGB(0,255,0),
|
||||
RGB(0,0,150),
|
||||
RGB(0,0,250)
|
||||
};
|
||||
|
||||
static HANDLE g_hCreateEvent = 0;
|
||||
static HANDLE g_hDestroyWindowEvent = 0;
|
||||
static HANDLE g_hDestroyWindowCompletedEvent = 0;
|
||||
|
||||
static CRITICAL_SECTION g_CS;
|
||||
|
||||
class CWUStatus
|
||||
{
|
||||
public:
|
||||
CWUStatus()
|
||||
{
|
||||
m_iState = 0;
|
||||
memset( &m_Rect, 0, sizeof( m_Rect ) );
|
||||
}
|
||||
|
||||
RECT m_Rect;
|
||||
int m_iState; // 0 = not sent yet
|
||||
// 1 = sent, 2 = sent recently
|
||||
// 3 = done, 4 = done recently
|
||||
// 5 = started, 6 = started recently
|
||||
float m_flTransitionTime;
|
||||
};
|
||||
CUtlVector<CWUStatus> g_WUStatus;
|
||||
int g_nChanges = 0;
|
||||
int g_nLastDrawnChanges = -1;
|
||||
|
||||
static LRESULT CALLBACK TrackerWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
||||
{
|
||||
switch( uMsg )
|
||||
{
|
||||
case WM_PAINT:
|
||||
{
|
||||
// Do one pass for each color..
|
||||
HBRUSH hStateColors[ARRAYSIZE( g_StateColors )];
|
||||
for ( int i=0; i < ARRAYSIZE( hStateColors ); i++ )
|
||||
hStateColors[i] = CreateSolidBrush( g_StateColors[i] );
|
||||
|
||||
// Copy the WU statuses.
|
||||
CUtlVector<CWUStatus> wuStatus;
|
||||
EnterCriticalSection( &g_CS );
|
||||
|
||||
g_nLastDrawnChanges = g_nChanges;
|
||||
wuStatus.SetSize( g_WUStatus.Count() );
|
||||
memcpy( wuStatus.Base(), g_WUStatus.Base(), wuStatus.Count() * sizeof( wuStatus[0] ) );
|
||||
|
||||
LeaveCriticalSection( &g_CS );
|
||||
|
||||
PAINTSTRUCT ps;
|
||||
HDC hDC = BeginPaint( hwnd, &ps );
|
||||
for ( int iState=0; iState < ARRAYSIZE( hStateColors ); iState++ )
|
||||
{
|
||||
HGDIOBJ hOldObj = SelectObject( hDC, hStateColors[iState] );
|
||||
|
||||
for ( int iWU=0; iWU < wuStatus.Count(); iWU++ )
|
||||
{
|
||||
if ( wuStatus[iWU].m_iState != iState )
|
||||
continue;
|
||||
|
||||
RECT &rc = wuStatus[iWU].m_Rect;
|
||||
Rectangle( hDC, rc.left, rc.top, rc.right, rc.bottom );
|
||||
}
|
||||
|
||||
SelectObject( hDC, hOldObj );
|
||||
DeleteObject( hStateColors[iState] );
|
||||
}
|
||||
EndPaint( hwnd, &ps );
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SIZE:
|
||||
{
|
||||
int width = LOWORD( lParam );
|
||||
int height = HIWORD( lParam );
|
||||
|
||||
g_LastSizeX = width;
|
||||
g_LastSizeY = height;
|
||||
|
||||
// Figure out the rectangles for everything.
|
||||
int nWorkUnits = g_WUStatus.Count();
|
||||
|
||||
// What is the max width of the grid elements so they will fit in the width and height.
|
||||
int testSize;
|
||||
for ( testSize=20; testSize > 1; testSize-- )
|
||||
{
|
||||
int nX = width / testSize;
|
||||
int nY = height / testSize;
|
||||
if ( nX * nY >= nWorkUnits )
|
||||
break;
|
||||
}
|
||||
static int minTestSize = 3;
|
||||
testSize = max( testSize, minTestSize );
|
||||
|
||||
int xPos=0, yPos=0;
|
||||
for ( int i=0; i < nWorkUnits; i++ )
|
||||
{
|
||||
g_WUStatus[i].m_Rect.left = xPos;
|
||||
g_WUStatus[i].m_Rect.top = yPos;
|
||||
g_WUStatus[i].m_Rect.right = xPos + testSize;
|
||||
g_WUStatus[i].m_Rect.bottom = yPos + testSize;
|
||||
|
||||
xPos += testSize;
|
||||
if ( (xPos+testSize) > width )
|
||||
{
|
||||
yPos += testSize;
|
||||
xPos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return DefWindowProc( hwnd, uMsg, wParam, lParam );
|
||||
}
|
||||
|
||||
static void CheckFlashTimers()
|
||||
{
|
||||
double flCurTime = Plat_FloatTime();
|
||||
|
||||
EnterCriticalSection( &g_CS );
|
||||
|
||||
// Check timers for the events that just happened (we show them in a brighter color if they just occurred).
|
||||
for ( int iWU=0; iWU < g_WUStatus.Count(); iWU++ )
|
||||
{
|
||||
CWUStatus &s = g_WUStatus[iWU];
|
||||
|
||||
if ( s.m_iState == 2 || s.m_iState == 4 || s.m_iState == 6 )
|
||||
{
|
||||
if ( flCurTime > s.m_flTransitionTime )
|
||||
{
|
||||
s.m_iState -= 1;
|
||||
++g_nChanges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection( &g_CS );
|
||||
}
|
||||
|
||||
static DWORD WINAPI ThreadProc( LPVOID lpParameter )
|
||||
{
|
||||
// Create the window.
|
||||
const char *pClassName = "VMPI_Tracker";
|
||||
|
||||
// Register the application
|
||||
WNDCLASSEX WndClsEx;
|
||||
WndClsEx.cbSize = sizeof(WNDCLASSEX);
|
||||
WndClsEx.style = CS_HREDRAW | CS_VREDRAW;
|
||||
WndClsEx.lpfnWndProc = TrackerWindowProc;
|
||||
WndClsEx.cbClsExtra = 0;
|
||||
WndClsEx.cbWndExtra = 0;
|
||||
WndClsEx.hIcon = NULL;
|
||||
WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
WndClsEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
|
||||
WndClsEx.lpszMenuName = NULL;
|
||||
WndClsEx.lpszClassName = pClassName;
|
||||
WndClsEx.hInstance = (HINSTANCE)GetCurrentProcess();
|
||||
WndClsEx.hIconSm = NULL;
|
||||
RegisterClassEx(&WndClsEx);
|
||||
|
||||
// Create the window.
|
||||
g_hWnd = CreateWindow(
|
||||
pClassName,
|
||||
"VMPI Tracker",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
0, 0, g_LastSizeX, g_LastSizeY,
|
||||
NULL, NULL,
|
||||
(HINSTANCE)GetCurrentProcess(),
|
||||
NULL );
|
||||
|
||||
ShowWindow( g_hWnd, SW_SHOW );
|
||||
|
||||
// Tell the main thread we're ready.
|
||||
SetEvent( g_hCreateEvent );
|
||||
|
||||
// Run our main loop.
|
||||
while ( WaitForSingleObject( g_hDestroyWindowEvent, 200 ) != WAIT_OBJECT_0 )
|
||||
{
|
||||
MSG msg;
|
||||
while ( PeekMessage( &msg, g_hWnd, 0, 0, PM_REMOVE ) )
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
CheckFlashTimers();
|
||||
|
||||
if ( g_nChanges != g_nLastDrawnChanges )
|
||||
InvalidateRect( g_hWnd, NULL, FALSE );
|
||||
}
|
||||
|
||||
// Tell the main thread we're done.
|
||||
SetEvent( g_hDestroyWindowCompletedEvent );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Graphical_Start()
|
||||
{
|
||||
g_bUseGraphics = VMPI_IsParamUsed( mpi_Graphics );
|
||||
if ( !g_bUseGraphics )
|
||||
return;
|
||||
|
||||
// Setup an event so we'll wait until the window is ready.
|
||||
if ( !g_hCreateEvent )
|
||||
{
|
||||
g_hCreateEvent = CreateEvent( 0, 0, 0, 0 );
|
||||
g_hDestroyWindowEvent = CreateEvent( 0, 0, 0, 0 );
|
||||
g_hDestroyWindowCompletedEvent = CreateEvent( 0, 0, 0, 0 );
|
||||
InitializeCriticalSection( &g_CS );
|
||||
}
|
||||
ResetEvent( g_hCreateEvent );
|
||||
ResetEvent( g_hDestroyWindowCompletedEvent );
|
||||
|
||||
g_WUStatus.SetSize( g_WorkUnits.Count() );
|
||||
for ( int i=0; i < g_WUStatus.Count(); i++ )
|
||||
g_WUStatus[i].m_iState = 0;
|
||||
|
||||
// Setup our thread.
|
||||
CreateThread( NULL, 0, ThreadProc, NULL, 0, NULL );
|
||||
|
||||
// Wait until the event is signaled.
|
||||
WaitForSingleObject( g_hCreateEvent, INFINITE );
|
||||
}
|
||||
|
||||
static void Graphical_WorkUnitSentToWorker( int iWorkUnit )
|
||||
{
|
||||
if ( !g_bUseGraphics )
|
||||
return;
|
||||
|
||||
EnterCriticalSection( &g_CS );
|
||||
|
||||
CWUStatus &s = g_WUStatus[iWorkUnit];
|
||||
if ( s.m_iState != 3 && s.m_iState != 4 && s.m_iState != 5 && s.m_iState != 6 )
|
||||
{
|
||||
s.m_iState = 2;
|
||||
s.m_flTransitionTime = Plat_FloatTime() + 0.1f;
|
||||
++g_nChanges;
|
||||
}
|
||||
|
||||
LeaveCriticalSection( &g_CS );
|
||||
}
|
||||
|
||||
static void Graphical_WorkUnitStarted( int iWorkUnit )
|
||||
{
|
||||
if ( !g_bUseGraphics )
|
||||
return;
|
||||
|
||||
EnterCriticalSection( &g_CS );
|
||||
|
||||
if ( g_WUStatus[iWorkUnit].m_iState != 3 && g_WUStatus[iWorkUnit].m_iState != 4 )
|
||||
{
|
||||
g_WUStatus[iWorkUnit].m_iState = 6;
|
||||
g_WUStatus[iWorkUnit].m_flTransitionTime = Plat_FloatTime() + 0.1f;
|
||||
++g_nChanges;
|
||||
}
|
||||
|
||||
LeaveCriticalSection( &g_CS );
|
||||
}
|
||||
|
||||
static void Graphical_WorkUnitCompleted( int iWorkUnit )
|
||||
{
|
||||
if ( !g_bUseGraphics )
|
||||
return;
|
||||
|
||||
EnterCriticalSection( &g_CS );
|
||||
g_WUStatus[iWorkUnit].m_iState = 4;
|
||||
g_WUStatus[iWorkUnit].m_flTransitionTime = Plat_FloatTime() + 0.1f;
|
||||
++g_nChanges;
|
||||
LeaveCriticalSection( &g_CS );
|
||||
}
|
||||
|
||||
static void Graphical_End()
|
||||
{
|
||||
if ( !g_bUseGraphics )
|
||||
return;
|
||||
|
||||
SetEvent( g_hDestroyWindowEvent );
|
||||
WaitForSingleObject( g_hDestroyWindowCompletedEvent, INFINITE );
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------ //
|
||||
// Interface functions.
|
||||
// ------------------------------------------------------------------------ //
|
||||
|
||||
void VMPITracker_Start( int nWorkUnits )
|
||||
{
|
||||
g_bTrackWorkUnitEvents = (VMPI_IsParamUsed( mpi_TrackEvents ) || VMPI_IsParamUsed( mpi_Graphics ));
|
||||
g_flJobStartTime = Plat_FloatTime();
|
||||
g_WorkUnits.Purge();
|
||||
|
||||
if ( g_bTrackWorkUnitEvents )
|
||||
{
|
||||
g_WorkUnits.SetSize( nWorkUnits );
|
||||
}
|
||||
|
||||
Graphical_Start();
|
||||
}
|
||||
|
||||
|
||||
void VMPITracker_WorkUnitSentToWorker( int iWorkUnit, int iWorker )
|
||||
{
|
||||
if ( g_bTrackWorkUnitEvents )
|
||||
{
|
||||
CWorkUnitEvent event;
|
||||
event.m_iEventType = EVENT_TYPE_SEND_WORK_UNIT;
|
||||
event.m_iWorker = iWorker;
|
||||
event.m_flTime = Plat_FloatTime();
|
||||
g_WorkUnits[iWorkUnit].m_Events.AddToTail( event );
|
||||
}
|
||||
|
||||
Graphical_WorkUnitSentToWorker( iWorkUnit );
|
||||
}
|
||||
|
||||
|
||||
void VMPITracker_WorkUnitStarted( int iWorkUnit, int iWorker )
|
||||
{
|
||||
if ( g_bTrackWorkUnitEvents )
|
||||
{
|
||||
CWorkUnitEvent event;
|
||||
event.m_iEventType = EVENT_TYPE_WU_STARTED;
|
||||
event.m_iWorker = iWorker;
|
||||
event.m_flTime = Plat_FloatTime();
|
||||
g_WorkUnits[iWorkUnit].m_Events.AddToTail( event );
|
||||
}
|
||||
|
||||
Graphical_WorkUnitStarted( iWorkUnit );
|
||||
}
|
||||
|
||||
|
||||
void VMPITracker_WorkUnitCompleted( int iWorkUnit, int iWorker )
|
||||
{
|
||||
if ( g_bTrackWorkUnitEvents )
|
||||
{
|
||||
CWorkUnitEvent event;
|
||||
event.m_iEventType = EVENT_TYPE_WU_COMPLETED;
|
||||
event.m_iWorker = iWorker;
|
||||
event.m_flTime = Plat_FloatTime();
|
||||
g_WorkUnits[iWorkUnit].m_Events.AddToTail( event );
|
||||
g_WorkUnits[iWorkUnit].m_iWorkerCompleted = iWorker;
|
||||
}
|
||||
|
||||
Graphical_WorkUnitCompleted( iWorkUnit );
|
||||
}
|
||||
|
||||
|
||||
void VMPITracker_End()
|
||||
{
|
||||
g_WorkUnits.Purge();
|
||||
Graphical_End();
|
||||
}
|
||||
|
||||
|
||||
bool VMPITracker_WriteDebugFile( const char *pFilename )
|
||||
{
|
||||
FILE *fp = fopen( pFilename, "wt" );
|
||||
if ( fp )
|
||||
{
|
||||
fprintf( fp, "# work units: %d\n", g_WorkUnits.Count() );
|
||||
fprintf( fp, "# active work units: %d\n", CountActiveWorkUnits() );
|
||||
|
||||
fprintf( fp, "\n" );
|
||||
fprintf( fp, "--- Events ---" );
|
||||
fprintf( fp, "\n" );
|
||||
fprintf( fp, "\n" );
|
||||
|
||||
for ( int i=0; i < g_WorkUnits.Count(); i++ )
|
||||
{
|
||||
CWorkUnit *wu = &g_WorkUnits[i];
|
||||
|
||||
if ( wu->m_iWorkerCompleted != -1 )
|
||||
continue;
|
||||
|
||||
fprintf( fp, " work unit %d\n", i );
|
||||
fprintf( fp, "\n" );
|
||||
|
||||
if ( wu->m_Events.Count() == 0 )
|
||||
{
|
||||
fprintf( fp, " *no events*\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int iEvent=0; iEvent < wu->m_Events.Count(); iEvent++ )
|
||||
{
|
||||
CWorkUnitEvent *pEvent = &wu->m_Events[iEvent];
|
||||
|
||||
if ( pEvent->m_iEventType == EVENT_TYPE_WU_STARTED )
|
||||
{
|
||||
fprintf( fp, " started (by worker %s) %.1f seconds ago\n",
|
||||
VMPI_GetMachineName( wu->m_Events[iEvent].m_iWorker ),
|
||||
Plat_FloatTime() - wu->m_Events[iEvent].m_flTime );
|
||||
}
|
||||
else if ( pEvent->m_iEventType == EVENT_TYPE_SEND_WORK_UNIT )
|
||||
{
|
||||
fprintf( fp, " sent (to worker %s) %.1f seconds ago\n",
|
||||
VMPI_GetMachineName( wu->m_Events[iEvent].m_iWorker ),
|
||||
Plat_FloatTime() - wu->m_Events[iEvent].m_flTime );
|
||||
}
|
||||
else if ( pEvent->m_iEventType == EVENT_TYPE_WU_COMPLETED )
|
||||
{
|
||||
fprintf( fp, " completed (by worker %s) %.1f seconds ago\n",
|
||||
VMPI_GetMachineName( wu->m_Events[iEvent].m_iWorker ),
|
||||
Plat_FloatTime() - wu->m_Events[iEvent].m_flTime );
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf( fp, "\n" );
|
||||
}
|
||||
|
||||
fclose( fp );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VMPITracker_HandleDebugKeypresses()
|
||||
{
|
||||
if ( !g_bTrackWorkUnitEvents )
|
||||
return;
|
||||
|
||||
if ( !kbhit() )
|
||||
return;
|
||||
|
||||
static int iState = 0;
|
||||
|
||||
int key = toupper( getch() );
|
||||
if ( iState == 0 )
|
||||
{
|
||||
if ( key == 'D' )
|
||||
{
|
||||
iState = 1;
|
||||
Warning("\n\n"
|
||||
"----------------------\n"
|
||||
"1. Write debug file (ascending filenames).\n"
|
||||
"2. Write debug file (c:\\vmpi_tracker_0.txt).\n"
|
||||
"3. Invite debug workers (password: 'debugworker').\n"
|
||||
"\n"
|
||||
"0. Exit menu.\n"
|
||||
"----------------------\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
else if ( iState == 1 )
|
||||
{
|
||||
if ( key == '1' )
|
||||
{
|
||||
iState = 0;
|
||||
|
||||
int nMaxTries = 128;
|
||||
char filename[512];
|
||||
int iFile = 1;
|
||||
for ( iFile; iFile < nMaxTries; iFile++ )
|
||||
{
|
||||
Q_snprintf( filename, sizeof( filename ), "c:\\vmpi_tracker_%d.txt", iFile );
|
||||
if ( _access( filename, 0 ) != 0 )
|
||||
break;
|
||||
}
|
||||
if ( iFile == nMaxTries )
|
||||
{
|
||||
Warning( "** Please delete c:\\vmpi_tracker_*.txt and try again.\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( VMPITracker_WriteDebugFile( filename ) )
|
||||
Warning( "Wrote %s successfully.\n", filename );
|
||||
else
|
||||
Warning( "Failed to write %s successfully.\n", filename );
|
||||
}
|
||||
}
|
||||
else if ( key == '2' )
|
||||
{
|
||||
iState = 0;
|
||||
|
||||
const char *filename = "c:\\vmpi_tracker_0.txt";
|
||||
if ( VMPITracker_WriteDebugFile( filename ) )
|
||||
Warning( "Wrote %s successfully.\n", filename );
|
||||
else
|
||||
Warning( "Failed to write %s successfully.\n", filename );
|
||||
}
|
||||
else if ( key == '3' )
|
||||
{
|
||||
iState = 0;
|
||||
Warning( "\nInviting debug workers with password 'debugworker'...\nGo ahead and connect them.\n" );
|
||||
VMPI_InviteDebugWorkers();
|
||||
}
|
||||
else if ( key == '0' )
|
||||
{
|
||||
iState = 0;
|
||||
Warning( "\n\nExited menu.\n\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
32
utils/vmpi/vmpi_distribute_tracker.h
Normal file
32
utils/vmpi/vmpi_distribute_tracker.h
Normal file
@@ -0,0 +1,32 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: vmpi_distribute_work sends events to this module, and this module
|
||||
// can track the events or display them graphically for debugging.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef VMPI_DISTRIBUTE_TRACKER_H
|
||||
#define VMPI_DISTRIBUTE_TRACKER_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
// If bDebugMode is set, then it will remember all the VMPI events
|
||||
// in case you call VMPITracker_WriteDebugFile.
|
||||
void VMPITracker_Start( int nWorkUnits );
|
||||
|
||||
void VMPITracker_WorkUnitSentToWorker( int iWorkUnit, int iWorker );
|
||||
void VMPITracker_WorkUnitStarted( int iWorkUnit, int iWorker );
|
||||
void VMPITracker_WorkUnitCompleted( int iWorkUnit, int iWorker );
|
||||
void VMPITracker_End();
|
||||
|
||||
// This will bring up a little menu they can use to
|
||||
// write a debug file.
|
||||
void VMPITracker_HandleDebugKeypresses();
|
||||
|
||||
bool VMPITracker_WriteDebugFile( const char *pFilename );
|
||||
|
||||
|
||||
#endif // VMPI_DISTRIBUTE_TRACKER_H
|
||||
|
||||
|
||||
628
utils/vmpi/vmpi_distribute_work.cpp
Normal file
628
utils/vmpi/vmpi_distribute_work.cpp
Normal file
@@ -0,0 +1,628 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include <windows.h>
|
||||
#include "vmpi.h"
|
||||
#include "vmpi_distribute_work.h"
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "utlvector.h"
|
||||
#include "utllinkedlist.h"
|
||||
#include "vmpi_dispatch.h"
|
||||
#include "pacifier.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "threadhelpers.h"
|
||||
#include "threads.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "tier1/utlmap.h"
|
||||
#include "tier1/smartptr.h"
|
||||
#include "tier0/icommandline.h"
|
||||
#include "cmdlib.h"
|
||||
#include "vmpi_distribute_tracker.h"
|
||||
#include "vmpi_distribute_work_internal.h"
|
||||
|
||||
|
||||
// To catch some bugs with 32-bit vs 64-bit and etc.
|
||||
#pragma warning( default : 4244 )
|
||||
#pragma warning( default : 4305 )
|
||||
#pragma warning( default : 4267 )
|
||||
#pragma warning( default : 4311 )
|
||||
#pragma warning( default : 4312 )
|
||||
|
||||
|
||||
const int MAX_DW_CALLS = 255;
|
||||
extern bool g_bSetThreadPriorities;
|
||||
|
||||
|
||||
// Subpacket IDs owned by DistributeWork.
|
||||
#define DW_SUBPACKETID_MASTER_READY 0
|
||||
#define DW_SUBPACKETID_WORKER_READY 1
|
||||
#define DW_SUBPACKETID_MASTER_FINISHED 2
|
||||
#define DW_SUBPACKETID_WU_RESULTS 4
|
||||
#define DW_SUBPACKETID_WU_STARTED 6 // A worker telling the master it has started processing a work unit.
|
||||
// NOTE VMPI_DISTRIBUTE_WORK_EXTRA_SUBPACKET_BASE is where the IWorkUnitDistributorX classes start their subpackets.
|
||||
|
||||
|
||||
IWorkUnitDistributorCallbacks *g_pDistributeWorkCallbacks = NULL;
|
||||
|
||||
|
||||
static CDSInfo g_DSInfo;
|
||||
static unsigned short g_iCurDSInfo = (unsigned short)-1; // This is incremented each time DistributeWork is called.
|
||||
static int g_iMasterFinishedDistributeWorkCall = -1; // The worker stores this to know which DistributeWork() calls the master has finished.
|
||||
static int g_iMasterReadyForDistributeWorkCall = -1;
|
||||
|
||||
// This is only valid if we're a worker and if the worker currently has threads chewing on work units.
|
||||
static CDSInfo *g_pCurWorkerThreadsInfo = NULL;
|
||||
|
||||
static CUtlVector<uint64> g_wuCountByProcess;
|
||||
static uint64 g_totalWUCountByProcess[512];
|
||||
|
||||
static uint64 g_nWUs; // How many work units there were this time around.
|
||||
static uint64 g_nCompletedWUs; // How many work units completed.
|
||||
static uint64 g_nDuplicatedWUs; // How many times a worker sent results for a work unit that was already completed.
|
||||
|
||||
// Set to true if Error() is called and we want to exit early. vrad and vvis check for this in their
|
||||
// thread functions, so the workers quit early when the master is done rather than finishing up
|
||||
// potentially time-consuming work units they're working on.
|
||||
bool g_bVMPIEarlyExit = false;
|
||||
|
||||
static bool g_bMasterDistributingWork = false;
|
||||
|
||||
static IWorkUnitDistributorWorker *g_pCurDistributorWorker = NULL;
|
||||
static IWorkUnitDistributorMaster *g_pCurDistributorMaster = NULL;
|
||||
|
||||
// For the stats database.
|
||||
WUIndexType g_ThreadWUs[4] = { ~0ull, ~0ull, ~0ull, ~0ull };
|
||||
|
||||
class CMasterWorkUnitCompletedList
|
||||
{
|
||||
public:
|
||||
CUtlVector<WUIndexType> m_CompletedWUs;
|
||||
};
|
||||
static CCriticalSectionData<CMasterWorkUnitCompletedList> g_MasterWorkUnitCompletedList;
|
||||
|
||||
|
||||
int SortByWUCount( const void *elem1, const void *elem2 )
|
||||
{
|
||||
uint64 a = g_wuCountByProcess[ *((const int*)elem1) ];
|
||||
uint64 b = g_wuCountByProcess[ *((const int*)elem2) ];
|
||||
if ( a < b )
|
||||
return 1;
|
||||
else if ( a == b )
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void PrepareDistributeWorkHeader( MessageBuffer *pBuf, unsigned char cSubpacketID )
|
||||
{
|
||||
char cPacketID[2] = { g_DSInfo.m_cPacketID, cSubpacketID };
|
||||
pBuf->write( cPacketID, 2 );
|
||||
pBuf->write( &g_iCurDSInfo, sizeof( g_iCurDSInfo ) );
|
||||
}
|
||||
|
||||
|
||||
void ShowMPIStats(
|
||||
double flTimeSpent,
|
||||
unsigned long nBytesSent,
|
||||
unsigned long nBytesReceived,
|
||||
unsigned long nMessagesSent,
|
||||
unsigned long nMessagesReceived )
|
||||
{
|
||||
double flKSent = (nBytesSent + 511) / 1024;
|
||||
double flKRecv = (nBytesReceived + 511) / 1024;
|
||||
|
||||
bool bShowOutput = VMPI_IsParamUsed( mpi_ShowDistributeWorkStats );
|
||||
|
||||
bool bOldSuppress = g_bSuppressPrintfOutput;
|
||||
g_bSuppressPrintfOutput = !bShowOutput;
|
||||
|
||||
Msg( "\n\n--------------------------------------------------------------\n");
|
||||
Msg( "Total Time : %.2f\n", flTimeSpent );
|
||||
Msg( "Total Bytes Sent : %dk (%.2fk/sec, %d messages)\n", (int)flKSent, flKSent / flTimeSpent, nMessagesSent );
|
||||
Msg( "Total Bytes Recv : %dk (%.2fk/sec, %d messages)\n", (int)flKRecv, flKRecv / flTimeSpent, nMessagesReceived );
|
||||
if ( g_bMPIMaster )
|
||||
{
|
||||
Msg( "Duplicated WUs : %I64u (%.1f%%)\n", g_nDuplicatedWUs, (float)g_nDuplicatedWUs * 100.0f / g_nWUs );
|
||||
|
||||
Msg( "\nWU count by proc:\n" );
|
||||
|
||||
int nProcs = VMPI_GetCurrentNumberOfConnections();
|
||||
|
||||
CUtlVector<int> sortedProcs;
|
||||
sortedProcs.SetSize( nProcs );
|
||||
for ( int i=0; i < nProcs; i++ )
|
||||
sortedProcs[i] = i;
|
||||
|
||||
qsort( sortedProcs.Base(), nProcs, sizeof( int ), SortByWUCount );
|
||||
|
||||
for ( int i=0; i < nProcs; i++ )
|
||||
{
|
||||
const char *pMachineName = VMPI_GetMachineName( sortedProcs[i] );
|
||||
Msg( "%s", pMachineName );
|
||||
|
||||
char formatStr[512];
|
||||
Q_snprintf( formatStr, sizeof( formatStr ), "%%%ds %I64u\n", 30 - strlen( pMachineName ), g_wuCountByProcess[ sortedProcs[i] ] );
|
||||
Msg( formatStr, ":" );
|
||||
}
|
||||
}
|
||||
Msg( "--------------------------------------------------------------\n\n ");
|
||||
|
||||
g_bSuppressPrintfOutput = bOldSuppress;
|
||||
}
|
||||
|
||||
|
||||
void VMPI_DistributeWork_DisconnectHandler( int procID, const char *pReason )
|
||||
{
|
||||
if ( g_bMasterDistributingWork )
|
||||
{
|
||||
// Show the disconnect in the database but not on the screen.
|
||||
bool bOldSuppress = g_bSuppressPrintfOutput;
|
||||
g_bSuppressPrintfOutput = true;
|
||||
Msg( "VMPI_DistributeWork_DisconnectHandler( %d )\n", procID );
|
||||
g_bSuppressPrintfOutput = bOldSuppress;
|
||||
|
||||
// Redistribute the WUs from this guy's partition to another worker.
|
||||
g_pCurDistributorMaster->DisconnectHandler( procID );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint64 VMPI_GetNumWorkUnitsCompleted( int iProc )
|
||||
{
|
||||
Assert( iProc >= 0 && iProc <= ARRAYSIZE( g_totalWUCountByProcess ) );
|
||||
return g_totalWUCountByProcess[iProc];
|
||||
}
|
||||
|
||||
|
||||
void HandleWorkUnitCompleted( CDSInfo *pInfo, int iSource, WUIndexType iWorkUnit, MessageBuffer *pBuf )
|
||||
{
|
||||
VMPITracker_WorkUnitCompleted( ( int ) iWorkUnit, iSource );
|
||||
|
||||
if ( g_pCurDistributorMaster->HandleWorkUnitResults( iWorkUnit ) )
|
||||
{
|
||||
if ( g_iVMPIVerboseLevel >= 1 )
|
||||
Msg( "-" );
|
||||
|
||||
++ g_nCompletedWUs;
|
||||
++ g_wuCountByProcess[iSource];
|
||||
++ g_totalWUCountByProcess[iSource];
|
||||
|
||||
// Let the master process the incoming WU data.
|
||||
if ( pBuf )
|
||||
{
|
||||
pInfo->m_MasterInfo.m_ReceiveFn( iWorkUnit, pBuf, iSource );
|
||||
}
|
||||
|
||||
UpdatePacifier( float( g_nCompletedWUs ) / pInfo->m_nWorkUnits );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore it if we already got the results for this work unit.
|
||||
++ g_nDuplicatedWUs;
|
||||
if ( g_iVMPIVerboseLevel >= 1 )
|
||||
Msg( "*" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DistributeWorkDispatch( MessageBuffer *pBuf, int iSource, int iPacketID )
|
||||
{
|
||||
unsigned short iCurDistributeWorkCall = *((unsigned short*)&pBuf->data[2]);
|
||||
if ( iCurDistributeWorkCall >= MAX_DW_CALLS )
|
||||
Error( "Got an invalid DistributeWork packet (id: %d, sub: %d) (iCurDW: %d).", pBuf->data[0], pBuf->data[1], iCurDistributeWorkCall );
|
||||
|
||||
CDSInfo *pInfo = &g_DSInfo;
|
||||
|
||||
pBuf->setOffset( 4 );
|
||||
|
||||
switch ( pBuf->data[1] )
|
||||
{
|
||||
case DW_SUBPACKETID_MASTER_READY:
|
||||
{
|
||||
g_iMasterReadyForDistributeWorkCall = iCurDistributeWorkCall;
|
||||
return true;
|
||||
}
|
||||
|
||||
case DW_SUBPACKETID_WORKER_READY:
|
||||
{
|
||||
if ( iCurDistributeWorkCall > g_iCurDSInfo || !g_bMPIMaster )
|
||||
Error( "State incorrect on master for DW_SUBPACKETID_WORKER_READY packet from %s.", VMPI_GetMachineName( iSource ) );
|
||||
|
||||
if ( iCurDistributeWorkCall == g_iCurDSInfo )
|
||||
{
|
||||
// Ok, give this guy some WUs.
|
||||
if ( g_pCurDistributorMaster )
|
||||
g_pCurDistributorMaster->OnWorkerReady( iSource );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case DW_SUBPACKETID_MASTER_FINISHED:
|
||||
{
|
||||
g_iMasterFinishedDistributeWorkCall = iCurDistributeWorkCall;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Worker sends this to tell the master it has started on a work unit.
|
||||
case DW_SUBPACKETID_WU_STARTED:
|
||||
{
|
||||
if ( iCurDistributeWorkCall != g_iCurDSInfo )
|
||||
return true;
|
||||
|
||||
WUIndexType iWU;
|
||||
pBuf->read( &iWU, sizeof( iWU ) );
|
||||
VMPITracker_WorkUnitStarted( ( int ) iWU, iSource );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
case DW_SUBPACKETID_WU_RESULTS:
|
||||
{
|
||||
// We only care about work results for the iteration we're in.
|
||||
if ( iCurDistributeWorkCall != g_iCurDSInfo )
|
||||
return true;
|
||||
|
||||
WUIndexType iWorkUnit;
|
||||
pBuf->read( &iWorkUnit, sizeof( iWorkUnit ) );
|
||||
if ( iWorkUnit >= pInfo->m_nWorkUnits )
|
||||
{
|
||||
Error( "DistributeWork: got an invalid work unit index (%I64u for WU count of %I64u).", iWorkUnit, pInfo->m_nWorkUnits );
|
||||
}
|
||||
|
||||
HandleWorkUnitCompleted( pInfo, iSource, iWorkUnit, pBuf );
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if ( g_pCurDistributorMaster )
|
||||
return g_pCurDistributorMaster->HandlePacket( pBuf, iSource, iCurDistributeWorkCall != g_iCurDSInfo );
|
||||
else if ( g_pCurDistributorWorker )
|
||||
return g_pCurDistributorWorker->HandlePacket( pBuf, iSource, iCurDistributeWorkCall != g_iCurDSInfo );
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EWorkUnitDistributor VMPI_GetActiveWorkUnitDistributor()
|
||||
{
|
||||
if ( VMPI_IsParamUsed( mpi_UseSDKDistributor ) )
|
||||
{
|
||||
Msg( "Found %s.\n", VMPI_GetParamString( mpi_UseSDKDistributor ) );
|
||||
return k_eWorkUnitDistributor_SDK;
|
||||
}
|
||||
else if ( VMPI_IsParamUsed( mpi_UseDefaultDistributor ) )
|
||||
{
|
||||
Msg( "Found %s.\n", VMPI_GetParamString( mpi_UseDefaultDistributor ) );
|
||||
return k_eWorkUnitDistributor_Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( VMPI_IsSDKMode() )
|
||||
return k_eWorkUnitDistributor_SDK;
|
||||
else
|
||||
return k_eWorkUnitDistributor_Default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PreDistributeWorkSync( CDSInfo *pInfo )
|
||||
{
|
||||
if ( g_bMPIMaster )
|
||||
{
|
||||
// Send a message telling all the workers we're ready to go on this DistributeWork call.
|
||||
MessageBuffer mb;
|
||||
PrepareDistributeWorkHeader( &mb, DW_SUBPACKETID_MASTER_READY );
|
||||
VMPI_SendData( mb.data, mb.getLen(), VMPI_PERSISTENT );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( g_iVMPIVerboseLevel >= 1 )
|
||||
Msg( "PreDistributeWorkSync: waiting for master\n" );
|
||||
|
||||
// Wait for the master's message saying it's ready to go.
|
||||
while ( g_iMasterReadyForDistributeWorkCall < g_iCurDSInfo )
|
||||
{
|
||||
VMPI_DispatchNextMessage();
|
||||
}
|
||||
|
||||
if ( g_iVMPIVerboseLevel >= 1 )
|
||||
Msg( "PreDistributeWorkSync: master ready\n" );
|
||||
|
||||
// Now tell the master we're ready.
|
||||
MessageBuffer mb;
|
||||
PrepareDistributeWorkHeader( &mb, DW_SUBPACKETID_WORKER_READY );
|
||||
VMPI_SendData( mb.data, mb.getLen(), VMPI_MASTER_ID );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DistributeWork_Master( CDSInfo *pInfo, ProcessWorkUnitFn processFn, ReceiveWorkUnitFn receiveFn )
|
||||
{
|
||||
pInfo->m_WorkerInfo.m_pProcessFn = processFn;
|
||||
pInfo->m_MasterInfo.m_ReceiveFn = receiveFn;
|
||||
|
||||
VMPITracker_Start( (int) pInfo->m_nWorkUnits );
|
||||
|
||||
g_bMasterDistributingWork = true;
|
||||
g_pCurDistributorMaster->DistributeWork_Master( pInfo );
|
||||
g_bMasterDistributingWork = false;
|
||||
|
||||
VMPITracker_End();
|
||||
|
||||
// Tell all workers to move on.
|
||||
MessageBuffer mb;
|
||||
PrepareDistributeWorkHeader( &mb, DW_SUBPACKETID_MASTER_FINISHED );
|
||||
VMPI_SendData( mb.data, mb.getLen(), VMPI_PERSISTENT );
|
||||
|
||||
// Clear the master's local completed work unit list.
|
||||
CMasterWorkUnitCompletedList *pList = g_MasterWorkUnitCompletedList.Lock();
|
||||
pList->m_CompletedWUs.RemoveAll();
|
||||
g_MasterWorkUnitCompletedList.Unlock();
|
||||
}
|
||||
|
||||
|
||||
void NotifyLocalMasterCompletedWorkUnit( WUIndexType iWorkUnit )
|
||||
{
|
||||
CMasterWorkUnitCompletedList *pList = g_MasterWorkUnitCompletedList.Lock();
|
||||
pList->m_CompletedWUs.AddToTail( iWorkUnit );
|
||||
g_MasterWorkUnitCompletedList.Unlock();
|
||||
}
|
||||
|
||||
void CheckLocalMasterCompletedWorkUnits()
|
||||
{
|
||||
CMasterWorkUnitCompletedList *pList = g_MasterWorkUnitCompletedList.Lock();
|
||||
|
||||
for ( int i=0; i < pList->m_CompletedWUs.Count(); i++ )
|
||||
{
|
||||
HandleWorkUnitCompleted( &g_DSInfo, 0, pList->m_CompletedWUs[i], NULL );
|
||||
}
|
||||
pList->m_CompletedWUs.RemoveAll();
|
||||
|
||||
g_MasterWorkUnitCompletedList.Unlock();
|
||||
}
|
||||
|
||||
|
||||
void TellMasterThatWorkerStartedAWorkUnit( MessageBuffer &mb, CDSInfo *pInfo, WUIndexType iWU )
|
||||
{
|
||||
mb.setLen( 0 );
|
||||
PrepareDistributeWorkHeader( &mb, DW_SUBPACKETID_WU_STARTED );
|
||||
mb.write( &iWU, sizeof( iWU ) );
|
||||
VMPI_SendData( mb.data, mb.getLen(), VMPI_MASTER_ID, k_eVMPISendFlags_GroupPackets );
|
||||
}
|
||||
|
||||
|
||||
void VMPI_WorkerThread( int iThread, void *pUserData )
|
||||
{
|
||||
CDSInfo *pInfo = (CDSInfo*)pUserData;
|
||||
CWorkerInfo *pWorkerInfo = &pInfo->m_WorkerInfo;
|
||||
|
||||
|
||||
// Get our index for running work units
|
||||
uint64 idxRunningWorkUnit = (uint64) iThread;
|
||||
{
|
||||
CCriticalSectionLock csLock( &pWorkerInfo->m_WorkUnitsRunningCS );
|
||||
csLock.Lock();
|
||||
pWorkerInfo->m_WorkUnitsRunning.ExpandWindow( idxRunningWorkUnit, ~0ull );
|
||||
csLock.Unlock();
|
||||
}
|
||||
|
||||
|
||||
MessageBuffer mb;
|
||||
PrepareDistributeWorkHeader( &mb, DW_SUBPACKETID_WU_RESULTS );
|
||||
|
||||
MessageBuffer mbStartedWorkUnit; // Special messagebuffer used to tell the master when we started a work unit.
|
||||
|
||||
while ( g_iMasterFinishedDistributeWorkCall < g_iCurDSInfo && !g_bVMPIEarlyExit )
|
||||
{
|
||||
WUIndexType iWU;
|
||||
|
||||
// Quit out when there are no more work units.
|
||||
if ( !g_pCurDistributorWorker->GetNextWorkUnit( &iWU ) )
|
||||
{
|
||||
// Wait until there are some WUs to do. This should probably use event handles.
|
||||
VMPI_Sleep( 10 );
|
||||
continue;
|
||||
}
|
||||
|
||||
CCriticalSectionLock csLock( &pWorkerInfo->m_WorkUnitsRunningCS );
|
||||
csLock.Lock();
|
||||
|
||||
// Check if this WU is not running
|
||||
WUIndexType const *pBegin = &pWorkerInfo->m_WorkUnitsRunning.Get( 0ull ), *pEnd = pBegin + pWorkerInfo->m_WorkUnitsRunning.PastVisibleIndex();
|
||||
WUIndexType const *pRunningWu = GenericFind( pBegin, pEnd, iWU );
|
||||
if ( pRunningWu != pEnd )
|
||||
continue;
|
||||
|
||||
// We are running it
|
||||
pWorkerInfo->m_WorkUnitsRunning.Get( idxRunningWorkUnit ) = iWU;
|
||||
|
||||
csLock.Unlock();
|
||||
|
||||
|
||||
// Process this WU and send the results to the master.
|
||||
mb.setLen( 4 );
|
||||
mb.write( &iWU, sizeof( iWU ) );
|
||||
|
||||
// Set the current WU for the stats database.
|
||||
if ( iThread >= 0 && iThread < 4 )
|
||||
{
|
||||
g_ThreadWUs[iThread] = iWU;
|
||||
}
|
||||
|
||||
// Tell the master we're starting on this WU.
|
||||
TellMasterThatWorkerStartedAWorkUnit( mbStartedWorkUnit, pInfo, iWU );
|
||||
|
||||
|
||||
pWorkerInfo->m_pProcessFn( iThread, iWU, &mb );
|
||||
g_pCurDistributorWorker->NoteLocalWorkUnitCompleted( iWU );
|
||||
|
||||
VMPI_SendData( mb.data, mb.getLen(), VMPI_MASTER_ID, /*k_eVMPISendFlags_GroupPackets*/0 );
|
||||
|
||||
// Flush grouped packets every once in a while.
|
||||
//VMPI_FlushGroupedPackets( 1000 );
|
||||
}
|
||||
|
||||
if ( g_iVMPIVerboseLevel >= 1 )
|
||||
Msg( "Worker thread exiting.\n" );
|
||||
}
|
||||
|
||||
|
||||
void DistributeWork_Worker( CDSInfo *pInfo, ProcessWorkUnitFn processFn )
|
||||
{
|
||||
if ( g_iVMPIVerboseLevel >= 1 )
|
||||
Msg( "VMPI_DistributeWork call %d started.\n", g_iCurDSInfo+1 );
|
||||
|
||||
CWorkerInfo *pWorkerInfo = &pInfo->m_WorkerInfo;
|
||||
pWorkerInfo->m_pProcessFn = processFn;
|
||||
|
||||
g_pCurWorkerThreadsInfo = pInfo;
|
||||
g_pCurDistributorWorker->Init( pInfo );
|
||||
|
||||
// Start a couple threads to do the work.
|
||||
RunThreads_Start( VMPI_WorkerThread, pInfo, g_bSetThreadPriorities ? k_eRunThreadsPriority_Idle : k_eRunThreadsPriority_UseGlobalState );
|
||||
if ( g_iVMPIVerboseLevel >= 1 )
|
||||
Msg( "RunThreads_Start finished successfully.\n" );
|
||||
|
||||
if ( VMPI_IsSDKMode() )
|
||||
{
|
||||
Msg( "\n" );
|
||||
while ( g_iMasterFinishedDistributeWorkCall < g_iCurDSInfo )
|
||||
{
|
||||
VMPI_DispatchNextMessage( 300 );
|
||||
|
||||
Msg( "\rThreads status: " );
|
||||
for ( int i=0; i < ARRAYSIZE( g_ThreadWUs ); i++ )
|
||||
{
|
||||
if ( g_ThreadWUs[i] != ~0ull )
|
||||
Msg( "%d: WU %5d ", i, (int)g_ThreadWUs[i] );
|
||||
}
|
||||
|
||||
VMPI_FlushGroupedPackets();
|
||||
}
|
||||
Msg( "\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
while ( g_iMasterFinishedDistributeWorkCall < g_iCurDSInfo )
|
||||
{
|
||||
VMPI_DispatchNextMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Close the threads.
|
||||
g_pCurWorkerThreadsInfo = NULL;
|
||||
RunThreads_End();
|
||||
|
||||
if ( g_iVMPIVerboseLevel >= 1 )
|
||||
Msg( "VMPI_DistributeWork call %d finished.\n", g_iCurDSInfo+1 );
|
||||
}
|
||||
|
||||
|
||||
// This is called by VMPI_Finalize in case it's shutting down due to an Error() call.
|
||||
// In this case, it's important that the worker threads here are shut down before VMPI shuts
|
||||
// down its sockets.
|
||||
void DistributeWork_Cancel()
|
||||
{
|
||||
if ( g_pCurWorkerThreadsInfo )
|
||||
{
|
||||
Msg( "\nDistributeWork_Cancel saves the day!\n" );
|
||||
g_pCurWorkerThreadsInfo->m_bMasterFinished = true;
|
||||
g_bVMPIEarlyExit = true;
|
||||
RunThreads_End();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns time it took to finish the work.
|
||||
double DistributeWork(
|
||||
uint64 nWorkUnits, // how many work units to dole out
|
||||
char cPacketID,
|
||||
ProcessWorkUnitFn processFn, // workers implement this to process a work unit and send results back
|
||||
ReceiveWorkUnitFn receiveFn // the master implements this to receive a work unit
|
||||
)
|
||||
{
|
||||
++g_iCurDSInfo;
|
||||
|
||||
if ( g_iCurDSInfo == 0 )
|
||||
{
|
||||
// Register our disconnect handler so we can deal with it if clients bail out.
|
||||
if ( g_bMPIMaster )
|
||||
{
|
||||
VMPI_AddDisconnectHandler( VMPI_DistributeWork_DisconnectHandler );
|
||||
}
|
||||
}
|
||||
else if ( g_iCurDSInfo >= MAX_DW_CALLS )
|
||||
{
|
||||
Error( "DistributeWork: called more than %d times.\n", MAX_DW_CALLS );
|
||||
}
|
||||
|
||||
CDSInfo *pInfo = &g_DSInfo;
|
||||
|
||||
pInfo->m_cPacketID = cPacketID;
|
||||
pInfo->m_nWorkUnits = nWorkUnits;
|
||||
|
||||
// Make all the workers wait until the master is ready.
|
||||
PreDistributeWorkSync( pInfo );
|
||||
|
||||
g_nWUs = nWorkUnits;
|
||||
g_nCompletedWUs = 0ull;
|
||||
g_nDuplicatedWUs = 0ull;
|
||||
|
||||
// Setup stats info.
|
||||
double flMPIStartTime = Plat_FloatTime();
|
||||
g_wuCountByProcess.SetCount( 512 );
|
||||
memset( g_wuCountByProcess.Base(), 0, sizeof( int ) * g_wuCountByProcess.Count() );
|
||||
|
||||
unsigned long nBytesSentStart = g_nBytesSent;
|
||||
unsigned long nBytesReceivedStart = g_nBytesReceived;
|
||||
unsigned long nMessagesSentStart = g_nMessagesSent;
|
||||
unsigned long nMessagesReceivedStart = g_nMessagesReceived;
|
||||
|
||||
EWorkUnitDistributor eWorkUnitDistributor = VMPI_GetActiveWorkUnitDistributor();
|
||||
if ( g_bMPIMaster )
|
||||
{
|
||||
Assert( !g_pCurDistributorMaster );
|
||||
g_pCurDistributorMaster = ( eWorkUnitDistributor == k_eWorkUnitDistributor_SDK ? CreateWUDistributor_SDKMaster() : CreateWUDistributor_DefaultMaster() );
|
||||
|
||||
DistributeWork_Master( pInfo, processFn, receiveFn );
|
||||
|
||||
g_pCurDistributorMaster->Release();
|
||||
g_pCurDistributorMaster = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( !g_pCurDistributorWorker );
|
||||
g_pCurDistributorWorker = ( eWorkUnitDistributor == k_eWorkUnitDistributor_SDK ? CreateWUDistributor_SDKWorker() : CreateWUDistributor_DefaultWorker() );
|
||||
|
||||
DistributeWork_Worker( pInfo, processFn );
|
||||
|
||||
g_pCurDistributorWorker->Release();
|
||||
g_pCurDistributorWorker = NULL;
|
||||
}
|
||||
|
||||
double flTimeSpent = Plat_FloatTime() - flMPIStartTime;
|
||||
ShowMPIStats(
|
||||
flTimeSpent,
|
||||
g_nBytesSent - nBytesSentStart,
|
||||
g_nBytesReceived - nBytesReceivedStart,
|
||||
g_nMessagesSent - nMessagesSentStart,
|
||||
g_nMessagesReceived - nMessagesReceivedStart
|
||||
);
|
||||
|
||||
// Mark that the threads aren't working on anything at the moment.
|
||||
for ( int i=0; i < ARRAYSIZE( g_ThreadWUs ); i++ )
|
||||
g_ThreadWUs[i] = ~0ull;
|
||||
|
||||
return flTimeSpent;
|
||||
}
|
||||
89
utils/vmpi/vmpi_distribute_work.h
Normal file
89
utils/vmpi/vmpi_distribute_work.h
Normal file
@@ -0,0 +1,89 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef VMPI_DISTRIBUTE_WORK_H
|
||||
#define VMPI_DISTRIBUTE_WORK_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "messbuf.h"
|
||||
#include "utlvector.h"
|
||||
|
||||
|
||||
class IWorkUnitDistributorCallbacks
|
||||
{
|
||||
public:
|
||||
// Called every 200ms or so as it does the work.
|
||||
// Return true to stop distributing work.
|
||||
virtual bool Update() { return false; }
|
||||
|
||||
// Called when a subsequent number of work units is completed.
|
||||
// e.g. results received in the following order will trigger
|
||||
// the following calls to OnWorkUnitsCompleted:
|
||||
// Work unit numbers: wu2 wu4 wu5 wu1 wu0 wu6 wu3
|
||||
// Calling OnWorkUnitsCompleted with arg: - - - - 3 - 7
|
||||
// because when wu0 is received we already have { wu0, wu1, wu2 } so we signal
|
||||
// that 3 subsequent work units completed, like wise by the time when wu3 is
|
||||
// received we already have a full set { wu0, wu1, wu2, wu3, wu4, wu5, wu6 }
|
||||
// and signal that 7 work units completed.
|
||||
virtual void OnWorkUnitsCompleted( uint64 numWorkUnits ) { return; }
|
||||
};
|
||||
|
||||
|
||||
enum EWorkUnitDistributor
|
||||
{
|
||||
k_eWorkUnitDistributor_Default,
|
||||
k_eWorkUnitDistributor_SDK
|
||||
};
|
||||
|
||||
// Tells which work unit distributor is going to be used.
|
||||
EWorkUnitDistributor VMPI_GetActiveWorkUnitDistributor();
|
||||
|
||||
|
||||
// Before calling DistributeWork, you can set this and it'll call your virtual functions.
|
||||
extern IWorkUnitDistributorCallbacks *g_pDistributeWorkCallbacks;
|
||||
|
||||
|
||||
// You must append data to pBuf with the work unit results.
|
||||
// Note: pBuf will be NULL if this is a local thread doing work on the master.
|
||||
typedef void (*ProcessWorkUnitFn)( int iThread, uint64 iWorkUnit, MessageBuffer *pBuf );
|
||||
|
||||
// pBuf is ready to read the results written to the buffer in ProcessWorkUnitFn.
|
||||
typedef void (*ReceiveWorkUnitFn)( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker );
|
||||
|
||||
|
||||
// Use a CDispatchReg to register this function with whatever packet ID you give to DistributeWork.
|
||||
bool DistributeWorkDispatch( MessageBuffer *pBuf, int iSource, int iPacketID );
|
||||
|
||||
|
||||
|
||||
// This is the function vrad and vvis use to divide the work units and send them out.
|
||||
// It maintains a sliding window of work units so it can always keep the clients busy.
|
||||
//
|
||||
// The workers implement processFn to do the job work in a work unit.
|
||||
// This function must send back a packet formatted with:
|
||||
// cPacketID (char), cSubPacketID (char), iWorkUnit (int), (app-specific data for the results)
|
||||
//
|
||||
// The masters implement receiveFn to receive a work unit's results.
|
||||
//
|
||||
// Returns time it took to finish the work.
|
||||
double DistributeWork(
|
||||
uint64 nWorkUnits, // how many work units to dole out
|
||||
char cPacketID, // This packet ID must be reserved for DistributeWork and DistributeWorkDispatch
|
||||
// must be registered with it.
|
||||
ProcessWorkUnitFn processFn, // workers implement this to process a work unit and send results back
|
||||
ReceiveWorkUnitFn receiveFn // the master implements this to receive a work unit
|
||||
);
|
||||
|
||||
|
||||
// VMPI calls this before shutting down because any threads that DistributeWork has running must stop,
|
||||
// otherwise it can crash if a thread tries to send data in the middle of shutting down.
|
||||
void DistributeWork_Cancel();
|
||||
|
||||
|
||||
#endif // VMPI_DISTRIBUTE_WORK_H
|
||||
602
utils/vmpi/vmpi_distribute_work_default.cpp
Normal file
602
utils/vmpi/vmpi_distribute_work_default.cpp
Normal file
@@ -0,0 +1,602 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "vmpi.h"
|
||||
#include "vmpi_distribute_work.h"
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "utlvector.h"
|
||||
#include "utllinkedlist.h"
|
||||
#include "vmpi_dispatch.h"
|
||||
#include "pacifier.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "threadhelpers.h"
|
||||
#include "threads.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "tier1/utlmap.h"
|
||||
#include "tier1/smartptr.h"
|
||||
#include "tier0/icommandline.h"
|
||||
#include "cmdlib.h"
|
||||
#include "vmpi_distribute_tracker.h"
|
||||
#include "vmpi_distribute_work_internal.h"
|
||||
|
||||
|
||||
#define DW_SUBPACKETID_WU_ASSIGNMENT (VMPI_DISTRIBUTE_WORK_EXTRA_SUBPACKET_BASE+0)
|
||||
|
||||
|
||||
|
||||
static int s_numWusToDeal = -1;
|
||||
|
||||
void VMPI_SetWorkUnitsPartitionSize( int numWusToDeal )
|
||||
{
|
||||
s_numWusToDeal = numWusToDeal;
|
||||
}
|
||||
|
||||
|
||||
class CWorkUnitInfo
|
||||
{
|
||||
public:
|
||||
WUIndexType m_iWorkUnit;
|
||||
};
|
||||
|
||||
|
||||
class CWULookupInfo
|
||||
{
|
||||
public:
|
||||
CWULookupInfo() : m_iWUInfo( -1 ), m_iPartition( -222222 ), m_iPartitionListIndex( -1 ) {}
|
||||
|
||||
public:
|
||||
int m_iWUInfo; // Index into m_WUInfo.
|
||||
int m_iPartition; // Which partition it's in.
|
||||
int m_iPartitionListIndex; // Index into its partition's m_WUs.
|
||||
};
|
||||
|
||||
|
||||
class CPartitionInfo
|
||||
{
|
||||
public:
|
||||
typedef CUtlLinkedList< WUIndexType, int > PartitionWUs;
|
||||
|
||||
public:
|
||||
int m_iPartition; // Index into m_Partitions.
|
||||
int m_iWorker; // Who owns this partition?
|
||||
PartitionWUs m_WUs; // Which WUs are in this partition?
|
||||
};
|
||||
|
||||
|
||||
// Work units tracker to track consecutive finished blocks
|
||||
class CWorkUnitsTracker
|
||||
{
|
||||
public:
|
||||
CWorkUnitsTracker() {}
|
||||
|
||||
public:
|
||||
// Initializes the unit tracker to receive numUnits in future
|
||||
void PrepareForWorkUnits( uint64 numUnits );
|
||||
// Signals that a work unit has been finished
|
||||
// returns a zero-based index of the next pending work unit
|
||||
// up to which the task list has been processed fully now
|
||||
// because the received work unit filled the gap or was the next pending work unit.
|
||||
// returns 0 to indicate that this work unit is a "faster processed future work unit".
|
||||
uint64 WorkUnitFinished( uint64 iWorkUnit );
|
||||
|
||||
public:
|
||||
enum WUInfo { kNone, kTrigger, kDone };
|
||||
CVisibleWindowVector< uint8 > m_arrInfo;
|
||||
};
|
||||
|
||||
void CWorkUnitsTracker::PrepareForWorkUnits( uint64 numUnits )
|
||||
{
|
||||
m_arrInfo.Reset( numUnits + 1 );
|
||||
|
||||
if ( numUnits )
|
||||
{
|
||||
m_arrInfo.ExpandWindow( 2ull, kNone );
|
||||
m_arrInfo.Get( 0ull ) = kTrigger;
|
||||
}
|
||||
}
|
||||
|
||||
uint64 CWorkUnitsTracker::WorkUnitFinished( uint64 iWorkUnit )
|
||||
{
|
||||
uint64 uiResult = uint64( 0 );
|
||||
|
||||
if ( iWorkUnit >= m_arrInfo.FirstPossibleIndex() && iWorkUnit < m_arrInfo.PastPossibleIndex() )
|
||||
{
|
||||
// Need to access the element
|
||||
m_arrInfo.ExpandWindow( iWorkUnit + 1, kNone );
|
||||
|
||||
// Set it done
|
||||
uint8 &rchThere = m_arrInfo.Get( iWorkUnit ), chThere = rchThere;
|
||||
rchThere = kDone;
|
||||
|
||||
// Should we trigger?
|
||||
if ( kTrigger == chThere )
|
||||
{
|
||||
// Go along all "done" work units and trigger the last found one
|
||||
while ( ( ( ++ iWorkUnit ) < m_arrInfo.PastVisibleIndex() ) &&
|
||||
( kDone == m_arrInfo.Get( iWorkUnit ) ) )
|
||||
continue;
|
||||
|
||||
m_arrInfo.Get( iWorkUnit ) = kTrigger;
|
||||
m_arrInfo.ShrinkWindow( iWorkUnit - 1 );
|
||||
uiResult = iWorkUnit;
|
||||
}
|
||||
else if( iWorkUnit == m_arrInfo.FirstPossibleIndex() )
|
||||
{
|
||||
// Go along all "done" work units and shrink including the last found one
|
||||
while ( ( ( ++ iWorkUnit ) < m_arrInfo.PastVisibleIndex() ) &&
|
||||
( kDone == m_arrInfo.Get( iWorkUnit ) ) )
|
||||
continue;
|
||||
|
||||
m_arrInfo.ShrinkWindow( iWorkUnit - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
return uiResult;
|
||||
}
|
||||
|
||||
CWorkUnitsTracker g_MasterWorkUnitsTracker;
|
||||
|
||||
|
||||
|
||||
static bool CompareSoonestWorkUnitSets( CPartitionInfo::PartitionWUs * const &x, CPartitionInfo::PartitionWUs * const &y )
|
||||
{
|
||||
// Compare by fourth/second/first job in the partitions
|
||||
WUIndexType missing = ~WUIndexType(0);
|
||||
WUIndexType jobsX[4] = { missing, missing, missing, missing };
|
||||
WUIndexType jobsY[4] = { missing, missing, missing, missing };
|
||||
int counter = 0;
|
||||
|
||||
counter = 0;
|
||||
FOR_EACH_LL( (*x), i )
|
||||
{
|
||||
jobsX[ counter ++ ] = (*x)[i];
|
||||
if ( counter >= 4 )
|
||||
break;
|
||||
}
|
||||
|
||||
counter = 0;
|
||||
FOR_EACH_LL( (*y), i )
|
||||
{
|
||||
jobsY[ counter ++ ] = (*y)[i];
|
||||
if ( counter >= 4 )
|
||||
break;
|
||||
}
|
||||
|
||||
// Compare
|
||||
if ( jobsX[3] != jobsY[3] )
|
||||
return ( jobsX[3] < jobsY[3] );
|
||||
|
||||
if ( jobsX[1] != jobsY[1] )
|
||||
return ( jobsX[1] < jobsY[1] );
|
||||
|
||||
return jobsX[0] < jobsY[0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CDistributor_DefaultMaster : public IWorkUnitDistributorMaster
|
||||
{
|
||||
public:
|
||||
virtual void Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual void DistributeWork_Master( CDSInfo *pInfo )
|
||||
{
|
||||
m_pInfo = pInfo;
|
||||
g_MasterWorkUnitsTracker.PrepareForWorkUnits( m_pInfo->m_nWorkUnits );
|
||||
|
||||
m_WULookup.Reset( pInfo->m_nWorkUnits );
|
||||
while ( m_WULookup.FirstPossibleIndex() < m_WULookup.PastPossibleIndex() )
|
||||
{
|
||||
VMPI_DispatchNextMessage( 200 );
|
||||
|
||||
VMPITracker_HandleDebugKeypresses();
|
||||
|
||||
if ( g_pDistributeWorkCallbacks && g_pDistributeWorkCallbacks->Update() )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnWorkerReady( int iSource )
|
||||
{
|
||||
AssignWUsToWorker( iSource );
|
||||
}
|
||||
|
||||
virtual bool HandleWorkUnitResults( WUIndexType iWorkUnit )
|
||||
{
|
||||
CWULookupInfo *pLookup = NULL;
|
||||
if ( iWorkUnit >= m_WULookup.FirstPossibleIndex() && iWorkUnit < m_WULookup.PastVisibleIndex() )
|
||||
pLookup = &m_WULookup.Get( iWorkUnit );
|
||||
|
||||
if ( !pLookup || pLookup->m_iWUInfo == -1 )
|
||||
return false;
|
||||
|
||||
// Mark this WU finished and remove it from the list of pending WUs.
|
||||
m_WUInfo.Remove( pLookup->m_iWUInfo );
|
||||
pLookup->m_iWUInfo = -1;
|
||||
|
||||
|
||||
// Get rid of the WU from its partition.
|
||||
int iPartition = pLookup->m_iPartition;
|
||||
CPartitionInfo *pPartition = m_Partitions[iPartition];
|
||||
pPartition->m_WUs.Remove( pLookup->m_iPartitionListIndex );
|
||||
|
||||
// Shrink the window of the lookup work units
|
||||
if ( iWorkUnit == m_WULookup.FirstPossibleIndex() )
|
||||
{
|
||||
WUIndexType kwu = iWorkUnit;
|
||||
for ( WUIndexType kwuEnd = m_WULookup.PastVisibleIndex(); kwu < kwuEnd; ++ kwu )
|
||||
{
|
||||
if ( -1 != m_WULookup.Get( kwu ).m_iWUInfo && kwu > iWorkUnit )
|
||||
break;
|
||||
}
|
||||
m_WULookup.ShrinkWindow( kwu - 1 );
|
||||
}
|
||||
|
||||
// Give the worker some new work if need be.
|
||||
if ( pPartition->m_WUs.Count() == 0 )
|
||||
{
|
||||
int iPartitionWorker = pPartition->m_iWorker;
|
||||
delete pPartition;
|
||||
m_Partitions.Remove( iPartition );
|
||||
|
||||
// If there are any more WUs remaining, give the worker from this partition some more of them.
|
||||
if ( m_WULookup.FirstPossibleIndex() < m_WULookup.PastPossibleIndex() )
|
||||
{
|
||||
AssignWUsToWorker( iPartitionWorker );
|
||||
}
|
||||
}
|
||||
|
||||
uint64 iDoneWorkUnits = g_MasterWorkUnitsTracker.WorkUnitFinished( iWorkUnit );
|
||||
if ( iDoneWorkUnits && g_pDistributeWorkCallbacks )
|
||||
{
|
||||
g_pDistributeWorkCallbacks->OnWorkUnitsCompleted( iDoneWorkUnits );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void DisconnectHandler( int workerID )
|
||||
{
|
||||
int iPartitionLookup = FindPartitionByWorker( workerID );
|
||||
if ( iPartitionLookup != -1 )
|
||||
{
|
||||
// Mark this guy's partition as unowned so another worker can get it.
|
||||
CPartitionInfo *pPartition = m_Partitions[iPartitionLookup];
|
||||
pPartition->m_iWorker = -1;
|
||||
}
|
||||
}
|
||||
|
||||
CPartitionInfo* AddPartition( int iWorker )
|
||||
{
|
||||
CPartitionInfo *pNew = new CPartitionInfo;
|
||||
pNew->m_iPartition = m_Partitions.AddToTail( pNew );
|
||||
pNew->m_iWorker = iWorker;
|
||||
return pNew;
|
||||
}
|
||||
|
||||
bool SplitWUsPartition( CPartitionInfo *pPartitionLarge,
|
||||
CPartitionInfo **ppFirstHalf, CPartitionInfo **ppSecondHalf,
|
||||
int iFirstHalfWorker, int iSecondHalfWorker )
|
||||
{
|
||||
int nCount = pPartitionLarge->m_WUs.Count();
|
||||
|
||||
if ( nCount > 1 ) // Allocate the partitions for the two workers
|
||||
{
|
||||
*ppFirstHalf = AddPartition( iFirstHalfWorker );
|
||||
*ppSecondHalf = AddPartition( iSecondHalfWorker );
|
||||
}
|
||||
else // Specially transfer a partition with too few work units
|
||||
{
|
||||
*ppFirstHalf = NULL;
|
||||
*ppSecondHalf = AddPartition( iSecondHalfWorker );
|
||||
}
|
||||
|
||||
// Prepare for transfer
|
||||
CPartitionInfo *arrNewParts[2] = { *ppFirstHalf ? *ppFirstHalf : *ppSecondHalf, *ppSecondHalf };
|
||||
|
||||
// Transfer the work units:
|
||||
// alternate first/second halves
|
||||
// don't put more than "half deal units" tasks into the second half
|
||||
// e.g. { 1, 2, 3, 4 }
|
||||
// becomes: 1st half { 1, 2 }, 2nd half { 3, 4 }
|
||||
for ( int k = 0; k < nCount; ++ k )
|
||||
{
|
||||
int iHead = pPartitionLarge->m_WUs.Head();
|
||||
WUIndexType iWU = pPartitionLarge->m_WUs[ iHead ];
|
||||
pPartitionLarge->m_WUs.Remove( iHead );
|
||||
|
||||
/*
|
||||
int nHalf = !!( ( k % 2 ) || ( k >= nCount - 1 ) );
|
||||
if ( k == 5 ) // no more than 2 jobs to branch off
|
||||
arrNewParts[ 1 ] = arrNewParts[ 0 ];
|
||||
*/
|
||||
int nHalf = !( k < nCount/2 );
|
||||
CPartitionInfo *pTo = arrNewParts[ nHalf ];
|
||||
|
||||
CWULookupInfo &li = m_WULookup.Get( iWU );
|
||||
li.m_iPartition = pTo->m_iPartition;
|
||||
li.m_iPartitionListIndex = pTo->m_WUs.AddToTail( iWU );
|
||||
}
|
||||
|
||||
// LogPartitionsWorkUnits( pInfo );
|
||||
return true;
|
||||
}
|
||||
|
||||
void AssignWUsToWorker( int iWorker )
|
||||
{
|
||||
// Get rid of this worker's old partition.
|
||||
int iPrevious = FindPartitionByWorker( iWorker );
|
||||
if ( iPrevious != -1 )
|
||||
{
|
||||
delete m_Partitions[iPrevious];
|
||||
m_Partitions.Remove( iPrevious );
|
||||
}
|
||||
|
||||
if ( g_iVMPIVerboseLevel >= 1 )
|
||||
Msg( "A" );
|
||||
|
||||
|
||||
CVisibleWindowVector< CWULookupInfo > &vlkup = m_WULookup;
|
||||
if ( CommandLine()->FindParm( "-mpi_NoScheduler" ) )
|
||||
{
|
||||
Warning( "\n\n-mpi_NoScheduler found: Warning - this should only be used for testing and with 1 worker!\n\n" );
|
||||
vlkup.ExpandWindow( m_pInfo->m_nWorkUnits );
|
||||
CPartitionInfo *pPartition = AddPartition( iWorker );
|
||||
for ( int i=0; i < m_pInfo->m_nWorkUnits; i++ )
|
||||
{
|
||||
CWorkUnitInfo info;
|
||||
info.m_iWorkUnit = i;
|
||||
|
||||
CWULookupInfo &li = vlkup.Get( i );
|
||||
li.m_iPartition = pPartition->m_iPartition;
|
||||
li.m_iPartitionListIndex = pPartition->m_WUs.AddToTail( i );
|
||||
li.m_iWUInfo = m_WUInfo.AddToTail( info );
|
||||
}
|
||||
|
||||
SendPartitionToWorker( pPartition, iWorker );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Any partitions abandoned by workers?
|
||||
int iAbandonedPartition = FindPartitionByWorker( -1 );
|
||||
if ( -1 != iAbandonedPartition )
|
||||
{
|
||||
CPartitionInfo *pPartition = m_Partitions[ iAbandonedPartition ];
|
||||
pPartition->m_iWorker = iWorker;
|
||||
SendPartitionToWorker( pPartition, iWorker );
|
||||
}
|
||||
|
||||
// Any absolutely untouched partitions yet?
|
||||
else if ( vlkup.PastVisibleIndex() < vlkup.PastPossibleIndex() )
|
||||
{
|
||||
// Figure out how many WUs to include in a batch
|
||||
int numWusToDeal = s_numWusToDeal;
|
||||
if ( numWusToDeal <= 0 )
|
||||
{
|
||||
uint64 uiFraction = vlkup.PastPossibleIndex() / g_nMaxWorkerCount;
|
||||
Assert( uiFraction < INT_MAX/2 );
|
||||
|
||||
numWusToDeal = int( uiFraction );
|
||||
if ( numWusToDeal <= 0 )
|
||||
numWusToDeal = 8;
|
||||
}
|
||||
|
||||
// Allocate room for upcoming work units lookup
|
||||
WUIndexType iBegin = vlkup.PastVisibleIndex();
|
||||
WUIndexType iEnd = min( iBegin + g_nMaxWorkerCount * numWusToDeal, vlkup.PastPossibleIndex() );
|
||||
vlkup.ExpandWindow( iEnd - 1 );
|
||||
|
||||
// Allocate a partition
|
||||
size_t numPartitions = min( ( size_t )(iEnd - iBegin), ( size_t )g_nMaxWorkerCount );
|
||||
CArrayAutoPtr< CPartitionInfo * > spArrPartitions( new CPartitionInfo* [ numPartitions ] );
|
||||
CPartitionInfo **arrPartitions = spArrPartitions.Get();
|
||||
|
||||
arrPartitions[0] = AddPartition( iWorker );
|
||||
for ( size_t k = 1; k < numPartitions; ++ k )
|
||||
arrPartitions[k] = AddPartition( -1 );
|
||||
|
||||
// Assign upcoming work units to the partitions.
|
||||
for ( WUIndexType i = iBegin ; i < iEnd; ++ i )
|
||||
{
|
||||
CWorkUnitInfo info;
|
||||
info.m_iWorkUnit = i;
|
||||
|
||||
CPartitionInfo *pPartition = arrPartitions[ size_t( (i - iBegin) % numPartitions ) ];
|
||||
|
||||
CWULookupInfo &li = vlkup.Get( i );
|
||||
li.m_iPartition = pPartition->m_iPartition;
|
||||
li.m_iPartitionListIndex = pPartition->m_WUs.AddToTail( i );
|
||||
li.m_iWUInfo = m_WUInfo.AddToTail( info );
|
||||
}
|
||||
|
||||
// Now send this guy the WU list in his partition.
|
||||
SendPartitionToWorker( arrPartitions[0], iWorker );
|
||||
}
|
||||
|
||||
// Split one of the last partitions to finish sooner
|
||||
else
|
||||
{
|
||||
// Find a partition to split.
|
||||
int iPartToSplit = FindSoonestPartition();
|
||||
if ( iPartToSplit >= 0 )
|
||||
{
|
||||
CPartitionInfo *pPartition = m_Partitions[ iPartToSplit ];
|
||||
|
||||
CPartitionInfo *pOldHalf = NULL, *pNewHalf = NULL;
|
||||
int iOldWorker = pPartition->m_iWorker, iNewWorker = iWorker;
|
||||
if ( SplitWUsPartition( pPartition, &pOldHalf, &pNewHalf, iOldWorker, iNewWorker ) )
|
||||
{
|
||||
if ( pOldHalf )
|
||||
SendPartitionToWorker( pOldHalf, iOldWorker );
|
||||
if ( pNewHalf )
|
||||
SendPartitionToWorker( pNewHalf, iNewWorker );
|
||||
|
||||
// Delete the partition that got split
|
||||
Assert( pPartition->m_WUs.Count() == 0 );
|
||||
delete pPartition;
|
||||
m_Partitions.Remove( iPartToSplit );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int FindSoonestPartition()
|
||||
{
|
||||
CUtlLinkedList < CPartitionInfo *, int > &lst = m_Partitions;
|
||||
|
||||
// Sorted partitions
|
||||
CUtlMap< CPartitionInfo::PartitionWUs *, int > sortedPartitions ( CompareSoonestWorkUnitSets );
|
||||
sortedPartitions.EnsureCapacity( lst.Count() );
|
||||
FOR_EACH_LL( lst, i )
|
||||
{
|
||||
sortedPartitions.Insert( &lst[i]->m_WUs, i );
|
||||
}
|
||||
|
||||
if ( sortedPartitions.Count() )
|
||||
{
|
||||
return sortedPartitions.Element( sortedPartitions.FirstInorder() );
|
||||
}
|
||||
|
||||
return lst.Head();
|
||||
}
|
||||
|
||||
int FindPartitionByWorker( int iWorker )
|
||||
{
|
||||
FOR_EACH_LL( m_Partitions, i )
|
||||
{
|
||||
if ( m_Partitions[i]->m_iWorker == iWorker )
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SendPartitionToWorker( CPartitionInfo *pPartition, int iWorker )
|
||||
{
|
||||
// Stuff the next nWUs work units into the buffer.
|
||||
MessageBuffer mb;
|
||||
PrepareDistributeWorkHeader( &mb, DW_SUBPACKETID_WU_ASSIGNMENT );
|
||||
|
||||
FOR_EACH_LL( pPartition->m_WUs, i )
|
||||
{
|
||||
WUIndexType iWU = pPartition->m_WUs[i];
|
||||
mb.write( &iWU, sizeof( iWU ) );
|
||||
VMPITracker_WorkUnitSentToWorker( ( int ) iWU, iWorker );
|
||||
}
|
||||
|
||||
VMPI_SendData( mb.data, mb.getLen(), iWorker );
|
||||
}
|
||||
|
||||
virtual bool HandlePacket( MessageBuffer *pBuf, int iSource, bool bIgnoreContents )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CDSInfo *m_pInfo;
|
||||
|
||||
CUtlLinkedList<CPartitionInfo*,int> m_Partitions;
|
||||
CVisibleWindowVector<CWULookupInfo> m_WULookup; // Map work unit index to CWorkUnitInfo.
|
||||
CUtlLinkedList<CWorkUnitInfo,int> m_WUInfo; // Sorted with most elegible WU at the head.
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CDistributor_DefaultWorker : public IWorkUnitDistributorWorker
|
||||
{
|
||||
public:
|
||||
virtual void Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual void Init( CDSInfo *pInfo )
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool GetNextWorkUnit( WUIndexType *pWUIndex )
|
||||
{
|
||||
CCriticalSectionLock csLock( &m_CS );
|
||||
csLock.Lock();
|
||||
|
||||
// NOTE: this is called from INSIDE worker threads.
|
||||
if ( m_WorkUnits.Count() == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pWUIndex = m_WorkUnits[ m_WorkUnits.Head() ];
|
||||
m_WorkUnits.Remove( m_WorkUnits.Head() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void NoteLocalWorkUnitCompleted( WUIndexType iWU )
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool HandlePacket( MessageBuffer *pBuf, int iSource, bool bIgnoreContents )
|
||||
{
|
||||
if ( pBuf->data[1] == DW_SUBPACKETID_WU_ASSIGNMENT )
|
||||
{
|
||||
// If the message wasn't even related to the current DistributeWork() call we're on, ignore it.
|
||||
if ( bIgnoreContents )
|
||||
return true;
|
||||
|
||||
if ( ((pBuf->getLen() - pBuf->getOffset()) % sizeof( WUIndexType )) != 0 )
|
||||
{
|
||||
Error( "DistributeWork: invalid work units packet from master" );
|
||||
}
|
||||
|
||||
// Parse out the work unit indices.
|
||||
CCriticalSectionLock csLock( &m_CS );
|
||||
csLock.Lock();
|
||||
|
||||
m_WorkUnits.Purge();
|
||||
|
||||
int nIndices = (pBuf->getLen() - pBuf->getOffset()) / sizeof( WUIndexType );
|
||||
for ( int i=0; i < nIndices; i++ )
|
||||
{
|
||||
WUIndexType iWU;
|
||||
pBuf->read( &iWU, sizeof( iWU ) );
|
||||
|
||||
// Add the index to the list.
|
||||
m_WorkUnits.AddToTail( iWU );
|
||||
}
|
||||
|
||||
csLock.Unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Threads eat up the list of WUs in here.
|
||||
CCriticalSection m_CS;
|
||||
CUtlLinkedList<WUIndexType, int> m_WorkUnits; // A list of work units assigned to this worker
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
IWorkUnitDistributorMaster* CreateWUDistributor_DefaultMaster()
|
||||
{
|
||||
return new CDistributor_DefaultMaster;
|
||||
}
|
||||
IWorkUnitDistributorWorker* CreateWUDistributor_DefaultWorker()
|
||||
{
|
||||
return new CDistributor_DefaultWorker;
|
||||
}
|
||||
251
utils/vmpi/vmpi_distribute_work_internal.h
Normal file
251
utils/vmpi/vmpi_distribute_work_internal.h
Normal file
@@ -0,0 +1,251 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef VMPI_DISTRIBUTE_WORK_INTERNAL_H
|
||||
#define VMPI_DISTRIBUTE_WORK_INTERNAL_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#define VMPI_DISTRIBUTE_WORK_EXTRA_SUBPACKET_BASE 50
|
||||
|
||||
|
||||
typedef uint64 WUIndexType;
|
||||
class CDSInfo;
|
||||
extern bool g_bVMPIEarlyExit;
|
||||
|
||||
|
||||
// These classes are overridden to handle the details of communicating and scheduling work units.
|
||||
class IWorkUnitDistributorMaster
|
||||
{
|
||||
public:
|
||||
virtual void Release() = 0;
|
||||
|
||||
virtual void DistributeWork_Master( CDSInfo *pInfo ) = 0;
|
||||
|
||||
virtual void OnWorkerReady( int iWorker ) = 0;
|
||||
virtual bool HandlePacket( MessageBuffer *pBuf, int iSource, bool bIgnoreContents ) = 0;
|
||||
|
||||
// Called when the results for a work unit arrive. This function must return false if
|
||||
// we've already received the results for this work unit.
|
||||
virtual bool HandleWorkUnitResults( WUIndexType iWorkUnit ) = 0;
|
||||
|
||||
virtual void DisconnectHandler( int workerID ) = 0;
|
||||
};
|
||||
|
||||
class IWorkUnitDistributorWorker
|
||||
{
|
||||
public:
|
||||
virtual void Release() = 0;
|
||||
|
||||
virtual void Init( CDSInfo *pInfo ) = 0;
|
||||
|
||||
// Called by worker threads to get the next work unit to do.
|
||||
virtual bool GetNextWorkUnit( WUIndexType *pWUIndex ) = 0;
|
||||
|
||||
// Called by the worker threads after a work unit is completed.
|
||||
virtual void NoteLocalWorkUnitCompleted( WUIndexType iWU ) = 0;
|
||||
|
||||
// Called by the main thread when a packet comes in.
|
||||
virtual bool HandlePacket( MessageBuffer *pBuf, int iSource, bool bIgnoreContents ) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template < typename T, typename Derived >
|
||||
class CVisibleWindowVectorT : protected CUtlVector < T >
|
||||
{
|
||||
typedef CUtlVector< T > BaseClass;
|
||||
|
||||
public:
|
||||
CVisibleWindowVectorT() : m_uiBase( 0 ), m_uiTotal( 0 ) {}
|
||||
|
||||
public:
|
||||
inline void PostInitElement( uint64 uiRealIdx, T &newElement ) { /* do nothing */ return; }
|
||||
inline void PostInitElement( uint64 uiRealIdx, T &newElement, T const &x ) { newElement = x; }
|
||||
|
||||
public:
|
||||
// Resets the content and makes size "uiTotal"
|
||||
void Reset( uint64 uiTotal ) {
|
||||
BaseClass::RemoveAll();
|
||||
BaseClass::EnsureCapacity( min( 100, (int)uiTotal ) );
|
||||
m_uiBase = 0;
|
||||
m_uiTotal = uiTotal;
|
||||
}
|
||||
|
||||
// Gets the element from the window, otherwise NULL
|
||||
T & Get( uint64 idx ) {
|
||||
T *pElement = (( idx >= m_uiBase && idx < m_uiBase + BaseClass::Count() ) ? ( BaseClass::Base() + size_t( idx - m_uiBase ) ) : NULL );
|
||||
Assert( pElement );
|
||||
return *pElement;
|
||||
}
|
||||
|
||||
// Expands the window to see the element by its index
|
||||
void ExpandWindow( uint64 idxAccessible ) {
|
||||
Assert( idxAccessible >= m_uiBase );
|
||||
if ( idxAccessible >= m_uiBase + BaseClass::Count() ) {
|
||||
int iOldCount = BaseClass::Count();
|
||||
int iAddElements = int(idxAccessible - m_uiBase) - iOldCount + 1;
|
||||
Assert( iOldCount + iAddElements <= 100000000 /* really need 100 Mb at once? */ );
|
||||
BaseClass::AddMultipleToTail( iAddElements );
|
||||
for ( int iNewCount = BaseClass::Count(); iOldCount < iNewCount; ++ iOldCount )
|
||||
static_cast< Derived * >( this )->PostInitElement( m_uiBase + iOldCount, BaseClass::Element( iOldCount ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Expands the window and initializes the new elements to a given value
|
||||
void ExpandWindow( uint64 idxAccessible, T const& x ) {
|
||||
Assert( idxAccessible >= m_uiBase );
|
||||
if ( idxAccessible >= m_uiBase + BaseClass::Count() ) {
|
||||
int iOldCount = BaseClass::Count();
|
||||
int iAddElements = int(idxAccessible - m_uiBase) - iOldCount + 1;
|
||||
Assert( unsigned(iAddElements) <= 50000000 /* growing 50 Mb at once? */ );
|
||||
BaseClass::AddMultipleToTail( iAddElements );
|
||||
for ( int iNewCount = BaseClass::Count(); iOldCount < iNewCount; ++ iOldCount )
|
||||
static_cast< Derived * >( this )->PostInitElement( m_uiBase + iOldCount, BaseClass::Element( iOldCount ), x );
|
||||
}
|
||||
}
|
||||
|
||||
// Shrinks the window to drop some of the elements from the head
|
||||
void ShrinkWindow( uint64 idxDrop ) {
|
||||
Assert( idxDrop >= m_uiBase && idxDrop <= m_uiBase + BaseClass::Count() );
|
||||
if ( idxDrop >= m_uiBase && idxDrop <= m_uiBase + BaseClass::Count() ) {
|
||||
int iDropElements = int( idxDrop - m_uiBase ) + 1;
|
||||
m_uiBase += iDropElements;
|
||||
BaseClass::RemoveMultiple( 0, min( iDropElements, BaseClass::Count() ) );
|
||||
}
|
||||
}
|
||||
|
||||
// First possible index in this vector (only past dropped items)
|
||||
uint64 FirstPossibleIndex() const { return m_uiBase; }
|
||||
|
||||
// Last possible index in this vector
|
||||
uint64 PastPossibleIndex() const { return m_uiTotal; }
|
||||
// Past visible window index in this vector
|
||||
uint64 PastVisibleIndex() const { return m_uiBase + BaseClass::Count(); }
|
||||
|
||||
protected:
|
||||
uint64 m_uiBase, m_uiTotal;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class CVisibleWindowVector : public CVisibleWindowVectorT < T, CVisibleWindowVector< T > >
|
||||
{
|
||||
public:
|
||||
CVisibleWindowVector() {}
|
||||
};
|
||||
|
||||
|
||||
template < typename T >
|
||||
T const * GenericFind( T const *pBegin, T const *pEnd, T const &x )
|
||||
{
|
||||
for ( ; pBegin != pEnd; ++ pBegin )
|
||||
if ( *pBegin == x )
|
||||
break;
|
||||
return pBegin;
|
||||
}
|
||||
|
||||
|
||||
template < typename T >
|
||||
T * GenericFind( T *pBegin, T *pEnd, T const &x )
|
||||
{
|
||||
for ( ; pBegin != pEnd; ++ pBegin )
|
||||
if ( *pBegin == x )
|
||||
break;
|
||||
return pBegin;
|
||||
}
|
||||
|
||||
|
||||
class CWorkerInfo
|
||||
{
|
||||
public:
|
||||
ProcessWorkUnitFn m_pProcessFn;
|
||||
CVisibleWindowVector<WUIndexType> m_WorkUnitsRunning; // A list of work units currently running, index is the thread index
|
||||
CCriticalSection m_WorkUnitsRunningCS;
|
||||
};
|
||||
|
||||
|
||||
class CMasterInfo
|
||||
{
|
||||
public:
|
||||
|
||||
// Only used by the master.
|
||||
ReceiveWorkUnitFn m_ReceiveFn;
|
||||
};
|
||||
|
||||
|
||||
class CDSInfo
|
||||
{
|
||||
public:
|
||||
inline void WriteWUIndex( WUIndexType iWU, MessageBuffer *pBuf )
|
||||
{
|
||||
if ( m_nWorkUnits <= 0xFFFF )
|
||||
{
|
||||
Assert( iWU <= 0xFFFF );
|
||||
unsigned short val = (unsigned short)iWU;
|
||||
pBuf->write( &val, sizeof( val ) );
|
||||
}
|
||||
else if ( m_nWorkUnits <= 0xFFFFFFFF )
|
||||
{
|
||||
Assert( iWU <= 0xFFFF );
|
||||
unsigned int val = (unsigned int)iWU;
|
||||
pBuf->write( &val, sizeof( val ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
pBuf->write( &iWU, sizeof( iWU ) );
|
||||
}
|
||||
}
|
||||
|
||||
inline void ReadWUIndex( WUIndexType *pWU, MessageBuffer *pBuf )
|
||||
{
|
||||
if ( m_nWorkUnits <= 0xFFFF )
|
||||
{
|
||||
unsigned short val;
|
||||
pBuf->read( &val, sizeof( val ) );
|
||||
*pWU = val;
|
||||
}
|
||||
else if ( m_nWorkUnits <= 0xFFFFFFFF )
|
||||
{
|
||||
unsigned int val;
|
||||
pBuf->read( &val, sizeof( val ) );
|
||||
*pWU = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
pBuf->read( pWU, sizeof( *pWU ) );
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
CWorkerInfo m_WorkerInfo;
|
||||
CMasterInfo m_MasterInfo;
|
||||
|
||||
bool m_bMasterReady; // Set to true when the master is ready to go.
|
||||
bool m_bMasterFinished;
|
||||
WUIndexType m_nWorkUnits;
|
||||
char m_cPacketID;
|
||||
};
|
||||
|
||||
|
||||
// Called to write the packet ID, the subpacket ID, and the ID of which DistributeWork() call we're at.
|
||||
void PrepareDistributeWorkHeader( MessageBuffer *pBuf, unsigned char cSubpacketID );
|
||||
|
||||
// Called from threads on the master to process a completed work unit.
|
||||
void NotifyLocalMasterCompletedWorkUnit( WUIndexType iWorkUnit );
|
||||
void CheckLocalMasterCompletedWorkUnits();
|
||||
|
||||
|
||||
// Creation functions for different distributors.
|
||||
IWorkUnitDistributorMaster* CreateWUDistributor_DefaultMaster();
|
||||
IWorkUnitDistributorWorker* CreateWUDistributor_DefaultWorker();
|
||||
|
||||
IWorkUnitDistributorMaster* CreateWUDistributor_SDKMaster();
|
||||
IWorkUnitDistributorWorker* CreateWUDistributor_SDKWorker();
|
||||
|
||||
|
||||
#endif // VMPI_DISTRIBUTE_WORK_INTERNAL_H
|
||||
699
utils/vmpi/vmpi_distribute_work_sdk.cpp
Normal file
699
utils/vmpi/vmpi_distribute_work_sdk.cpp
Normal file
@@ -0,0 +1,699 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "vmpi.h"
|
||||
#include "vmpi_distribute_work.h"
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "utlvector.h"
|
||||
#include "utllinkedlist.h"
|
||||
#include "vmpi_dispatch.h"
|
||||
#include "pacifier.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "mathlib/mathlib.h"
|
||||
#include "threadhelpers.h"
|
||||
#include "threads.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "tier1/utlmap.h"
|
||||
#include "tier1/smartptr.h"
|
||||
#include "tier0/icommandline.h"
|
||||
#include "cmdlib.h"
|
||||
#include "vmpi_distribute_tracker.h"
|
||||
#include "vmpi_distribute_work_internal.h"
|
||||
|
||||
|
||||
|
||||
#define DW_SUBPACKETID_SHUFFLE (VMPI_DISTRIBUTE_WORK_EXTRA_SUBPACKET_BASE+0)
|
||||
#define DW_SUBPACKETID_REQUEST_SHUFFLE (VMPI_DISTRIBUTE_WORK_EXTRA_SUBPACKET_BASE+1)
|
||||
#define DW_SUBPACKETID_WUS_COMPLETED_LIST (VMPI_DISTRIBUTE_WORK_EXTRA_SUBPACKET_BASE+2)
|
||||
|
||||
|
||||
|
||||
// This is a pretty simple iterator. Basically, it holds a matrix of numbers.
|
||||
// Each row is assigned to a worker, and the worker just walks through his row.
|
||||
//
|
||||
// When a worker reaches the end of his row, it gets a little trickier.
|
||||
// They'll start doing their neighbor's row
|
||||
// starting at the back and continue on. At about this time, the master should reshuffle the
|
||||
// remaining work units to evenly distribute them amongst the workers.
|
||||
class CWorkUnitWalker
|
||||
{
|
||||
public:
|
||||
CWorkUnitWalker()
|
||||
{
|
||||
m_nWorkUnits = 0;
|
||||
}
|
||||
|
||||
// This is all that's needed for it to start assigning work units.
|
||||
void Init( WUIndexType matrixWidth, WUIndexType matrixHeight, WUIndexType nWorkUnits )
|
||||
{
|
||||
m_nWorkUnits = nWorkUnits;
|
||||
m_MatrixWidth = matrixWidth;
|
||||
m_MatrixHeight = matrixHeight;
|
||||
Assert( m_MatrixWidth * m_MatrixHeight >= nWorkUnits );
|
||||
|
||||
m_WorkerInfos.RemoveAll();
|
||||
m_WorkerInfos.EnsureCount( m_MatrixHeight );
|
||||
for ( int i=0; i < m_MatrixHeight; i++ )
|
||||
{
|
||||
m_WorkerInfos[i].m_iStartWorkUnit = matrixWidth * i;
|
||||
m_WorkerInfos[i].m_iWorkUnitOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// This is the main function of the shuffler
|
||||
bool GetNextWorkUnit( int iWorker, WUIndexType *pWUIndex, bool *bWorkerFinishedHisColumn )
|
||||
{
|
||||
if ( iWorker < 0 || iWorker >= m_WorkerInfos.Count() )
|
||||
{
|
||||
Assert( false );
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this worker has walked through all the work units, then he's done.
|
||||
CWorkerInfo *pWorker = &m_WorkerInfos[iWorker];
|
||||
if ( pWorker->m_iWorkUnitOffset >= m_nWorkUnits )
|
||||
return false;
|
||||
|
||||
// If we've gone past the end of our work unit list, then we start at the BACK of the other rows of work units
|
||||
// in the hopes that we won't collide with the guy working there. We also should tell the master to reshuffle.
|
||||
WUIndexType iWorkUnitOffset = pWorker->m_iWorkUnitOffset;
|
||||
if ( iWorkUnitOffset >= m_MatrixWidth )
|
||||
{
|
||||
WUIndexType xOffset = iWorkUnitOffset % m_MatrixWidth;
|
||||
WUIndexType yOffset = iWorkUnitOffset / m_MatrixWidth;
|
||||
xOffset = m_MatrixWidth - xOffset - 1;
|
||||
iWorkUnitOffset = yOffset * m_MatrixWidth + xOffset;
|
||||
*bWorkerFinishedHisColumn = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*bWorkerFinishedHisColumn = false;
|
||||
}
|
||||
|
||||
*pWUIndex = (pWorker->m_iStartWorkUnit + iWorkUnitOffset) % m_nWorkUnits;
|
||||
++pWorker->m_iWorkUnitOffset;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
class CWorkerInfo
|
||||
{
|
||||
public:
|
||||
WUIndexType m_iStartWorkUnit;
|
||||
WUIndexType m_iWorkUnitOffset; // Which work unit in my list of work units am I working on?
|
||||
};
|
||||
|
||||
WUIndexType m_nWorkUnits;
|
||||
WUIndexType m_MatrixWidth;
|
||||
WUIndexType m_MatrixHeight;
|
||||
CUtlVector<CWorkerInfo> m_WorkerInfos;
|
||||
};
|
||||
|
||||
|
||||
class IShuffleRequester
|
||||
{
|
||||
public:
|
||||
virtual void RequestShuffle() = 0;
|
||||
};
|
||||
|
||||
|
||||
// This is updated every time the master decides to reshuffle.
|
||||
// In-between shuffles, you can call NoteWorkUnitCompleted when a work unit is completed
|
||||
// and it'll avoid returning that work unit from GetNextWorkUnit again, but it WON'T
|
||||
class CShuffledWorkUnitWalker
|
||||
{
|
||||
public:
|
||||
void Init( WUIndexType nWorkUnits, IShuffleRequester *pRequester )
|
||||
{
|
||||
m_iLastShuffleRequest = 0;
|
||||
m_iCurShuffle = 1;
|
||||
m_flLastShuffleTime = Plat_FloatTime();
|
||||
m_pShuffleRequester = pRequester;
|
||||
|
||||
int nBytes = PAD_NUMBER( nWorkUnits, 8 ) / 8;
|
||||
m_CompletedWUBits.SetSize( nBytes );
|
||||
m_LocalCompletedWUBits.SetSize( nBytes );
|
||||
for ( WUIndexType i=0; i < m_CompletedWUBits.Count(); i++ )
|
||||
m_LocalCompletedWUBits[i] = m_CompletedWUBits[i] = 0;
|
||||
|
||||
// Setup our list of work units remaining.
|
||||
for ( WUIndexType iWU=0; iWU < nWorkUnits; iWU++ )
|
||||
{
|
||||
// Note: we're making an assumption here that if we add entries to a CUtlLinkedList in ascending order, their indices
|
||||
// will be ascending 1-by-1 as well. If that assumption breaks, we can create an extra array here to map WU indices to the linked list indices.
|
||||
WUIndexType index = m_WorkUnitsRemaining.AddToTail( iWU );
|
||||
if ( index != iWU )
|
||||
{
|
||||
Error( "CShuffledWorkUnitWalker: assumption on CUtlLinkedList indexing failed.\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Shuffle( int nWorkers )
|
||||
{
|
||||
if ( nWorkers == 0 )
|
||||
return;
|
||||
|
||||
++m_iCurShuffle;
|
||||
m_flLastShuffleTime = Plat_FloatTime();
|
||||
|
||||
CCriticalSectionLock csLock( &m_CS );
|
||||
csLock.Lock();
|
||||
|
||||
m_WorkUnitsMap.RemoveAll();
|
||||
m_WorkUnitsMap.EnsureCount( m_WorkUnitsRemaining.Count() );
|
||||
|
||||
// Here's the shuffle. The CWorkUnitWalker is going to walk each worker through its own group from 0-W,
|
||||
// and our job is to interleave it so when worker 0 goes [0,1,2] and worker 1 goes [100,101,102], they're actually
|
||||
// doing [0,N,2N] and [1,N+1,2N+1] where N=# of workers.
|
||||
|
||||
// The grid is RxW long, and R*W is >= nWorkUnits.
|
||||
// R = # units per worker = width of the matrix
|
||||
// W = # workers = height of the matrix
|
||||
WUIndexType matrixHeight = nWorkers;
|
||||
WUIndexType matrixWidth = m_WorkUnitsRemaining.Count() / matrixHeight;
|
||||
if ( (m_WorkUnitsRemaining.Count() % matrixHeight) != 0 )
|
||||
++matrixWidth;
|
||||
|
||||
Assert( matrixWidth * matrixHeight >= m_WorkUnitsRemaining.Count() );
|
||||
|
||||
WUIndexType iWorkUnit = 0;
|
||||
FOR_EACH_LL( m_WorkUnitsRemaining, i )
|
||||
{
|
||||
WUIndexType xCoord = iWorkUnit / matrixHeight;
|
||||
WUIndexType yCoord = iWorkUnit % matrixHeight;
|
||||
Assert( xCoord < matrixWidth );
|
||||
Assert( yCoord < matrixHeight );
|
||||
|
||||
m_WorkUnitsMap[yCoord*matrixWidth+xCoord] = m_WorkUnitsRemaining[i];
|
||||
++iWorkUnit;
|
||||
}
|
||||
|
||||
m_Walker.Init( matrixWidth, matrixHeight, m_WorkUnitsRemaining.Count() );
|
||||
}
|
||||
|
||||
// Threadsafe.
|
||||
bool Thread_IsWorkUnitCompleted( WUIndexType iWU )
|
||||
{
|
||||
CCriticalSectionLock csLock( &m_CS );
|
||||
csLock.Lock();
|
||||
|
||||
byte val = m_CompletedWUBits[iWU >> 3] & (1 << (iWU & 7));
|
||||
return (val != 0);
|
||||
}
|
||||
|
||||
WUIndexType Thread_NumWorkUnitsRemaining()
|
||||
{
|
||||
CCriticalSectionLock csLock( &m_CS );
|
||||
csLock.Lock();
|
||||
|
||||
return m_WorkUnitsRemaining.Count();
|
||||
}
|
||||
|
||||
bool Thread_GetNextWorkUnit( int iWorker, WUIndexType *pWUIndex )
|
||||
{
|
||||
CCriticalSectionLock csLock( &m_CS );
|
||||
csLock.Lock();
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
WUIndexType iUnmappedWorkUnit;
|
||||
bool bWorkerFinishedHisColumn;
|
||||
if ( !m_Walker.GetNextWorkUnit( iWorker, &iUnmappedWorkUnit, &bWorkerFinishedHisColumn ) )
|
||||
return false;
|
||||
|
||||
// If we've done all the work units assigned to us in the last shuffle, then request a reshuffle.
|
||||
if ( bWorkerFinishedHisColumn )
|
||||
HandleWorkerFinishedColumn();
|
||||
|
||||
// Check the pending list.
|
||||
*pWUIndex = m_WorkUnitsMap[iUnmappedWorkUnit];
|
||||
byte bIsCompleted = m_CompletedWUBits[*pWUIndex >> 3] & (1 << (*pWUIndex & 7));
|
||||
byte bIsCompletedLocally = m_LocalCompletedWUBits[*pWUIndex >> 3] & (1 << (*pWUIndex & 7));
|
||||
if ( !bIsCompleted && !bIsCompletedLocally )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleWorkerFinishedColumn()
|
||||
{
|
||||
if ( m_iLastShuffleRequest != m_iCurShuffle )
|
||||
{
|
||||
double flCurTime = Plat_FloatTime();
|
||||
if ( flCurTime - m_flLastShuffleTime > 2.0f )
|
||||
{
|
||||
m_pShuffleRequester->RequestShuffle();
|
||||
m_iLastShuffleRequest = m_iCurShuffle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Thread_NoteWorkUnitCompleted( WUIndexType iWU )
|
||||
{
|
||||
CCriticalSectionLock csLock( &m_CS );
|
||||
csLock.Lock();
|
||||
|
||||
byte val = m_CompletedWUBits[iWU >> 3] & (1 << (iWU & 7));
|
||||
if ( val == 0 )
|
||||
{
|
||||
m_WorkUnitsRemaining.Remove( iWU );
|
||||
m_CompletedWUBits[iWU >> 3] |= (1 << (iWU & 7));
|
||||
}
|
||||
}
|
||||
|
||||
void Thread_NoteLocalWorkUnitCompleted( WUIndexType iWU )
|
||||
{
|
||||
CCriticalSectionLock csLock( &m_CS );
|
||||
csLock.Lock();
|
||||
m_LocalCompletedWUBits[iWU >> 3] |= (1 << (iWU & 7));
|
||||
}
|
||||
|
||||
CRC32_t GetShuffleCRC()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
static bool bCalcShuffleCRC = true;
|
||||
#else
|
||||
static bool bCalcShuffleCRC = VMPI_IsParamUsed( mpi_CalcShuffleCRC );
|
||||
#endif
|
||||
if ( bCalcShuffleCRC )
|
||||
{
|
||||
CCriticalSectionLock csLock( &m_CS );
|
||||
csLock.Lock();
|
||||
|
||||
CRC32_t ret;
|
||||
CRC32_Init( &ret );
|
||||
|
||||
FOR_EACH_LL( m_WorkUnitsRemaining, i )
|
||||
{
|
||||
WUIndexType iWorkUnit = m_WorkUnitsRemaining[i];
|
||||
CRC32_ProcessBuffer( &ret, &iWorkUnit, sizeof( iWorkUnit ) );
|
||||
}
|
||||
|
||||
for ( int i=0; i < m_WorkUnitsMap.Count(); i++ )
|
||||
{
|
||||
WUIndexType iWorkUnit = m_WorkUnitsMap[i];
|
||||
CRC32_ProcessBuffer( &ret, &iWorkUnit, sizeof( iWorkUnit ) );
|
||||
}
|
||||
|
||||
CRC32_Final( &ret );
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// These are PENDING WU completions until we call Shuffle() again, at which point we actually reorder the list
|
||||
// based on the completed WUs.
|
||||
CUtlVector<byte> m_CompletedWUBits; // Bit vector of completed WUs.
|
||||
CUtlLinkedList<WUIndexType, WUIndexType> m_WorkUnitsRemaining;
|
||||
CUtlVector<WUIndexType> m_WorkUnitsMap; // Maps the 0-N indices in the CWorkUnitWalker to the list of remaining work units.
|
||||
|
||||
// Helps us avoid some duplicates that happen during shuffling if we've completed some WUs and sent them
|
||||
// to the master, but the master hasn't included them in the DW_SUBPACKETID_WUS_COMPLETED_LIST yet.
|
||||
CUtlVector<byte> m_LocalCompletedWUBits; // Bit vector of completed WUs.
|
||||
|
||||
// Used to control how frequently we request a reshuffle.
|
||||
unsigned int m_iCurShuffle;
|
||||
unsigned int m_iLastShuffleRequest; // The index of the shuffle we last requested a reshuffle on (don't request a reshuffle on the same one).
|
||||
double m_flLastShuffleTime;
|
||||
IShuffleRequester *m_pShuffleRequester;
|
||||
|
||||
CWorkUnitWalker m_Walker;
|
||||
CCriticalSection m_CS;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CDistributor_SDKMaster : public IWorkUnitDistributorMaster, public IShuffleRequester
|
||||
{
|
||||
public:
|
||||
virtual void Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
static void Master_WorkerThread_Static( int iThread, void *pUserData )
|
||||
{
|
||||
((CDistributor_SDKMaster*)pUserData)->Master_WorkerThread( iThread );
|
||||
}
|
||||
|
||||
void Master_WorkerThread( int iThread )
|
||||
{
|
||||
while ( m_WorkUnitWalker.Thread_NumWorkUnitsRemaining() > 0 && !g_bVMPIEarlyExit )
|
||||
{
|
||||
WUIndexType iWU;
|
||||
if ( !m_WorkUnitWalker.Thread_GetNextWorkUnit( 0, &iWU ) )
|
||||
{
|
||||
// Wait until there are some WUs to do.
|
||||
VMPI_Sleep( 10 );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do this work unit.
|
||||
m_WorkUnitWalker.Thread_NoteLocalWorkUnitCompleted( iWU ); // We do this before it's completed because otherwise if a Shuffle() occurs,
|
||||
// the other thread might happen to pickup this work unit and we don't want that.
|
||||
m_pInfo->m_WorkerInfo.m_pProcessFn( iThread, iWU, NULL );
|
||||
NotifyLocalMasterCompletedWorkUnit( iWU );
|
||||
}
|
||||
}
|
||||
|
||||
virtual void DistributeWork_Master( CDSInfo *pInfo )
|
||||
{
|
||||
m_pInfo = pInfo;
|
||||
m_bForceShuffle = false;
|
||||
m_bShuffleRequested = false;
|
||||
m_flLastShuffleRequestServiceTime = Plat_FloatTime();
|
||||
|
||||
// Spawn idle-priority worker threads right here.
|
||||
m_bUsingMasterLocalThreads = (pInfo->m_WorkerInfo.m_pProcessFn != 0);
|
||||
if ( VMPI_IsParamUsed( mpi_NoMasterWorkerThreads ) )
|
||||
{
|
||||
Msg( "%s found. No worker threads will be created.\n", VMPI_GetParamString( mpi_NoMasterWorkerThreads ) );
|
||||
m_bUsingMasterLocalThreads = false;
|
||||
}
|
||||
m_WorkUnitWalker.Init( pInfo->m_nWorkUnits, this );
|
||||
Shuffle();
|
||||
|
||||
if ( m_bUsingMasterLocalThreads )
|
||||
RunThreads_Start( Master_WorkerThread_Static, this, k_eRunThreadsPriority_Idle );
|
||||
|
||||
uint64 lastShuffleTime = Plat_MSTime();
|
||||
while ( m_WorkUnitWalker.Thread_NumWorkUnitsRemaining() > 0 )
|
||||
{
|
||||
VMPI_DispatchNextMessage( 200 );
|
||||
CheckLocalMasterCompletedWorkUnits();
|
||||
|
||||
VMPITracker_HandleDebugKeypresses();
|
||||
if ( g_pDistributeWorkCallbacks && g_pDistributeWorkCallbacks->Update() )
|
||||
break;
|
||||
|
||||
// Reshuffle the work units optimally every certain interval.
|
||||
if ( m_bForceShuffle || CheckShuffleRequest() )
|
||||
{
|
||||
Shuffle();
|
||||
lastShuffleTime = Plat_MSTime();
|
||||
m_bForceShuffle = false;
|
||||
}
|
||||
}
|
||||
|
||||
RunThreads_End();
|
||||
}
|
||||
|
||||
virtual void RequestShuffle()
|
||||
{
|
||||
m_bShuffleRequested = true;
|
||||
}
|
||||
|
||||
bool CheckShuffleRequest()
|
||||
{
|
||||
if ( m_bShuffleRequested )
|
||||
{
|
||||
double flCurTime = Plat_FloatTime();
|
||||
if ( flCurTime - m_flLastShuffleRequestServiceTime > 2.0f ) // Only handle shuffle requests every so often.
|
||||
{
|
||||
m_flLastShuffleRequestServiceTime = flCurTime;
|
||||
m_bShuffleRequested = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Shuffle()
|
||||
{
|
||||
// Build a list of who's working.
|
||||
CUtlVector<unsigned short> whosWorking;
|
||||
if ( m_bUsingMasterLocalThreads )
|
||||
{
|
||||
whosWorking.AddToTail( VMPI_MASTER_ID );
|
||||
Assert( VMPI_MASTER_ID == 0 );
|
||||
}
|
||||
|
||||
{
|
||||
CWorkersReady *pWorkersReady = m_WorkersReadyCS.Lock();
|
||||
for ( int i=0; i < pWorkersReady->m_WorkersReady.Count(); i++ )
|
||||
{
|
||||
int iWorker = pWorkersReady->m_WorkersReady[i];
|
||||
if ( VMPI_IsProcConnected( iWorker ) )
|
||||
whosWorking.AddToTail( iWorker );
|
||||
}
|
||||
m_WorkersReadyCS.Unlock();
|
||||
}
|
||||
|
||||
// Before sending the shuffle command, tell any of these active workers about the pending WUs completed.
|
||||
CWUsCompleted *pWUsCompleted = m_WUsCompletedCS.Lock();
|
||||
|
||||
m_WUSCompletedMessageBuffer.setLen( 0 );
|
||||
if ( BuildWUsCompletedMessage( pWUsCompleted->m_Pending, m_WUSCompletedMessageBuffer ) > 0 )
|
||||
{
|
||||
for ( int i=m_bUsingMasterLocalThreads; i < whosWorking.Count(); i++ )
|
||||
{
|
||||
VMPI_SendData( m_WUSCompletedMessageBuffer.data, m_WUSCompletedMessageBuffer.getLen(), whosWorking[i] );
|
||||
}
|
||||
}
|
||||
pWUsCompleted->m_Completed.AddMultipleToTail( pWUsCompleted->m_Pending.Count(), pWUsCompleted->m_Pending.Base() ); // Add the pending ones to the full list now.
|
||||
pWUsCompleted->m_Pending.RemoveAll();
|
||||
|
||||
m_WUsCompletedCS.Unlock();
|
||||
|
||||
// Shuffle ourselves.
|
||||
m_WorkUnitWalker.Shuffle( whosWorking.Count() );
|
||||
|
||||
// Send the shuffle command to the workers.
|
||||
MessageBuffer mb;
|
||||
PrepareDistributeWorkHeader( &mb, DW_SUBPACKETID_SHUFFLE );
|
||||
|
||||
unsigned short nWorkers = whosWorking.Count();
|
||||
mb.write( &nWorkers, sizeof( nWorkers ) );
|
||||
|
||||
CRC32_t shuffleCRC = m_WorkUnitWalker.GetShuffleCRC();
|
||||
mb.write( &shuffleCRC, sizeof( shuffleCRC ) );
|
||||
|
||||
// Now for each worker, assign him an index in the shuffle and send the shuffle command.
|
||||
int workerIDPos = mb.getLen();
|
||||
unsigned short id = 0;
|
||||
mb.write( &id, sizeof( id ) );
|
||||
for ( int i=m_bUsingMasterLocalThreads; i < whosWorking.Count(); i++ )
|
||||
{
|
||||
id = (unsigned short)i;
|
||||
mb.update( workerIDPos, &id, sizeof( id ) );
|
||||
VMPI_SendData( mb.data, mb.getLen(), whosWorking[i] );
|
||||
}
|
||||
}
|
||||
|
||||
int BuildWUsCompletedMessage( CUtlVector<WUIndexType> &wusCompleted, MessageBuffer &mb )
|
||||
{
|
||||
PrepareDistributeWorkHeader( &mb, DW_SUBPACKETID_WUS_COMPLETED_LIST );
|
||||
m_pInfo->WriteWUIndex( wusCompleted.Count(), &mb );
|
||||
for ( int i=0; i < wusCompleted.Count(); i++ )
|
||||
{
|
||||
m_pInfo->WriteWUIndex( wusCompleted[i], &mb );
|
||||
}
|
||||
return wusCompleted.Count();
|
||||
}
|
||||
|
||||
virtual void OnWorkerReady( int iSource )
|
||||
{
|
||||
CWorkersReady *pWorkersReady = m_WorkersReadyCS.Lock();
|
||||
if ( pWorkersReady->m_WorkersReady.Find( iSource ) == -1 )
|
||||
{
|
||||
pWorkersReady->m_WorkersReady.AddToTail( iSource );
|
||||
|
||||
// Get this guy up to speed on which WUs are done.
|
||||
{
|
||||
CWUsCompleted *pWUsCompleted = m_WUsCompletedCS.Lock();
|
||||
m_WUSCompletedMessageBuffer.setLen( 0 );
|
||||
BuildWUsCompletedMessage( pWUsCompleted->m_Completed, m_WUSCompletedMessageBuffer );
|
||||
m_WUsCompletedCS.Unlock();
|
||||
}
|
||||
|
||||
VMPI_SendData( m_WUSCompletedMessageBuffer.data, m_WUSCompletedMessageBuffer.getLen(), iSource );
|
||||
m_bForceShuffle = true;
|
||||
}
|
||||
m_WorkersReadyCS.Unlock();
|
||||
}
|
||||
|
||||
virtual bool HandleWorkUnitResults( WUIndexType iWorkUnit )
|
||||
{
|
||||
return Thread_HandleWorkUnitResults( iWorkUnit );
|
||||
}
|
||||
|
||||
bool Thread_HandleWorkUnitResults( WUIndexType iWorkUnit )
|
||||
{
|
||||
if ( m_WorkUnitWalker.Thread_IsWorkUnitCompleted( iWorkUnit ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_WorkUnitWalker.Thread_NoteWorkUnitCompleted( iWorkUnit );
|
||||
|
||||
// We need the lock on here because our own worker threads can call into here.
|
||||
CWUsCompleted *pWUsCompleted = m_WUsCompletedCS.Lock();
|
||||
pWUsCompleted->m_Pending.AddToTail( iWorkUnit );
|
||||
m_WUsCompletedCS.Unlock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool HandlePacket( MessageBuffer *pBuf, int iSource, bool bIgnoreContents )
|
||||
{
|
||||
if ( pBuf->data[1] == DW_SUBPACKETID_REQUEST_SHUFFLE )
|
||||
{
|
||||
if ( bIgnoreContents )
|
||||
return true;
|
||||
|
||||
m_bShuffleRequested = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void DisconnectHandler( int workerID )
|
||||
{
|
||||
CWorkersReady *pWorkersReady = m_WorkersReadyCS.Lock();
|
||||
|
||||
if ( pWorkersReady->m_WorkersReady.Find( workerID ) != -1 )
|
||||
m_bForceShuffle = true;
|
||||
|
||||
m_WorkersReadyCS.Unlock();
|
||||
}
|
||||
|
||||
public:
|
||||
CDSInfo *m_pInfo;
|
||||
|
||||
class CWorkersReady
|
||||
{
|
||||
public:
|
||||
CUtlVector<int> m_WorkersReady; // The list of workers who have said they're ready to participate.
|
||||
};
|
||||
CCriticalSectionData<CWorkersReady> m_WorkersReadyCS;
|
||||
|
||||
class CWUsCompleted
|
||||
{
|
||||
public:
|
||||
CUtlVector<WUIndexType> m_Completed; // WUs completed that we have sent to workers.
|
||||
CUtlVector<WUIndexType> m_Pending; // WUs completed that we haven't sent to workers.
|
||||
};
|
||||
CCriticalSectionData<CWUsCompleted> m_WUsCompletedCS;
|
||||
MessageBuffer m_WUSCompletedMessageBuffer; // Used to send lists of completed WUs.
|
||||
int m_bUsingMasterLocalThreads;
|
||||
|
||||
bool m_bForceShuffle;
|
||||
bool m_bShuffleRequested;
|
||||
double m_flLastShuffleRequestServiceTime;
|
||||
|
||||
CShuffledWorkUnitWalker m_WorkUnitWalker;
|
||||
};
|
||||
|
||||
|
||||
class CDistributor_SDKWorker : public IWorkUnitDistributorWorker, public IShuffleRequester
|
||||
{
|
||||
public:
|
||||
virtual void Init( CDSInfo *pInfo )
|
||||
{
|
||||
m_iMyWorkUnitWalkerID = -1;
|
||||
m_pInfo = pInfo;
|
||||
m_WorkUnitWalker.Init( pInfo->m_nWorkUnits, this );
|
||||
}
|
||||
|
||||
virtual void Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual bool GetNextWorkUnit( WUIndexType *pWUIndex )
|
||||
{
|
||||
// If we don't have an ID yet, we haven't received a Shuffle() command, so we're waiting for that before working.
|
||||
// TODO: we could do some random WUs here while we're waiting, although that could suck if the WUs take forever to do
|
||||
// and they're duplicates.
|
||||
if ( m_iMyWorkUnitWalkerID == -1 )
|
||||
return false;
|
||||
|
||||
// Look in our current shuffled list of work units for the next one.
|
||||
return m_WorkUnitWalker.Thread_GetNextWorkUnit( m_iMyWorkUnitWalkerID, pWUIndex );
|
||||
}
|
||||
|
||||
virtual void NoteLocalWorkUnitCompleted( WUIndexType iWU )
|
||||
{
|
||||
m_WorkUnitWalker.Thread_NoteLocalWorkUnitCompleted( iWU );
|
||||
}
|
||||
|
||||
virtual bool HandlePacket( MessageBuffer *pBuf, int iSource, bool bIgnoreContents )
|
||||
{
|
||||
// If it's a SHUFFLE message, then shuffle..
|
||||
if ( pBuf->data[1] == DW_SUBPACKETID_SHUFFLE )
|
||||
{
|
||||
if ( bIgnoreContents )
|
||||
return true;
|
||||
|
||||
unsigned short nWorkers, myID;
|
||||
CRC32_t shuffleCRC;
|
||||
pBuf->read( &nWorkers, sizeof( nWorkers ) );
|
||||
pBuf->read( &shuffleCRC, sizeof( shuffleCRC ) );
|
||||
pBuf->read( &myID, sizeof( myID ) );
|
||||
m_iMyWorkUnitWalkerID = myID;
|
||||
|
||||
m_WorkUnitWalker.Shuffle( nWorkers );
|
||||
if ( m_WorkUnitWalker.GetShuffleCRC() != shuffleCRC )
|
||||
{
|
||||
static int nWarnings = 1;
|
||||
if ( ++nWarnings <= 2 )
|
||||
Warning( "\nShuffle CRC mismatch\n" );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if ( pBuf->data[1] == DW_SUBPACKETID_WUS_COMPLETED_LIST )
|
||||
{
|
||||
if ( bIgnoreContents )
|
||||
return true;
|
||||
|
||||
WUIndexType nCompleted;
|
||||
m_pInfo->ReadWUIndex( &nCompleted, pBuf );
|
||||
for ( WUIndexType i=0; i < nCompleted; i++ )
|
||||
{
|
||||
WUIndexType iWU;
|
||||
m_pInfo->ReadWUIndex( &iWU, pBuf );
|
||||
m_WorkUnitWalker.Thread_NoteWorkUnitCompleted( iWU );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void RequestShuffle()
|
||||
{
|
||||
// Ok.. request a reshuffle.
|
||||
MessageBuffer mb;
|
||||
PrepareDistributeWorkHeader( &mb, DW_SUBPACKETID_REQUEST_SHUFFLE );
|
||||
VMPI_SendData( mb.data, mb.getLen(), VMPI_MASTER_ID );
|
||||
}
|
||||
|
||||
private:
|
||||
CDSInfo *m_pInfo;
|
||||
CShuffledWorkUnitWalker m_WorkUnitWalker;
|
||||
int m_iMyWorkUnitWalkerID;
|
||||
};
|
||||
|
||||
|
||||
|
||||
IWorkUnitDistributorMaster* CreateWUDistributor_SDKMaster()
|
||||
{
|
||||
return new CDistributor_SDKMaster;
|
||||
}
|
||||
|
||||
IWorkUnitDistributorWorker* CreateWUDistributor_SDKWorker()
|
||||
{
|
||||
return new CDistributor_SDKWorker;
|
||||
}
|
||||
|
||||
366
utils/vmpi/vmpi_filesystem.cpp
Normal file
366
utils/vmpi/vmpi_filesystem.cpp
Normal file
@@ -0,0 +1,366 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "vmpi_filesystem_internal.h"
|
||||
#include "tier1/utlbuffer.h"
|
||||
|
||||
|
||||
bool g_bDisableFileAccess = false;
|
||||
|
||||
|
||||
CBaseVMPIFileSystem *g_pBaseVMPIFileSystem = NULL;
|
||||
IFileSystem *g_pOriginalPassThruFileSystem = NULL;
|
||||
|
||||
void* GetVMPIFileSystem()
|
||||
{
|
||||
return (IBaseFileSystem*)g_pBaseVMPIFileSystem;
|
||||
}
|
||||
|
||||
EXPOSE_INTERFACE_FN( GetVMPIFileSystem, IBaseFileSystem, BASEFILESYSTEM_INTERFACE_VERSION )
|
||||
|
||||
|
||||
IFileSystem* VMPI_FileSystem_Init( int maxMemoryUsage, IFileSystem *pPassThru )
|
||||
{
|
||||
Assert( g_bUseMPI );
|
||||
Assert( !g_pBaseVMPIFileSystem );
|
||||
g_pOriginalPassThruFileSystem = pPassThru;
|
||||
|
||||
if ( g_bMPIMaster )
|
||||
{
|
||||
extern CBaseVMPIFileSystem* CreateMasterVMPIFileSystem( int maxMemoryUsage, IFileSystem *pPassThru );
|
||||
CreateMasterVMPIFileSystem( maxMemoryUsage, pPassThru );
|
||||
}
|
||||
else
|
||||
{
|
||||
extern CBaseVMPIFileSystem* CreateWorkerVMPIFileSystem();
|
||||
CreateWorkerVMPIFileSystem();
|
||||
}
|
||||
|
||||
// The Create function should have set this. Normally, we'd set g_pBaseVMPIFileSystem right here, but
|
||||
// the create functions may want to receive some messages, in which case they need to set g_pBaseVMPIFileSystem
|
||||
// so the packets get routed appropriately.
|
||||
Assert( g_pBaseVMPIFileSystem );
|
||||
return g_pBaseVMPIFileSystem;
|
||||
}
|
||||
|
||||
|
||||
IFileSystem* VMPI_FileSystem_Term()
|
||||
{
|
||||
if ( g_pBaseVMPIFileSystem )
|
||||
{
|
||||
g_pBaseVMPIFileSystem->Release();
|
||||
g_pBaseVMPIFileSystem = NULL;
|
||||
|
||||
if ( g_iVMPIVerboseLevel >= 1 )
|
||||
{
|
||||
if ( g_bMPIMaster )
|
||||
Msg( "Multicast send: %dk\n", (g_nMulticastBytesSent + 511) / 1024 );
|
||||
else
|
||||
Msg( "Multicast recv: %dk\n", (g_nMulticastBytesReceived + 511) / 1024 );
|
||||
}
|
||||
}
|
||||
|
||||
IFileSystem *pRet = g_pOriginalPassThruFileSystem;
|
||||
g_pOriginalPassThruFileSystem = NULL;
|
||||
return pRet;
|
||||
}
|
||||
|
||||
|
||||
void VMPI_FileSystem_DisableFileAccess()
|
||||
{
|
||||
g_bDisableFileAccess = true;
|
||||
}
|
||||
|
||||
|
||||
CreateInterfaceFn VMPI_FileSystem_GetFactory()
|
||||
{
|
||||
return Sys_GetFactoryThis();
|
||||
}
|
||||
|
||||
|
||||
void VMPI_FileSystem_CreateVirtualFile( const char *pFilename, const void *pData, unsigned long fileLength )
|
||||
{
|
||||
g_pBaseVMPIFileSystem->CreateVirtualFile( pFilename, pData, fileLength );
|
||||
}
|
||||
|
||||
|
||||
// Register our packet ID.
|
||||
bool FileSystemRecv( MessageBuffer *pBuf, int iSource, int iPacketID )
|
||||
{
|
||||
if ( g_pBaseVMPIFileSystem )
|
||||
return g_pBaseVMPIFileSystem->HandleFileSystemPacket( pBuf, iSource, iPacketID );
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
CDispatchReg g_DispatchReg_FileSystem( VMPI_PACKETID_FILESYSTEM, FileSystemRecv );
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------------ //
|
||||
// CVMPIFile_Memory implementation.
|
||||
// ------------------------------------------------------------------------------------------------------------------------ //
|
||||
|
||||
void CVMPIFile_Memory::Init( const char *pData, long len, char chMode /* = 'b' */ )
|
||||
{
|
||||
m_pData = pData;
|
||||
m_DataLen = len;
|
||||
m_iCurPos = 0;
|
||||
m_chMode = chMode;
|
||||
}
|
||||
|
||||
void CVMPIFile_Memory::Close()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CVMPIFile_Memory::Seek( int pos, FileSystemSeek_t seekType )
|
||||
{
|
||||
if ( seekType == FILESYSTEM_SEEK_HEAD )
|
||||
m_iCurPos = pos;
|
||||
else if ( seekType == FILESYSTEM_SEEK_CURRENT )
|
||||
m_iCurPos += pos;
|
||||
else
|
||||
m_iCurPos = m_DataLen - pos;
|
||||
}
|
||||
|
||||
unsigned int CVMPIFile_Memory::Tell()
|
||||
{
|
||||
return m_iCurPos;
|
||||
}
|
||||
|
||||
unsigned int CVMPIFile_Memory::Size()
|
||||
{
|
||||
return m_DataLen;
|
||||
}
|
||||
|
||||
void CVMPIFile_Memory::Flush()
|
||||
{
|
||||
}
|
||||
|
||||
int CVMPIFile_Memory::Read( void* pOutput, int size )
|
||||
{
|
||||
Assert( m_iCurPos >= 0 );
|
||||
int nToRead = min( (int)(m_DataLen - m_iCurPos), size );
|
||||
|
||||
if ( m_chMode != 't' )
|
||||
{
|
||||
memcpy( pOutput, &m_pData[m_iCurPos], nToRead );
|
||||
m_iCurPos += nToRead;
|
||||
|
||||
return nToRead;
|
||||
}
|
||||
else
|
||||
{
|
||||
int iRead = 0;
|
||||
const char *pData = m_pData + m_iCurPos;
|
||||
int len = m_DataLen - m_iCurPos;
|
||||
|
||||
// Perform crlf translation
|
||||
while ( const char *crlf = ( const char * ) memchr( pData, '\r', len ) )
|
||||
{
|
||||
int canCopy = min( size, crlf - pData );
|
||||
memcpy( pOutput, pData, canCopy );
|
||||
|
||||
m_iCurPos += canCopy;
|
||||
pData += canCopy;
|
||||
len -= canCopy;
|
||||
|
||||
iRead += canCopy;
|
||||
( char * & ) pOutput += canCopy;
|
||||
size -= canCopy;
|
||||
|
||||
if ( size && len )
|
||||
{
|
||||
if ( ( len > 1 ) && ( pData[1] == '\n' ) )
|
||||
{
|
||||
++ m_iCurPos;
|
||||
++ pData;
|
||||
-- len;
|
||||
}
|
||||
|
||||
* ( char * & ) pOutput = *pData;
|
||||
|
||||
++ m_iCurPos;
|
||||
++ pData;
|
||||
-- len;
|
||||
|
||||
++ iRead;
|
||||
++ ( char * & ) pOutput;
|
||||
-- size;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if ( size && len )
|
||||
{
|
||||
// No crlf characters left
|
||||
int canCopy = min( size, len );
|
||||
memcpy( pOutput, pData, canCopy );
|
||||
|
||||
m_iCurPos += canCopy;
|
||||
pData += canCopy;
|
||||
len -= canCopy;
|
||||
|
||||
iRead += canCopy;
|
||||
( char * & ) pOutput += canCopy;
|
||||
size -= canCopy;
|
||||
}
|
||||
|
||||
return iRead;
|
||||
}
|
||||
}
|
||||
|
||||
int CVMPIFile_Memory::Write( void const* pInput, int size )
|
||||
{
|
||||
Assert( false ); return 0;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------------ //
|
||||
// CBaseVMPIFileSystem implementation.
|
||||
// ------------------------------------------------------------------------------------------------------------------------ //
|
||||
|
||||
CBaseVMPIFileSystem::~CBaseVMPIFileSystem()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CBaseVMPIFileSystem::Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
||||
void CBaseVMPIFileSystem::Close( FileHandle_t file )
|
||||
{
|
||||
if ( file )
|
||||
((IVMPIFile*)file)->Close();
|
||||
}
|
||||
|
||||
int CBaseVMPIFileSystem::Read( void* pOutput, int size, FileHandle_t file )
|
||||
{
|
||||
return ((IVMPIFile*)file)->Read( pOutput, size );
|
||||
}
|
||||
|
||||
int CBaseVMPIFileSystem::Write( void const* pInput, int size, FileHandle_t file )
|
||||
{
|
||||
return ((IVMPIFile*)file)->Write( pInput, size );
|
||||
}
|
||||
|
||||
void CBaseVMPIFileSystem::Seek( FileHandle_t file, int pos, FileSystemSeek_t seekType )
|
||||
{
|
||||
((IVMPIFile*)file)->Seek( pos, seekType );
|
||||
}
|
||||
|
||||
unsigned int CBaseVMPIFileSystem::Tell( FileHandle_t file )
|
||||
{
|
||||
return ((IVMPIFile*)file)->Tell();
|
||||
}
|
||||
|
||||
unsigned int CBaseVMPIFileSystem::Size( FileHandle_t file )
|
||||
{
|
||||
return ((IVMPIFile*)file)->Size();
|
||||
}
|
||||
|
||||
unsigned int CBaseVMPIFileSystem::Size( const char *pFilename, const char *pathID = 0 )
|
||||
{
|
||||
FileHandle_t hFile = Open( pFilename, "rb", NULL );
|
||||
if ( hFile == FILESYSTEM_INVALID_HANDLE )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int ret = Size( hFile );
|
||||
Close( hFile );
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
bool CBaseVMPIFileSystem::FileExists( const char *pFileName, const char *pPathID )
|
||||
{
|
||||
FileHandle_t hFile = Open( pFileName, "rb", NULL );
|
||||
if ( hFile )
|
||||
{
|
||||
Close( hFile );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CBaseVMPIFileSystem::Flush( FileHandle_t file )
|
||||
{
|
||||
((IVMPIFile*)file)->Flush();
|
||||
}
|
||||
|
||||
bool CBaseVMPIFileSystem::Precache( const char* pFileName, const char *pPathID )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// NOTE: This is an exact copy of code in BaseFileSystem.cpp which
|
||||
// has to be here because they want to call
|
||||
// the implementation of Open/Size/Read/Write in CBaseVMPIFileSystem
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CBaseVMPIFileSystem::ReadFile( const char *pFileName, const char *pPath, CUtlBuffer &buf, int nMaxBytes, int nStartingByte, FSAllocFunc_t pfnAlloc )
|
||||
{
|
||||
const char *pReadFlags = "rb";
|
||||
if ( buf.IsText() && !buf.ContainsCRLF() )
|
||||
{
|
||||
pReadFlags = "rt";
|
||||
}
|
||||
|
||||
FileHandle_t fp = Open( pFileName, buf.IsText() ? "rt" : "rb", pPath );
|
||||
if ( !fp )
|
||||
return false;
|
||||
|
||||
int nBytesToRead = Size( fp );
|
||||
if ( nMaxBytes > 0 )
|
||||
{
|
||||
nBytesToRead = min( nMaxBytes, nBytesToRead );
|
||||
}
|
||||
buf.EnsureCapacity( nBytesToRead + buf.TellPut() );
|
||||
|
||||
if ( nStartingByte != 0 )
|
||||
{
|
||||
Seek( fp, nStartingByte, FILESYSTEM_SEEK_HEAD );
|
||||
}
|
||||
|
||||
int nBytesRead = Read( buf.PeekPut(), nBytesToRead, fp );
|
||||
buf.SeekPut( CUtlBuffer::SEEK_CURRENT, nBytesRead );
|
||||
|
||||
Close( fp );
|
||||
return (nBytesRead != 0);
|
||||
}
|
||||
|
||||
bool CBaseVMPIFileSystem::WriteFile( const char *pFileName, const char *pPath, CUtlBuffer &buf )
|
||||
{
|
||||
const char *pWriteFlags = "wb";
|
||||
if ( buf.IsText() && !buf.ContainsCRLF() )
|
||||
{
|
||||
pWriteFlags = "wt";
|
||||
}
|
||||
|
||||
FileHandle_t fp = Open( pFileName, buf.IsText() ? "wt" : "wb", pPath );
|
||||
if ( !fp )
|
||||
return false;
|
||||
|
||||
int nBytesWritten = Write( buf.Base(), buf.TellPut(), fp );
|
||||
|
||||
Close( fp );
|
||||
return (nBytesWritten != 0);
|
||||
}
|
||||
|
||||
53
utils/vmpi/vmpi_filesystem.h
Normal file
53
utils/vmpi/vmpi_filesystem.h
Normal file
@@ -0,0 +1,53 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef VMPI_FILESYSTEM_H
|
||||
#define VMPI_FILESYSTEM_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "interface.h"
|
||||
|
||||
|
||||
class IFileSystem;
|
||||
class MessageBuffer;
|
||||
|
||||
|
||||
// Use this to read virtual files.
|
||||
#define VMPI_VIRTUAL_FILES_PATH_ID "VMPI_VIRTUAL_FILES_PATH_ID"
|
||||
|
||||
|
||||
// When you hook the file system with VMPI and are a worker, it blocks on file reads
|
||||
// and uses MPI to communicate with the master to transfer files it needs over.
|
||||
//
|
||||
// The filesystem, by default (and it maxFileSystemMemoryUsage is left at zero),
|
||||
// keeps the contents of the files that get opened in memory. You can pass in a
|
||||
// value here to put a cap on it, in which case it'll unload the least-recently-used
|
||||
// files when it hits the limit.
|
||||
IFileSystem* VMPI_FileSystem_Init( int maxFileSystemMemoryUsage, IFileSystem *pPassThru );
|
||||
|
||||
// On the master machine, this really should be called before the app shuts down and
|
||||
// global destructors are called. If it isn't, it might lock up waiting for a thread to exit.
|
||||
//
|
||||
// This returns the original filesystem you passed into VMPI_FileSystem_Init so you can uninitialize it.
|
||||
IFileSystem* VMPI_FileSystem_Term();
|
||||
|
||||
// Causes it to error out on any Open() calls.
|
||||
void VMPI_FileSystem_DisableFileAccess();
|
||||
|
||||
// Returns a factory that will hand out BASEFILESYSTEM_INTERFACE_VERSION when asked for it.
|
||||
CreateInterfaceFn VMPI_FileSystem_GetFactory();
|
||||
|
||||
// This function creates a virtual file that workers can then open and read out of.
|
||||
// NOTE: when reading from the file, you must use VMPI_VIRTUAL_FILES_PATH_ID as the path ID
|
||||
// or else it won't find the file.
|
||||
void VMPI_FileSystem_CreateVirtualFile( const char *pFilename, const void *pData, unsigned long fileLength );
|
||||
|
||||
|
||||
#endif // VMPI_FILESYSTEM_H
|
||||
129
utils/vmpi/vmpi_filesystem_internal.h
Normal file
129
utils/vmpi/vmpi_filesystem_internal.h
Normal file
@@ -0,0 +1,129 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef VMPI_FILESYSTEM_INTERNAL_H
|
||||
#define VMPI_FILESYSTEM_INTERNAL_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "vmpi_filesystem.h"
|
||||
#include "filesystem.h"
|
||||
#include "messbuf.h"
|
||||
#include "iphelpers.h"
|
||||
#include "vmpi.h"
|
||||
#include "utlvector.h"
|
||||
#include "utllinkedlist.h"
|
||||
#include "filesystem_passthru.h"
|
||||
|
||||
|
||||
// Sub packet IDs specific to the VMPI file system.
|
||||
#define VMPI_FSPACKETID_FILE_REQUEST 1 // Sent by the worker to request a file.
|
||||
#define VMPI_FSPACKETID_FILE_RESPONSE 2 // Master's response to a file request.
|
||||
#define VMPI_FSPACKETID_CHUNK_RECEIVED 3 // Sent by workers to tell the master they received a chunk.
|
||||
#define VMPI_FSPACKETID_FILE_RECEIVED 4 // Sent by workers to tell the master they received the whole file.
|
||||
#define VMPI_FSPACKETID_MULTICAST_ADDR 5
|
||||
#define VMPI_FSPACKETID_FILE_CHUNK 6 // Used to send file data when using TCP.
|
||||
|
||||
|
||||
// In TCP mode, we send larger chunks in a sliding window.
|
||||
#define TCP_CHUNK_QUEUE_LEN 16
|
||||
|
||||
#define TCP_CHUNK_PAYLOAD_SIZE (16*1024)
|
||||
#define MULTICAST_CHUNK_PAYLOAD_SIZE (1024*1)
|
||||
#define MAX_CHUNK_PAYLOAD_SIZE MAX( TCP_CHUNK_PAYLOAD_SIZE, MULTICAST_CHUNK_PAYLOAD_SIZE )
|
||||
|
||||
|
||||
class CMulticastFileInfo
|
||||
{
|
||||
public:
|
||||
unsigned long m_CompressedSize;
|
||||
unsigned long m_UncompressedSize;
|
||||
unsigned short m_FileID;
|
||||
unsigned short m_nChunks;
|
||||
};
|
||||
|
||||
|
||||
class CBaseVMPIFileSystem : public CFileSystemPassThru
|
||||
{
|
||||
public:
|
||||
virtual ~CBaseVMPIFileSystem();
|
||||
virtual void Release();
|
||||
|
||||
virtual void CreateVirtualFile( const char *pFilename, const void *pData, int fileLength ) = 0;
|
||||
virtual bool HandleFileSystemPacket( MessageBuffer *pBuf, int iSource, int iPacketID ) = 0;
|
||||
|
||||
virtual void Close( FileHandle_t file );
|
||||
virtual int Read( void* pOutput, int size, FileHandle_t file );
|
||||
virtual int Write( void const* pInput, int size, FileHandle_t file );
|
||||
virtual void Seek( FileHandle_t file, int pos, FileSystemSeek_t seekType );
|
||||
virtual unsigned int Tell( FileHandle_t file );
|
||||
virtual unsigned int Size( FileHandle_t file );
|
||||
virtual unsigned int Size( const char *pFilename, const char *pathID );
|
||||
virtual bool FileExists( const char *pFileName, const char *pPathID );
|
||||
virtual void Flush( FileHandle_t file );
|
||||
virtual bool Precache( const char* pFileName, const char *pPathID );
|
||||
virtual bool ReadFile( const char *pFileName, const char *pPath, CUtlBuffer &buf, int nMaxBytes = 0, int nStartingByte = 0, FSAllocFunc_t pfnAlloc = 0 );
|
||||
virtual bool WriteFile( const char *pFileName, const char *pPath, CUtlBuffer &buf );
|
||||
|
||||
// All the IFileSystem-specific ones pass the calls through.
|
||||
// The worker opens its own filesystem_stdio fthrough.
|
||||
|
||||
protected:
|
||||
CIPAddr m_MulticastIP;
|
||||
};
|
||||
|
||||
|
||||
class IVMPIFile
|
||||
{
|
||||
public:
|
||||
virtual void Close() = 0;
|
||||
virtual void Seek( int pos, FileSystemSeek_t seekType ) = 0;
|
||||
virtual unsigned int Tell() = 0;
|
||||
virtual unsigned int Size() = 0;
|
||||
virtual void Flush() = 0;
|
||||
virtual int Read( void* pOutput, int size ) = 0;
|
||||
virtual int Write( void const* pInput, int size ) = 0;
|
||||
};
|
||||
|
||||
|
||||
// Both the workers and masters use this to hand out the file data.
|
||||
class CVMPIFile_Memory : public IVMPIFile
|
||||
{
|
||||
public:
|
||||
void Init( const char *pData, long len, char chMode = 'b' );
|
||||
virtual void Close();
|
||||
virtual void Seek( int pos, FileSystemSeek_t seekType );
|
||||
virtual unsigned int Tell();
|
||||
virtual unsigned int Size();
|
||||
virtual void Flush();
|
||||
virtual int Read( void* pOutput, int size ) ;
|
||||
virtual int Write( void const* pInput, int size );
|
||||
|
||||
private:
|
||||
const char *m_pData;
|
||||
long m_DataLen;
|
||||
int m_iCurPos;
|
||||
char m_chMode; // 'b' or 't'
|
||||
};
|
||||
|
||||
|
||||
// We use different payload sizes if we're using TCP mode vs. multicast/broadcast mode.
|
||||
inline int VMPI_GetChunkPayloadSize()
|
||||
{
|
||||
if ( VMPI_GetFileSystemMode() == VMPI_FILESYSTEM_TCP )
|
||||
return TCP_CHUNK_PAYLOAD_SIZE;
|
||||
else
|
||||
return MULTICAST_CHUNK_PAYLOAD_SIZE;
|
||||
}
|
||||
|
||||
|
||||
extern bool g_bDisableFileAccess;
|
||||
extern CBaseVMPIFileSystem *g_pBaseVMPIFileSystem;
|
||||
|
||||
|
||||
#endif // VMPI_FILESYSTEM_INTERNAL_H
|
||||
1606
utils/vmpi/vmpi_filesystem_master.cpp
Normal file
1606
utils/vmpi/vmpi_filesystem_master.cpp
Normal file
File diff suppressed because it is too large
Load Diff
815
utils/vmpi/vmpi_filesystem_worker.cpp
Normal file
815
utils/vmpi/vmpi_filesystem_worker.cpp
Normal file
@@ -0,0 +1,815 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include <winsock2.h>
|
||||
#include "vmpi_filesystem_internal.h"
|
||||
#include "threadhelpers.h"
|
||||
#include "zlib.h"
|
||||
|
||||
|
||||
#define NUM_BUFFERED_CHUNK_ACKS 512
|
||||
#define ACK_FLUSH_INTERVAL 500 // Flush the ack queue twice per second.
|
||||
|
||||
|
||||
static bool g_bReceivedMulticastIP = false;
|
||||
static CIPAddr g_MulticastIP;
|
||||
|
||||
|
||||
CCriticalSection g_FileResponsesCS;
|
||||
|
||||
class CFileResponse
|
||||
{
|
||||
public:
|
||||
int m_RequestID;
|
||||
int m_Response;
|
||||
bool m_bZeroLength;
|
||||
};
|
||||
|
||||
CUtlVector<CFileResponse> g_FileResponses;
|
||||
int g_RequestID = 0;
|
||||
|
||||
|
||||
class CFileChunkPacket
|
||||
{
|
||||
public:
|
||||
int m_Len;
|
||||
char m_Data[1];
|
||||
};
|
||||
CUtlLinkedList<CFileChunkPacket*, int> g_FileChunkPackets; // This is also protected by g_FileResponsesCS.
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------------ //
|
||||
// Classes.
|
||||
// ------------------------------------------------------------------------------------------------------------------------ //
|
||||
|
||||
class CWorkerFile
|
||||
{
|
||||
public:
|
||||
const char* GetFilename() { return m_Filename.Base(); }
|
||||
const char* GetPathID() { return m_PathID.Base(); }
|
||||
bool IsReadyToRead() const { return m_nChunksToReceive == 0; }
|
||||
|
||||
|
||||
public:
|
||||
CFastTimer m_Timer; // To see how long it takes to download the file.
|
||||
|
||||
// This has to be sent explicitly as part of the file info or else the protocol
|
||||
// breaks on empty files.
|
||||
bool m_bZeroLength;
|
||||
|
||||
// This is false until we get any packets about the file. In the packets,
|
||||
// we find out what the size is supposed to be.
|
||||
bool m_bGotCompressedSize;
|
||||
|
||||
// The ID the master uses to refer to this file.
|
||||
int m_FileID;
|
||||
|
||||
CUtlVector<char> m_Filename;
|
||||
CUtlVector<char> m_PathID;
|
||||
|
||||
// First data comes in here, then when it's all there, it is inflated into m_UncompressedData.
|
||||
CUtlVector<char> m_CompressedData;
|
||||
|
||||
// 1 bit for each chunk.
|
||||
CUtlVector<unsigned char> m_ChunksReceived;
|
||||
|
||||
// When this is zero, the file is done being received and m_UncompressedData is valid.
|
||||
int m_nChunksToReceive;
|
||||
CUtlVector<char> m_UncompressedData;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------------ //
|
||||
// Global helpers.
|
||||
// ------------------------------------------------------------------------------------------------------------------------ //
|
||||
|
||||
static void RecvMulticastIP( CIPAddr *pAddr )
|
||||
{
|
||||
while ( !g_bReceivedMulticastIP )
|
||||
VMPI_DispatchNextMessage();
|
||||
|
||||
*pAddr = g_MulticastIP;
|
||||
}
|
||||
|
||||
|
||||
static bool ZLibDecompress( const void *pInput, int inputLen, void *pOut, int outLen )
|
||||
{
|
||||
if ( inputLen == 0 )
|
||||
{
|
||||
// Zero-length file?
|
||||
return true;
|
||||
}
|
||||
|
||||
z_stream decompressStream;
|
||||
|
||||
// Initialize the decompression stream.
|
||||
memset( &decompressStream, 0, sizeof( decompressStream ) );
|
||||
if ( inflateInit( &decompressStream ) != Z_OK )
|
||||
return false;
|
||||
|
||||
// Decompress all this stuff and write it to the file.
|
||||
decompressStream.next_in = (unsigned char*)pInput;
|
||||
decompressStream.avail_in = inputLen;
|
||||
|
||||
char *pOutChar = (char*)pOut;
|
||||
while ( decompressStream.avail_in )
|
||||
{
|
||||
decompressStream.total_out = 0;
|
||||
decompressStream.next_out = (unsigned char*)pOutChar;
|
||||
decompressStream.avail_out = outLen - (pOutChar - (char*)pOut);
|
||||
|
||||
int ret = inflate( &decompressStream, Z_NO_FLUSH );
|
||||
if ( ret != Z_OK && ret != Z_STREAM_END )
|
||||
return false;
|
||||
|
||||
|
||||
pOutChar += decompressStream.total_out;
|
||||
|
||||
if ( ret == Z_STREAM_END )
|
||||
{
|
||||
if ( (pOutChar - (char*)pOut) == outLen )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( false );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert( false ); // Should have gotten to Z_STREAM_END.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------------ //
|
||||
// CWorkerMulticastListener implementation.
|
||||
// ------------------------------------------------------------------------------------------------------------------------ //
|
||||
|
||||
class CWorkerMulticastListener
|
||||
{
|
||||
public:
|
||||
CWorkerMulticastListener()
|
||||
{
|
||||
m_nUnfinishedFiles = 0;
|
||||
}
|
||||
|
||||
~CWorkerMulticastListener()
|
||||
{
|
||||
Term();
|
||||
}
|
||||
|
||||
bool Init( const CIPAddr &mcAddr )
|
||||
{
|
||||
m_MulticastAddr = mcAddr;
|
||||
m_hMainThread = GetCurrentThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Term()
|
||||
{
|
||||
m_WorkerFiles.PurgeAndDeleteElements();
|
||||
}
|
||||
|
||||
|
||||
CWorkerFile* RequestFileFromServer( const char *pFilename, const char *pPathID )
|
||||
{
|
||||
Assert( pPathID );
|
||||
Assert( FindWorkerFile( pFilename, pPathID ) == NULL );
|
||||
|
||||
// Send a request to the master to find out if this file even exists.
|
||||
CCriticalSectionLock csLock( &g_FileResponsesCS );
|
||||
csLock.Lock();
|
||||
int requestID = g_RequestID++;
|
||||
csLock.Unlock();
|
||||
|
||||
unsigned char packetID[2] = { VMPI_PACKETID_FILESYSTEM, VMPI_FSPACKETID_FILE_REQUEST };
|
||||
const void *pChunks[4] = { packetID, &requestID, (void*)pFilename, pPathID };
|
||||
int chunkLengths[4] = { sizeof( packetID ), sizeof( requestID ), strlen( pFilename ) + 1, strlen( pPathID ) + 1 };
|
||||
VMPI_SendChunks( pChunks, chunkLengths, ARRAYSIZE( pChunks ), 0 );
|
||||
|
||||
// Wait for the file ID to come back.
|
||||
CFileResponse response;
|
||||
response.m_Response = -1;
|
||||
response.m_bZeroLength = true;
|
||||
|
||||
// We're in a worker thread.. the main thread should be dispatching all the messages, so let it
|
||||
// do that until we get our response.
|
||||
while ( 1 )
|
||||
{
|
||||
bool bGotIt = false;
|
||||
csLock.Lock();
|
||||
for ( int iResponse=0; iResponse < g_FileResponses.Count(); iResponse++ )
|
||||
{
|
||||
if ( g_FileResponses[iResponse].m_RequestID == requestID )
|
||||
{
|
||||
response = g_FileResponses[iResponse];
|
||||
g_FileResponses.Remove( iResponse );
|
||||
bGotIt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
csLock.Unlock();
|
||||
|
||||
if ( bGotIt )
|
||||
break;
|
||||
|
||||
if ( GetCurrentThread() == m_hMainThread )
|
||||
VMPI_DispatchNextMessage( 20 );
|
||||
else
|
||||
Sleep( 20 );
|
||||
}
|
||||
|
||||
// If we get -1 back, it means the file doesn't exist.
|
||||
int fileID = response.m_Response;
|
||||
if ( fileID == -1 )
|
||||
return NULL;
|
||||
|
||||
CWorkerFile *pTestFile = new CWorkerFile;
|
||||
|
||||
pTestFile->m_Filename.SetSize( strlen( pFilename ) + 1 );
|
||||
strcpy( pTestFile->m_Filename.Base(), pFilename );
|
||||
|
||||
pTestFile->m_PathID.SetSize( strlen( pPathID ) + 1 );
|
||||
strcpy( pTestFile->m_PathID.Base(), pPathID );
|
||||
|
||||
pTestFile->m_FileID = fileID;
|
||||
pTestFile->m_nChunksToReceive = 9999;
|
||||
pTestFile->m_Timer.Start();
|
||||
m_WorkerFiles.AddToTail( pTestFile );
|
||||
pTestFile->m_bGotCompressedSize = false;
|
||||
pTestFile->m_bZeroLength = response.m_bZeroLength;
|
||||
|
||||
++m_nUnfinishedFiles;
|
||||
|
||||
return pTestFile;
|
||||
}
|
||||
|
||||
void FlushAckChunks( unsigned short chunksToAck[NUM_BUFFERED_CHUNK_ACKS][2], int &nChunksToAck, DWORD &lastAckTime )
|
||||
{
|
||||
if ( nChunksToAck )
|
||||
{
|
||||
// Tell the master we received this chunk.
|
||||
unsigned char packetID[2] = { VMPI_PACKETID_FILESYSTEM, VMPI_FSPACKETID_CHUNK_RECEIVED };
|
||||
void *pChunks[2] = { packetID, chunksToAck };
|
||||
int chunkLengths[2] = { sizeof( packetID ), nChunksToAck * 4 };
|
||||
VMPI_SendChunks( pChunks, chunkLengths, 2, 0 );
|
||||
nChunksToAck = 0;
|
||||
}
|
||||
|
||||
lastAckTime = GetTickCount();
|
||||
}
|
||||
|
||||
void MaybeFlushAckChunks( unsigned short chunksToAck[NUM_BUFFERED_CHUNK_ACKS][2], int &nChunksToAck, DWORD &lastAckTime )
|
||||
{
|
||||
if ( nChunksToAck && GetTickCount() - lastAckTime > ACK_FLUSH_INTERVAL )
|
||||
FlushAckChunks( chunksToAck, nChunksToAck, lastAckTime );
|
||||
}
|
||||
|
||||
void AddAckChunk(
|
||||
unsigned short chunksToAck[NUM_BUFFERED_CHUNK_ACKS][2],
|
||||
int &nChunksToAck,
|
||||
DWORD &lastAckTime,
|
||||
int fileID,
|
||||
int iChunk )
|
||||
{
|
||||
chunksToAck[nChunksToAck][0] = (unsigned short)fileID;
|
||||
chunksToAck[nChunksToAck][1] = (unsigned short)iChunk;
|
||||
|
||||
// TCP filesystem acks all chunks immediately so it'll send more.
|
||||
++nChunksToAck;
|
||||
if ( nChunksToAck == NUM_BUFFERED_CHUNK_ACKS || VMPI_GetFileSystemMode() == VMPI_FILESYSTEM_TCP )
|
||||
{
|
||||
FlushAckChunks( chunksToAck, nChunksToAck, lastAckTime );
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the length of the packet's data or -1 if there is nothing.
|
||||
int CheckFileChunkPackets( char *data, int dataSize )
|
||||
{
|
||||
// Using TCP.. pop the next received packet off the stack.
|
||||
CCriticalSectionLock csLock( &g_FileResponsesCS );
|
||||
csLock.Lock();
|
||||
if ( g_FileChunkPackets.Count() <= 0 )
|
||||
return -1;
|
||||
|
||||
CFileChunkPacket *pPacket = g_FileChunkPackets[ g_FileChunkPackets.Head() ];
|
||||
g_FileChunkPackets.Remove( g_FileChunkPackets.Head() );
|
||||
|
||||
// Yes, this is inefficient, but the amount of data we're handling here is tiny so the
|
||||
// effect is negligible.
|
||||
int len;
|
||||
if ( pPacket->m_Len > dataSize )
|
||||
{
|
||||
len = -1;
|
||||
Warning( "CWorkerMulticastListener::ListenFor: Got a section of data too long (%d bytes).", pPacket->m_Len );
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy( data, pPacket->m_Data, pPacket->m_Len );
|
||||
len = pPacket->m_Len;
|
||||
}
|
||||
|
||||
free( pPacket );
|
||||
return len;
|
||||
}
|
||||
|
||||
void ShowSDKWorkerMsg( const char *pMsg, ... )
|
||||
{
|
||||
if ( !g_bMPIMaster && VMPI_IsSDKMode() )
|
||||
{
|
||||
va_list marker;
|
||||
va_start( marker, pMsg );
|
||||
char str[4096];
|
||||
V_vsnprintf( str, sizeof( str ), pMsg, marker );
|
||||
va_end( marker );
|
||||
Msg( "%s", str );
|
||||
}
|
||||
}
|
||||
|
||||
// This is the main function the workers use to pick files out of the multicast stream.
|
||||
// The app is waiting for a specific file, but we receive and ack any files we can until
|
||||
// we get the file they're looking for, then we return.
|
||||
//
|
||||
// NOTE: ideally, this would be in a thread, but it adds lots of complications and may
|
||||
// not be worth it.
|
||||
CWorkerFile* ListenFor( const char *pFilename, const char *pPathID )
|
||||
{
|
||||
CWorkerFile *pFile = FindWorkerFile( pFilename, pPathID );
|
||||
if ( !pFile )
|
||||
{
|
||||
// Ok, we haven't requested this file yet. Create an entry for it and
|
||||
// tell the master we'd like this file.
|
||||
pFile = RequestFileFromServer( pFilename, pPathID );
|
||||
if ( !pFile )
|
||||
return NULL;
|
||||
|
||||
// If it's zero-length, we can return right now.
|
||||
if ( pFile->m_bZeroLength )
|
||||
{
|
||||
--m_nUnfinishedFiles;
|
||||
return pFile;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup a filename to print some debug spew with.
|
||||
char printableFilename[58];
|
||||
if ( V_strlen( pFilename ) > ARRAYSIZE( printableFilename ) - 1 )
|
||||
{
|
||||
V_strncpy( printableFilename, "[...]", sizeof( printableFilename ) );
|
||||
V_strncat( printableFilename, &pFilename[V_strlen(pFilename) - ARRAYSIZE(printableFilename) + 1 + V_strlen(printableFilename)], sizeof( printableFilename ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
V_strncpy( printableFilename, pFilename, sizeof( printableFilename ) );
|
||||
}
|
||||
ShowSDKWorkerMsg( "\rRecv %s (0%%) ", printableFilename );
|
||||
int iChunkPayloadSize = VMPI_GetChunkPayloadSize();
|
||||
|
||||
// Now start listening to the stream.
|
||||
// Note: no need to setup anything when in TCP mode - we just use the regular
|
||||
// VMPI dispatch stuff to handle that.
|
||||
ISocket *pSocket = NULL;
|
||||
if ( VMPI_GetFileSystemMode() == VMPI_FILESYSTEM_MULTICAST )
|
||||
{
|
||||
pSocket = CreateMulticastListenSocket( m_MulticastAddr );
|
||||
|
||||
if ( !pSocket )
|
||||
{
|
||||
char str[512];
|
||||
IP_GetLastErrorString( str, sizeof( str ) );
|
||||
Warning( "CreateMulticastListenSocket (%d.%d.%d.%d:%d) failed\n%s\n", EXPAND_ADDR( m_MulticastAddr ), str );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if ( VMPI_GetFileSystemMode() == VMPI_FILESYSTEM_BROADCAST )
|
||||
{
|
||||
pSocket = CreateIPSocket();
|
||||
if ( !pSocket->BindToAny( m_MulticastAddr.port ) )
|
||||
{
|
||||
pSocket->Release();
|
||||
pSocket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short chunksToAck[NUM_BUFFERED_CHUNK_ACKS][2];
|
||||
int nChunksToAck = 0;
|
||||
DWORD lastAckTime = GetTickCount();
|
||||
|
||||
// Now just receive multicast data until this file has been received.
|
||||
while ( m_nUnfinishedFiles > 0 )
|
||||
{
|
||||
char data[MAX_CHUNK_PAYLOAD_SIZE+1024];
|
||||
int len = -1;
|
||||
|
||||
if ( pSocket )
|
||||
{
|
||||
CIPAddr ipFrom;
|
||||
len = pSocket->RecvFrom( data, sizeof( data ), &ipFrom );
|
||||
}
|
||||
else
|
||||
{
|
||||
len = CheckFileChunkPackets( data, sizeof( data ) );
|
||||
}
|
||||
|
||||
if ( len == -1 )
|
||||
{
|
||||
// Sleep for 10ms and also handle socket errors.
|
||||
Sleep( 0 );
|
||||
VMPI_DispatchNextMessage( 10 );
|
||||
continue;
|
||||
}
|
||||
|
||||
g_nMulticastBytesReceived += len;
|
||||
|
||||
// Alrighty. Figure out what the deal is with this file.
|
||||
CMulticastFileInfo *pInfo = (CMulticastFileInfo*)data;
|
||||
int *piChunk = (int*)( pInfo + 1 );
|
||||
const char *pTestFilename = (const char*)( piChunk + 1 );
|
||||
const char *pPayload = pTestFilename + strlen( pFilename ) + 1;
|
||||
int payloadLen = len - ( pPayload - data );
|
||||
if ( payloadLen < 0 )
|
||||
{
|
||||
Warning( "CWorkerMulticastListener::ListenFor: invalid packet received on multicast group\n" );
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if ( pInfo->m_FileID != pFile->m_FileID )
|
||||
continue;
|
||||
|
||||
CWorkerFile *pTestFile = FindWorkerFile( pInfo->m_FileID );
|
||||
if ( !pTestFile )
|
||||
Error( "FindWorkerFile( %s ) failed\n", pTestFilename );
|
||||
|
||||
// TODO: reenable this code and disable the if right above here.
|
||||
// We always get "invalid payload length" errors on the workers when using this, but
|
||||
// I haven't been able to figure out why yet.
|
||||
/*
|
||||
// Put the data into whatever file it belongs in.
|
||||
if ( !pTestFile )
|
||||
{
|
||||
pTestFile = RequestFileFromServer( pTestFilename );
|
||||
if ( !pTestFile )
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
|
||||
// Is this the first packet about this file?
|
||||
if ( !pTestFile->m_bGotCompressedSize )
|
||||
{
|
||||
pTestFile->m_bGotCompressedSize = true;
|
||||
pTestFile->m_CompressedData.SetSize( pInfo->m_CompressedSize );
|
||||
pTestFile->m_UncompressedData.SetSize( pInfo->m_UncompressedSize );
|
||||
pTestFile->m_ChunksReceived.SetSize( PAD_NUMBER( pInfo->m_nChunks, 8 ) / 8 );
|
||||
pTestFile->m_nChunksToReceive = pInfo->m_nChunks;
|
||||
memset( pTestFile->m_ChunksReceived.Base(), 0, pTestFile->m_ChunksReceived.Count() );
|
||||
}
|
||||
|
||||
// Validate the chunk index and uncompressed size.
|
||||
int iChunk = *piChunk;
|
||||
if ( iChunk < 0 || iChunk >= pInfo->m_nChunks )
|
||||
{
|
||||
Error( "ListenFor(): invalid chunk index (%d) for file '%s'\n", iChunk, pTestFilename );
|
||||
}
|
||||
|
||||
// Only handle this if we didn't already received the chunk.
|
||||
if ( !(pTestFile->m_ChunksReceived[iChunk >> 3] & (1 << (iChunk & 7))) )
|
||||
{
|
||||
// Make sure the file is properly setup to receive the data into.
|
||||
if ( (int)pInfo->m_UncompressedSize != pTestFile->m_UncompressedData.Count() ||
|
||||
(int)pInfo->m_CompressedSize != pTestFile->m_CompressedData.Count() )
|
||||
{
|
||||
Error( "ListenFor(): invalid compressed or uncompressed size.\n"
|
||||
"pInfo = '%s', pTestFile = '%s'\n"
|
||||
"Compressed (pInfo = %d, pTestFile = %d)\n"
|
||||
"Uncompressed (pInfo = %d, pTestFile = %d)\n",
|
||||
pTestFilename,
|
||||
pTestFile->GetFilename(),
|
||||
pInfo->m_CompressedSize,
|
||||
pTestFile->m_CompressedData.Count(),
|
||||
pInfo->m_UncompressedSize,
|
||||
pTestFile->m_UncompressedData.Count()
|
||||
);
|
||||
}
|
||||
|
||||
int iChunkStart = iChunk * iChunkPayloadSize;
|
||||
int iChunkEnd = min( iChunkStart + iChunkPayloadSize, pTestFile->m_CompressedData.Count() );
|
||||
int chunkLen = iChunkEnd - iChunkStart;
|
||||
|
||||
if ( chunkLen != payloadLen )
|
||||
{
|
||||
Error( "ListenFor(): invalid payload length for '%s' (%d should be %d)\n"
|
||||
"pInfo = '%s', pTestFile = '%s'\n"
|
||||
"Chunk %d out of %d. Compressed size: %d\n",
|
||||
pTestFile->GetFilename(),
|
||||
payloadLen,
|
||||
chunkLen,
|
||||
pTestFilename,
|
||||
pTestFile->GetFilename(),
|
||||
iChunk,
|
||||
pInfo->m_nChunks,
|
||||
pInfo->m_CompressedSize
|
||||
);
|
||||
}
|
||||
|
||||
memcpy( &pTestFile->m_CompressedData[iChunkStart], pPayload, chunkLen );
|
||||
pTestFile->m_ChunksReceived[iChunk >> 3] |= (1 << (iChunk & 7));
|
||||
|
||||
--pTestFile->m_nChunksToReceive;
|
||||
|
||||
if ( pTestFile == pFile )
|
||||
{
|
||||
int percent = 100 - (100 * pFile->m_nChunksToReceive) / pInfo->m_nChunks;
|
||||
ShowSDKWorkerMsg( "\rRecv %s (%d%%) [chunk %d/%d] ", printableFilename, percent, pInfo->m_nChunks - pFile->m_nChunksToReceive, pInfo->m_nChunks );
|
||||
}
|
||||
|
||||
// Remember to ack what we received.
|
||||
AddAckChunk( chunksToAck, nChunksToAck, lastAckTime, pInfo->m_FileID, iChunk );
|
||||
|
||||
// If we're done receiving the data, unpack it.
|
||||
if ( pTestFile->m_nChunksToReceive == 0 )
|
||||
{
|
||||
// Ack the file.
|
||||
FlushAckChunks( chunksToAck, nChunksToAck, lastAckTime );
|
||||
|
||||
pTestFile->m_Timer.End();
|
||||
|
||||
pTestFile->m_UncompressedData.SetSize( pInfo->m_UncompressedSize );
|
||||
--m_nUnfinishedFiles;
|
||||
|
||||
if ( !ZLibDecompress(
|
||||
pTestFile->m_CompressedData.Base(),
|
||||
pTestFile->m_CompressedData.Count(),
|
||||
pTestFile->m_UncompressedData.Base(),
|
||||
pTestFile->m_UncompressedData.Count() ) )
|
||||
{
|
||||
if ( pSocket )
|
||||
pSocket->Release();
|
||||
FlushAckChunks( chunksToAck, nChunksToAck, lastAckTime );
|
||||
Error( "ZLibDecompress failed.\n" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char str[512];
|
||||
V_snprintf( str, sizeof( str ), "Got %s (%dk) in %.2fs",
|
||||
printableFilename,
|
||||
(pTestFile->m_UncompressedData.Count() + 511) / 1024,
|
||||
pTestFile->m_Timer.GetDuration().GetSeconds()
|
||||
);
|
||||
Msg( "\r%-79s\n", str );
|
||||
|
||||
// Won't be needing this anymore.
|
||||
pTestFile->m_CompressedData.Purge();
|
||||
}
|
||||
}
|
||||
|
||||
MaybeFlushAckChunks( chunksToAck, nChunksToAck, lastAckTime );
|
||||
}
|
||||
|
||||
Assert( pFile->IsReadyToRead() );
|
||||
FlushAckChunks( chunksToAck, nChunksToAck, lastAckTime );
|
||||
if ( pSocket )
|
||||
pSocket->Release();
|
||||
|
||||
return pFile;
|
||||
}
|
||||
|
||||
CWorkerFile* FindWorkerFile( const char *pFilename, const char *pPathID )
|
||||
{
|
||||
FOR_EACH_LL( m_WorkerFiles, i )
|
||||
{
|
||||
CWorkerFile *pWorkerFile = m_WorkerFiles[i];
|
||||
|
||||
if ( stricmp( pWorkerFile->GetFilename(), pFilename ) == 0 && stricmp( pWorkerFile->GetPathID(), pPathID ) == 0 )
|
||||
return pWorkerFile;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CWorkerFile* FindWorkerFile( int fileID )
|
||||
{
|
||||
FOR_EACH_LL( m_WorkerFiles, i )
|
||||
{
|
||||
if ( m_WorkerFiles[i]->m_FileID == fileID )
|
||||
return m_WorkerFiles[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
CIPAddr m_MulticastAddr;
|
||||
|
||||
CUtlLinkedList<CWorkerFile*, int> m_WorkerFiles;
|
||||
|
||||
HANDLE m_hMainThread;
|
||||
|
||||
// How many files do we have open that we haven't finished receiving from the server yet?
|
||||
// We always keep waiting for data until this is zero.
|
||||
int m_nUnfinishedFiles;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------------ //
|
||||
// CWorkerVMPIFileSystem implementation.
|
||||
// ------------------------------------------------------------------------------------------------------------------------ //
|
||||
|
||||
class CWorkerVMPIFileSystem : public CBaseVMPIFileSystem
|
||||
{
|
||||
public:
|
||||
InitReturnVal_t Init();
|
||||
virtual void Term();
|
||||
|
||||
virtual FileHandle_t Open( const char *pFilename, const char *pOptions, const char *pathID );
|
||||
virtual bool HandleFileSystemPacket( MessageBuffer *pBuf, int iSource, int iPacketID );
|
||||
|
||||
virtual void CreateVirtualFile( const char *pFilename, const void *pData, int fileLength );
|
||||
virtual long GetFileTime( const char *pFileName, const char *pathID );
|
||||
virtual bool IsFileWritable( const char *pFileName, const char *pPathID );
|
||||
virtual bool SetFileWritable( char const *pFileName, bool writable, const char *pPathID );
|
||||
|
||||
virtual CSysModule *LoadModule( const char *pFileName, const char *pPathID, bool bValidatedDllOnly );
|
||||
virtual void UnloadModule( CSysModule *pModule );
|
||||
|
||||
private:
|
||||
CWorkerMulticastListener m_Listener;
|
||||
};
|
||||
|
||||
|
||||
CBaseVMPIFileSystem* CreateWorkerVMPIFileSystem()
|
||||
{
|
||||
CWorkerVMPIFileSystem *pRet = new CWorkerVMPIFileSystem;
|
||||
g_pBaseVMPIFileSystem = pRet;
|
||||
if ( pRet->Init() )
|
||||
{
|
||||
return pRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pRet;
|
||||
g_pBaseVMPIFileSystem = NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
InitReturnVal_t CWorkerVMPIFileSystem::Init()
|
||||
{
|
||||
// Get the multicast addr to listen on.
|
||||
CIPAddr mcAddr;
|
||||
RecvMulticastIP( &mcAddr );
|
||||
|
||||
return m_Listener.Init( mcAddr ) ? INIT_OK : INIT_FAILED;
|
||||
}
|
||||
|
||||
|
||||
void CWorkerVMPIFileSystem::Term()
|
||||
{
|
||||
m_Listener.Term();
|
||||
}
|
||||
|
||||
|
||||
FileHandle_t CWorkerVMPIFileSystem::Open( const char *pFilename, const char *pOptions, const char *pathID )
|
||||
{
|
||||
Assert( g_bUseMPI );
|
||||
|
||||
// When it finally asks the filesystem for a file, it'll pass NULL for pathID if it's "".
|
||||
if ( !pathID )
|
||||
pathID = "";
|
||||
|
||||
if ( g_bDisableFileAccess )
|
||||
Error( "Open( %s, %s ) - file access has been disabled.", pFilename, pOptions );
|
||||
|
||||
// Workers can't open anything for write access.
|
||||
bool bWriteAccess = (Q_stristr( pOptions, "w" ) != 0);
|
||||
if ( bWriteAccess )
|
||||
return FILESYSTEM_INVALID_HANDLE;
|
||||
|
||||
// Do we have this file's data already?
|
||||
CWorkerFile *pFile = m_Listener.FindWorkerFile( pFilename, pathID );
|
||||
if ( !pFile || !pFile->IsReadyToRead() )
|
||||
{
|
||||
// Ok, start listening to the multicast stream until we get the file we want.
|
||||
|
||||
// NOTE: it might make sense here to have the client ask for a list of ALL the files that
|
||||
// the master currently has and wait to receive all of them (so we don't come back a bunch
|
||||
// of times and listen
|
||||
|
||||
// NOTE NOTE: really, the best way to do this is to have a thread on the workers that sits there
|
||||
// and listens to the multicast stream. Any time the master opens a new file up, it assumes
|
||||
// all the workers need the file, and it starts to send it on the multicast stream until
|
||||
// the worker threads respond that they all have it.
|
||||
//
|
||||
// (NOTE: this probably means that the clients would have to ack the chunks on a UDP socket that
|
||||
// the thread owns).
|
||||
//
|
||||
// This would simplify all the worries about a client missing half the stream and having to
|
||||
// wait for another cycle through it.
|
||||
pFile = m_Listener.ListenFor( pFilename, pathID );
|
||||
|
||||
if ( !pFile )
|
||||
{
|
||||
return FILESYSTEM_INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
// Ok! Got the file. now setup a memory stream they can read out of it with.
|
||||
CVMPIFile_Memory *pOut = new CVMPIFile_Memory;
|
||||
pOut->Init( pFile->m_UncompressedData.Base(), pFile->m_UncompressedData.Count(), strchr( pOptions, 't' ) ? 't' : 'b' );
|
||||
return (FileHandle_t)pOut;
|
||||
}
|
||||
|
||||
|
||||
void CWorkerVMPIFileSystem::CreateVirtualFile( const char *pFilename, const void *pData, int fileLength )
|
||||
{
|
||||
Error( "CreateVirtualFile not supported in VMPI worker filesystem." );
|
||||
}
|
||||
|
||||
|
||||
long CWorkerVMPIFileSystem::GetFileTime( const char *pFileName, const char *pathID )
|
||||
{
|
||||
Error( "GetFileTime not supported in VMPI worker filesystem." );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool CWorkerVMPIFileSystem::IsFileWritable( const char *pFileName, const char *pPathID )
|
||||
{
|
||||
Error( "GetFileTime not supported in VMPI worker filesystem." );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool CWorkerVMPIFileSystem::SetFileWritable( char const *pFileName, bool writable, const char *pPathID )
|
||||
{
|
||||
Error( "GetFileTime not supported in VMPI worker filesystem." );
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CWorkerVMPIFileSystem::HandleFileSystemPacket( MessageBuffer *pBuf, int iSource, int iPacketID )
|
||||
{
|
||||
// Handle this packet.
|
||||
int subPacketID = pBuf->data[1];
|
||||
switch( subPacketID )
|
||||
{
|
||||
case VMPI_FSPACKETID_MULTICAST_ADDR:
|
||||
{
|
||||
char *pInPos = &pBuf->data[2];
|
||||
|
||||
g_MulticastIP = *((CIPAddr*)pInPos);
|
||||
pInPos += sizeof( g_MulticastIP );
|
||||
|
||||
g_bReceivedMulticastIP = true;
|
||||
}
|
||||
return true;
|
||||
|
||||
case VMPI_FSPACKETID_FILE_RESPONSE:
|
||||
{
|
||||
CCriticalSectionLock csLock( &g_FileResponsesCS );
|
||||
csLock.Lock();
|
||||
|
||||
CFileResponse res;
|
||||
res.m_RequestID = *((int*)&pBuf->data[2]);
|
||||
res.m_Response = *((int*)&pBuf->data[6]);
|
||||
res.m_bZeroLength = *((bool*)&pBuf->data[10]);
|
||||
|
||||
g_FileResponses.AddToTail( res );
|
||||
}
|
||||
return true;
|
||||
|
||||
case VMPI_FSPACKETID_FILE_CHUNK:
|
||||
{
|
||||
int nDataBytes = pBuf->getLen() - 2;
|
||||
|
||||
CFileChunkPacket *pPacket = (CFileChunkPacket*)malloc( sizeof( CFileChunkPacket ) + nDataBytes - 1 );
|
||||
memcpy( pPacket->m_Data, &pBuf->data[2], nDataBytes );
|
||||
pPacket->m_Len = nDataBytes;
|
||||
|
||||
CCriticalSectionLock csLock( &g_FileResponsesCS );
|
||||
csLock.Lock();
|
||||
g_FileChunkPackets.AddToTail( pPacket );
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CSysModule* CWorkerVMPIFileSystem::LoadModule( const char *pFileName, const char *pPathID, bool bValidatedDllOnly )
|
||||
{
|
||||
return Sys_LoadModule( pFileName );
|
||||
}
|
||||
|
||||
void CWorkerVMPIFileSystem::UnloadModule( CSysModule *pModule )
|
||||
{
|
||||
Sys_UnloadModule( pModule );
|
||||
}
|
||||
473
utils/vmpi/vmpi_job_search/JobSearchDlg.cpp
Normal file
473
utils/vmpi/vmpi_job_search/JobSearchDlg.cpp
Normal file
@@ -0,0 +1,473 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// JobSearchDlg.cpp : implementation file
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "JobSearchDlg.h"
|
||||
#include "imysqlwrapper.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "utllinkedlist.h"
|
||||
#include "vmpi_browser_helpers.h"
|
||||
#include "vmpi_defs.h"
|
||||
#include "net_view_thread.h"
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define new DEBUG_NEW
|
||||
#undef THIS_FILE
|
||||
static char THIS_FILE[] = __FILE__;
|
||||
#endif
|
||||
|
||||
|
||||
// These are stored with jobs to help with sorting and to remember the job ID.
|
||||
class CJobInfo
|
||||
{
|
||||
public:
|
||||
unsigned long m_JobID;
|
||||
CString m_StartTimeUnformatted;
|
||||
CString m_MachineName;
|
||||
CString m_BSPFilename;
|
||||
DWORD m_RunningTimeMS;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CJobSearchDlg dialog
|
||||
|
||||
|
||||
CJobSearchDlg::CJobSearchDlg(CWnd* pParent /*=NULL*/)
|
||||
: CDialog(CJobSearchDlg::IDD, pParent)
|
||||
{
|
||||
//{{AFX_DATA_INIT(CJobSearchDlg)
|
||||
//}}AFX_DATA_INIT
|
||||
m_pSQL = NULL;
|
||||
m_hMySQLDLL = NULL;
|
||||
}
|
||||
|
||||
|
||||
CJobSearchDlg::~CJobSearchDlg()
|
||||
{
|
||||
if ( m_pSQL )
|
||||
{
|
||||
m_pSQL->Release();
|
||||
}
|
||||
|
||||
if ( m_hMySQLDLL )
|
||||
{
|
||||
Sys_UnloadModule( m_hMySQLDLL );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CJobSearchDlg::DoDataExchange(CDataExchange* pDX)
|
||||
{
|
||||
CDialog::DoDataExchange(pDX);
|
||||
//{{AFX_DATA_MAP(CJobSearchDlg)
|
||||
DDX_Control(pDX, IDC_WORKER_LIST, m_WorkerList);
|
||||
DDX_Control(pDX, IDC_USER_LIST, m_UserList);
|
||||
DDX_Control(pDX, IDC_JOBS_LIST, m_JobsList);
|
||||
//}}AFX_DATA_MAP
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(CJobSearchDlg, CDialog)
|
||||
//{{AFX_MSG_MAP(CJobSearchDlg)
|
||||
ON_NOTIFY(NM_DBLCLK, IDC_JOBS_LIST, OnDblclkJobsList)
|
||||
ON_LBN_DBLCLK(IDC_USER_LIST, OnDblclkUserList)
|
||||
ON_LBN_DBLCLK(IDC_WORKER_LIST, OnDblclkWorkerList)
|
||||
ON_BN_CLICKED(IDC_QUIT, OnQuit)
|
||||
ON_WM_SIZE()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
|
||||
int CJobSearchDlg::GetSelectedJobIndex()
|
||||
{
|
||||
POSITION pos = m_JobsList.GetFirstSelectedItemPosition();
|
||||
if ( pos )
|
||||
return m_JobsList.GetNextSelectedItem( pos );
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CJobSearchDlg message handlers
|
||||
|
||||
void CJobSearchDlg::OnDblclkJobsList(NMHDR* pNMHDR, LRESULT* pResult)
|
||||
{
|
||||
int iItem = GetSelectedJobIndex();
|
||||
if ( iItem != -1 )
|
||||
{
|
||||
CJobInfo *pInfo = (CJobInfo*)m_JobsList.GetItemData( iItem );
|
||||
|
||||
CString cmdLine;
|
||||
cmdLine.Format( "vmpi_job_watch -JobID %d -dbname \"%s\" -hostname \"%s\" -username \"%s\"",
|
||||
pInfo->m_JobID, (const char*)m_DBName, (const char*)m_HostName, (const char*)m_UserName );
|
||||
|
||||
STARTUPINFO si;
|
||||
memset( &si, 0, sizeof( si ) );
|
||||
si.cb = sizeof( si );
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
memset( &pi, 0, sizeof( pi ) );
|
||||
|
||||
if ( !CreateProcess(
|
||||
NULL,
|
||||
(char*)(const char*)cmdLine,
|
||||
NULL, // security
|
||||
NULL,
|
||||
TRUE,
|
||||
0, // flags
|
||||
NULL, // environment
|
||||
NULL, // current directory
|
||||
&si,
|
||||
&pi ) )
|
||||
{
|
||||
CString errStr;
|
||||
errStr.Format( "Error launching '%s'", cmdLine.GetBuffer() );
|
||||
MessageBox( errStr, "Error", MB_OK );
|
||||
}
|
||||
}
|
||||
|
||||
*pResult = 0;
|
||||
}
|
||||
|
||||
|
||||
static int CALLBACK JobsSortFn( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
|
||||
{
|
||||
CJobInfo *pInfo1 = (CJobInfo*)iItem1;
|
||||
CJobInfo *pInfo2 = (CJobInfo*)iItem2;
|
||||
|
||||
return strcmp( pInfo2->m_StartTimeUnformatted, pInfo1->m_StartTimeUnformatted );
|
||||
}
|
||||
|
||||
|
||||
void CJobSearchDlg::ClearJobsList()
|
||||
{
|
||||
// First, delete all the JobInfo structures we have in it.
|
||||
int nItems = m_JobsList.GetItemCount();
|
||||
for ( int i=0; i < nItems; i++ )
|
||||
{
|
||||
CJobInfo *pInfo = (CJobInfo*)m_JobsList.GetItemData( i );
|
||||
delete pInfo;
|
||||
}
|
||||
|
||||
m_JobsList.DeleteAllItems();
|
||||
}
|
||||
|
||||
|
||||
void CJobSearchDlg::RepopulateJobsList()
|
||||
{
|
||||
// It's assumed coming into this routine that the caller just executed a query that we can iterate over.
|
||||
ClearJobsList();
|
||||
|
||||
CUtlLinkedList<CJobInfo*, int> jobInfos;
|
||||
while ( GetMySQL()->NextRow() )
|
||||
{
|
||||
CJobInfo *pInfo = new CJobInfo;
|
||||
pInfo->m_StartTimeUnformatted = GetMySQL()->GetColumnValue( "StartTime" ).String();
|
||||
pInfo->m_JobID = GetMySQL()->GetColumnValue( "JobID" ).Int32();
|
||||
pInfo->m_MachineName = GetMySQL()->GetColumnValue( "MachineName" ).String();
|
||||
pInfo->m_BSPFilename = GetMySQL()->GetColumnValue( "BSPFilename" ).String();
|
||||
pInfo->m_RunningTimeMS = GetMySQL()->GetColumnValue( "RunningTimeMS" ).Int32();
|
||||
|
||||
jobInfos.AddToTail( pInfo );
|
||||
}
|
||||
|
||||
|
||||
FOR_EACH_LL( jobInfos, j )
|
||||
{
|
||||
CJobInfo *pInfo = jobInfos[j];
|
||||
|
||||
// Add the item.
|
||||
int iItem = m_JobsList.InsertItem( 0, "", NULL );
|
||||
|
||||
// Associate it with the job structure.
|
||||
m_JobsList.SetItemData( iItem, (DWORD)pInfo );
|
||||
|
||||
char dateStr[128];
|
||||
const char *pDate = pInfo->m_StartTimeUnformatted;
|
||||
if ( strlen( pDate ) == 14 ) // yyyymmddhhmmss
|
||||
{
|
||||
Q_snprintf( dateStr, sizeof( dateStr ), "%c%c/%c%c %c%c:%c%c:00",
|
||||
pDate[4], pDate[5],
|
||||
pDate[6], pDate[7],
|
||||
pDate[8], pDate[9],
|
||||
pDate[10], pDate[11] );
|
||||
}
|
||||
|
||||
m_JobsList.SetItemText( iItem, 0, dateStr );
|
||||
m_JobsList.SetItemText( iItem, 1, pInfo->m_MachineName );
|
||||
m_JobsList.SetItemText( iItem, 2, pInfo->m_BSPFilename );
|
||||
|
||||
char timeStr[512];
|
||||
if ( pInfo->m_RunningTimeMS == RUNNINGTIME_MS_SENTINEL )
|
||||
{
|
||||
Q_strncpy( timeStr, "?", sizeof( timeStr ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
FormatTimeString( pInfo->m_RunningTimeMS / 1000, timeStr, sizeof( timeStr ) );
|
||||
}
|
||||
m_JobsList.SetItemText( iItem, 3, timeStr );
|
||||
|
||||
char jobIDStr[512];
|
||||
Q_snprintf( jobIDStr, sizeof( jobIDStr ), "%d", pInfo->m_JobID );
|
||||
m_JobsList.SetItemText( iItem, 4, jobIDStr );
|
||||
}
|
||||
|
||||
m_JobsList.SortItems( JobsSortFn, (LPARAM)&m_JobsList );
|
||||
}
|
||||
|
||||
|
||||
void CJobSearchDlg::OnDblclkUserList()
|
||||
{
|
||||
int sel = m_UserList.GetCurSel();
|
||||
if ( sel != LB_ERR )
|
||||
{
|
||||
CString computerName;
|
||||
m_UserList.GetText( sel, computerName );
|
||||
|
||||
// Look for jobs that this user initiated.
|
||||
char query[4096];
|
||||
Q_snprintf( query, sizeof( query ), "select RunningTimeMS, JobID, BSPFilename, StartTime, MachineName from job_master_start where MachineName=\"%s\"", (const char*)computerName );
|
||||
GetMySQL()->Execute( query );
|
||||
|
||||
RepopulateJobsList();
|
||||
}
|
||||
}
|
||||
|
||||
void CJobSearchDlg::OnDblclkWorkerList()
|
||||
{
|
||||
int sel = m_WorkerList.GetCurSel();
|
||||
if ( sel != LB_ERR )
|
||||
{
|
||||
CString computerName;
|
||||
m_WorkerList.GetText( sel, computerName );
|
||||
|
||||
// This query does:
|
||||
// 1. Take the workers with the specified MachineName.
|
||||
// 2. Only use IsMaster = 0.
|
||||
// 3. Now get all the job_master_start records with the same JobID.
|
||||
char query[4096];
|
||||
Q_snprintf( query, sizeof( query ), "select job_master_start.RunningTimeMS, job_master_start.JobID, job_master_start.BSPFilename, job_master_start.StartTime, job_master_start.MachineName "
|
||||
"from job_master_start, job_worker_start "
|
||||
"where job_worker_start.MachineName = \"%s\" and "
|
||||
"IsMaster = 0 and "
|
||||
"job_master_start.JobID = job_worker_start.JobID",
|
||||
(const char*)computerName );
|
||||
GetMySQL()->Execute( query );
|
||||
|
||||
RepopulateJobsList();
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadStringFromFile( FILE *fp, char *pStr, int strSize )
|
||||
{
|
||||
int i=0;
|
||||
for ( i; i < strSize-2; i++ )
|
||||
{
|
||||
if ( fread( &pStr[i], 1, 1, fp ) != 1 ||
|
||||
pStr[i] == '\n' )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pStr[i] = 0;
|
||||
return i != 0;
|
||||
}
|
||||
|
||||
const char* FindArg( const char *pArgName, const char *pDefault="" )
|
||||
{
|
||||
for ( int i=1; i < __argc; i++ )
|
||||
{
|
||||
if ( Q_stricmp( pArgName, __argv[i] ) == 0 )
|
||||
{
|
||||
if ( (i+1) < __argc )
|
||||
return __argv[i+1];
|
||||
else
|
||||
return pDefault;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BOOL CJobSearchDlg::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
m_JobsList.SetExtendedStyle( LVS_EX_FULLROWSELECT );
|
||||
|
||||
char str[512];
|
||||
|
||||
// Init the mysql database.
|
||||
const char *pDBName = FindArg( "-dbname", NULL );
|
||||
const char *pHostName = FindArg( "-hostname", NULL );
|
||||
const char *pUserName = FindArg( "-username", NULL );
|
||||
|
||||
if ( pDBName && pHostName && pUserName )
|
||||
{
|
||||
m_DBName = pDBName;
|
||||
m_HostName = pHostName;
|
||||
m_UserName = pUserName;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load the dbinfo_browser.txt file to get the database information.
|
||||
const char *pFilename = FindArg( "-dbinfo", NULL );
|
||||
if ( !pFilename )
|
||||
pFilename = "dbinfo_job_search.txt";
|
||||
|
||||
FILE *fp = fopen( pFilename, "rt" );
|
||||
if ( !fp )
|
||||
{
|
||||
Q_snprintf( str, sizeof( str ), "Can't open '%s' for database info.", pFilename );
|
||||
MessageBox( str, "Error", MB_OK );
|
||||
EndDialog( 0 );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
char hostName[512], dbName[512], userName[512];
|
||||
if ( !ReadStringFromFile( fp, hostName, sizeof( hostName ) ) ||
|
||||
!ReadStringFromFile( fp, dbName, sizeof( dbName ) ) ||
|
||||
!ReadStringFromFile( fp, userName, sizeof( userName ) )
|
||||
)
|
||||
{
|
||||
fclose( fp );
|
||||
Q_snprintf( str, sizeof( str ), "'%s' has invalid format.", pFilename );
|
||||
MessageBox( str, "Error", MB_OK );
|
||||
EndDialog( 0 );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_DBName = dbName;
|
||||
m_HostName = hostName;
|
||||
m_UserName = userName;
|
||||
|
||||
fclose( fp );
|
||||
}
|
||||
|
||||
// Get the mysql interface.
|
||||
if ( !Sys_LoadInterface( "mysql_wrapper", MYSQL_WRAPPER_VERSION_NAME, &m_hMySQLDLL, (void**)&m_pSQL ) )
|
||||
return false;
|
||||
|
||||
if ( !m_pSQL->InitMySQL( m_DBName, m_HostName, m_UserName ) )
|
||||
{
|
||||
Q_snprintf( str, sizeof( str ), "Can't init MYSQL db (db = '%s', host = '%s', user = '%s')", (const char*)m_DBName, (const char*)m_HostName, (const char*)m_UserName );
|
||||
MessageBox( str, "Error", MB_OK );
|
||||
EndDialog( 0 );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Setup the headers for the job info list.
|
||||
struct
|
||||
{
|
||||
char *pText;
|
||||
int width;
|
||||
} titles[] =
|
||||
{
|
||||
{"Date", 100},
|
||||
{"User", 100},
|
||||
{"BSP Filename", 100},
|
||||
{"Running Time", 100},
|
||||
{"Job ID", 100}
|
||||
};
|
||||
for ( int i=0; i < ARRAYSIZE( titles ); i++ )
|
||||
{
|
||||
m_JobsList.InsertColumn( i, titles[i].pText, LVCFMT_LEFT, titles[i].width, i );
|
||||
}
|
||||
|
||||
|
||||
CUtlVector<char*> computerNames;
|
||||
CNetViewThread netView;
|
||||
netView.Init();
|
||||
DWORD startTime = GetTickCount();
|
||||
while ( 1 )
|
||||
{
|
||||
netView.GetComputerNames( computerNames );
|
||||
if ( computerNames.Count() > 0 )
|
||||
break;
|
||||
|
||||
Sleep( 30 );
|
||||
if ( GetTickCount() - startTime > 5000 )
|
||||
{
|
||||
Q_snprintf( str, sizeof( str ), "Unable to get computer names Can't init MYSQL db (db = '%s', host = '%s', user = '%s')", (const char*)m_DBName, (const char*)m_HostName, (const char*)m_UserName );
|
||||
MessageBox( str, "Error", MB_OK );
|
||||
EndDialog( 0 );
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
PopulateWorkerList( computerNames );
|
||||
PopulateUserList( computerNames );
|
||||
|
||||
|
||||
// Auto-select a worker?
|
||||
const char *pSelectWorker = FindArg( "-SelectWorker", NULL );
|
||||
if ( pSelectWorker )
|
||||
{
|
||||
int index = m_WorkerList.FindString( -1, pSelectWorker );
|
||||
if ( index != LB_ERR )
|
||||
{
|
||||
m_WorkerList.SetCurSel( index );
|
||||
OnDblclkWorkerList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Setup our anchors.
|
||||
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_SEARCH_BY_USER_PANEL ), ANCHOR_LEFT, ANCHOR_TOP, ANCHOR_WIDTH_PERCENT, ANCHOR_HEIGHT_PERCENT );
|
||||
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_USER_LIST ), ANCHOR_LEFT, ANCHOR_TOP, ANCHOR_WIDTH_PERCENT, ANCHOR_HEIGHT_PERCENT );
|
||||
|
||||
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_WORKER_PANEL ), ANCHOR_WIDTH_PERCENT, ANCHOR_TOP, ANCHOR_RIGHT, ANCHOR_HEIGHT_PERCENT );
|
||||
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_WORKER_LIST ), ANCHOR_WIDTH_PERCENT, ANCHOR_TOP, ANCHOR_RIGHT, ANCHOR_HEIGHT_PERCENT );
|
||||
|
||||
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_JOBS_PANEL ), ANCHOR_LEFT, ANCHOR_HEIGHT_PERCENT, ANCHOR_RIGHT, ANCHOR_BOTTOM );
|
||||
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_JOBS_LIST ), ANCHOR_LEFT, ANCHOR_HEIGHT_PERCENT, ANCHOR_RIGHT, ANCHOR_BOTTOM );
|
||||
|
||||
m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_QUIT ), ANCHOR_WIDTH_PERCENT, ANCHOR_BOTTOM, ANCHOR_WIDTH_PERCENT, ANCHOR_BOTTOM );
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void CJobSearchDlg::PopulateWorkerList( CUtlVector<char*> &computerNames )
|
||||
{
|
||||
m_WorkerList.ResetContent();
|
||||
for ( int i=0; i < computerNames.Count(); i++ )
|
||||
{
|
||||
m_WorkerList.AddString( computerNames[i] );
|
||||
}
|
||||
}
|
||||
|
||||
void CJobSearchDlg::PopulateUserList( CUtlVector<char*> &computerNames )
|
||||
{
|
||||
m_UserList.ResetContent();
|
||||
for ( int i=0; i < computerNames.Count(); i++ )
|
||||
{
|
||||
m_UserList.AddString( computerNames[i] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CJobSearchDlg::OnQuit()
|
||||
{
|
||||
EndDialog( 0 );
|
||||
}
|
||||
|
||||
void CJobSearchDlg::OnSize(UINT nType, int cx, int cy)
|
||||
{
|
||||
CDialog::OnSize(nType, cx, cy);
|
||||
|
||||
m_AnchorMgr.UpdateAnchors( this );
|
||||
}
|
||||
86
utils/vmpi/vmpi_job_search/JobSearchDlg.h
Normal file
86
utils/vmpi/vmpi_job_search/JobSearchDlg.h
Normal file
@@ -0,0 +1,86 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#if !defined(AFX_JOBSEARCHDLG_H__833A83FF_35CC_42B5_AEB3_AE31C7FDF492__INCLUDED_)
|
||||
#define AFX_JOBSEARCHDLG_H__833A83FF_35CC_42B5_AEB3_AE31C7FDF492__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
// JobSearchDlg.h : header file
|
||||
//
|
||||
|
||||
#include "resource.h"
|
||||
#include "imysqlwrapper.h"
|
||||
#include "window_anchor_mgr.h"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CJobSearchDlg dialog
|
||||
|
||||
class CJobSearchDlg : public CDialog
|
||||
{
|
||||
// Construction
|
||||
public:
|
||||
CJobSearchDlg(CWnd* pParent = NULL); // standard constructor
|
||||
virtual ~CJobSearchDlg();
|
||||
|
||||
// Dialog Data
|
||||
//{{AFX_DATA(CJobSearchDlg)
|
||||
enum { IDD = IDD_VMPI_JOB_SEARCH };
|
||||
CListBox m_WorkerList;
|
||||
CListBox m_UserList;
|
||||
CListCtrl m_JobsList;
|
||||
//}}AFX_DATA
|
||||
|
||||
|
||||
// Overrides
|
||||
// ClassWizard generated virtual function overrides
|
||||
//{{AFX_VIRTUAL(CJobSearchDlg)
|
||||
protected:
|
||||
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
|
||||
//}}AFX_VIRTUAL
|
||||
|
||||
// Implementation
|
||||
protected:
|
||||
|
||||
void ClearJobsList();
|
||||
void RepopulateJobsList();
|
||||
|
||||
void PopulateWorkerList( CUtlVector<char*> &computerNames );
|
||||
void PopulateUserList( CUtlVector<char*> &computerNames );
|
||||
|
||||
int GetSelectedJobIndex();
|
||||
|
||||
|
||||
// Info on how we connected to the database so we can pass it to apps we launch.
|
||||
CString m_DBName, m_HostName, m_UserName;
|
||||
|
||||
|
||||
IMySQL* GetMySQL() { return m_pSQL; }
|
||||
IMySQL *m_pSQL;
|
||||
CSysModule *m_hMySQLDLL;
|
||||
|
||||
CWindowAnchorMgr m_AnchorMgr;
|
||||
|
||||
|
||||
// Generated message map functions
|
||||
//{{AFX_MSG(CJobSearchDlg)
|
||||
afx_msg void OnDblclkJobsList(NMHDR* pNMHDR, LRESULT* pResult);
|
||||
afx_msg void OnDblclkUserList();
|
||||
afx_msg void OnDblclkWorkerList();
|
||||
virtual BOOL OnInitDialog();
|
||||
afx_msg void OnQuit();
|
||||
afx_msg void OnSize(UINT nType, int cx, int cy);
|
||||
//}}AFX_MSG
|
||||
DECLARE_MESSAGE_MAP()
|
||||
};
|
||||
|
||||
//{{AFX_INSERT_LOCATION}}
|
||||
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||
|
||||
#endif // !defined(AFX_JOBSEARCHDLG_H__833A83FF_35CC_42B5_AEB3_AE31C7FDF492__INCLUDED_)
|
||||
15
utils/vmpi/vmpi_job_search/StdAfx.cpp
Normal file
15
utils/vmpi/vmpi_job_search/StdAfx.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// vmpi_browser_job_search.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user