From 64f3cac5e905fad42dc90fe2f9beb615539650ca Mon Sep 17 00:00:00 2001 From: GAMMACASE <31375974+GAMMACASE@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:19:41 +0300 Subject: [PATCH] Update IKeyValuesSystem & obsolete public/vstdlib --- public/cdll_int.h | 2 +- public/eiface.h | 2 +- public/particles/particles.h | 2 +- public/{vstdlib => tier1}/IKeyValuesSystem.h | 147 +- public/tier1/KeyValues.h | 13 +- public/tier1/callqueue.h | 4 +- public/tier1/exprevaluator.h | 39 +- public/{vstdlib => tier1}/iprocessutils.h | 260 +- public/{vstdlib => tier1}/jobthread.h | 2878 +++++++++--------- public/{vstdlib => tier1}/random.h | 226 +- public/tier1/utlbuffer.h | 22 +- public/vstdlib/cvar.h | 25 - public/vstdlib/ieventsystem.h | 16 - public/vstdlib/pch_vstdlib.h | 51 - public/vstdlib/vcover.h | 125 - public/vstdlib/vstdlib.h | 33 - 16 files changed, 1807 insertions(+), 2038 deletions(-) rename public/{vstdlib => tier1}/IKeyValuesSystem.h (78%) rename public/{vstdlib => tier1}/iprocessutils.h (97%) rename public/{vstdlib => tier1}/jobthread.h (96%) rename public/{vstdlib => tier1}/random.h (78%) delete mode 100644 public/vstdlib/cvar.h delete mode 100644 public/vstdlib/ieventsystem.h delete mode 100644 public/vstdlib/pch_vstdlib.h delete mode 100644 public/vstdlib/vcover.h delete mode 100644 public/vstdlib/vstdlib.h diff --git a/public/cdll_int.h b/public/cdll_int.h index 60654f73..2717f190 100644 --- a/public/cdll_int.h +++ b/public/cdll_int.h @@ -198,7 +198,7 @@ typedef void (*pfnDemoCustomDataCallback)( uint8 *pData, size_t iSize ); //----------------------------------------------------------------------------- // Just an interface version name for the random number interface -// See vstdlib/random.h for the interface definition +// See tier1/random.h for the interface definition // NOTE: If you change this, also change VENGINE_SERVER_RANDOM_INTERFACE_VERSION in eiface.h //----------------------------------------------------------------------------- #define VENGINE_CLIENT_RANDOM_INTERFACE_VERSION "VEngineRandom001" diff --git a/public/eiface.h b/public/eiface.h index 557c4efe..6de9e9c2 100644 --- a/public/eiface.h +++ b/public/eiface.h @@ -483,7 +483,7 @@ public: //----------------------------------------------------------------------------- // Just an interface version name for the random number interface -// See vstdlib/random.h for the interface definition +// See tier1/random.h for the interface definition // NOTE: If you change this, also change VENGINE_CLIENT_RANDOM_INTERFACE_VERSION in cdll_int.h //----------------------------------------------------------------------------- #define VENGINE_SERVER_RANDOM_INTERFACE_VERSION "VEngineRandom001" diff --git a/public/particles/particles.h b/public/particles/particles.h index ad406f77..8617653a 100644 --- a/public/particles/particles.h +++ b/public/particles/particles.h @@ -24,7 +24,7 @@ class IMatRenderContext; #include "dmxloader/dmxelement.h" #include "tier1/utlintrusivelist.h" -#include "vstdlib/random.h" +#include "tier1/random.h" #include "tier1/utlobjectreference.h" #include "tier1/utlstringmap.h" #include "tier1/utlmap.h" diff --git a/public/vstdlib/IKeyValuesSystem.h b/public/tier1/IKeyValuesSystem.h similarity index 78% rename from public/vstdlib/IKeyValuesSystem.h rename to public/tier1/IKeyValuesSystem.h index 14f5576b..feb1174f 100644 --- a/public/vstdlib/IKeyValuesSystem.h +++ b/public/tier1/IKeyValuesSystem.h @@ -1,67 +1,80 @@ -//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// -#ifndef VSTDLIB_IKEYVALUESSYSTEM_H -#define VSTDLIB_IKEYVALUESSYSTEM_H -#ifdef _WIN32 -#pragma once -#endif - -#include "vstdlib/vstdlib.h" - -class KeyValues; - -class HKeySymbol -{ -public: - HKeySymbol() : nIndex(~0) { } - HKeySymbol(uint32 idx) : nIndex(idx) { } - - inline uint32 Get() { return nIndex; } - -private: - uint32 nIndex; -}; - -//----------------------------------------------------------------------------- -// Purpose: Interface to shared data repository for KeyValues (included in vgui_controls.lib) -// allows for central data storage point of KeyValues symbol table -//----------------------------------------------------------------------------- -class IKeyValuesSystem -{ -public: - // allocates/frees a KeyValues object from the shared mempool - virtual void *AllocKeyValuesMemory() = 0; - virtual void FreeKeyValuesMemory(KeyValues *pKV) = 0; - - // symbol table access (used for key names) - virtual HKeySymbol GetSymbolForString( const char *name, bool bCreate = true ) = 0; - virtual const char *GetStringForSymbol(HKeySymbol symbol) = 0; - - // for debugging, adds KeyValues record into global list so we can track memory leaks - virtual void AddKeyValuesToMemoryLeakList(void *pMem, HKeySymbol name) = 0; - virtual void RemoveKeyValuesFromMemoryLeakList(void *pMem) = 0; - - // set/get a value for keyvalues resolution symbol - // e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE] - virtual void SetKeyValuesExpressionSymbol( const char *name, bool bValue ) = 0; - virtual bool GetKeyValuesExpressionSymbol( const char *name ) = 0; - - // symbol table access from code with case-preserving requirements (used for key names) - virtual HKeySymbol GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *name, bool bCreate = true ) = 0; - virtual HKeySymbol GetCaseInsensitiveSymbolFromCaseSensitiveSymbol( HKeySymbol symbol ) = 0; - - virtual const char *CopyString( const char * ) = 0; - virtual void ReleaseStringCopy( const char * ) = 0; - virtual const wchar_t *CopyWString( const wchar_t * ) = 0; - virtual void ReleaseWStringCopy( const wchar_t * ) = 0; -}; - -VSTDLIB_INTERFACE IKeyValuesSystem *KeyValuesSystem(); - -// #define KEYVALUESSYSTEM_INTERFACE_VERSION "KeyValuesSystem002" - -#endif // VSTDLIB_IKEYVALUESSYSTEM_H +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#ifndef VSTDLIB_IKEYVALUESSYSTEM_H +#define VSTDLIB_IKEYVALUESSYSTEM_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/platform.h" + +class CUtlCharConversion; +class CUtlScratchMemoryPool; +class CTemporaryKeyValues; +class KeyValues; + +class HKeySymbol +{ +public: + HKeySymbol() : nIndex(~0) { } + HKeySymbol(uint32 idx) : nIndex(idx) { } + + inline uint32 Get() { return nIndex; } + +private: + uint32 nIndex; +}; + +//----------------------------------------------------------------------------- +// Purpose: Interface to shared data repository for KeyValues (included in vgui_controls.lib) +// allows for central data storage point of KeyValues symbol table +//----------------------------------------------------------------------------- +class IKeyValuesSystem +{ +public: + virtual ~IKeyValuesSystem() = 0; + + // allocates/frees a KeyValues object from the shared mempool + virtual KeyValues *AllocKeyValuesMemory() = 0; + virtual void FreeKeyValuesMemory(KeyValues *pKV) = 0; + + // symbol table access (used for key names) + virtual HKeySymbol GetSymbolForString( const char *name, bool bCreate = true ) = 0; + virtual const char *GetStringForSymbol(HKeySymbol symbol) = 0; + + // for debugging, adds KeyValues record into global list so we can track memory leaks + virtual void AddKeyValuesToMemoryLeakList(void *pMem, HKeySymbol name) = 0; + virtual void RemoveKeyValuesFromMemoryLeakList(void *pMem) = 0; + + virtual void unk001() = 0; + + // set/get a value for keyvalues resolution symbol + // e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE] + virtual void SetKeyValuesExpressionSymbol( const char *name, bool bValue ) = 0; + virtual bool GetKeyValuesExpressionSymbol( const char *name ) = 0; + + // symbol table access from code with case-preserving requirements (used for key names) + virtual HKeySymbol GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *name, bool bCreate = true ) = 0; + virtual HKeySymbol GetCaseInsensitiveSymbolFromCaseSensitiveSymbol( HKeySymbol symbol ) = 0; + + virtual const char *CopyString( const char * ) = 0; + virtual void ReleaseStringCopy( const char * ) = 0; + virtual const wchar_t *CopyWString( const wchar_t * ) = 0; + virtual void ReleaseWStringCopy( const wchar_t * ) = 0; + + virtual CUtlCharConversion *GetCharacterConversion( bool is_cstring ) = 0; + + virtual CTemporaryKeyValues *AllocateTemporaryKeyValues() = 0; + virtual void ReleaseTemporaryKeyValues( CTemporaryKeyValues *temp_kv ) = 0; + + // Returns previously used memory pool if any + virtual CUtlScratchMemoryPool *SetNewScratchMemoryPool( CUtlScratchMemoryPool *pool ) = 0; +}; + +PLATFORM_INTERFACE IKeyValuesSystem *KeyValuesSystem(); + +#endif // VSTDLIB_IKEYVALUESSYSTEM_H diff --git a/public/tier1/KeyValues.h b/public/tier1/KeyValues.h index 9b263310..64f978bb 100644 --- a/public/tier1/KeyValues.h +++ b/public/tier1/KeyValues.h @@ -20,10 +20,11 @@ #endif #endif +#include "platform.h" #include "utlvector.h" #include "Color.h" #include "exprevaluator.h" -#include +#include "IKeyValuesSystem.h" class IFileSystem; class CUtlBuffer; @@ -49,15 +50,15 @@ typedef void *FileHandle_t; DECLARE_POINTER_HANDLE( HTemporaryKeyValueAllocationScope ); +PLATFORM_INTERFACE KeyValues *KeyValuesFromJSON( CUtlBuffer *buf, bool allow_unterminated = false, bool *parsed_successfully = nullptr ); +PLATFORM_INTERFACE bool KeyValuesToJSON( KeyValues *kv, CUtlBuffer *buf ); + class CTemporaryKeyValues { - CTemporaryKeyValues() : m_pKeyValues(nullptr), m_hScope() {} + CTemporaryKeyValues() : m_pKeyValues( nullptr ), m_hScope() {} ~CTemporaryKeyValues() { - // GAMMACASE: TODO: Complete with actual KeyValuesSystem call once it's reversed too. -#if 0 - KeyValuesSystem()->ReleaseTemporaryAllocationScope( m_hScope ); -#endif + KeyValuesSystem()->ReleaseTemporaryKeyValues( this ); } private: diff --git a/public/tier1/callqueue.h b/public/tier1/callqueue.h index 0651db8b..73b1e5b9 100644 --- a/public/tier1/callqueue.h +++ b/public/tier1/callqueue.h @@ -1,4 +1,4 @@ -//========== Copyright © 2006, Valve Corporation, All rights reserved. ======== +//========== Copyright � 2006, Valve Corporation, All rights reserved. ======== // // Purpose: // @@ -9,7 +9,7 @@ #include "tier0/tslist.h" #include "functors.h" -#include "vstdlib/jobthread.h" +#include "jobthread.h" #if defined( _WIN32 ) #pragma once diff --git a/public/tier1/exprevaluator.h b/public/tier1/exprevaluator.h index 1dd75f2f..b4b4f0d6 100644 --- a/public/tier1/exprevaluator.h +++ b/public/tier1/exprevaluator.h @@ -34,32 +34,29 @@ struct ExprNode typedef ExprNode *ExprTree; // callback to evaluate a $ during evaluation, return true or false -typedef bool (*GetSymbolProc_t)( const char *pKey, void *pUnknown ); -typedef void (*SyntaxErrorProc_t)( const char *pReason ); +typedef bool (*GetSymbolProc_t)( const char *pKey, void *pData ); +typedef void (*SyntaxErrorProc_t)( const char *pReason, void *pData ); class CExpressionEvaluator { public: - CExpressionEvaluator(); - ~CExpressionEvaluator(); - bool Evaluate( bool &result, const char *pInfixExpression, GetSymbolProc_t pGetSymbolProc = 0, SyntaxErrorProc_t pSyntaxErrorProc = 0 ); + DLL_CLASS_IMPORT CExpressionEvaluator(); + DLL_CLASS_IMPORT ~CExpressionEvaluator(); + + DLL_CLASS_IMPORT bool Evaluate( + bool &result, const char *pInfixExpression, + GetSymbolProc_t pGetSymbolProc = nullptr, SyntaxErrorProc_t pSyntaxErrorProc = nullptr, + void *pSymbolProcAdditionalData = nullptr, void *pErrorProcAdditionalData = nullptr + ); + + DLL_CLASS_IMPORT bool EvaluateAsUnmarkedSymbols( + bool &result, const char *pInfixExpression, + GetSymbolProc_t pGetSymbolProc = nullptr, SyntaxErrorProc_t pSyntaxErrorProc = nullptr, + void *pSymbolProcAdditionalData = nullptr, void *pErrorProcAdditionalData = nullptr + ); private: - CExpressionEvaluator( CExpressionEvaluator& ); // prevent copy constructor being used - - char GetNextToken( void ); - void FreeNode( ExprNode *pNode ); - ExprNode *AllocateNode( void ); - void FreeTree( ExprTree &node ); - bool IsConditional( bool &bCondition, const char token ); - bool IsNotOp( const char token ); - bool IsIdentifierOrConstant( const char token ); - bool MakeExprNode( ExprTree &tree, char token, Kind kind, ExprTree left, ExprTree right ); - bool MakeFactor( ExprTree &tree ); - bool MakeTerm( ExprTree &tree ); - bool MakeExpression( ExprTree &tree ); - bool BuildExpression( void ); - bool SimplifyNode( ExprTree &node ); + CExpressionEvaluator( CExpressionEvaluator & ) = delete; ExprTree m_ExprTree; // Tree representation of the expression char m_CurToken; // Current token read from the input expression @@ -68,6 +65,8 @@ private: char m_Identifier[MAX_IDENTIFIER_LEN]; // Stores the identifier string GetSymbolProc_t m_pGetSymbolProc; SyntaxErrorProc_t m_pSyntaxErrorProc; + void *m_pSymbolProcData; + void *m_pErrorProcData; bool m_bSetup; }; diff --git a/public/vstdlib/iprocessutils.h b/public/tier1/iprocessutils.h similarity index 97% rename from public/vstdlib/iprocessutils.h rename to public/tier1/iprocessutils.h index 2aec95ca..364e1947 100644 --- a/public/vstdlib/iprocessutils.h +++ b/public/tier1/iprocessutils.h @@ -1,130 +1,130 @@ -//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// -// -// Purpose: -// -//===========================================================================// - -#ifndef IPROCESSUTILS_H -#define IPROCESSUTILS_H - -#ifdef _WIN32 -#pragma once -#endif - - -#include "appframework/IAppSystem.h" -#include "tier1/utlstring.h" -#include "tier1/utlbuffer.h" - - -//----------------------------------------------------------------------------- -// Handle to a process. This is only for b/w compatibility. -//----------------------------------------------------------------------------- -class IProcess; - - -//----------------------------------------------------------------------------- -// Interface version -//----------------------------------------------------------------------------- -#define PIPEREAD_INFINITE INT32_MAX - -abstract_class IPipeRead -{ -public: - // NONBLOCKING FUNCTIONS - - // Returns how much you can Read() without blocking. - virtual int GetNumBytesAvailable() = 0; - - // Read whatever is available without blocking. - // This is the same as Read( sStr, GetNumBytesAvailable() ). - virtual void ReadAvailable( CUtlString &sStr, int32 nMaxBytes=PIPEREAD_INFINITE ) = 0; - virtual void ReadAvailable( CUtlBuffer* pOutBuffer, int32 nMaxBytes=PIPEREAD_INFINITE ) = 0; - - - // (POTENTIALLY) BLOCKING FUNCTIONS - - // Read one line of output (also returns when the process quits). - // sStr will not include the \n (or \r\n) at the end of the line. - virtual void ReadLine( CUtlString &sStr ) = 0; - - // This will block the calling thread until it gets the number of bytes specified - // or until the process exits. If sStr.Length() != nBytes, then you know the process exited. - // - // The returned string will always be null-terminated. - // If you call with nBytes=PIPEREAD_INFINITE, it'll read until the process exits. - virtual void Read( CUtlString &sStr, int32 nBytes=PIPEREAD_INFINITE ) = 0; -}; - - -abstract_class IProcess -{ -public: - // Note: If the process is still running, this will auto kill it unless you started the process with - // STARTPROCESS_NOAUTOKILL. - virtual void Release() = 0; - - // Kill the running process. You still must call IProcess::Release to free the resources. - virtual void Abort() = 0; - - // Returns true if a process is complete - virtual bool IsComplete() = 0; - - // Waits until a process is complete. - // Returns the return value from the process. - virtual int WaitUntilComplete() = 0; - - // Write to the process' stdin. - // This blocks until the process has read it. - virtual int WriteStdin( char *pBuf, int nBufLen ) = 0; - - // Get stuff to read the outputs. - virtual IPipeRead* GetStdout() = 0; - virtual IPipeRead* GetStderr() = 0; // NOTE: Only valid if you used STARTPROCESS_SEPARATE_STDERR. - - // Returns the exit code for the process. Doesn't work unless the process is complete. - // Returns -1 on error or if the process isn't complete. - virtual int GetExitCode() = 0; -}; - - -// Flags to IProcessUtils::StartProcess. -#define STARTPROCESS_CONNECTSTDPIPES 0x01 // Necessary to use the stdin/stdout/stderr io functions. -#define STARTPROCESS_SHARE_CONSOLE 0x02 // The process writes directly to your console. The pipe objects returned by - // IProcess::GetStdout and GetStderr won't do anything. -#define STARTPROCESS_SEPARATE_STDERR 0x04 // Rather than having to read stdout and stderr to get the output, the default is to put the stderr output into stdout. - // This flag can change that behavior so you can get that output separately. - // Warning: There may be deadlock problems with this, specifically in CProcessPipeRead::GetActualProcessOutput if - // it's blocked reading stdout's pipe but the process is blocked waiting for us to flush stderr's pipe first. - // To fully support that case, we'd need threads, overlapped IO, or a more careful (and slower) GetActualProcessOutput call - // that bounces between the two pipes and never stalls. - // - // You can also get around this on the client side by reading the pipes from threads. -#define STARTPROCESS_NOAUTOKILL 0x08 // Prevents the process from being auto-terminated in IProcess::Release() - // or when IProcessUtils' Shutdown function is called. -#define STARTPROCESS_FATPIPES 0x10 // Use I/O pipes larger than the default size for processes that do lots of stdio - // (Only works with STARTPROCESS_CONNECTSTDPIPES) - -//----------------------------------------------------------------------------- -// Interface for makefiles to build differently depending on where they are run from -//----------------------------------------------------------------------------- -abstract_class IProcessUtils : public IAppSystem -{ -public: - // Starts, stops a process. - // If pWorkingDir is left at NULL, it'll use this process' working directory. - virtual IProcess* StartProcess( const char *pCommandLine, int fFlags, const char *pWorkingDir=NULL )= 0; - virtual IProcess* StartProcess( int argc, const char **argv, int fFlags, const char *pWorkingDir=NULL ) = 0; - - // Run a process and get its output. - // If pStdout is set, then stdout AND stderr are put into pStdout. - // If not, then the text output is ignored. - // - // Returns -1 if it was unable to run the process. Otherwise, returns the exit code from the process. - virtual int SimpleRunProcess( const char *pCommandLine, const char *pWorkingDir=NULL, CUtlString *pStdout=NULL ) = 0; -}; - -DECLARE_TIER1_INTERFACE( IProcessUtils, g_pProcessUtils ); - - -#endif // IPROCESSUTILS_H +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef IPROCESSUTILS_H +#define IPROCESSUTILS_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "appframework/IAppSystem.h" +#include "tier1/utlstring.h" +#include "tier1/utlbuffer.h" + + +//----------------------------------------------------------------------------- +// Handle to a process. This is only for b/w compatibility. +//----------------------------------------------------------------------------- +class IProcess; + + +//----------------------------------------------------------------------------- +// Interface version +//----------------------------------------------------------------------------- +#define PIPEREAD_INFINITE INT32_MAX + +abstract_class IPipeRead +{ +public: + // NONBLOCKING FUNCTIONS + + // Returns how much you can Read() without blocking. + virtual int GetNumBytesAvailable() = 0; + + // Read whatever is available without blocking. + // This is the same as Read( sStr, GetNumBytesAvailable() ). + virtual void ReadAvailable( CUtlString &sStr, int32 nMaxBytes=PIPEREAD_INFINITE ) = 0; + virtual void ReadAvailable( CUtlBuffer* pOutBuffer, int32 nMaxBytes=PIPEREAD_INFINITE ) = 0; + + + // (POTENTIALLY) BLOCKING FUNCTIONS + + // Read one line of output (also returns when the process quits). + // sStr will not include the \n (or \r\n) at the end of the line. + virtual void ReadLine( CUtlString &sStr ) = 0; + + // This will block the calling thread until it gets the number of bytes specified + // or until the process exits. If sStr.Length() != nBytes, then you know the process exited. + // + // The returned string will always be null-terminated. + // If you call with nBytes=PIPEREAD_INFINITE, it'll read until the process exits. + virtual void Read( CUtlString &sStr, int32 nBytes=PIPEREAD_INFINITE ) = 0; +}; + + +abstract_class IProcess +{ +public: + // Note: If the process is still running, this will auto kill it unless you started the process with + // STARTPROCESS_NOAUTOKILL. + virtual void Release() = 0; + + // Kill the running process. You still must call IProcess::Release to free the resources. + virtual void Abort() = 0; + + // Returns true if a process is complete + virtual bool IsComplete() = 0; + + // Waits until a process is complete. + // Returns the return value from the process. + virtual int WaitUntilComplete() = 0; + + // Write to the process' stdin. + // This blocks until the process has read it. + virtual int WriteStdin( char *pBuf, int nBufLen ) = 0; + + // Get stuff to read the outputs. + virtual IPipeRead* GetStdout() = 0; + virtual IPipeRead* GetStderr() = 0; // NOTE: Only valid if you used STARTPROCESS_SEPARATE_STDERR. + + // Returns the exit code for the process. Doesn't work unless the process is complete. + // Returns -1 on error or if the process isn't complete. + virtual int GetExitCode() = 0; +}; + + +// Flags to IProcessUtils::StartProcess. +#define STARTPROCESS_CONNECTSTDPIPES 0x01 // Necessary to use the stdin/stdout/stderr io functions. +#define STARTPROCESS_SHARE_CONSOLE 0x02 // The process writes directly to your console. The pipe objects returned by + // IProcess::GetStdout and GetStderr won't do anything. +#define STARTPROCESS_SEPARATE_STDERR 0x04 // Rather than having to read stdout and stderr to get the output, the default is to put the stderr output into stdout. + // This flag can change that behavior so you can get that output separately. + // Warning: There may be deadlock problems with this, specifically in CProcessPipeRead::GetActualProcessOutput if + // it's blocked reading stdout's pipe but the process is blocked waiting for us to flush stderr's pipe first. + // To fully support that case, we'd need threads, overlapped IO, or a more careful (and slower) GetActualProcessOutput call + // that bounces between the two pipes and never stalls. + // + // You can also get around this on the client side by reading the pipes from threads. +#define STARTPROCESS_NOAUTOKILL 0x08 // Prevents the process from being auto-terminated in IProcess::Release() + // or when IProcessUtils' Shutdown function is called. +#define STARTPROCESS_FATPIPES 0x10 // Use I/O pipes larger than the default size for processes that do lots of stdio + // (Only works with STARTPROCESS_CONNECTSTDPIPES) + +//----------------------------------------------------------------------------- +// Interface for makefiles to build differently depending on where they are run from +//----------------------------------------------------------------------------- +abstract_class IProcessUtils : public IAppSystem +{ +public: + // Starts, stops a process. + // If pWorkingDir is left at NULL, it'll use this process' working directory. + virtual IProcess* StartProcess( const char *pCommandLine, int fFlags, const char *pWorkingDir=NULL )= 0; + virtual IProcess* StartProcess( int argc, const char **argv, int fFlags, const char *pWorkingDir=NULL ) = 0; + + // Run a process and get its output. + // If pStdout is set, then stdout AND stderr are put into pStdout. + // If not, then the text output is ignored. + // + // Returns -1 if it was unable to run the process. Otherwise, returns the exit code from the process. + virtual int SimpleRunProcess( const char *pCommandLine, const char *pWorkingDir=NULL, CUtlString *pStdout=NULL ) = 0; +}; + +DECLARE_TIER1_INTERFACE( IProcessUtils, g_pProcessUtils ); + + +#endif // IPROCESSUTILS_H diff --git a/public/vstdlib/jobthread.h b/public/tier1/jobthread.h similarity index 96% rename from public/vstdlib/jobthread.h rename to public/tier1/jobthread.h index 3e50cc46..cc21195f 100644 --- a/public/vstdlib/jobthread.h +++ b/public/tier1/jobthread.h @@ -1,1439 +1,1439 @@ -//========== Copyright � 2005, Valve Corporation, All rights reserved. ======== -// -// Purpose: A utility for a discrete job-oriented worker thread. -// -// The class CThreadPool is both the job queue, and the -// worker thread. Except when the main thread attempts to -// synchronously execute a job, most of the inter-thread locking -// on the queue. -// -// The queue threading model uses a manual reset event for optimal -// throughput. Adding to the queue is guarded by a semaphore that -// will block the inserting thread if the queue has overflown. -// This prevents the worker thread from being starved out even if -// not running at a higher priority than the master thread. -// -// The thread function waits for jobs, services jobs, and manages -// communication between the worker and master threads. The nature -// of the work is opaque to the Executer. -// -// CJob instances actually do the work. The base class -// calls virtual methods for job primitives, so derivations don't -// need to worry about threading models. All of the variants of -// job and OS can be expressed in this hierarchy. Instances of -// CJob are the items placed in the queue, and by -// overriding the job primitives they are the manner by which -// users of the Executer control the state of the job. -// -//============================================================================= - -#include -#include "tier0/threadtools.h" -#include "tier1/refcount.h" -#include "tier1/utllinkedlist.h" -#include "tier1/utlvector.h" -#include "tier1/functors.h" - -#include "vstdlib/vstdlib.h" - -#ifndef JOBTHREAD_H -#define JOBTHREAD_H - -#ifdef AddJob // windows.h print function collisions -#undef AddJob -#undef GetJob -#endif - -#ifdef VSTDLIB_DLL_EXPORT -#define JOB_INTERFACE DLL_EXPORT -#define JOB_OVERLOAD DLL_GLOBAL_EXPORT -#define JOB_CLASS DLL_CLASS_EXPORT -#else -#define JOB_INTERFACE DLL_IMPORT -#define JOB_OVERLOAD DLL_GLOBAL_IMPORT -#define JOB_CLASS DLL_CLASS_IMPORT -#endif - -#if defined( _WIN32 ) -#pragma once -#endif - -//----------------------------------------------------------------------------- -// -//----------------------------------------------------------------------------- - -class CJob; - -//----------------------------------------------------------------------------- -// -//----------------------------------------------------------------------------- -enum JobStatusEnum_t -{ - // Use negative for errors - JOB_OK, // operation is successful - JOB_STATUS_PENDING, // file is properly queued, waiting for service - JOB_STATUS_INPROGRESS, // file is being accessed - JOB_STATUS_ABORTED, // file was aborted by caller - JOB_STATUS_UNSERVICED, // file is not yet queued -}; - -typedef int JobStatus_t; - -enum JobFlags_t -{ - JF_IO = ( 1 << 0 ), // The job primarily blocks on IO or hardware - JF_BOOST_THREAD = ( 1 << 1 ), // Up the thread priority to max allowed while processing task - JF_SERIAL = ( 1 << 2 ), // Job cannot be executed out of order relative to other "strict" jobs - JF_QUEUE = ( 1 << 3 ), // Queue it, even if not an IO job -}; - -enum JobPriority_t -{ - JP_LOW, - JP_NORMAL, - JP_HIGH -}; - -#define TP_MAX_POOL_THREADS 64 -struct ThreadPoolStartParams_t -{ - ThreadPoolStartParams_t( bool bIOThreads = false, unsigned nThreads = -1, int *pAffinities = NULL, ThreeState_t fDistribute = TRS_NONE, unsigned nStackSize = -1, int iThreadPriority = SHRT_MIN ) - : bIOThreads( bIOThreads ), nThreads( nThreads ), fDistribute( fDistribute ), nStackSize( nStackSize ), iThreadPriority( iThreadPriority ) - { - bUseAffinityTable = ( pAffinities != NULL ) && ( fDistribute == TRS_TRUE ) && ( nThreads != -1 ); - if ( bUseAffinityTable ) - { - // user supplied an optional 1:1 affinity mapping to override normal distribute behavior - nThreads = MIN( TP_MAX_POOL_THREADS, nThreads ); - for ( unsigned int i = 0; i < nThreads; i++ ) - { - iAffinityTable[i] = pAffinities[i]; - } - } - } - - int nThreads; - ThreeState_t fDistribute; - int nStackSize; - int iThreadPriority; - int iAffinityTable[TP_MAX_POOL_THREADS]; - - bool bIOThreads : 1; - bool bUseAffinityTable : 1; -}; - -//----------------------------------------------------------------------------- -// -// IThreadPool -// -//----------------------------------------------------------------------------- - -typedef bool (*JobFilter_t)( CJob * ); - -//--------------------------------------------------------- -// Messages supported through the CallWorker() method -//--------------------------------------------------------- -enum ThreadPoolMessages_t -{ - TPM_EXIT, // Exit the thread - TPM_SUSPEND, // Suspend after next operation -}; - -//--------------------------------------------------------- - -#ifdef Yield -#undef Yield -#endif - -abstract_class IThreadPool : public IRefCounted -{ -public: - virtual ~IThreadPool() {}; - - //----------------------------------------------------- - // Thread functions - //----------------------------------------------------- - virtual bool Start( const ThreadPoolStartParams_t &startParams = ThreadPoolStartParams_t() ) = 0; - virtual bool Stop( int timeout = TT_INFINITE ) = 0; - - //----------------------------------------------------- - // Functions for any thread - //----------------------------------------------------- - virtual unsigned GetJobCount() = 0; - virtual int NumThreads() = 0; - virtual int NumIdleThreads() = 0; - - //----------------------------------------------------- - // Pause/resume processing jobs - //----------------------------------------------------- - virtual int SuspendExecution() = 0; - virtual int ResumeExecution() = 0; - - //----------------------------------------------------- - // Offer the current thread to the pool - //----------------------------------------------------- - virtual int YieldWait( CThreadEvent **pEvents, int nEvents, bool bWaitAll = true, unsigned timeout = TT_INFINITE ) = 0; - virtual int YieldWait( CJob **, int nJobs, bool bWaitAll = true, unsigned timeout = TT_INFINITE ) = 0; - virtual void Yield( unsigned timeout ) = 0; - - bool YieldWait( CThreadEvent &event, unsigned timeout = TT_INFINITE ); - bool YieldWait( CJob *, unsigned timeout = TT_INFINITE ); - - //----------------------------------------------------- - // Add a native job to the queue (master thread) - // See AddPerFrameJob below if you want to add a job that - // wants to be run before the end of the frame - //----------------------------------------------------- - virtual void AddJob( CJob * ) = 0; - - //----------------------------------------------------- - // Add an function object to the queue (master thread) - //----------------------------------------------------- - virtual void AddFunctor( CFunctor *pFunctor, CJob **ppJob = NULL, const char *pszDescription = NULL, unsigned flags = 0 ) { AddFunctorInternal( RetAddRef( pFunctor ), ppJob, pszDescription, flags ); } - - //----------------------------------------------------- - // Change the priority of an active job - //----------------------------------------------------- - virtual void ChangePriority( CJob *p, JobPriority_t priority ) = 0; - - //----------------------------------------------------- - // Bulk job manipulation (blocking) - //----------------------------------------------------- - int ExecuteAll( JobFilter_t pfnFilter = NULL ) { return ExecuteToPriority( JP_LOW, pfnFilter ); } - virtual int ExecuteToPriority( JobPriority_t toPriority, JobFilter_t pfnFilter = NULL ) = 0; - virtual int AbortAll() = 0; - - //----------------------------------------------------- - // Add a native job to the queue (master thread) - // Call YieldWaitPerFrameJobs() to wait only until all per-frame jobs are done - //----------------------------------------------------- - virtual void AddPerFrameJob( CJob * ) = 0; - - //----------------------------------------------------- - // Add an arbitrary call to the queue (master thread) - // - // Avert thy eyes! Imagine rather: - // - // CJob *AddCall( , [args1, [arg2,]...] - // CJob *AddCall( , , [args1, [arg2,]...] - // CJob *AddRefCall( , , [args1, [arg2,]...] - // CJob *QueueCall( , [args1, [arg2,]...] - // CJob *QueueCall( , , [args1, [arg2,]...] - //----------------------------------------------------- - - #define DEFINE_NONMEMBER_ADD_CALL(N) \ - template \ - CJob *AddCall(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ - { \ - CJob *pJob; \ - if ( !NumIdleThreads() ) \ - { \ - pJob = GetDummyJob(); \ - FunctorDirectCall( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ - } \ - else \ - { \ - AddFunctorInternal( CreateFunctor( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ - } \ - \ - return pJob; \ - } - - //------------------------------------- - - #define DEFINE_MEMBER_ADD_CALL(N) \ - template \ - CJob *AddCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ - { \ - CJob *pJob; \ - if ( !NumIdleThreads() ) \ - { \ - pJob = GetDummyJob(); \ - FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ - } \ - else \ - { \ - AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ - } \ - \ - return pJob; \ - } - - //------------------------------------- - - #define DEFINE_CONST_MEMBER_ADD_CALL(N) \ - template \ - CJob *AddCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ - { \ - CJob *pJob; \ - if ( !NumIdleThreads() ) \ - { \ - pJob = GetDummyJob(); \ - FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ - } \ - else \ - { \ - AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ - } \ - \ - return pJob; \ - } - - //------------------------------------- - - #define DEFINE_REF_COUNTING_MEMBER_ADD_CALL(N) \ - template \ - CJob *AddRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ - { \ - CJob *pJob; \ - if ( !NumIdleThreads() ) \ - { \ - pJob = GetDummyJob(); \ - FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ - } \ - else \ - { \ - AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ - } \ - \ - return pJob; \ - } - - //------------------------------------- - - #define DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL(N) \ - template \ - CJob *AddRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ - { \ - CJob *pJob; \ - if ( !NumIdleThreads() ) \ - { \ - pJob = GetDummyJob(); \ - FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ - } \ - else \ - { \ - AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ - } \ - \ - return pJob; \ - } - - //----------------------------------------------------------------------------- - - #define DEFINE_NONMEMBER_QUEUE_CALL(N) \ - template \ - CJob *QueueCall(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ - { \ - CJob *pJob; \ - AddFunctorInternal( CreateFunctor( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ - return pJob; \ - } - - //------------------------------------- - - #define DEFINE_MEMBER_QUEUE_CALL(N) \ - template \ - CJob *QueueCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ - { \ - CJob *pJob; \ - AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ - return pJob; \ - } - - //------------------------------------- - - #define DEFINE_CONST_MEMBER_QUEUE_CALL(N) \ - template \ - CJob *QueueCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ - { \ - CJob *pJob; \ - AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ - return pJob; \ - } - - //------------------------------------- - - #define DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL(N) \ - template \ - CJob *QueueRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ - { \ - CJob *pJob; \ - AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ - return pJob; \ - } - - //------------------------------------- - - #define DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL(N) \ - template \ - CJob *QueueRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ - { \ - CJob *pJob; \ - AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ - \ - return pJob; \ - } - - FUNC_GENERATE_ALL( DEFINE_NONMEMBER_ADD_CALL ); - FUNC_GENERATE_ALL( DEFINE_MEMBER_ADD_CALL ); - FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_ADD_CALL ); - FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_MEMBER_ADD_CALL ); - FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL ); - FUNC_GENERATE_ALL( DEFINE_NONMEMBER_QUEUE_CALL ); - FUNC_GENERATE_ALL( DEFINE_MEMBER_QUEUE_CALL ); - FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_QUEUE_CALL ); - FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL ); - FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL ); - - #undef DEFINE_NONMEMBER_ADD_CALL - #undef DEFINE_MEMBER_ADD_CALL - #undef DEFINE_CONST_MEMBER_ADD_CALL - #undef DEFINE_REF_COUNTING_MEMBER_ADD_CALL - #undef DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL - #undef DEFINE_NONMEMBER_QUEUE_CALL - #undef DEFINE_MEMBER_QUEUE_CALL - #undef DEFINE_CONST_MEMBER_QUEUE_CALL - #undef DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL - #undef DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL - -private: - virtual void AddFunctorInternal( CFunctor *, CJob ** = NULL, const char *pszDescription = NULL, unsigned flags = 0 ) = 0; - - //----------------------------------------------------- - // Services for internal use by job instances - //----------------------------------------------------- - friend class CJob; - - virtual CJob *GetDummyJob() = 0; - -public: - virtual void Distribute( bool bDistribute = true, int *pAffinityTable = NULL ) = 0; - - virtual bool Start( const ThreadPoolStartParams_t &startParams, const char *pszNameOverride ) = 0; - - virtual int YieldWaitPerFrameJobs( ) = 0; -}; - -//----------------------------------------------------------------------------- - -JOB_INTERFACE IThreadPool *CreateNewThreadPool(); -JOB_INTERFACE void DestroyThreadPool( IThreadPool *pPool ); - -//------------------------------------- - -JOB_INTERFACE void RunThreadPoolTests(); - -//----------------------------------------------------------------------------- - -JOB_INTERFACE IThreadPool *g_pThreadPool; -#ifdef _X360 -JOB_INTERFACE IThreadPool *g_pAlternateThreadPool; -#endif - -//----------------------------------------------------------------------------- -// Class to combine the metadata for an operation and the ability to perform -// the operation. Meant for inheritance. All functions inline, defers to executor -//----------------------------------------------------------------------------- -DECLARE_POINTER_HANDLE( ThreadPoolData_t ); -#define JOB_NO_DATA ((ThreadPoolData_t)-1) - -class CJob : public CRefCounted1 -{ -public: - CJob( JobPriority_t priority = JP_NORMAL ) - : m_status( JOB_STATUS_UNSERVICED ), - m_ThreadPoolData( JOB_NO_DATA ), - m_priority( priority ), - m_flags( 0 ), - m_pThreadPool( NULL ), - m_CompleteEvent( true ), - m_iServicingThread( -1 ) - { - } - - //----------------------------------------------------- - // Priority (not thread safe) - //----------------------------------------------------- - void SetPriority( JobPriority_t priority ) { m_priority = priority; } - JobPriority_t GetPriority() const { return m_priority; } - - //----------------------------------------------------- - - void SetFlags( unsigned flags ) { m_flags = flags; } - unsigned GetFlags() const { return m_flags; } - - //----------------------------------------------------- - - void SetServiceThread( int iServicingThread ) { m_iServicingThread = (char)iServicingThread; } - int GetServiceThread() const { return m_iServicingThread; } - void ClearServiceThread() { m_iServicingThread = -1; } - - //----------------------------------------------------- - // Fast queries - //----------------------------------------------------- - bool Executed() const { return ( m_status == JOB_OK ); } - bool CanExecute() const { return ( m_status == JOB_STATUS_PENDING || m_status == JOB_STATUS_UNSERVICED ); } - bool IsFinished() const { return ( m_status != JOB_STATUS_PENDING && m_status != JOB_STATUS_INPROGRESS && m_status != JOB_STATUS_UNSERVICED ); } - JobStatus_t GetStatus() const { return m_status; } - - //----------------------------------------------------- - // Try to acquire ownership (to satisfy). If you take the lock, you must either execute or abort. - //----------------------------------------------------- - bool TryLock() volatile { return m_mutex.TryLock(); } - void Lock() volatile { m_mutex.Lock(); } - void Unlock() volatile { m_mutex.Unlock(); } - - //----------------------------------------------------- - // Thread event support (safe for NULL this to simplify code ) - //----------------------------------------------------- - bool WaitForFinish( uint32 dwTimeout = TT_INFINITE ) { if (!this) return true; return ( !IsFinished() ) ? g_pThreadPool->YieldWait( this, dwTimeout ) : true; } - bool WaitForFinishAndRelease( uint32 dwTimeout = TT_INFINITE ) { if (!this) return true; bool bResult = WaitForFinish( dwTimeout); Release(); return bResult; } - CThreadEvent *AccessEvent() { return &m_CompleteEvent; } - - //----------------------------------------------------- - // Perform the job - //----------------------------------------------------- - JobStatus_t Execute(); - JobStatus_t TryExecute(); - JobStatus_t ExecuteAndRelease() { JobStatus_t status = Execute(); Release(); return status; } - JobStatus_t TryExecuteAndRelease() { JobStatus_t status = TryExecute(); Release(); return status; } - - //----------------------------------------------------- - // Terminate the job, discard if partially or wholly fulfilled - //----------------------------------------------------- - JobStatus_t Abort( bool bDiscard = true ); - - virtual char const *Describe() { return "Job"; } - -private: - //----------------------------------------------------- - friend class CThreadPool; - - JobStatus_t m_status; - JobPriority_t m_priority; - CThreadFastMutex m_mutex; - unsigned char m_flags; - char m_iServicingThread; - short m_reserved; - ThreadPoolData_t m_ThreadPoolData; - IThreadPool * m_pThreadPool; - CThreadEvent m_CompleteEvent; - -private: - //----------------------------------------------------- - CJob( const CJob &fromRequest ); - void operator=(const CJob &fromRequest ); - - virtual JobStatus_t DoExecute() = 0; - virtual JobStatus_t DoAbort( bool bDiscard ) { return JOB_STATUS_ABORTED; } - virtual void DoCleanup() {} -}; - -//----------------------------------------------------------------------------- - -class CFunctorJob : public CJob -{ -public: - CFunctorJob( CFunctor *pFunctor, const char *pszDescription = NULL ) - : m_pFunctor( pFunctor ) - { - if ( pszDescription ) - { - Q_strncpy( m_szDescription, pszDescription, sizeof(m_szDescription) ); - } - else - { - m_szDescription[0] = 0; - } - } - - virtual JobStatus_t DoExecute() - { - (*m_pFunctor)(); - return JOB_OK; - } - - const char *Describe() - { - return m_szDescription; - } - -private: - CRefPtr m_pFunctor; - char m_szDescription[16]; -}; - -//----------------------------------------------------------------------------- -// Utility for managing multiple jobs -//----------------------------------------------------------------------------- - -class CJobSet -{ -public: - CJobSet( CJob *pJob = NULL ) - { - if ( pJob ) - { - m_jobs.AddToTail( pJob ); - } - } - - CJobSet( CJob **ppJobs, int nJobs ) - { - if ( ppJobs ) - { - m_jobs.AddMultipleToTail( nJobs, ppJobs ); - } - } - - ~CJobSet() - { - for ( int i = 0; i < m_jobs.Count(); i++ ) - { - m_jobs[i]->Release(); - } - } - - void operator+=( CJob *pJob ) - { - m_jobs.AddToTail( pJob ); - } - - void operator-=( CJob *pJob ) - { - m_jobs.FindAndRemove( pJob ); - } - - void Execute( bool bRelease = true ) - { - for ( int i = 0; i < m_jobs.Count(); i++ ) - { - m_jobs[i]->Execute(); - if ( bRelease ) - { - m_jobs[i]->Release(); - } - } - - if ( bRelease ) - { - m_jobs.RemoveAll(); - } - } - - void Abort( bool bRelease = true ) - { - for ( int i = 0; i < m_jobs.Count(); i++ ) - { - m_jobs[i]->Abort(); - if ( bRelease ) - { - m_jobs[i]->Release(); - } - } - - if ( bRelease ) - { - m_jobs.RemoveAll(); - } - } - - void WaitForFinish( bool bRelease = true ) - { - for ( int i = 0; i < m_jobs.Count(); i++ ) - { - m_jobs[i]->WaitForFinish(); - if ( bRelease ) - { - m_jobs[i]->Release(); - } - } - - if ( bRelease ) - { - m_jobs.RemoveAll(); - } - } - - void WaitForFinish( IThreadPool *pPool, bool bRelease = true ) - { - pPool->YieldWait( m_jobs.Base(), m_jobs.Count() ); - - if ( bRelease ) - { - for ( int i = 0; i < m_jobs.Count(); i++ ) - { - m_jobs[i]->Release(); - } - - m_jobs.RemoveAll(); - } - } - -private: - CUtlVectorFixed m_jobs; -}; - -//----------------------------------------------------------------------------- -// Job helpers -//----------------------------------------------------------------------------- - -#define ThreadExecute g_pThreadPool->QueueCall -#define ThreadExecuteRef g_pThreadPool->QueueRefCall - -#define BeginExecuteParallel() do { CJobSet jobSet -#define EndExecuteParallel() jobSet.WaitForFinish( g_pThreadPool ); } while (0) - -#define ExecuteParallel jobSet += g_pThreadPool->QueueCall -#define ExecuteRefParallel jobSet += g_pThreadPool->QueueCallRef - - -//----------------------------------------------------------------------------- -// Work splitting: array split, best when cost per item is roughly equal -//----------------------------------------------------------------------------- - -#pragma warning(push) -#pragma warning(disable:4389) -#pragma warning(disable:4018) -#pragma warning(disable:4701) - -#define DEFINE_NON_MEMBER_ITER_RANGE_PARALLEL(N) \ - template \ - void IterRangeParallel(FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( ITERTYPE1, ITERTYPE2 FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ), ITERTYPE1 from, ITERTYPE2 to FUNC_ARG_FORMAL_PARAMS_##N ) \ - { \ - const int MAX_THREADS = 16; \ - int nIdle = g_pThreadPool->NumIdleThreads(); \ - ITERTYPE1 range = to - from; \ - int nThreads = min( nIdle + 1, range ); \ - if ( nThreads > MAX_THREADS ) \ - { \ - nThreads = MAX_THREADS; \ - } \ - if ( nThreads < 2 ) \ - { \ - FunctorDirectCall( pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \ - } \ - else \ - { \ - ITERTYPE1 nIncrement = range / nThreads; \ - \ - CJobSet jobSet; \ - while ( --nThreads ) \ - { \ - ITERTYPE2 thisTo = from + nIncrement; \ - jobSet += g_pThreadPool->AddCall( pfnProxied, from, thisTo FUNC_FUNCTOR_CALL_ARGS_##N ); \ - from = thisTo; \ - } \ - FunctorDirectCall( pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \ - jobSet.WaitForFinish( g_pThreadPool ); \ - } \ - \ - } - -FUNC_GENERATE_ALL( DEFINE_NON_MEMBER_ITER_RANGE_PARALLEL ); - -#define DEFINE_MEMBER_ITER_RANGE_PARALLEL(N) \ - template \ - void IterRangeParallel(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( ITERTYPE1, ITERTYPE2 FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ), ITERTYPE1 from, ITERTYPE2 to FUNC_ARG_FORMAL_PARAMS_##N ) \ - { \ - const int MAX_THREADS = 16; \ - int nIdle = g_pThreadPool->NumIdleThreads(); \ - ITERTYPE1 range = to - from; \ - int nThreads = min( nIdle + 1, range ); \ - if ( nThreads > MAX_THREADS ) \ - { \ - nThreads = MAX_THREADS; \ - } \ - if ( nThreads < 2 ) \ - { \ - FunctorDirectCall( pObject, pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \ - } \ - else \ - { \ - ITERTYPE1 nIncrement = range / nThreads; \ - \ - CJobSet jobSet; \ - while ( --nThreads ) \ - { \ - ITERTYPE2 thisTo = from + nIncrement; \ - jobSet += g_pThreadPool->AddCall( pObject, pfnProxied, from, thisTo FUNC_FUNCTOR_CALL_ARGS_##N ); \ - from = thisTo; \ - } \ - FunctorDirectCall( pObject, pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \ - jobSet.WaitForFinish( g_pThreadPool ); \ - } \ - \ - } - -FUNC_GENERATE_ALL( DEFINE_MEMBER_ITER_RANGE_PARALLEL ); - - -//----------------------------------------------------------------------------- -// Work splitting: competitive, best when cost per item varies a lot -//----------------------------------------------------------------------------- - -template -class CJobItemProcessor -{ -public: - typedef T ItemType_t; - void Begin() {} - // void Process( ItemType_t & ) {} - void End() {} -}; - -template -class CFuncJobItemProcessor : public CJobItemProcessor -{ -public: - void Init(void (*pfnProcess)( T & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL ) - { - m_pfnProcess = pfnProcess; - m_pfnBegin = pfnBegin; - m_pfnEnd = pfnEnd; - } - - //CFuncJobItemProcessor(OBJECT_TYPE_PTR pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL ); - void Begin() { if ( m_pfnBegin ) (*m_pfnBegin)(); } - void Process( T &item ) { (*m_pfnProcess)( item ); } - void End() { if ( m_pfnEnd ) (*m_pfnEnd)(); } - -protected: - void (*m_pfnProcess)( T & ); - void (*m_pfnBegin)(); - void (*m_pfnEnd)(); -}; - -template -class CMemberFuncJobItemProcessor : public CJobItemProcessor -{ -public: - void Init( OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( T & ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL ) - { - m_pObject = pObject; - m_pfnProcess = pfnProcess; - m_pfnBegin = pfnBegin; - m_pfnEnd = pfnEnd; - } - - void Begin() { if ( m_pfnBegin ) ((*m_pObject).*m_pfnBegin)(); } - void Process( T &item ) { ((*m_pObject).*m_pfnProcess)( item ); } - void End() { if ( m_pfnEnd ) ((*m_pObject).*m_pfnEnd)(); } - -protected: - OBJECT_TYPE *m_pObject; - - void (FUNCTION_CLASS::*m_pfnProcess)( T & ); - void (FUNCTION_CLASS::*m_pfnBegin)(); - void (FUNCTION_CLASS::*m_pfnEnd)(); -}; - -template -class CLoopFuncJobItemProcessor : public CJobItemProcessor -{ -public: - void Init(void (*pfnProcess)( T*, int, int ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL ) - { - m_pfnProcess = pfnProcess; - m_pfnBegin = pfnBegin; - m_pfnEnd = pfnEnd; - } - - void Begin() { if ( m_pfnBegin ) (*m_pfnBegin)(); } - void Process( T* pContext, int nFirst, int nCount ) { (*m_pfnProcess)( pContext, nFirst, nCount ); } - void End() { if ( m_pfnEnd ) (*m_pfnEnd)(); } - -protected: - void (*m_pfnProcess)( T*, int, int ); - void (*m_pfnBegin)(); - void (*m_pfnEnd)(); -}; - -template -class CLoopMemberFuncJobItemProcessor : public CJobItemProcessor -{ -public: - void Init( OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( T*, int, int ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL ) - { - m_pObject = pObject; - m_pfnProcess = pfnProcess; - m_pfnBegin = pfnBegin; - m_pfnEnd = pfnEnd; - } - - void Begin() { if ( m_pfnBegin ) ((*m_pObject).*m_pfnBegin)(); } - void Process( T *item, int nFirst, int nCount ) { ((*m_pObject).*m_pfnProcess)( item, nFirst, nCount ); } - void End() { if ( m_pfnEnd ) ((*m_pObject).*m_pfnEnd)(); } - -protected: - OBJECT_TYPE *m_pObject; - - void (FUNCTION_CLASS::*m_pfnProcess)( T*, int, int ); - void (FUNCTION_CLASS::*m_pfnBegin)(); - void (FUNCTION_CLASS::*m_pfnEnd)(); -}; - - -#pragma warning(push) -#pragma warning(disable:4189) - -template -class CParallelProcessor -{ -public: - CParallelProcessor() - { - m_pItems = m_pLimit= 0; - } - - void Run( ITEM_TYPE *pItems, unsigned nItems, int nChunkSize = 1, int nMaxParallel = INT_MAX, IThreadPool *pThreadPool = NULL ) - { - if ( nItems == 0 ) - return; - -#if defined(_X360) - volatile int ignored = ID_TO_PREVENT_COMDATS_IN_PROFILES; -#endif - - m_nChunkSize = nChunkSize; - if ( !pThreadPool ) - { - pThreadPool = g_pThreadPool; - } - - m_pItems = pItems; - m_pLimit = pItems + nItems; - - int nJobs = nItems - 1; - - if ( nJobs > nMaxParallel ) - { - nJobs = nMaxParallel; - } - - if (! pThreadPool ) // only possible on linux - { - DoExecute( ); - return; - } - - int nThreads = pThreadPool->NumThreads(); - if ( nJobs > nThreads ) - { - nJobs = nThreads; - } - - if ( nJobs > 0 ) - { - CJob **jobs = (CJob **)stackalloc( nJobs * sizeof(CJob **) ); - int i = nJobs; - - while( i-- ) - { - jobs[i] = pThreadPool->QueueCall( this, &CParallelProcessor::DoExecute ); - } - - DoExecute(); - - for ( i = 0; i < nJobs; i++ ) - { - jobs[i]->Abort(); // will either abort ones that never got a thread, or noop on ones that did - jobs[i]->Release(); - } - } - else - { - DoExecute(); - } - } - - ITEM_PROCESSOR_TYPE m_ItemProcessor; - -private: - void DoExecute() - { - if ( m_pItems < m_pLimit ) - { -#if defined(_X360) - volatile int ignored = ID_TO_PREVENT_COMDATS_IN_PROFILES; -#endif - m_ItemProcessor.Begin(); - - ITEM_TYPE *pLimit = m_pLimit; - - int nChunkSize = m_nChunkSize; - for (;;) - { - ITEM_TYPE *pCurrent = m_pItems.AtomicAdd( nChunkSize ); - ITEM_TYPE *pLast = MIN( pLimit, pCurrent + nChunkSize ); - while( pCurrent < pLast ) - { - m_ItemProcessor.Process( *pCurrent ); - pCurrent++; - } - if ( pCurrent >= pLimit ) - { - break; - } - } - m_ItemProcessor.End(); - } - } - CInterlockedPtr m_pItems; - ITEM_TYPE * m_pLimit; - int m_nChunkSize; - -}; - -#pragma warning(pop) - -template -inline void ParallelProcess( ITEM_TYPE *pItems, unsigned nItems, void (*pfnProcess)( ITEM_TYPE & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) -{ - CParallelProcessor > processor; - processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd ); - processor.Run( pItems, nItems, 1, nMaxParallel ); -} - -template -inline void ParallelProcess( ITEM_TYPE *pItems, unsigned nItems, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) -{ - CParallelProcessor > processor; - processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd ); - processor.Run( pItems, nItems, 1, nMaxParallel ); -} - -// Parallel Process that lets you specify threadpool -template -inline void ParallelProcess( IThreadPool *pPool, ITEM_TYPE *pItems, unsigned nItems, void (*pfnProcess)( ITEM_TYPE & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) -{ - CParallelProcessor > processor; - processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd ); - processor.Run( pItems, nItems, 1, nMaxParallel, pPool ); -} - -template -inline void ParallelProcess( IThreadPool *pPool, ITEM_TYPE *pItems, unsigned nItems, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) -{ - CParallelProcessor > processor; - processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd ); - processor.Run( pItems, nItems, 1, nMaxParallel, pPool ); -} - -// ParallelProcessChunks lets you specify a minimum # of items to process per job. Use this when -// you may have a large set of work items which only take a small amount of time per item, and so -// need to reduce dispatch overhead. -template -inline void ParallelProcessChunks( ITEM_TYPE *pItems, unsigned nItems, void (*pfnProcess)( ITEM_TYPE & ), int nChunkSize, int nMaxParallel = INT_MAX ) -{ - CParallelProcessor > processor; - processor.m_ItemProcessor.Init( pfnProcess, NULL, NULL ); - processor.Run( pItems, nItems, nChunkSize, nMaxParallel ); -} - -template -inline void ParallelProcessChunks( ITEM_TYPE *pItems, unsigned nItems, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), int nChunkSize, int nMaxParallel = INT_MAX ) -{ - CParallelProcessor > processor; - processor.m_ItemProcessor.Init( pObject, pfnProcess, NULL, NULL ); - processor.Run( pItems, nItems, nChunkSize, nMaxParallel ); -} - -template -inline void ParallelProcessChunks( IThreadPool *pPool, ITEM_TYPE *pItems, unsigned nItems, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), int nChunkSize, int nMaxParallel = INT_MAX ) -{ - CParallelProcessor > processor; - processor.m_ItemProcessor.Init( pObject, pfnProcess, NULL, NULL ); - processor.Run( pItems, nItems, nChunkSize, nMaxParallel, pPool ); -} - - -template -class CParallelLoopProcessor -{ -public: - CParallelLoopProcessor() - { - m_nIndex = m_nLimit = 0; - m_nChunkCount = 0; - m_nActive = 0; - } - - void Run( CONTEXT_TYPE *pContext, int nBegin, int nItems, int nChunkCount, int nMaxParallel = INT_MAX, IThreadPool *pThreadPool = NULL ) - { - if ( !nItems ) - return; - - if ( !pThreadPool ) - { - pThreadPool = g_pThreadPool; - } - - m_pContext = pContext; - m_nIndex = nBegin; - m_nLimit = nBegin + nItems; - nChunkCount = MAX( MIN( nItems, nChunkCount ), 1 ); - m_nChunkCount = ( nItems + nChunkCount - 1 ) / nChunkCount; - int nJobs = ( nItems + m_nChunkCount - 1 ) / m_nChunkCount; - if ( nJobs > nMaxParallel ) - { - nJobs = nMaxParallel; - } - - if ( !pThreadPool ) // only possible on linux - { - DoExecute( ); - return; - } - - int nThreads = pThreadPool->NumThreads(); - if ( nJobs > nThreads ) - { - nJobs = nThreads; - } - - if ( nJobs > 0 ) - { - CJob **jobs = (CJob **)stackalloc( nJobs * sizeof(CJob **) ); - int i = nJobs; - - while( i-- ) - { - jobs[i] = pThreadPool->QueueCall( this, &CParallelLoopProcessor::DoExecute ); - } - - DoExecute(); - - for ( i = 0; i < nJobs; i++ ) - { - jobs[i]->Abort(); // will either abort ones that never got a thread, or noop on ones that did - jobs[i]->Release(); - } - } - else - { - DoExecute(); - } - } - - ITEM_PROCESSOR_TYPE m_ItemProcessor; - -private: - void DoExecute() - { - m_ItemProcessor.Begin(); - for (;;) - { - int nIndex = m_nIndex.AtomicAdd( m_nChunkCount ); - if ( nIndex < m_nLimit ) - { - int nCount = MIN( m_nChunkCount, m_nLimit - nIndex ); - m_ItemProcessor.Process( m_pContext, nIndex, nCount ); - } - else - { - break; - } - } - m_ItemProcessor.End(); - --m_nActive; - } - - CONTEXT_TYPE *m_pContext; - CInterlockedInt m_nIndex; - int m_nLimit; - int m_nChunkCount; - CInterlockedInt m_nActive; -}; - -template < typename CONTEXT_TYPE > -inline void ParallelLoopProcess( IThreadPool *pPool, CONTEXT_TYPE *pContext, int nStart, int nCount, void (*pfnProcess)( CONTEXT_TYPE*, int, int ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) -{ - CParallelLoopProcessor< CONTEXT_TYPE, CLoopFuncJobItemProcessor< CONTEXT_TYPE > > processor; - processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd ); - processor.Run( pContext, nStart, nCount, 1, nMaxParallel, pPool ); -} - -template < typename CONTEXT_TYPE, typename OBJECT_TYPE, typename FUNCTION_CLASS > -inline void ParallelLoopProcess( IThreadPool *pPool, CONTEXT_TYPE *pContext, int nStart, int nCount, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( CONTEXT_TYPE*, int, int ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) -{ - CParallelLoopProcessor< CONTEXT_TYPE, CLoopMemberFuncJobItemProcessor > processor; - processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd ); - processor.Run( pContext, nStart, nCount, 1, nMaxParallel, pPool ); -} - -template < typename CONTEXT_TYPE > -inline void ParallelLoopProcessChunks( IThreadPool *pPool, CONTEXT_TYPE *pContext, int nStart, int nCount, int nChunkSize, void (*pfnProcess)( CONTEXT_TYPE*, int, int ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) -{ - CParallelLoopProcessor< CONTEXT_TYPE, CLoopFuncJobItemProcessor< CONTEXT_TYPE > > processor; - processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd ); - processor.Run( pContext, nStart, nCount, nChunkSize, nMaxParallel, pPool ); -} - -template < typename CONTEXT_TYPE, typename OBJECT_TYPE, typename FUNCTION_CLASS > -inline void ParallelLoopProcessChunks( IThreadPool *pPool, CONTEXT_TYPE *pContext, int nStart, int nCount, int nChunkSize, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( CONTEXT_TYPE*, int, int ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) -{ - CParallelLoopProcessor< CONTEXT_TYPE, CLoopMemberFuncJobItemProcessor > processor; - processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd ); - processor.Run( pContext, nStart, nCount, nChunkSize, nMaxParallel, pPool ); -} - -template -class CParallelProcessorBase -{ -protected: - typedef CParallelProcessorBase ThisParallelProcessorBase_t; - typedef Derived ThisParallelProcessorDerived_t; - -public: - CParallelProcessorBase() - { - m_nActive = 0; - } - -protected: - void Run( int nMaxParallel = INT_MAX, int threadOverride = -1 ) - { - int i = g_pThreadPool->NumIdleThreads(); - - if ( nMaxParallel < i) - { - i = nMaxParallel; - } - - while( i -- > 0 ) - { - if ( threadOverride == -1 || i == threadOverride - 1 ) - { - ++ m_nActive; - ThreadExecute( this, &ThisParallelProcessorBase_t::DoExecute )->Release(); - } - } - - if ( threadOverride == -1 || threadOverride == 0 ) - { - ++ m_nActive; - DoExecute(); - } - - while ( m_nActive ) - { - ThreadPause(); - } - } - -protected: - void OnBegin() {} - bool OnProcess() { return false; } - void OnEnd() {} - -private: - void DoExecute() - { - static_cast( this )->OnBegin(); - - while ( static_cast( this )->OnProcess() ) - continue; - - static_cast(this)->OnEnd(); - - -- m_nActive; - } - - CInterlockedInt m_nActive; -}; - - - - -//----------------------------------------------------------------------------- -// Raw thread launching -//----------------------------------------------------------------------------- - -inline unsigned FunctorExecuteThread( void *pParam ) -{ - CFunctor *pFunctor = (CFunctor *)pParam; - (*pFunctor)(); - pFunctor->Release(); - return 0; -} - -inline ThreadHandle_t ThreadExecuteSoloImpl( CFunctor *pFunctor, const char *pszName = NULL ) -{ - ThreadHandle_t hThread; - hThread = CreateSimpleThread( FunctorExecuteThread, pFunctor ); - if ( pszName ) - { - ThreadSetDebugName( hThread, pszName ); - } - return hThread; -} - -inline ThreadHandle_t ThreadExecuteSolo( CJob *pJob ) { return ThreadExecuteSoloImpl( CreateFunctor( pJob, &CJob::Execute ), pJob->Describe() ); } - -template -inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4, a5 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4, a5, a6 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4, a5, a6, a7 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4, a5, a6, a7, a8 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4, a5 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4, a5, a6 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4, a5, a6, a7 ), pszName ); } - -template -inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4, a5, a6, a7, a8 ), pszName ); } - -//----------------------------------------------------------------------------- - -inline bool IThreadPool::YieldWait( CThreadEvent &theEvent, unsigned timeout ) -{ - CThreadEvent *pEvent = &theEvent; - return ( YieldWait( &pEvent, 1, true, timeout ) != TW_TIMEOUT ); -} - -inline bool IThreadPool::YieldWait( CJob *pJob, unsigned timeout ) -{ - return ( YieldWait( &pJob, 1, true, timeout ) != TW_TIMEOUT ); -} - -//----------------------------------------------------------------------------- - -inline JobStatus_t CJob::Execute() -{ - if ( IsFinished() ) - { - return m_status; - } - - AUTO_LOCK( m_mutex ); - AddRef(); - - JobStatus_t result; - - switch ( m_status ) - { - case JOB_STATUS_UNSERVICED: - case JOB_STATUS_PENDING: - { - // Service it - m_status = JOB_STATUS_INPROGRESS; - result = m_status = DoExecute(); - DoCleanup(); - m_CompleteEvent.Set(); - break; - } - - case JOB_STATUS_INPROGRESS: - AssertMsg(0, "Mutex Should have protected use while processing"); - // fall through... - - case JOB_OK: - case JOB_STATUS_ABORTED: - result = m_status; - break; - - default: - AssertMsg( m_status < JOB_OK, "Unknown job state"); - result = m_status; - } - - Release(); - - return result; -} - - -//--------------------------------------------------------- - -inline JobStatus_t CJob::TryExecute() -{ - // TryLock() would only fail if another thread has entered - // Execute() or Abort() - if ( !IsFinished() && TryLock() ) - { - // ...service the request - Execute(); - Unlock(); - } - return m_status; -} - -//--------------------------------------------------------- - -inline JobStatus_t CJob::Abort( bool bDiscard ) -{ - if ( IsFinished() ) - { - return m_status; - } - - AUTO_LOCK( m_mutex ); - AddRef(); - - JobStatus_t result; - - switch ( m_status ) - { - case JOB_STATUS_UNSERVICED: - case JOB_STATUS_PENDING: - { - result = m_status = DoAbort( bDiscard ); - if ( bDiscard ) - DoCleanup(); - m_CompleteEvent.Set(); - } - break; - - case JOB_STATUS_ABORTED: - case JOB_STATUS_INPROGRESS: - case JOB_OK: - result = m_status; - break; - - default: - AssertMsg( m_status < JOB_OK, "Unknown job state"); - result = m_status; - } - - Release(); - - return result; -} - -//----------------------------------------------------------------------------- - -#endif // JOBTHREAD_H +//========== Copyright � 2005, Valve Corporation, All rights reserved. ======== +// +// Purpose: A utility for a discrete job-oriented worker thread. +// +// The class CThreadPool is both the job queue, and the +// worker thread. Except when the main thread attempts to +// synchronously execute a job, most of the inter-thread locking +// on the queue. +// +// The queue threading model uses a manual reset event for optimal +// throughput. Adding to the queue is guarded by a semaphore that +// will block the inserting thread if the queue has overflown. +// This prevents the worker thread from being starved out even if +// not running at a higher priority than the master thread. +// +// The thread function waits for jobs, services jobs, and manages +// communication between the worker and master threads. The nature +// of the work is opaque to the Executer. +// +// CJob instances actually do the work. The base class +// calls virtual methods for job primitives, so derivations don't +// need to worry about threading models. All of the variants of +// job and OS can be expressed in this hierarchy. Instances of +// CJob are the items placed in the queue, and by +// overriding the job primitives they are the manner by which +// users of the Executer control the state of the job. +// +//============================================================================= + +#include +#include "tier0/threadtools.h" +#include "tier1/refcount.h" +#include "tier1/utllinkedlist.h" +#include "tier1/utlvector.h" +#include "tier1/functors.h" + +#include "platform.h" + +#ifndef JOBTHREAD_H +#define JOBTHREAD_H + +#ifdef AddJob // windows.h print function collisions +#undef AddJob +#undef GetJob +#endif + +#ifdef VSTDLIB_DLL_EXPORT +#define JOB_INTERFACE DLL_EXPORT +#define JOB_OVERLOAD DLL_GLOBAL_EXPORT +#define JOB_CLASS DLL_CLASS_EXPORT +#else +#define JOB_INTERFACE DLL_IMPORT +#define JOB_OVERLOAD DLL_GLOBAL_IMPORT +#define JOB_CLASS DLL_CLASS_IMPORT +#endif + +#if defined( _WIN32 ) +#pragma once +#endif + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +class CJob; + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +enum JobStatusEnum_t +{ + // Use negative for errors + JOB_OK, // operation is successful + JOB_STATUS_PENDING, // file is properly queued, waiting for service + JOB_STATUS_INPROGRESS, // file is being accessed + JOB_STATUS_ABORTED, // file was aborted by caller + JOB_STATUS_UNSERVICED, // file is not yet queued +}; + +typedef int JobStatus_t; + +enum JobFlags_t +{ + JF_IO = ( 1 << 0 ), // The job primarily blocks on IO or hardware + JF_BOOST_THREAD = ( 1 << 1 ), // Up the thread priority to max allowed while processing task + JF_SERIAL = ( 1 << 2 ), // Job cannot be executed out of order relative to other "strict" jobs + JF_QUEUE = ( 1 << 3 ), // Queue it, even if not an IO job +}; + +enum JobPriority_t +{ + JP_LOW, + JP_NORMAL, + JP_HIGH +}; + +#define TP_MAX_POOL_THREADS 64 +struct ThreadPoolStartParams_t +{ + ThreadPoolStartParams_t( bool bIOThreads = false, unsigned nThreads = -1, int *pAffinities = NULL, ThreeState_t fDistribute = TRS_NONE, unsigned nStackSize = -1, int iThreadPriority = SHRT_MIN ) + : bIOThreads( bIOThreads ), nThreads( nThreads ), fDistribute( fDistribute ), nStackSize( nStackSize ), iThreadPriority( iThreadPriority ) + { + bUseAffinityTable = ( pAffinities != NULL ) && ( fDistribute == TRS_TRUE ) && ( nThreads != -1 ); + if ( bUseAffinityTable ) + { + // user supplied an optional 1:1 affinity mapping to override normal distribute behavior + nThreads = MIN( TP_MAX_POOL_THREADS, nThreads ); + for ( unsigned int i = 0; i < nThreads; i++ ) + { + iAffinityTable[i] = pAffinities[i]; + } + } + } + + int nThreads; + ThreeState_t fDistribute; + int nStackSize; + int iThreadPriority; + int iAffinityTable[TP_MAX_POOL_THREADS]; + + bool bIOThreads : 1; + bool bUseAffinityTable : 1; +}; + +//----------------------------------------------------------------------------- +// +// IThreadPool +// +//----------------------------------------------------------------------------- + +typedef bool (*JobFilter_t)( CJob * ); + +//--------------------------------------------------------- +// Messages supported through the CallWorker() method +//--------------------------------------------------------- +enum ThreadPoolMessages_t +{ + TPM_EXIT, // Exit the thread + TPM_SUSPEND, // Suspend after next operation +}; + +//--------------------------------------------------------- + +#ifdef Yield +#undef Yield +#endif + +abstract_class IThreadPool : public IRefCounted +{ +public: + virtual ~IThreadPool() {}; + + //----------------------------------------------------- + // Thread functions + //----------------------------------------------------- + virtual bool Start( const ThreadPoolStartParams_t &startParams = ThreadPoolStartParams_t() ) = 0; + virtual bool Stop( int timeout = TT_INFINITE ) = 0; + + //----------------------------------------------------- + // Functions for any thread + //----------------------------------------------------- + virtual unsigned GetJobCount() = 0; + virtual int NumThreads() = 0; + virtual int NumIdleThreads() = 0; + + //----------------------------------------------------- + // Pause/resume processing jobs + //----------------------------------------------------- + virtual int SuspendExecution() = 0; + virtual int ResumeExecution() = 0; + + //----------------------------------------------------- + // Offer the current thread to the pool + //----------------------------------------------------- + virtual int YieldWait( CThreadEvent **pEvents, int nEvents, bool bWaitAll = true, unsigned timeout = TT_INFINITE ) = 0; + virtual int YieldWait( CJob **, int nJobs, bool bWaitAll = true, unsigned timeout = TT_INFINITE ) = 0; + virtual void Yield( unsigned timeout ) = 0; + + bool YieldWait( CThreadEvent &event, unsigned timeout = TT_INFINITE ); + bool YieldWait( CJob *, unsigned timeout = TT_INFINITE ); + + //----------------------------------------------------- + // Add a native job to the queue (master thread) + // See AddPerFrameJob below if you want to add a job that + // wants to be run before the end of the frame + //----------------------------------------------------- + virtual void AddJob( CJob * ) = 0; + + //----------------------------------------------------- + // Add an function object to the queue (master thread) + //----------------------------------------------------- + virtual void AddFunctor( CFunctor *pFunctor, CJob **ppJob = NULL, const char *pszDescription = NULL, unsigned flags = 0 ) { AddFunctorInternal( RetAddRef( pFunctor ), ppJob, pszDescription, flags ); } + + //----------------------------------------------------- + // Change the priority of an active job + //----------------------------------------------------- + virtual void ChangePriority( CJob *p, JobPriority_t priority ) = 0; + + //----------------------------------------------------- + // Bulk job manipulation (blocking) + //----------------------------------------------------- + int ExecuteAll( JobFilter_t pfnFilter = NULL ) { return ExecuteToPriority( JP_LOW, pfnFilter ); } + virtual int ExecuteToPriority( JobPriority_t toPriority, JobFilter_t pfnFilter = NULL ) = 0; + virtual int AbortAll() = 0; + + //----------------------------------------------------- + // Add a native job to the queue (master thread) + // Call YieldWaitPerFrameJobs() to wait only until all per-frame jobs are done + //----------------------------------------------------- + virtual void AddPerFrameJob( CJob * ) = 0; + + //----------------------------------------------------- + // Add an arbitrary call to the queue (master thread) + // + // Avert thy eyes! Imagine rather: + // + // CJob *AddCall( , [args1, [arg2,]...] + // CJob *AddCall( , , [args1, [arg2,]...] + // CJob *AddRefCall( , , [args1, [arg2,]...] + // CJob *QueueCall( , [args1, [arg2,]...] + // CJob *QueueCall( , , [args1, [arg2,]...] + //----------------------------------------------------- + + #define DEFINE_NONMEMBER_ADD_CALL(N) \ + template \ + CJob *AddCall(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + if ( !NumIdleThreads() ) \ + { \ + pJob = GetDummyJob(); \ + FunctorDirectCall( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + AddFunctorInternal( CreateFunctor( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ + } \ + \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_MEMBER_ADD_CALL(N) \ + template \ + CJob *AddCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + if ( !NumIdleThreads() ) \ + { \ + pJob = GetDummyJob(); \ + FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ + } \ + \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_CONST_MEMBER_ADD_CALL(N) \ + template \ + CJob *AddCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + if ( !NumIdleThreads() ) \ + { \ + pJob = GetDummyJob(); \ + FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ + } \ + \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_REF_COUNTING_MEMBER_ADD_CALL(N) \ + template \ + CJob *AddRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + if ( !NumIdleThreads() ) \ + { \ + pJob = GetDummyJob(); \ + FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ + } \ + \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL(N) \ + template \ + CJob *AddRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + if ( !NumIdleThreads() ) \ + { \ + pJob = GetDummyJob(); \ + FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \ + } \ + \ + return pJob; \ + } + + //----------------------------------------------------------------------------- + + #define DEFINE_NONMEMBER_QUEUE_CALL(N) \ + template \ + CJob *QueueCall(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + AddFunctorInternal( CreateFunctor( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_MEMBER_QUEUE_CALL(N) \ + template \ + CJob *QueueCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_CONST_MEMBER_QUEUE_CALL(N) \ + template \ + CJob *QueueCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL(N) \ + template \ + CJob *QueueRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ + return pJob; \ + } + + //------------------------------------- + + #define DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL(N) \ + template \ + CJob *QueueRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + CJob *pJob; \ + AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \ + \ + return pJob; \ + } + + FUNC_GENERATE_ALL( DEFINE_NONMEMBER_ADD_CALL ); + FUNC_GENERATE_ALL( DEFINE_MEMBER_ADD_CALL ); + FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_ADD_CALL ); + FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_MEMBER_ADD_CALL ); + FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL ); + FUNC_GENERATE_ALL( DEFINE_NONMEMBER_QUEUE_CALL ); + FUNC_GENERATE_ALL( DEFINE_MEMBER_QUEUE_CALL ); + FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_QUEUE_CALL ); + FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL ); + FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL ); + + #undef DEFINE_NONMEMBER_ADD_CALL + #undef DEFINE_MEMBER_ADD_CALL + #undef DEFINE_CONST_MEMBER_ADD_CALL + #undef DEFINE_REF_COUNTING_MEMBER_ADD_CALL + #undef DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL + #undef DEFINE_NONMEMBER_QUEUE_CALL + #undef DEFINE_MEMBER_QUEUE_CALL + #undef DEFINE_CONST_MEMBER_QUEUE_CALL + #undef DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL + #undef DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL + +private: + virtual void AddFunctorInternal( CFunctor *, CJob ** = NULL, const char *pszDescription = NULL, unsigned flags = 0 ) = 0; + + //----------------------------------------------------- + // Services for internal use by job instances + //----------------------------------------------------- + friend class CJob; + + virtual CJob *GetDummyJob() = 0; + +public: + virtual void Distribute( bool bDistribute = true, int *pAffinityTable = NULL ) = 0; + + virtual bool Start( const ThreadPoolStartParams_t &startParams, const char *pszNameOverride ) = 0; + + virtual int YieldWaitPerFrameJobs( ) = 0; +}; + +//----------------------------------------------------------------------------- + +JOB_INTERFACE IThreadPool *CreateNewThreadPool(); +JOB_INTERFACE void DestroyThreadPool( IThreadPool *pPool ); + +//------------------------------------- + +JOB_INTERFACE void RunThreadPoolTests(); + +//----------------------------------------------------------------------------- + +JOB_INTERFACE IThreadPool *g_pThreadPool; +#ifdef _X360 +JOB_INTERFACE IThreadPool *g_pAlternateThreadPool; +#endif + +//----------------------------------------------------------------------------- +// Class to combine the metadata for an operation and the ability to perform +// the operation. Meant for inheritance. All functions inline, defers to executor +//----------------------------------------------------------------------------- +DECLARE_POINTER_HANDLE( ThreadPoolData_t ); +#define JOB_NO_DATA ((ThreadPoolData_t)-1) + +class CJob : public CRefCounted1 +{ +public: + CJob( JobPriority_t priority = JP_NORMAL ) + : m_status( JOB_STATUS_UNSERVICED ), + m_ThreadPoolData( JOB_NO_DATA ), + m_priority( priority ), + m_flags( 0 ), + m_pThreadPool( NULL ), + m_CompleteEvent( true ), + m_iServicingThread( -1 ) + { + } + + //----------------------------------------------------- + // Priority (not thread safe) + //----------------------------------------------------- + void SetPriority( JobPriority_t priority ) { m_priority = priority; } + JobPriority_t GetPriority() const { return m_priority; } + + //----------------------------------------------------- + + void SetFlags( unsigned flags ) { m_flags = flags; } + unsigned GetFlags() const { return m_flags; } + + //----------------------------------------------------- + + void SetServiceThread( int iServicingThread ) { m_iServicingThread = (char)iServicingThread; } + int GetServiceThread() const { return m_iServicingThread; } + void ClearServiceThread() { m_iServicingThread = -1; } + + //----------------------------------------------------- + // Fast queries + //----------------------------------------------------- + bool Executed() const { return ( m_status == JOB_OK ); } + bool CanExecute() const { return ( m_status == JOB_STATUS_PENDING || m_status == JOB_STATUS_UNSERVICED ); } + bool IsFinished() const { return ( m_status != JOB_STATUS_PENDING && m_status != JOB_STATUS_INPROGRESS && m_status != JOB_STATUS_UNSERVICED ); } + JobStatus_t GetStatus() const { return m_status; } + + //----------------------------------------------------- + // Try to acquire ownership (to satisfy). If you take the lock, you must either execute or abort. + //----------------------------------------------------- + bool TryLock() volatile { return m_mutex.TryLock(); } + void Lock() volatile { m_mutex.Lock(); } + void Unlock() volatile { m_mutex.Unlock(); } + + //----------------------------------------------------- + // Thread event support (safe for NULL this to simplify code ) + //----------------------------------------------------- + bool WaitForFinish( uint32 dwTimeout = TT_INFINITE ) { if (!this) return true; return ( !IsFinished() ) ? g_pThreadPool->YieldWait( this, dwTimeout ) : true; } + bool WaitForFinishAndRelease( uint32 dwTimeout = TT_INFINITE ) { if (!this) return true; bool bResult = WaitForFinish( dwTimeout); Release(); return bResult; } + CThreadEvent *AccessEvent() { return &m_CompleteEvent; } + + //----------------------------------------------------- + // Perform the job + //----------------------------------------------------- + JobStatus_t Execute(); + JobStatus_t TryExecute(); + JobStatus_t ExecuteAndRelease() { JobStatus_t status = Execute(); Release(); return status; } + JobStatus_t TryExecuteAndRelease() { JobStatus_t status = TryExecute(); Release(); return status; } + + //----------------------------------------------------- + // Terminate the job, discard if partially or wholly fulfilled + //----------------------------------------------------- + JobStatus_t Abort( bool bDiscard = true ); + + virtual char const *Describe() { return "Job"; } + +private: + //----------------------------------------------------- + friend class CThreadPool; + + JobStatus_t m_status; + JobPriority_t m_priority; + CThreadFastMutex m_mutex; + unsigned char m_flags; + char m_iServicingThread; + short m_reserved; + ThreadPoolData_t m_ThreadPoolData; + IThreadPool * m_pThreadPool; + CThreadEvent m_CompleteEvent; + +private: + //----------------------------------------------------- + CJob( const CJob &fromRequest ); + void operator=(const CJob &fromRequest ); + + virtual JobStatus_t DoExecute() = 0; + virtual JobStatus_t DoAbort( bool bDiscard ) { return JOB_STATUS_ABORTED; } + virtual void DoCleanup() {} +}; + +//----------------------------------------------------------------------------- + +class CFunctorJob : public CJob +{ +public: + CFunctorJob( CFunctor *pFunctor, const char *pszDescription = NULL ) + : m_pFunctor( pFunctor ) + { + if ( pszDescription ) + { + Q_strncpy( m_szDescription, pszDescription, sizeof(m_szDescription) ); + } + else + { + m_szDescription[0] = 0; + } + } + + virtual JobStatus_t DoExecute() + { + (*m_pFunctor)(); + return JOB_OK; + } + + const char *Describe() + { + return m_szDescription; + } + +private: + CRefPtr m_pFunctor; + char m_szDescription[16]; +}; + +//----------------------------------------------------------------------------- +// Utility for managing multiple jobs +//----------------------------------------------------------------------------- + +class CJobSet +{ +public: + CJobSet( CJob *pJob = NULL ) + { + if ( pJob ) + { + m_jobs.AddToTail( pJob ); + } + } + + CJobSet( CJob **ppJobs, int nJobs ) + { + if ( ppJobs ) + { + m_jobs.AddMultipleToTail( nJobs, ppJobs ); + } + } + + ~CJobSet() + { + for ( int i = 0; i < m_jobs.Count(); i++ ) + { + m_jobs[i]->Release(); + } + } + + void operator+=( CJob *pJob ) + { + m_jobs.AddToTail( pJob ); + } + + void operator-=( CJob *pJob ) + { + m_jobs.FindAndRemove( pJob ); + } + + void Execute( bool bRelease = true ) + { + for ( int i = 0; i < m_jobs.Count(); i++ ) + { + m_jobs[i]->Execute(); + if ( bRelease ) + { + m_jobs[i]->Release(); + } + } + + if ( bRelease ) + { + m_jobs.RemoveAll(); + } + } + + void Abort( bool bRelease = true ) + { + for ( int i = 0; i < m_jobs.Count(); i++ ) + { + m_jobs[i]->Abort(); + if ( bRelease ) + { + m_jobs[i]->Release(); + } + } + + if ( bRelease ) + { + m_jobs.RemoveAll(); + } + } + + void WaitForFinish( bool bRelease = true ) + { + for ( int i = 0; i < m_jobs.Count(); i++ ) + { + m_jobs[i]->WaitForFinish(); + if ( bRelease ) + { + m_jobs[i]->Release(); + } + } + + if ( bRelease ) + { + m_jobs.RemoveAll(); + } + } + + void WaitForFinish( IThreadPool *pPool, bool bRelease = true ) + { + pPool->YieldWait( m_jobs.Base(), m_jobs.Count() ); + + if ( bRelease ) + { + for ( int i = 0; i < m_jobs.Count(); i++ ) + { + m_jobs[i]->Release(); + } + + m_jobs.RemoveAll(); + } + } + +private: + CUtlVectorFixed m_jobs; +}; + +//----------------------------------------------------------------------------- +// Job helpers +//----------------------------------------------------------------------------- + +#define ThreadExecute g_pThreadPool->QueueCall +#define ThreadExecuteRef g_pThreadPool->QueueRefCall + +#define BeginExecuteParallel() do { CJobSet jobSet +#define EndExecuteParallel() jobSet.WaitForFinish( g_pThreadPool ); } while (0) + +#define ExecuteParallel jobSet += g_pThreadPool->QueueCall +#define ExecuteRefParallel jobSet += g_pThreadPool->QueueCallRef + + +//----------------------------------------------------------------------------- +// Work splitting: array split, best when cost per item is roughly equal +//----------------------------------------------------------------------------- + +#pragma warning(push) +#pragma warning(disable:4389) +#pragma warning(disable:4018) +#pragma warning(disable:4701) + +#define DEFINE_NON_MEMBER_ITER_RANGE_PARALLEL(N) \ + template \ + void IterRangeParallel(FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( ITERTYPE1, ITERTYPE2 FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ), ITERTYPE1 from, ITERTYPE2 to FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + const int MAX_THREADS = 16; \ + int nIdle = g_pThreadPool->NumIdleThreads(); \ + ITERTYPE1 range = to - from; \ + int nThreads = min( nIdle + 1, range ); \ + if ( nThreads > MAX_THREADS ) \ + { \ + nThreads = MAX_THREADS; \ + } \ + if ( nThreads < 2 ) \ + { \ + FunctorDirectCall( pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + ITERTYPE1 nIncrement = range / nThreads; \ + \ + CJobSet jobSet; \ + while ( --nThreads ) \ + { \ + ITERTYPE2 thisTo = from + nIncrement; \ + jobSet += g_pThreadPool->AddCall( pfnProxied, from, thisTo FUNC_FUNCTOR_CALL_ARGS_##N ); \ + from = thisTo; \ + } \ + FunctorDirectCall( pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \ + jobSet.WaitForFinish( g_pThreadPool ); \ + } \ + \ + } + +FUNC_GENERATE_ALL( DEFINE_NON_MEMBER_ITER_RANGE_PARALLEL ); + +#define DEFINE_MEMBER_ITER_RANGE_PARALLEL(N) \ + template \ + void IterRangeParallel(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( ITERTYPE1, ITERTYPE2 FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ), ITERTYPE1 from, ITERTYPE2 to FUNC_ARG_FORMAL_PARAMS_##N ) \ + { \ + const int MAX_THREADS = 16; \ + int nIdle = g_pThreadPool->NumIdleThreads(); \ + ITERTYPE1 range = to - from; \ + int nThreads = min( nIdle + 1, range ); \ + if ( nThreads > MAX_THREADS ) \ + { \ + nThreads = MAX_THREADS; \ + } \ + if ( nThreads < 2 ) \ + { \ + FunctorDirectCall( pObject, pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \ + } \ + else \ + { \ + ITERTYPE1 nIncrement = range / nThreads; \ + \ + CJobSet jobSet; \ + while ( --nThreads ) \ + { \ + ITERTYPE2 thisTo = from + nIncrement; \ + jobSet += g_pThreadPool->AddCall( pObject, pfnProxied, from, thisTo FUNC_FUNCTOR_CALL_ARGS_##N ); \ + from = thisTo; \ + } \ + FunctorDirectCall( pObject, pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \ + jobSet.WaitForFinish( g_pThreadPool ); \ + } \ + \ + } + +FUNC_GENERATE_ALL( DEFINE_MEMBER_ITER_RANGE_PARALLEL ); + + +//----------------------------------------------------------------------------- +// Work splitting: competitive, best when cost per item varies a lot +//----------------------------------------------------------------------------- + +template +class CJobItemProcessor +{ +public: + typedef T ItemType_t; + void Begin() {} + // void Process( ItemType_t & ) {} + void End() {} +}; + +template +class CFuncJobItemProcessor : public CJobItemProcessor +{ +public: + void Init(void (*pfnProcess)( T & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL ) + { + m_pfnProcess = pfnProcess; + m_pfnBegin = pfnBegin; + m_pfnEnd = pfnEnd; + } + + //CFuncJobItemProcessor(OBJECT_TYPE_PTR pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL ); + void Begin() { if ( m_pfnBegin ) (*m_pfnBegin)(); } + void Process( T &item ) { (*m_pfnProcess)( item ); } + void End() { if ( m_pfnEnd ) (*m_pfnEnd)(); } + +protected: + void (*m_pfnProcess)( T & ); + void (*m_pfnBegin)(); + void (*m_pfnEnd)(); +}; + +template +class CMemberFuncJobItemProcessor : public CJobItemProcessor +{ +public: + void Init( OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( T & ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL ) + { + m_pObject = pObject; + m_pfnProcess = pfnProcess; + m_pfnBegin = pfnBegin; + m_pfnEnd = pfnEnd; + } + + void Begin() { if ( m_pfnBegin ) ((*m_pObject).*m_pfnBegin)(); } + void Process( T &item ) { ((*m_pObject).*m_pfnProcess)( item ); } + void End() { if ( m_pfnEnd ) ((*m_pObject).*m_pfnEnd)(); } + +protected: + OBJECT_TYPE *m_pObject; + + void (FUNCTION_CLASS::*m_pfnProcess)( T & ); + void (FUNCTION_CLASS::*m_pfnBegin)(); + void (FUNCTION_CLASS::*m_pfnEnd)(); +}; + +template +class CLoopFuncJobItemProcessor : public CJobItemProcessor +{ +public: + void Init(void (*pfnProcess)( T*, int, int ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL ) + { + m_pfnProcess = pfnProcess; + m_pfnBegin = pfnBegin; + m_pfnEnd = pfnEnd; + } + + void Begin() { if ( m_pfnBegin ) (*m_pfnBegin)(); } + void Process( T* pContext, int nFirst, int nCount ) { (*m_pfnProcess)( pContext, nFirst, nCount ); } + void End() { if ( m_pfnEnd ) (*m_pfnEnd)(); } + +protected: + void (*m_pfnProcess)( T*, int, int ); + void (*m_pfnBegin)(); + void (*m_pfnEnd)(); +}; + +template +class CLoopMemberFuncJobItemProcessor : public CJobItemProcessor +{ +public: + void Init( OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( T*, int, int ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL ) + { + m_pObject = pObject; + m_pfnProcess = pfnProcess; + m_pfnBegin = pfnBegin; + m_pfnEnd = pfnEnd; + } + + void Begin() { if ( m_pfnBegin ) ((*m_pObject).*m_pfnBegin)(); } + void Process( T *item, int nFirst, int nCount ) { ((*m_pObject).*m_pfnProcess)( item, nFirst, nCount ); } + void End() { if ( m_pfnEnd ) ((*m_pObject).*m_pfnEnd)(); } + +protected: + OBJECT_TYPE *m_pObject; + + void (FUNCTION_CLASS::*m_pfnProcess)( T*, int, int ); + void (FUNCTION_CLASS::*m_pfnBegin)(); + void (FUNCTION_CLASS::*m_pfnEnd)(); +}; + + +#pragma warning(push) +#pragma warning(disable:4189) + +template +class CParallelProcessor +{ +public: + CParallelProcessor() + { + m_pItems = m_pLimit= 0; + } + + void Run( ITEM_TYPE *pItems, unsigned nItems, int nChunkSize = 1, int nMaxParallel = INT_MAX, IThreadPool *pThreadPool = NULL ) + { + if ( nItems == 0 ) + return; + +#if defined(_X360) + volatile int ignored = ID_TO_PREVENT_COMDATS_IN_PROFILES; +#endif + + m_nChunkSize = nChunkSize; + if ( !pThreadPool ) + { + pThreadPool = g_pThreadPool; + } + + m_pItems = pItems; + m_pLimit = pItems + nItems; + + int nJobs = nItems - 1; + + if ( nJobs > nMaxParallel ) + { + nJobs = nMaxParallel; + } + + if (! pThreadPool ) // only possible on linux + { + DoExecute( ); + return; + } + + int nThreads = pThreadPool->NumThreads(); + if ( nJobs > nThreads ) + { + nJobs = nThreads; + } + + if ( nJobs > 0 ) + { + CJob **jobs = (CJob **)stackalloc( nJobs * sizeof(CJob **) ); + int i = nJobs; + + while( i-- ) + { + jobs[i] = pThreadPool->QueueCall( this, &CParallelProcessor::DoExecute ); + } + + DoExecute(); + + for ( i = 0; i < nJobs; i++ ) + { + jobs[i]->Abort(); // will either abort ones that never got a thread, or noop on ones that did + jobs[i]->Release(); + } + } + else + { + DoExecute(); + } + } + + ITEM_PROCESSOR_TYPE m_ItemProcessor; + +private: + void DoExecute() + { + if ( m_pItems < m_pLimit ) + { +#if defined(_X360) + volatile int ignored = ID_TO_PREVENT_COMDATS_IN_PROFILES; +#endif + m_ItemProcessor.Begin(); + + ITEM_TYPE *pLimit = m_pLimit; + + int nChunkSize = m_nChunkSize; + for (;;) + { + ITEM_TYPE *pCurrent = m_pItems.AtomicAdd( nChunkSize ); + ITEM_TYPE *pLast = MIN( pLimit, pCurrent + nChunkSize ); + while( pCurrent < pLast ) + { + m_ItemProcessor.Process( *pCurrent ); + pCurrent++; + } + if ( pCurrent >= pLimit ) + { + break; + } + } + m_ItemProcessor.End(); + } + } + CInterlockedPtr m_pItems; + ITEM_TYPE * m_pLimit; + int m_nChunkSize; + +}; + +#pragma warning(pop) + +template +inline void ParallelProcess( ITEM_TYPE *pItems, unsigned nItems, void (*pfnProcess)( ITEM_TYPE & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pItems, nItems, 1, nMaxParallel ); +} + +template +inline void ParallelProcess( ITEM_TYPE *pItems, unsigned nItems, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pItems, nItems, 1, nMaxParallel ); +} + +// Parallel Process that lets you specify threadpool +template +inline void ParallelProcess( IThreadPool *pPool, ITEM_TYPE *pItems, unsigned nItems, void (*pfnProcess)( ITEM_TYPE & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pItems, nItems, 1, nMaxParallel, pPool ); +} + +template +inline void ParallelProcess( IThreadPool *pPool, ITEM_TYPE *pItems, unsigned nItems, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pItems, nItems, 1, nMaxParallel, pPool ); +} + +// ParallelProcessChunks lets you specify a minimum # of items to process per job. Use this when +// you may have a large set of work items which only take a small amount of time per item, and so +// need to reduce dispatch overhead. +template +inline void ParallelProcessChunks( ITEM_TYPE *pItems, unsigned nItems, void (*pfnProcess)( ITEM_TYPE & ), int nChunkSize, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pfnProcess, NULL, NULL ); + processor.Run( pItems, nItems, nChunkSize, nMaxParallel ); +} + +template +inline void ParallelProcessChunks( ITEM_TYPE *pItems, unsigned nItems, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), int nChunkSize, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pObject, pfnProcess, NULL, NULL ); + processor.Run( pItems, nItems, nChunkSize, nMaxParallel ); +} + +template +inline void ParallelProcessChunks( IThreadPool *pPool, ITEM_TYPE *pItems, unsigned nItems, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), int nChunkSize, int nMaxParallel = INT_MAX ) +{ + CParallelProcessor > processor; + processor.m_ItemProcessor.Init( pObject, pfnProcess, NULL, NULL ); + processor.Run( pItems, nItems, nChunkSize, nMaxParallel, pPool ); +} + + +template +class CParallelLoopProcessor +{ +public: + CParallelLoopProcessor() + { + m_nIndex = m_nLimit = 0; + m_nChunkCount = 0; + m_nActive = 0; + } + + void Run( CONTEXT_TYPE *pContext, int nBegin, int nItems, int nChunkCount, int nMaxParallel = INT_MAX, IThreadPool *pThreadPool = NULL ) + { + if ( !nItems ) + return; + + if ( !pThreadPool ) + { + pThreadPool = g_pThreadPool; + } + + m_pContext = pContext; + m_nIndex = nBegin; + m_nLimit = nBegin + nItems; + nChunkCount = MAX( MIN( nItems, nChunkCount ), 1 ); + m_nChunkCount = ( nItems + nChunkCount - 1 ) / nChunkCount; + int nJobs = ( nItems + m_nChunkCount - 1 ) / m_nChunkCount; + if ( nJobs > nMaxParallel ) + { + nJobs = nMaxParallel; + } + + if ( !pThreadPool ) // only possible on linux + { + DoExecute( ); + return; + } + + int nThreads = pThreadPool->NumThreads(); + if ( nJobs > nThreads ) + { + nJobs = nThreads; + } + + if ( nJobs > 0 ) + { + CJob **jobs = (CJob **)stackalloc( nJobs * sizeof(CJob **) ); + int i = nJobs; + + while( i-- ) + { + jobs[i] = pThreadPool->QueueCall( this, &CParallelLoopProcessor::DoExecute ); + } + + DoExecute(); + + for ( i = 0; i < nJobs; i++ ) + { + jobs[i]->Abort(); // will either abort ones that never got a thread, or noop on ones that did + jobs[i]->Release(); + } + } + else + { + DoExecute(); + } + } + + ITEM_PROCESSOR_TYPE m_ItemProcessor; + +private: + void DoExecute() + { + m_ItemProcessor.Begin(); + for (;;) + { + int nIndex = m_nIndex.AtomicAdd( m_nChunkCount ); + if ( nIndex < m_nLimit ) + { + int nCount = MIN( m_nChunkCount, m_nLimit - nIndex ); + m_ItemProcessor.Process( m_pContext, nIndex, nCount ); + } + else + { + break; + } + } + m_ItemProcessor.End(); + --m_nActive; + } + + CONTEXT_TYPE *m_pContext; + CInterlockedInt m_nIndex; + int m_nLimit; + int m_nChunkCount; + CInterlockedInt m_nActive; +}; + +template < typename CONTEXT_TYPE > +inline void ParallelLoopProcess( IThreadPool *pPool, CONTEXT_TYPE *pContext, int nStart, int nCount, void (*pfnProcess)( CONTEXT_TYPE*, int, int ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelLoopProcessor< CONTEXT_TYPE, CLoopFuncJobItemProcessor< CONTEXT_TYPE > > processor; + processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pContext, nStart, nCount, 1, nMaxParallel, pPool ); +} + +template < typename CONTEXT_TYPE, typename OBJECT_TYPE, typename FUNCTION_CLASS > +inline void ParallelLoopProcess( IThreadPool *pPool, CONTEXT_TYPE *pContext, int nStart, int nCount, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( CONTEXT_TYPE*, int, int ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelLoopProcessor< CONTEXT_TYPE, CLoopMemberFuncJobItemProcessor > processor; + processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pContext, nStart, nCount, 1, nMaxParallel, pPool ); +} + +template < typename CONTEXT_TYPE > +inline void ParallelLoopProcessChunks( IThreadPool *pPool, CONTEXT_TYPE *pContext, int nStart, int nCount, int nChunkSize, void (*pfnProcess)( CONTEXT_TYPE*, int, int ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelLoopProcessor< CONTEXT_TYPE, CLoopFuncJobItemProcessor< CONTEXT_TYPE > > processor; + processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pContext, nStart, nCount, nChunkSize, nMaxParallel, pPool ); +} + +template < typename CONTEXT_TYPE, typename OBJECT_TYPE, typename FUNCTION_CLASS > +inline void ParallelLoopProcessChunks( IThreadPool *pPool, CONTEXT_TYPE *pContext, int nStart, int nCount, int nChunkSize, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( CONTEXT_TYPE*, int, int ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL, int nMaxParallel = INT_MAX ) +{ + CParallelLoopProcessor< CONTEXT_TYPE, CLoopMemberFuncJobItemProcessor > processor; + processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd ); + processor.Run( pContext, nStart, nCount, nChunkSize, nMaxParallel, pPool ); +} + +template +class CParallelProcessorBase +{ +protected: + typedef CParallelProcessorBase ThisParallelProcessorBase_t; + typedef Derived ThisParallelProcessorDerived_t; + +public: + CParallelProcessorBase() + { + m_nActive = 0; + } + +protected: + void Run( int nMaxParallel = INT_MAX, int threadOverride = -1 ) + { + int i = g_pThreadPool->NumIdleThreads(); + + if ( nMaxParallel < i) + { + i = nMaxParallel; + } + + while( i -- > 0 ) + { + if ( threadOverride == -1 || i == threadOverride - 1 ) + { + ++ m_nActive; + ThreadExecute( this, &ThisParallelProcessorBase_t::DoExecute )->Release(); + } + } + + if ( threadOverride == -1 || threadOverride == 0 ) + { + ++ m_nActive; + DoExecute(); + } + + while ( m_nActive ) + { + ThreadPause(); + } + } + +protected: + void OnBegin() {} + bool OnProcess() { return false; } + void OnEnd() {} + +private: + void DoExecute() + { + static_cast( this )->OnBegin(); + + while ( static_cast( this )->OnProcess() ) + continue; + + static_cast(this)->OnEnd(); + + -- m_nActive; + } + + CInterlockedInt m_nActive; +}; + + + + +//----------------------------------------------------------------------------- +// Raw thread launching +//----------------------------------------------------------------------------- + +inline uintp FunctorExecuteThread( void *pParam ) +{ + CFunctor *pFunctor = (CFunctor *)pParam; + (*pFunctor)(); + pFunctor->Release(); + return 0; +} + +inline ThreadHandle_t ThreadExecuteSoloImpl( CFunctor *pFunctor, const char *pszName = NULL ) +{ + ThreadHandle_t hThread; + hThread = CreateSimpleThread( FunctorExecuteThread, pFunctor ); + if ( pszName ) + { + ThreadSetDebugName( hThread, pszName ); + } + return hThread; +} + +inline ThreadHandle_t ThreadExecuteSolo( CJob *pJob ) { return ThreadExecuteSoloImpl( CreateFunctor( pJob, &CJob::Execute ), pJob->Describe() ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4, a5 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4, a5, a6 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4, a5, a6, a7 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4, a5, a6, a7, a8 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4, a5 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4, a5, a6 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4, a5, a6, a7 ), pszName ); } + +template +inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4, a5, a6, a7, a8 ), pszName ); } + +//----------------------------------------------------------------------------- + +inline bool IThreadPool::YieldWait( CThreadEvent &theEvent, unsigned timeout ) +{ + CThreadEvent *pEvent = &theEvent; + return ( YieldWait( &pEvent, 1, true, timeout ) != TW_TIMEOUT ); +} + +inline bool IThreadPool::YieldWait( CJob *pJob, unsigned timeout ) +{ + return ( YieldWait( &pJob, 1, true, timeout ) != TW_TIMEOUT ); +} + +//----------------------------------------------------------------------------- + +inline JobStatus_t CJob::Execute() +{ + if ( IsFinished() ) + { + return m_status; + } + + AUTO_LOCK( m_mutex ); + AddRef(); + + JobStatus_t result; + + switch ( m_status ) + { + case JOB_STATUS_UNSERVICED: + case JOB_STATUS_PENDING: + { + // Service it + m_status = JOB_STATUS_INPROGRESS; + result = m_status = DoExecute(); + DoCleanup(); + m_CompleteEvent.Set(); + break; + } + + case JOB_STATUS_INPROGRESS: + AssertMsg(0, "Mutex Should have protected use while processing"); + // fall through... + + case JOB_OK: + case JOB_STATUS_ABORTED: + result = m_status; + break; + + default: + AssertMsg( m_status < JOB_OK, "Unknown job state"); + result = m_status; + } + + Release(); + + return result; +} + + +//--------------------------------------------------------- + +inline JobStatus_t CJob::TryExecute() +{ + // TryLock() would only fail if another thread has entered + // Execute() or Abort() + if ( !IsFinished() && TryLock() ) + { + // ...service the request + Execute(); + Unlock(); + } + return m_status; +} + +//--------------------------------------------------------- + +inline JobStatus_t CJob::Abort( bool bDiscard ) +{ + if ( IsFinished() ) + { + return m_status; + } + + AUTO_LOCK( m_mutex ); + AddRef(); + + JobStatus_t result; + + switch ( m_status ) + { + case JOB_STATUS_UNSERVICED: + case JOB_STATUS_PENDING: + { + result = m_status = DoAbort( bDiscard ); + if ( bDiscard ) + DoCleanup(); + m_CompleteEvent.Set(); + } + break; + + case JOB_STATUS_ABORTED: + case JOB_STATUS_INPROGRESS: + case JOB_OK: + result = m_status; + break; + + default: + AssertMsg( m_status < JOB_OK, "Unknown job state"); + result = m_status; + } + + Release(); + + return result; +} + +//----------------------------------------------------------------------------- + +#endif // JOBTHREAD_H diff --git a/public/vstdlib/random.h b/public/tier1/random.h similarity index 78% rename from public/vstdlib/random.h rename to public/tier1/random.h index 6e85a828..fb3b8b8e 100644 --- a/public/vstdlib/random.h +++ b/public/tier1/random.h @@ -1,113 +1,113 @@ -//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// -// -// Purpose: Random number generator -// -// $Workfile: $ -// $NoKeywords: $ -//===========================================================================// - -#ifndef VSTDLIB_RANDOM_H -#define VSTDLIB_RANDOM_H - -#include "vstdlib/vstdlib.h" -#include "tier0/basetypes.h" -#include "tier0/threadtools.h" - -#define NTAB 32 - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning( disable:4251 ) -#endif - -//----------------------------------------------------------------------------- -// A generator of uniformly distributed random numbers -//----------------------------------------------------------------------------- -class IUniformRandomStream -{ -public: - // Sets the seed of the random number generator - virtual void SetSeed( int iSeed ) = 0; - - // Generates random numbers - virtual float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f ) = 0; - virtual int RandomInt( int iMinVal, int iMaxVal ) = 0; - virtual float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f ) = 0; -}; - - -//----------------------------------------------------------------------------- -// The standard generator of uniformly distributed random numbers -//----------------------------------------------------------------------------- -class VSTDLIB_CLASS CUniformRandomStream : public IUniformRandomStream -{ -public: - CUniformRandomStream(); - - // Sets the seed of the random number generator - virtual void SetSeed( int iSeed ); - - // Generates random numbers - virtual float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f ); - virtual int RandomInt( int iMinVal, int iMaxVal ); - virtual float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f ); - -private: - int GenerateRandomNumber(); - - int m_idum; - int m_iy; - int m_iv[NTAB]; - - CThreadFastMutex m_mutex; -}; - - -//----------------------------------------------------------------------------- -// A generator of gaussian distributed random numbers -//----------------------------------------------------------------------------- -class VSTDLIB_CLASS CGaussianRandomStream -{ -public: - // Passing in NULL will cause the gaussian stream to use the - // installed global random number generator - CGaussianRandomStream( IUniformRandomStream *pUniformStream = NULL ); - - // Attaches to a random uniform stream - void AttachToStream( IUniformRandomStream *pUniformStream = NULL ); - - // Generates random numbers - float RandomFloat( float flMean = 0.0f, float flStdDev = 1.0f ); - -private: - IUniformRandomStream *m_pUniformStream; - bool m_bHaveValue; - float m_flRandomValue; - - CThreadFastMutex m_mutex; -}; - - -//----------------------------------------------------------------------------- -// A couple of convenience functions to access the library's global uniform stream -//----------------------------------------------------------------------------- -VSTDLIB_INTERFACE void RandomSeed( int iSeed ); -VSTDLIB_INTERFACE float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f ); -VSTDLIB_INTERFACE float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f ); -VSTDLIB_INTERFACE int RandomInt( int iMinVal, int iMaxVal ); -VSTDLIB_INTERFACE float RandomGaussianFloat( float flMean = 0.0f, float flStdDev = 1.0f ); - - -//----------------------------------------------------------------------------- -// Installs a global random number generator, which will affect the Random functions above -//----------------------------------------------------------------------------- -VSTDLIB_INTERFACE void InstallUniformRandomStream( IUniformRandomStream *pStream ); - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#endif // VSTDLIB_RANDOM_H - - - +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: Random number generator +// +// $Workfile: $ +// $NoKeywords: $ +//===========================================================================// + +#ifndef VSTDLIB_RANDOM_H +#define VSTDLIB_RANDOM_H + +#include "platform.h" +#include "tier0/basetypes.h" +#include "tier0/threadtools.h" + +#define NTAB 32 + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning( disable:4251 ) +#endif + +//----------------------------------------------------------------------------- +// A generator of uniformly distributed random numbers +//----------------------------------------------------------------------------- +class IUniformRandomStream +{ +public: + // Sets the seed of the random number generator + virtual void SetSeed( int iSeed ) = 0; + + // Generates random numbers + virtual float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f ) = 0; + virtual int RandomInt( int iMinVal, int iMaxVal ) = 0; + virtual float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f ) = 0; +}; + + +//----------------------------------------------------------------------------- +// The standard generator of uniformly distributed random numbers +//----------------------------------------------------------------------------- +class DLL_CLASS_IMPORT CUniformRandomStream : public IUniformRandomStream +{ +public: + CUniformRandomStream(); + + // Sets the seed of the random number generator + virtual void SetSeed( int iSeed ); + + // Generates random numbers + virtual float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f ); + virtual int RandomInt( int iMinVal, int iMaxVal ); + virtual float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f ); + +private: + int GenerateRandomNumber(); + + int m_idum; + int m_iy; + int m_iv[NTAB]; + + CThreadFastMutex m_mutex; +}; + + +//----------------------------------------------------------------------------- +// A generator of gaussian distributed random numbers +//----------------------------------------------------------------------------- +class DLL_CLASS_IMPORT CGaussianRandomStream +{ +public: + // Passing in NULL will cause the gaussian stream to use the + // installed global random number generator + CGaussianRandomStream( IUniformRandomStream *pUniformStream = NULL ); + + // Attaches to a random uniform stream + void AttachToStream( IUniformRandomStream *pUniformStream = NULL ); + + // Generates random numbers + float RandomFloat( float flMean = 0.0f, float flStdDev = 1.0f ); + +private: + IUniformRandomStream *m_pUniformStream; + bool m_bHaveValue; + float m_flRandomValue; + + CThreadFastMutex m_mutex; +}; + + +//----------------------------------------------------------------------------- +// A couple of convenience functions to access the library's global uniform stream +//----------------------------------------------------------------------------- +DLL_IMPORT void RandomSeed( int iSeed ); +DLL_IMPORT float RandomFloat( float flMinVal = 0.0f, float flMaxVal = 1.0f ); +DLL_IMPORT float RandomFloatExp( float flMinVal = 0.0f, float flMaxVal = 1.0f, float flExponent = 1.0f ); +DLL_IMPORT int RandomInt( int iMinVal, int iMaxVal ); +DLL_IMPORT float RandomGaussianFloat( float flMean = 0.0f, float flStdDev = 1.0f ); + + +//----------------------------------------------------------------------------- +// Installs a global random number generator, which will affect the Random functions above +//----------------------------------------------------------------------------- +DLL_IMPORT void InstallUniformRandomStream( IUniformRandomStream *pStream ); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // VSTDLIB_RANDOM_H + + + diff --git a/public/tier1/utlbuffer.h b/public/tier1/utlbuffer.h index dd43c28f..5865d57f 100644 --- a/public/tier1/utlbuffer.h +++ b/public/tier1/utlbuffer.h @@ -16,6 +16,7 @@ #include "unitlib/unitlib.h" // just here for tests - remove before checking in!!! +#include "platform.h" #include "tier1/utlmemory.h" #include "tier1/cbyteswap.h" #include "tier1/bufferstring.h" @@ -36,7 +37,7 @@ struct characterset_t; // { '\t', "t" } // END_CHAR_CONVERSION( CStringConversion, '\\' ) //----------------------------------------------------------------------------- -class CUtlCharConversion +class DLL_CLASS_IMPORT CUtlCharConversion { public: struct ConversionArray_t @@ -45,7 +46,14 @@ public: const char *m_pReplacementString; }; - CUtlCharConversion( char nEscapeChar, const char *pDelimiter, int nCount, ConversionArray_t *pArray ); + CUtlCharConversion( char nEscapeChar, const char *pDelimiter, int nCount, const ConversionArray_t *pArray ); + + CUtlCharConversion( CUtlCharConversion &&rhs ); + CUtlCharConversion( const CUtlCharConversion &rhs ); + + CUtlCharConversion &operator=( CUtlCharConversion &&rhs ); + CUtlCharConversion &operator=( const CUtlCharConversion &rhs ); + char GetEscapeChar() const; const char *GetDelimiter() const; int GetDelimiterLength() const; @@ -90,12 +98,12 @@ protected: //----------------------------------------------------------------------------- // Character conversions for C strings //----------------------------------------------------------------------------- -CUtlCharConversion *GetCStringCharConversion(); +PLATFORM_INTERFACE CUtlCharConversion *GetCStringCharConversion(); //----------------------------------------------------------------------------- -// Character conversions for quoted strings, with no escape sequences +// Character conversions for JSON strings //----------------------------------------------------------------------------- -CUtlCharConversion *GetNoEscCharConversion(); +PLATFORM_INTERFACE CUtlCharConversion *GetJSONCharConversion(); //----------------------------------------------------------------------------- @@ -106,13 +114,11 @@ CUtlCharConversion *GetNoEscCharConversion(); -typedef unsigned short ushort; - template < class A > static const char *GetFmtStr( int nRadix = 10, bool bPrint = true ) { Assert( 0 ); return ""; } template <> inline const char *GetFmtStr< short > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hd"; } -template <> inline const char *GetFmtStr< ushort > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hu"; } +template <> inline const char *GetFmtStr< uint16 > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hu"; } template <> inline const char *GetFmtStr< int > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%d"; } template <> inline const char *GetFmtStr< uint > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 || nRadix == 16 ); return nRadix == 16 ? "%x" : "%u"; } template <> inline const char *GetFmtStr< int64 > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%lld"; } diff --git a/public/vstdlib/cvar.h b/public/vstdlib/cvar.h deleted file mode 100644 index 92de286c..00000000 --- a/public/vstdlib/cvar.h +++ /dev/null @@ -1,25 +0,0 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - -#if !defined( CVAR_H ) -#define CVAR_H -#ifdef _WIN32 -#pragma once -#endif - -#include "vstdlib/vstdlib.h" -#include "icvar.h" - - -//----------------------------------------------------------------------------- -// Returns a CVar dictionary for tool usage -//----------------------------------------------------------------------------- -VSTDLIB_INTERFACE CreateInterfaceFn VStdLib_GetICVarFactory(); - - -#endif // CVAR_H diff --git a/public/vstdlib/ieventsystem.h b/public/vstdlib/ieventsystem.h deleted file mode 100644 index 324964ec..00000000 --- a/public/vstdlib/ieventsystem.h +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include - -class IEventQueue; - -abstract_class IEventSystem : public IAppSystem -{ -public: - virtual IEventQueue *CreateEventQueue() = 0; - virtual void DestroyEventQueue( IEventQueue *pQueue ) = 0; - virtual void RunEvents( IEventQueue *pQueue ) = 0; - virtual int RegisterEvent( const char *pszName ) = 0; - virtual void FireEvent( int eventId, IEventQueue *pQueue, const void *pListener, CFunctorData *pData ) = 0; - virtual void RegisterListener( int eventId, IEventQueue *pQueue, CFunctorCallback *pCallback ) = 0; - virtual void UnregisterListener( int eventId, IEventQueue *pQueue, CFunctorCallback *pCallback ) = 0; -}; \ No newline at end of file diff --git a/public/vstdlib/pch_vstdlib.h b/public/vstdlib/pch_vstdlib.h deleted file mode 100644 index aace1740..00000000 --- a/public/vstdlib/pch_vstdlib.h +++ /dev/null @@ -1,51 +0,0 @@ -//======== (C) Copyright 1999, 2000 Valve, L.L.C. All rights reserved. ======== -// -// The copyright to the contents herein is the property of Valve, L.L.C. -// The contents may be used and/or copied only with the written permission of -// Valve, L.L.C., or in accordance with the terms and conditions stipulated in -// the agreement/contract under which the contents have been supplied. -// -// Purpose: -// -// $Workfile: $ -// $NoKeywords: $ -//============================================================================= - - -#pragma warning(disable: 4514) - -// First include standard libraries -#include -#include -#include -#include -#include -#include - -// Next, include public -#include "tier0/basetypes.h" -#include "tier0/dbg.h" -#include "tier0/valobject.h" - -// Next, include vstdlib -#include "vstdlib/vstdlib.h" -#include "tier1/strtools.h" -#include "vstdlib/random.h" -#include "tier1/keyvalues.h" -#include "tier1/utlmemory.h" -#include "tier1/utlrbtree.h" -#include "tier1/utlvector.h" -#include "tier1/utllinkedlist.h" -#include "tier1/utlmultilist.h" -#include "tier1/utlsymbol.h" -#include "tier0/icommandline.h" -#include "tier1/netadr.h" -#include "tier1/mempool.h" -#include "tier1/utlbuffer.h" -#include "tier1/utlstring.h" -#include "tier1/utlmap.h" - -#include "tier0/memdbgon.h" - - - diff --git a/public/vstdlib/vcover.h b/public/vstdlib/vcover.h deleted file mode 100644 index c8d7d31e..00000000 --- a/public/vstdlib/vcover.h +++ /dev/null @@ -1,125 +0,0 @@ -//========== Copyright © 2005, Valve Corporation, All rights reserved. ======== -// -// Purpose: A simple tool for coverage tests -// -//============================================================================= - -#ifndef VCOVER_H -#define VCOVER_H - -#include "tier1/utlrbtree.h" -#include "vstdlib.h" - -#if defined( _WIN32 ) -#pragma once -#endif - -class CVCoverage -{ -public: - CVCoverage() : - m_bActive( false ), - m_depth( 0 ), - m_token( 1 ) - { - } - - bool IsActive() const - { - return m_bActive; - } - - void SetActive( bool bActive ) - { - Assert( bActive != m_bActive ); - m_bActive = bActive; - if ( bActive ) - ++m_token; - } - - void Begin() - { - ++m_depth; - } - - void End() - { - --m_depth; - } - - void Reset() - { - m_locations.RemoveAll(); - } - - bool ShouldCover( unsigned token ) const - { - return ( m_bActive && m_depth > 0 && token != m_token ); - } - - unsigned Cover( const char *pszFile, int line ) - { - Location_t location = { pszFile, line }; - - m_locations.Insert( location ); - - return m_token; - } - - void Report() - { - for ( int i = m_locations.FirstInorder(); i != m_locations.InvalidIndex(); i = m_locations.NextInorder( i ) ) - { - Msg( "%s(%d) :\n", m_locations[i].pszFile, m_locations[i].line ); - } - } - -private: - struct Location_t - { - const char *pszFile; - int line; - - }; - - class CLocationLess - { - public: - CLocationLess( int ignored ) {} - bool operator!() { return false; } - - bool operator()( const Location_t &lhs, const Location_t &rhs ) const - { - if ( lhs.line < rhs.line ) - { - return true; - } - - return CaselessStringLessThan( lhs.pszFile, rhs.pszFile ); - } - }; - - bool m_bActive; - int m_depth; - unsigned m_token; - - CUtlRBTree< Location_t, unsigned short, CLocationLess > m_locations; -}; - -VSTDLIB_INTERFACE CVCoverage g_VCoverage; - -#ifdef VCOVER_ENABLED -#define VCOVER() \ - do \ - { \ - static token; \ - if ( g_VCoverage.ShouldCover( token ) ) \ - { \ - token = g_VCoverage.Cover( __FILE__, __LINE__ ); \ - } \ - } while( 0 ) -#else -#define VCOVER() ((void)0) -#endif - -#endif // VCOVER_H diff --git a/public/vstdlib/vstdlib.h b/public/vstdlib/vstdlib.h deleted file mode 100644 index f3998e78..00000000 --- a/public/vstdlib/vstdlib.h +++ /dev/null @@ -1,33 +0,0 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - -#ifndef VSTDLIB_H -#define VSTDLIB_H - -#ifdef _WIN32 -#pragma once -#endif - -#include "tier0/platform.h" - -//----------------------------------------------------------------------------- -// dll export stuff -//----------------------------------------------------------------------------- -#ifdef VSTDLIB_DLL_EXPORT -#define VSTDLIB_INTERFACE DLL_EXPORT -#define VSTDLIB_OVERLOAD DLL_GLOBAL_EXPORT -#define VSTDLIB_CLASS DLL_CLASS_EXPORT -#define VSTDLIB_GLOBAL DLL_GLOBAL_EXPORT -#else -#define VSTDLIB_INTERFACE DLL_IMPORT -#define VSTDLIB_OVERLOAD DLL_GLOBAL_IMPORT -#define VSTDLIB_CLASS DLL_CLASS_IMPORT -#define VSTDLIB_GLOBAL DLL_GLOBAL_IMPORT -#endif - -#endif // VSTDLIB_H