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.

462 lines
16 KiB

  1. //========= Copyright � 1996-2004, Valve LLC, All rights reserved. ============
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #ifndef GC_JOBMGR_H
  8. #define GC_JOBMGR_H
  9. #ifdef _WIN32
  10. #pragma once
  11. #endif
  12. #include "tier0/fasttimer.h"
  13. #include "tier1/utlpriorityqueue.h"
  14. #include "tier1/utlhashmaplarge.h"
  15. #include "tier1/utlpair.h"
  16. #include "job.h"
  17. #include "workthreadpool.h"
  18. class GCConVar;
  19. #include "tier0/memdbgon.h"
  20. namespace GCSDK
  21. {
  22. #if defined(_DEBUG)
  23. // this is restricted to debug builds due to the performance cost
  24. // that could be changed by removing the expensive sm_listAllJobs.Find() command
  25. #define DEBUG_JOB_LIST
  26. #endif // defined(_DEBUG)
  27. struct JobStats_t
  28. {
  29. uint m_cJobsCurrent;
  30. uint m_cJobsTotal;
  31. uint m_cJobsFailed;
  32. uint64 m_cJobsTimedOut; // # of jobs timed out ever
  33. double m_flSumJobTimeMicrosec;
  34. uint64 m_unMaxJobTimeMicrosec;
  35. uint m_cTimeslices;
  36. JobStats_t()
  37. {
  38. memset( this, 0, sizeof(JobStats_t) );
  39. }
  40. };
  41. struct JobStatsBucket_t
  42. {
  43. JobStatsBucket_t()
  44. {
  45. memset( this, 0, sizeof(JobStatsBucket_t) );
  46. }
  47. char m_rgchName[64];
  48. uint64 m_cCompletes;
  49. uint64 m_u64RunTimeMax;
  50. uint64 m_cTimeoutNetMsg;
  51. uint64 m_cLongInterYieldTime;
  52. uint64 m_cLocksAttempted;
  53. uint64 m_cLocksWaitedFor;
  54. uint64 m_cLocksFailed;
  55. uint64 m_cLocksLongHeld;
  56. uint64 m_cLocksLongWait;
  57. uint64 m_cWaitTimeout;
  58. uint64 m_u64JobDuration;
  59. uint64 m_cJobsPaused;
  60. uint64 m_cJobsFailed;
  61. uint64 m_u64RunTime;
  62. // use by ListJobs
  63. uint64 m_cPauseReasonNetworkMsg;
  64. uint64 m_cPauseReasonSleepForTime;
  65. uint64 m_cPauseReasonWaitingForLock;
  66. uint64 m_cPauseReasonYield;
  67. uint64 m_cPauseReasonSQL;
  68. uint64 m_cPauseReasonWorkItem;
  69. };
  70. enum EJobProfileAction
  71. {
  72. k_EJobProfileAction_ErrorReport = 0,
  73. k_EJobProfileAction_Start = 1,
  74. k_EJobProfileAction_Stop = 2,
  75. k_EJobProfileAction_Dump = 3,
  76. k_EJobProfileAction_Clear = 4,
  77. };
  78. enum EJobProfileSortOrder
  79. {
  80. k_EJobProfileSortOrder_Alpha = 0,
  81. k_EJobProfileSortOrder_Count = 1,
  82. k_EJobProfileSortOrder_TotalRuntime = 2,
  83. };
  84. //the various msg contexts that exist for the GC
  85. enum EGCMsgContext
  86. {
  87. k_eGCMsgContext_None = 0, // This message cannot be called by anyone. This is intended only to be used by messages that are temporarily disabled but left in the codebase for some reason
  88. k_eGCMsgContext_Client = (1<<0), // This message is expected to arrive from a player/client
  89. k_eGCMsgContext_UnsecuredServer = (1<<1), // This message should come from a dedicated server, but one that is not securely managed and should not be trusted
  90. k_eGCMsgContext_TrustedServer = (1<<2), // This message should come from a dedicated server, but one that is securely managed
  91. k_eGCMsgContext_System = (1<<3), // This message should come from the GCH system
  92. k_eGCMsgContext_OtherGC = (1<<4), // This message should arrive from another sub GC or the master GC
  93. k_eGCMsgContext_AllServers = k_eGCMsgContext_UnsecuredServer | k_eGCMsgContext_TrustedServer, // a message from any server
  94. k_eGCMsgContext_All = k_eGCMsgContext_Client | k_eGCMsgContext_TrustedServer | k_eGCMsgContext_UnsecuredServer | k_eGCMsgContext_System | k_eGCMsgContext_OtherGC
  95. };
  96. struct JobProfileStats_t
  97. {
  98. int m_iJobProfileSort;
  99. CUtlHashMapLarge< uint32, JobStatsBucket_t > *pmapStatsBucket;
  100. };
  101. //-----------------------------------------------------------------------------
  102. // Purpose: This keeps track of all jobs that belong to a given hub.
  103. // It's primarily used for routing incoming messages to jobs.
  104. //-----------------------------------------------------------------------------
  105. class CJobMgr
  106. {
  107. public:
  108. // Constructors & destructors
  109. CJobMgr();
  110. ~CJobMgr();
  111. // gets the next available job ID
  112. JobID_t GetNewJobID();
  113. // Set the thread count for the internal thread pool(s)
  114. void SetThreadPoolSize( uint cThreads );
  115. // Run any sleeping jobs who's wakeup time has arrived and check for timeouts
  116. bool BFrameFuncRunSleepingJobs( CLimitTimer &limitTimer );
  117. // Run any yielding jobs, even low priority ones
  118. bool BFrameFuncRunYieldingJobs( CLimitTimer &limitTimer );
  119. // Route this message to an existing Job, or create a new one if that JobID does not exist. This takes the context that the message
  120. // is being created within so that it can validate that the message is being called from the right context. If the context doesn't overlap,
  121. // it will fail to create the job
  122. bool BRouteMsgToJob( void *pParent, IMsgNetPacket *pNetPacket, const JobMsgInfo_t &jobMsgInfo, EGCMsgContext nCreateContext = k_eGCMsgContext_All );
  123. // Adds a new Job to the mgr and generates a JobID for it.
  124. void InsertJob( CJob &job );
  125. // Removes a Job from the mgr (the caller is still responsible for freeing it)
  126. void RemoveJob( CJob &job );
  127. //called by a job that has just been started to place itself on the yield queue instead of running
  128. void AddDelayedJobToYieldList( CJob &job );
  129. #ifdef GC
  130. //given a message, this will determine if there is any custom routing associated with the message. It will return a function pointer
  131. //if custom routing is found, or NULL otherwise
  132. JobRoutingFunc_t GetRoutingForMsg( IMsgNetPacket *pNetPacket );
  133. JobRoutingFunc_t GetRoutingForMsg( MsgType_t eMsg );
  134. // resumes the specified job if it is, in fact, waiting for a SQL query to return
  135. bool BResumeSQLJob( JobID_t jobID );
  136. // yields waiting for a query response
  137. bool BYieldingRunQuery( CJob &job, CGCSQLQueryGroup *pQueryGroup, ESchemaCatalog eSchemaCatalog );
  138. //stats collected for SQL queries that run long
  139. struct LongSQLStats_t
  140. {
  141. //the string for this query
  142. CUtlString m_sQuery;
  143. //total amount of time spent in over limit queries
  144. uint32 m_nTotalTimeMS;
  145. uint32 m_nNumQueries;
  146. //the parameters for the longest running query
  147. uint32 m_nMaxQueryMS;
  148. CUtlString m_sMaxQueryParams;
  149. //a collection of N parameters that also exceeded the time limit
  150. static const uint32 knMaxParams = 8;
  151. CUtlVectorFixed< CUtlString, knMaxParams > m_Params;
  152. };
  153. //access to the mapfor all the long running queries
  154. typedef CUtlHashMapLarge< const char*, LongSQLStats_t*, CaseSensitiveStrEquals, MurmurHash3ConstCharPtr > TLongSQLMap;
  155. TLongSQLMap& GetLongSQLMap() { return m_LongSqlMap; }
  156. // SQL profiling
  157. enum ESQLProfileSort
  158. {
  159. k_ESQLProfileSortTotalTime,
  160. k_ESQLProfileSortTotalCount,
  161. k_ESQLProfileSortAvgTime,
  162. k_ESQLProfileSortName
  163. };
  164. void StartSQLProfiling();
  165. void StopSQLProfiling();
  166. void DumpSQLProfile( ESQLProfileSort eSort );
  167. uint32 GetNumSQLQueriesInFlight() const { return m_mapSQLQueriesInFlight.Count(); }
  168. #endif
  169. // returns true if we're running any jobs of the specified name
  170. // slow to call if lots of jobs are running, should only be used by tests
  171. bool BIsJobRunning( const char *pchJobName );
  172. // passes a network msg directly to the specified job
  173. void PassMsgToJob( CJob &job, IMsgNetPacket *pNetPacket, const JobMsgInfo_t &jobMsgInfo );
  174. // Enter an advisory do-not-yield scope with line-and-file string for debugging
  175. void PushDoNotYield( CJob &job, const char *pchFileAndLine );
  176. // Exit the innermost do-not-yield scope which matches this job
  177. void PopDoNotYield( CJob &job );
  178. // yields until a network message is received
  179. bool BYieldingWaitForMsg( CJob &job );
  180. // yields for a set amount of time
  181. bool BYieldingWaitTime( CJob &job, uint32 cMicrosecondsToSleep );
  182. // simple yield until Run() called again
  183. bool BYield( CJob &job );
  184. // Yield only if job manager decides we need to
  185. bool BYieldIfNeeded( CJob &job, bool *pbYielded );
  186. // Thread pool work item
  187. bool BYieldingWaitForWorkItem( CJob &job, const char *pszWorkItemName = NULL );
  188. bool BRouteWorkItemCompleted( JobID_t jobID, bool bWorkItemCanceled ) { return BRouteWorkItemCompletedInternal( jobID, bWorkItemCanceled, /* bShouldExist */ true, /* bResumeImmediately */ true ); }
  189. bool BRouteWorkItemCompletedIfExists( JobID_t jobID, bool bWorkItemCanceled ) { return BRouteWorkItemCompletedInternal( jobID, bWorkItemCanceled, /* bShouldExist */ false, /* bResumeImmediately */ true ); }
  190. bool BRouteWorkItemCompletedDelayed( JobID_t jobID, bool bWorkItemCanceled ) { return BRouteWorkItemCompletedInternal( jobID, bWorkItemCanceled, /* bShouldExist */ true, /* bResumeImmediately */ false ); }
  191. bool BRouteWorkItemCompletedIfExistsDelayed( JobID_t jobID, bool bWorkItemCanceled ) { return BRouteWorkItemCompletedInternal( jobID, bWorkItemCanceled, /* bShouldExist */ false, /* bResumeImmediately */ false ); }
  192. void AddThreadedJobWorkItem( CWorkItem *pWorkItem );
  193. void StopWorkThreads() { m_WorkThreadPool.StopWorkThreads(); }
  194. static int ProfileSortFunc( void *pCtx, const int *lhs, const int *rhs );
  195. void ProfileJobs( EJobProfileAction ejobProfileAction, EJobProfileSortOrder iSortOrder = k_EJobProfileSortOrder_Alpha );
  196. int DumpJobSummary();
  197. void DumpJob( JobID_t jobID, int nPrintLocksMax = 20 ) const;
  198. int CountJobs() const; // counts currently active jobs
  199. void CheckThreadID(); // make sure we are still in the correct thread
  200. int CountYieldingJobs() const { return m_ListJobsYieldingRegPri.Count(); } // counts jobs currently in a yielding state
  201. bool HasOutstandingThreadPoolWorkItems();
  202. void SetIsShuttingDown( bool bIsShuttingDown ) { m_bIsShuttingDown = bIsShuttingDown; }
  203. bool GetIsShuttingDown() const { return m_bIsShuttingDown; }
  204. void *GetMainMemoryDebugInfo() { return g_memMainDebugInfo.Base(); }
  205. // wakes up a job that was waiting on a lock
  206. void WakeupLockedJob( CJob &job );
  207. // returns true if there is a job active with the specified ID
  208. bool BJobExists( JobID_t jobID ) const;
  209. // returns a job
  210. CJob *GetPJob( JobID_t jobID );
  211. const CJob *GetPJob( JobID_t jobID ) const;
  212. JobStats_t& GetJobStats() { return m_JobStats; }
  213. // Access work thread pool directly
  214. CWorkThreadPool *AccessWorkThreadPool() { return &m_WorkThreadPool; }
  215. // Debug helpers
  216. // dumps a list of all running jobs across ALL job managers
  217. void DumpJobs( const char *pszJobName, int nMax, int nPrintLocksMax = 1 ) const;
  218. // cause a debug break in the given job
  219. static void DebugJob( int iJob );
  220. //sets a whitelist of messages that should be exempt from context filtering. This is meant only to be a utility during development to address
  221. //messages that were misclassified until a fix can be deployed
  222. void SetMsgContextWhitelist( const CUtlVector< MsgType_t >& msgList ) { m_MsgContextWhitelist = msgList; }
  223. private:
  224. //given a message, this will find the appropriate job type structure for handling this message
  225. const JobType_t* GetJobInfoForMsg( IMsgNetPacket *pNetPacket ) const;
  226. bool BRouteWorkItemCompletedInternal( JobID_t jobID, bool bWorkItemCanceled, bool bShouldExist, bool bResumeImmediately );
  227. // Create a new job for this message
  228. bool BLaunchJobFromNetworkMsg( void *pParent, const JobMsgInfo_t &jobMsgInfo, IMsgNetPacket *pNetPacket, EGCMsgContext nCreateContext );
  229. // Internal add to yield list (looks at priority)
  230. void AddToYieldList( CJob &job );
  231. // Get an IJob given a job ID and pause reason
  232. bool BGetIJob( JobID_t jobID, EJobPauseReason eJobPauseReason, bool bShouldExist, int *pIJob );
  233. // Map containing all of our jobs
  234. CUtlHashMapLarge<JobID_t, CJob *> m_MapJob;
  235. // jobs simply waiting until the next Run()
  236. struct JobYielding_t
  237. {
  238. JobID_t m_JobID;
  239. uint m_nIteration;
  240. };
  241. CUtlLinkedList<JobYielding_t, int> m_ListJobsYieldingRegPri;
  242. bool BResumeYieldingJobs( CLimitTimer &limitTimer );
  243. bool BResumeYieldingJobsFromList( CUtlLinkedList<JobYielding_t, int> &listJobsYielding, uint nCurrentIteration, CLimitTimer &limitTimer );
  244. uint m_nCurrentYieldIterationRegPri;
  245. // jobs waiting on a timer
  246. struct JobSleeping_t
  247. {
  248. JobID_t m_JobID;
  249. CJobTime m_SWakeupTime;
  250. CJobTime m_STimeTouched;
  251. };
  252. CUtlPriorityQueue<JobSleeping_t> m_QueueJobSleeping;
  253. bool BResumeSleepingJobs( CLimitTimer &limitTimer );
  254. static bool JobSleepingLessFunc( JobSleeping_t const &lhs, JobSleeping_t const &rhs );
  255. // timeout list of jobs, ordered from oldest to newest
  256. struct JobTimeout_t
  257. {
  258. JobID_t m_JobID;
  259. CJobTime m_STimePaused;
  260. CJobTime m_STimeTouched;
  261. uint32 m_cHeartbeatsBeforeTimeout;
  262. };
  263. CUtlLinkedList<JobTimeout_t, int> m_ListJobTimeouts;
  264. CUtlHashMapLarge< JobID_t, int > m_MapJobTimeoutsIndexByJobID;
  265. void PauseJob( CJob &job, EJobPauseReason eJobPauseReason, const char *pszPauseResourceName = NULL );
  266. void CheckForJobTimeouts( CLimitTimer &limitTimer );
  267. void TimeoutJob( CJob &job );
  268. bool m_bJobTimedOut;
  269. // thread pool usage, for running job functions in other threads
  270. CWorkThreadPool m_WorkThreadPool;
  271. void AccumulateStatsofJob( CJob &job );
  272. void RecordOrphanedMessage( MsgType_t eMsg, JobID_t jobIDTarget );
  273. // stats info
  274. JobStats_t m_JobStats;
  275. // static job registration
  276. static void RegisterJobType( const JobType_t *pJobType );
  277. friend void Job_RegisterJobType( const JobType_t *pJobType );
  278. JobID_t m_unNextJobID;
  279. uint m_unFrameFuncThreadID; // the thread is JobMgr is working in
  280. bool m_bProfiling;
  281. bool m_bIsShuttingDown;
  282. int m_cErrorsToReport;
  283. CUtlHashMapLarge< uint32, JobStatsBucket_t > m_mapStatsBucket;
  284. CUtlHashMapLarge< MsgType_t, int > m_mapOrphanMessages;
  285. CUtlMemory<unsigned char> g_memMainDebugInfo;
  286. //a white list of messages to ignore context issues with
  287. CUtlVector< MsgType_t > m_MsgContextWhitelist;
  288. #ifdef GC
  289. // sql profiling
  290. bool m_bSQLProfiling;
  291. CFastTimer m_sqlTimer;
  292. // long SQL utilities
  293. TLongSQLMap m_LongSqlMap;
  294. struct PendingSQLJob_t
  295. {
  296. uint64 m_nStartMicrosec;
  297. CGCSQLQueryGroup *m_pQueryGroup;
  298. int32 m_iBucket;
  299. };
  300. //utility function that given a pending SQL query will handle displaying it out to the console along with all of its parameters. Useful
  301. //for debugging and monitoring
  302. void DisplayPendingSQLJob( const PendingSQLJob_t& sqlJob );
  303. struct SQLProfileBucket_t
  304. {
  305. uint64 m_nTotalMicrosec;
  306. uint32 m_unCount;
  307. uint32 m_unMaxMS;
  308. };
  309. CUtlHashMapLarge<GID_t, PendingSQLJob_t> m_mapSQLQueriesInFlight;
  310. CUtlDict<SQLProfileBucket_t> m_dictSQLBuckets;
  311. struct SQLProfileCtx_t
  312. {
  313. ESQLProfileSort m_eSort;
  314. CUtlDict<SQLProfileBucket_t> *pdictBuckets;
  315. };
  316. static int SQLProfileSortFunc( void *pCtx, const int *lhs, const int *rhs );
  317. #endif
  318. #ifdef DEBUG_JOB_LIST
  319. // static job debug list
  320. static CUtlLinkedList<CJob *, int> sm_listAllJobs;
  321. #endif
  322. // A stack of do not yield guards so we can print the right warning if they're nested
  323. // (Note that we also track jobid since it is not an error to start another job which
  324. // executes and immediately yields back to the parent, even if the parent can't yield)
  325. CUtlVector< CUtlPair< JobID_t, const char * > > m_stackDoNotYieldGuards;
  326. };
  327. //-----------------------------------------------------------------------------
  328. // Purpose: passthrough function just so the CJob internal data can be kept private
  329. //-----------------------------------------------------------------------------
  330. inline void Job_RegisterJobType( const JobType_t *pJobType )
  331. {
  332. CJobMgr::RegisterJobType( pJobType );
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose: passthrough function just so the CJob internal data can be kept private
  336. //-----------------------------------------------------------------------------
  337. inline void Job_SetJobType( CJob &job, const JobType_t *pJobType )
  338. {
  339. job.m_pJobType = pJobType;
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Purpose: job registration macro
  343. //-----------------------------------------------------------------------------
  344. #define GC_REG_JOB_FULL( parentclass, jobclass, jobname, msg, contexts, routingfn, consolevar ) \
  345. GCSDK::CJob *CreateJob_##jobclass( parentclass *pParent, void * pvStartParam ); \
  346. static const GCSDK::JobType_t g_JobType_##jobclass = { jobname, (GCSDK::MsgType_t)msg, contexts, (GCSDK::JobCreationFunc_t)CreateJob_##jobclass, routingfn, consolevar }; \
  347. GCSDK::CJob *CreateJob_##jobclass( parentclass *pParent, void * pvStartParam ) \
  348. { \
  349. GCSDK::CJob *job = GCSDK::CJob::AllocateJob<jobclass>( pParent ); \
  350. if ( job ) \
  351. { \
  352. Job_SetJobType( *job, &g_JobType_##jobclass ); \
  353. if ( pvStartParam ) job->SetStartParam( pvStartParam ); \
  354. } \
  355. else \
  356. { \
  357. AssertMsg( job, "CJob::AllocateJob<" #jobclass "> returned NULL!" ); \
  358. } \
  359. return job; \
  360. } \
  361. static class CRegJob_##jobclass \
  362. { \
  363. public: CRegJob_##jobclass() \
  364. { \
  365. Job_RegisterJobType( &g_JobType_##jobclass ); \
  366. } \
  367. } g_RegJob_##jobclass;
  368. #define GC_REG_JOB( parentclass, jobclass, jobname, msg ) \
  369. GC_REG_JOB_FULL( parentclass, jobclass, jobname, msg, GCSDK::k_eGCMsgContext_All, NULL, NULL )
  370. } // namespace GCSDK
  371. #include "tier0/memdbgoff.h"
  372. #endif // GC_JOBMGR_H