Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

460 lines
18 KiB

//========= Copyright 1996-2004, Valve LLC, All rights reserved. ============
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#ifndef GC_JOB_H
#define GC_JOB_H
#ifdef _WIN32
#pragma once
#endif
#include "tier0/memdbgon.h"
#include "tier1/functors.h"
#include "workthreadpool.h"
class GCConVar;
namespace GCSDK
{
class CJobMgr;
class CLock;
class CJob;
class IMsgNetPacket;
// job creation function responsible for allocating the job of the specific type
typedef CJob *(*JobCreationFunc_t)( void *pvServerParent, void * pvStartParam );
// job routing function, which allows for controlling message routing through the system. The GCs that should have the message sent to it should be added to the vector. It is fine to add the actual self GC
typedef void (*JobRoutingFunc_t)( CUtlVector< uint32 >& vecGCsToSendTo, IMsgNetPacket *pNetPacket );
//-----------------------------------------------------------------------------
// Purpose: static job information
// contains information relevant to one type of CJob
//-----------------------------------------------------------------------------
struct JobType_t
{
const char *m_pchName; // name of this type of job
MsgType_t m_eCreationMsg; // network message that creates this job
uint32 m_nValidContexts; // a bit field indicating which contexts this message can be called from (i.e. from a server, or from a client, etc)
JobCreationFunc_t m_pJobFactory; // virtual constructor
JobRoutingFunc_t m_pJobRouter; // routing function for this job
const GCConVar* m_pControlCV; // optional console variable that can be used to disable this message
};
//-----------------------------------------------------------------------------
// Purpose: reason as to why the current job has yielded to the main thread (paused)
// if this is updated, k_prgchJobPauseReason[] in job.cpp also needs to be updated
//-----------------------------------------------------------------------------
enum EJobPauseReason
{
k_EJobPauseReasonNone,
k_EJobPauseReasonNotStarted,
k_EJobPauseReasonNetworkMsg,
k_EJobPauseReasonSleepForTime,
k_EJobPauseReasonWaitingForLock,
k_EJobPauseReasonYield,
k_EJobPauseReasonSQL,
k_EJobPauseReasonWorkItem,
k_EJobPauseReasonCount
};
//-----------------------------------------------------------------------------
// Purpose: contains information used to route a message to a job, or to
// create a new job from that message type
//-----------------------------------------------------------------------------
struct JobMsgInfo_t
{
MsgType_t m_eMsg;
JobID_t m_JobIDSource;
JobID_t m_JobIDTarget;
JobMsgInfo_t()
{
m_eMsg = (MsgType_t)0;
m_JobIDSource = k_GIDNil;
m_JobIDTarget = k_GIDNil;
}
JobMsgInfo_t( MsgType_t eMsg, JobID_t jobIDSource, JobID_t jobIDTarget )
{
m_eMsg = eMsg;
m_JobIDSource = jobIDSource;
m_JobIDTarget = jobIDTarget;
}
};
typedef void (CJob::*JobThreadFunc_t)();
#define BYieldingAcquireLock( lock ) _BYieldingAcquireLock( lock, __FILE__, __LINE__ )
#define BAcquireLockImmediate( lock ) _BAcquireLockImmediate( lock, __FILE__, __LINE__ )
#define ReleaseLock( lock ) _ReleaseLock( lock, false, __FILE__, __LINE__ )
//-----------------------------------------------------------------------------
// Purpose: A job is any server operation that requires state. Typically, we use jobs for
// operations that need to pause waiting for responses from other servers. The
// job object persists the state of the operation while it waits, and the reply
// from the remote server re-activates the job.
//-----------------------------------------------------------------------------
class CJob
{
public:
// Constructors & destructors, when overriding job name a static string pointer must be used
explicit CJob( CJobMgr &jobMgr, char const *pchJobName = NULL );
virtual ~CJob();
// starts the job, storing off the network msg and calling it's Run() function
void StartJobFromNetworkMsg( IMsgNetPacket *pNetPacket, const JobID_t &gidJobIDSrc, uint32 nContextMask );
// accessors
JobID_t GetJobID() const { return m_JobID; }
// called to start a job. The default behavior of starting a job is to start it scheduled (i.e. delayed). This is largely deprecated, and the more explicit versions
// below should be used instead
void StartJob( void * pvStartParam );
// schedules the job for execution, but does not interrupt the currently running job. Effectively starts the job on the yielding list as if it had immediately yielded
// although is more efficient than actually doing so
void StartJobDelayed( void * pvStartParam );
// starts a job immediately, interrupting the current job if one is already running. This should only be used in special cases, and in a lot of ways should be considered
// yielding in that another job can run and perform modifications, although the caller will receive attention back the first time that the inner job itself yields
void StartJobImmediate( void * pvStartParam );
// string name of the job
const char *GetName() const;
// return reason why we're paused
EJobPauseReason GetPauseReason() const { return m_ePauseReason; }
// string description of why we're paused
const char *GetPauseReasonDescription() const;
// return time at which this job was last paused or continued
const CJobTime& GetTimeSwitched() const { return m_STimeSwitched; }
// return microseconds run since we were last continued
uint64 GetMicrosecondsRun() const { return m_FastTimerDelta.GetDurationInProgress().GetUlMicroseconds(); }
bool BJobNeedsToHeartbeat() const { return ( m_STimeNextHeartbeat.CServerMicroSecsPassed() >= 0 ); }
// --- locking pointers
bool _BYieldingAcquireLock( CLock *pLock, const char *filename = "unknown", int line = 0 );
bool _BAcquireLockImmediate( CLock *pLock, const char *filename = "unknown", int line = 0 );
void _ReleaseLock( CLock *pLock, bool bForce = false, const char *filename = "unknown", int line = 0 );
void ReleaseLocks();
bool BJobHoldsLock( uint16 nType, uint64 nSubType ) const;
bool BJobHoldsLock( const CLock* pLock ) const;
/// If we hold any locks, spew about them and release them.
/// This is useful for long running jobs, to make sure they don't leak
/// locks that never get cleaned up
void ShouldNotHoldAnyLocks();
// --- general methods for waiting for events
// Simple yield to other jobs until Run() called again
bool BYield();
// Yield IF JobMgr thinks we need to based on how long we've run and our priority
bool BYieldIfNeeded( bool *pbYielded = NULL );
// waits for a set amount of time
bool BYieldingWaitTime( uint32 m_cMicrosecondsToSleep );
bool BYieldingWaitOneFrame();
// waits for another network msg, returning false if none returns
bool BYieldingWaitForMsg( IMsgNetPacket **ppNetPacket );
bool BYieldingWaitForMsg( CGCMsgBase *pMsg, MsgType_t eMsg );
bool BYieldingWaitForMsg( CProtoBufMsgBase *pMsg, MsgType_t eMsg );
// waits for another job(s) to complete
bool BYieldingWaitForJob( JobID_t jobToWaitFor );
bool BYieldingWaitForJobs( const CUtlVector<JobID_t> &vecJobsToWaitFor );
#ifdef GC
void SOVALIDATE_SetSteamID( const CSteamID steamID ) { m_SOCacheValidSteamID = steamID; }
CSteamID SOVALIDATE_GetSteamID() const { return m_SOCacheValidSteamID; }
void VALIDATE_SetJobAccessType( uint32 nAccess) { m_nGCJobAccessType = nAccess; }
uint32 VALIDATE_GetJobAccessType() const { return m_nGCJobAccessType; }
bool BYieldingWaitForMsg( CGCMsgBase *pMsg, MsgType_t eMsg, const CSteamID &expectedID );
bool BYieldingWaitForMsg( CProtoBufMsgBase *pMsg, MsgType_t eMsg, const CSteamID &expectedID );
bool BYieldingRunQuery( CGCSQLQueryGroup *pQueryGroup, ESchemaCatalog eSchemaCatalog );
#endif
void RecordWaitTimeout() { m_flags.m_bits.m_bWaitTimeout = true; }
// wait for pending work items before deleting job
void WaitForThreadFuncWorkItemBlocking();
// waits for a work item completion callback
// You can pass a string that describes what sort of work item you are waiting on.
// WARNING: This function saves the pointer to the string, it doesn't copy the string
bool BYieldingWaitForWorkItem( const char *pszWorkItemName = NULL );
// adds this work item to threaded work pool and waits for it
bool BYieldingWaitForThreadFuncWorkItem( CWorkItem * );
// calls a local function in a thread, and yields until it's done
bool BYieldingWaitForThreadFunc( CFunctor *jobFunctor );
// creates a job
template <typename JOB_TYPE, typename PARAM_TYPE>
static JOB_TYPE *AllocateJob( PARAM_TYPE *pParam )
{
return new JOB_TYPE( pParam );
}
// delete a job (the job knows what allocator to use)
static void DeleteJob( CJob *pJob );
void SetStartParam( void * pvStartParam ) { Assert( NULL == m_pvStartParam ); m_pvStartParam = pvStartParam; }
void SetFromFromMsg( bool bRunFromMsg ) { m_bRunFromMsg = true; }
void AddPacketToList( IMsgNetPacket *pNetPacket, const JobID_t gidJobIDSrc );
// marks a packet as being finished with, releases the packet and frees the memory
void ReleaseNetPacket( IMsgNetPacket *pNetPacket );
void EndPause( EJobPauseReason eExpectedState );
// Generate an assertion in the coroutine of this job
// (creating a minidump). Useful for inspecting stuck jobs
void GenerateAssert( const char *pchMsg = NULL );
//called to determine if the requested context is valid. If multiple contexts are provided, this will return true only if ALL the contexts are valid
bool BHasContext( uint32 nContext ) const { return ( m_nContextMask & nContext ) == nContext; }
uint32 GetContexts() const { return m_nContextMask; }
//called to control the default behavior for starting jobs, immediate, or delayed
static void SetStartDefaultJobsDelayed( bool bStartJobsDelayed ) { s_bStartDefaultJobsDelayed = bStartJobsDelayed; }
// accessor to get access to the JobMgr from the server we belong to
CJobMgr &GetJobMgr();
protected:
// main job implementation, in the coroutine. Every job must implement at least one of these methods.
virtual bool BYieldingRunJob( void * pvStartParam ) { Assert( false ); return true; } // implement this if your job can be started directly
virtual bool BYieldingRunJobFromMsg( IMsgNetPacket * pNetPacket ) { Assert( false ); return true; } // implement this if your job can be started by a network message
// Can be overridden to return a different timeout per job class
virtual uint32 CHeartbeatsBeforeTimeout();
// Called by CJobMgr to send heartbeat message to our listeners during long operations
void Heartbeat();
uint32 m_bRunFromMsg:1,
m_bWorkItemCanceled:1, // true if the work item we were waiting on was canceled
m_bIsTest:1,
m_bIsLongRunning:1;
private:
// starts the coroutine that activates the job
void InitCoroutine();
// continues the current job
void Continue();
// break into this coroutine - can only be called from OUTSIDE this coroutine
void Debug();
// pauses the current job - can only be called from inside a coroutine
void Pause( EJobPauseReason eReason, const char *pszResourceName );
static void BRunProxy( void *pvThis );
JobID_t m_JobID; // Our unique identifier.
HCoroutine m_hCoroutine;
void * m_pvStartParam; // Start params for our job, if any
// all these flags indicate some kind of failure and we will want to report them
union {
struct {
uint32
m_bJobFailed:1, // true if BYieldingRunJob returned false
m_bLocksFailed:1,
m_bLocksLongHeld:1,
m_bLocksLongWait:1,
m_bWaitTimeout:1,
m_bLongInterYield:1,
m_bTimeoutNetMsg:1,
m_bTimeoutOther:1,
m_uUnused:24;
} m_bits;
uint32 m_uFlags;
} m_flags;
int m_cLocksAttempted;
int m_cLocksWaitedFor;
EJobPauseReason m_ePauseReason;
const char *m_pszPauseResourceName;
MsgType_t m_unWaitMsgType;
CJobTime m_STimeStarted; // time (frame) at which this job started
CJobTime m_STimeSwitched; // time (frame) at which we were last paused or continued
CJobTime m_STimeNextHeartbeat; // Time at which next heartbeat should be performed
CFastTimer m_FastTimerDelta; // How much time we've been running for without yielding
CCycleCount m_cyclecountTotal; // Total runtime
uint32 m_nContextMask; // The context that this job was created in. Typically only used for message jobs to indicate the initiator of the message
CJob *m_pJobPrev; // the job that launched us
// lock manipulation
void _SetLock( CLock *pLock, const char *filename, int line );
void UnsetLock( CLock *pLock );
void PassLockToJob( CJob *pNewJob, CLock *pLock );
void OnLockDeleted( CLock *pLock );
void AddJobToNotifyOnLockRelease( CJob *pJob );
CUtlVectorFixedGrowable< CLock *, 2 > m_vecLocks;
CLock *m_pWaitingOnLock; // lock we're waiting on, if any
const char *m_pWaitingOnLockFilename;
int m_waitingOnLockLine;
CJob *m_pJobToNotifyOnLockRelease; // other job that wants this lock
CWorkItem *m_pWaitingOnWorkItem; // set if job is waiting for this work item
#ifdef GC
//context flags indicating what this job can access. Temporary and only for validating access on the GC
uint32 m_nGCJobAccessType;
//the steam ID that we are stating is safe to access. This is temporary to validate jobs during the split of the GC
CSteamID m_SOCacheValidSteamID;
#endif
CJobMgr &m_JobMgr; // our job manager
CUtlVectorFixedGrowable< IMsgNetPacket *, 1 > m_vecNetPackets; // list of tcp packets currently held by this job (ie, needing release on job exit)
// pointer to our own static job info
struct JobType_t const *m_pJobType;
// Name of the job for when it's not registered
const char *m_pchJobName;
// setting the job info
friend void Job_SetJobType( CJob &job, const JobType_t *pJobType );
friend class CJobMgr;
friend class CLock;
// used to store the memory allocation stack
CUtlMemory< unsigned char > m_memAllocStack;
static bool s_bStartDefaultJobsDelayed;
};
// Only one job can be running at a time. We keep a global accessor to it.
extern CJob *g_pJobCur;
inline CJob &GJobCur() { Assert( g_pJobCur != NULL ); return *g_pJobCur; }
#define AssertRunningJob() { Assert( NULL != g_pJobCur ); }
#define AssertRunningThisJob() { Assert( this == g_pJobCur ); }
#define AssertNotRunningThisJob() { Assert( this != g_pJobCur ); }
#define AssertNotRunningJob() { Assert( NULL == g_pJobCur ); }
//-----------------------------------------------------------------------------
// Purpose: simple locking class
// add this object to any classes you want jobs to be able to lock
//-----------------------------------------------------------------------------
#if defined( GC )
#include "tier0/memdbgoff.h"
#endif
class CLock
{
#if defined( GC )
DECLARE_CLASS_MEMPOOL( CLock );
#endif
public:
CLock( );
~CLock();
bool BIsLocked() const { return m_pJob != NULL; }
CJob *GetJobLocking() { return m_pJob; }
CJob *GetJobWaitingQueueHead() { return m_pJobToNotifyOnLockRelease; }
CJob *GetJobWaitingQueueTail() { return m_pJobWaitingQueueTail; }
void AddToWaitingQueue( CJob *pJob );
const char *GetName() const;
void SetName( const char *pchName );
int16 GetLockType() const { return m_nsLockType; }
void SetLockType( int16 nsLockType ) { m_nsLockType = nsLockType; }
uint64 GetLockSubType() const { return m_unLockSubType; }
void SetLockSubType( uint64 unLockSubType ) { m_unLockSubType = unLockSubType; }
int32 GetWaitingCount() const { return m_nWaitingCount; }
int64 GetMicroSecondsSinceLock() const { return m_sTimeAcquired.CServerMicroSecsPassed(); }
void IncrementReference();
int DecrementReference();
void ClearReference() { m_nRefCount = 0; }
int32 GetReferenceCount() const { return m_nRefCount; }
void Dump( const char *pszPrefix = "\t\t", int nPrintMax = 1, bool bPrintWaiting = true ) const;
private:
CJob *m_pJob; // the job that's currently locking us
CJob *m_pJobToNotifyOnLockRelease; // Pointer to the first job waiting on us
CJob *m_pJobWaitingQueueTail; // Pointer to the last job waiting on us
const char *m_pchConstStr; // Prefix part of the lock's name
int32 m_nRefCount; // # of times locked
int32 m_nWaitingCount; // Count of jobs waiting on the lock
CJobTime m_sTimeAcquired; // Time the lock was last locked
uint64 m_unLockSubType;
const char *m_pFilename; // Filename of the source file who acquired this lock
int m_line; // Line number of the filename
int16 m_nsLockType; // Lock priority for safely waiting on multiple locks
friend class CJob;
};
#if defined( GC )
#include "tier0/memdbgon.h"
#endif
//-----------------------------------------------------------------------------------------
// An auto lock class which handles auto lifetime management of a lock
//-----------------------------------------------------------------------------------------
class CGCAutoLock
{
public:
CGCAutoLock() : m_pLock( NULL ) {}
CGCAutoLock( const CGCAutoLock& rhs ) { Acquire( rhs.m_pLock ); }
~CGCAutoLock() { Release(); }
//determines if this lock is currently locked or not
bool IsLocked() const { return ( m_pLock != NULL ); }
//swaps two locks (faster than doing reassignments due to not needing all the reference counting)
void Swap( CGCAutoLock& rhs ) { std::swap( m_pLock, rhs.m_pLock ); }
CGCAutoLock& operator=( const CGCAutoLock& rhs );
//called to acquire a lock (the odd naming convention is to match the macro format to automatically provide the file and line of the call site)
bool _BYieldingAcquireLock( CLock& lock, const char* pszFile, uint32 nLine );
//called to release the current lock
void Release();
private:
void Acquire( CLock* pLock );
CLock* m_pLock;
};
//-----------------------------------------------------------------------------
// Purpose: Use these macros to declare blocks where it is unsafe to yield.
// The job will assert if it pauses within the block
//-----------------------------------------------------------------------------
#define DO_NOT_YIELD_THIS_SCOPE() CDoNotYieldScopeImpl doNotYieldScope_##line( FILE_AND_LINE )
#define BEGIN_DO_NOT_YIELD() CDoNotYieldScopeImpl::InternalPush( FILE_AND_LINE )
#define END_DO_NOT_YIELD() CDoNotYieldScopeImpl::InternalPop()
class CDoNotYieldScopeImpl
{
public:
explicit CDoNotYieldScopeImpl( const char *pchLocation ) { InternalPush( pchLocation ); }
~CDoNotYieldScopeImpl() { InternalPop(); }
static void InternalPush( const char *pchLocation );
static void InternalPop();
private:
// Disallow these constructors and operators
CDoNotYieldScopeImpl( const CDoNotYieldScopeImpl &that );
CDoNotYieldScopeImpl &operator=( const CDoNotYieldScopeImpl &that );
};
} // namespace GCSDK
#include "tier0/memdbgoff.h"
#endif // GC_JOB_H