This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions

View 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

File diff suppressed because it is too large Load Diff

View 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;
}
}

View 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

View 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

View 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_)

View 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;
}

View 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

Binary file not shown.

318
utils/vmpi/ZLib/deflate.h Normal file
View 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

View 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));

View 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
View 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
View 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}
};

View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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();
}

View 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

File diff suppressed because it is too large Load Diff

84
utils/vmpi/tcpsocket.h Normal file
View 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

View 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;
}

View 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

View 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

View 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;
}

View 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_)

View 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

View 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 &quot;$(TargetPath)&quot; ..\..\..\..\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 &quot;$(TargetPath)&quot; ..\..\..\..\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>

View 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 );
}
}

View 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_)

View 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"

View 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_)

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

View 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...
/////////////////////////////////////////////////////////////////////////////

View 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

View 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);
}

View 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__

View 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

View 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_)

View File

@@ -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;
}

View File

@@ -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>

View 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

View 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_)

View 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;
}

View 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>

View 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

View 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_)

View 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;
}

View 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>

View 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

View 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_)

View 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;
}

View 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"
}
}

View 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

View 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_)

View 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;
}

View 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 &quot;$(TargetPath)&quot; ..\..\..\..\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 &quot;$(TargetPath)&quot; ..\..\..\..\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>

View 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
View 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

File diff suppressed because it is too large Load Diff

217
utils/vmpi/vmpi.h Normal file
View 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
View 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"
}
}

View 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 );
}
}

View 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
View 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

View 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"

View 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

View 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" );
}
}
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}

View 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);
}

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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 );
}

View 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 );
}

View 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_)

View 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