Team Fortress 2 Source Code as on 22/4/2020
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.

465 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #ifndef GC_JOB_H
  8. #define GC_JOB_H
  9. #ifdef _WIN32
  10. #pragma once
  11. #endif
  12. #include "tier0/memdbgon.h"
  13. #include "tier1/functors.h"
  14. #include "workthreadpool.h"
  15. namespace GCSDK
  16. {
  17. class CJobMgr;
  18. class CLock;
  19. class CJob;
  20. class IMsgNetPacket;
  21. //-----------------------------------------------------------------------------
  22. // Purpose: Use these macros to declare blocks where it is unsafe to yield.
  23. // The job will assert if it pauses within the block
  24. //-----------------------------------------------------------------------------
  25. #define DO_NOT_YIELD_THIS_SCOPE() GCSDK::CDoNotYieldScope doNotYieldScope_##line( FILE_AND_LINE )
  26. #define BEGIN_DO_NOT_YIELD() GJobCur().PushDoNotYield( FILE_AND_LINE )
  27. #define END_DO_NOT_YIELD() GJobCur().PopDoNotYield()
  28. class CDoNotYieldScope
  29. {
  30. public:
  31. CDoNotYieldScope( const char *pchLocation );
  32. ~CDoNotYieldScope();
  33. private:
  34. // Disallow these constructors and operators
  35. CDoNotYieldScope();
  36. CDoNotYieldScope( const CDoNotYieldScope &that );
  37. CDoNotYieldScope &operator=( const CDoNotYieldScope &that );
  38. };
  39. //-----------------------------------------------------------------------------
  40. // job creation function
  41. typedef CJob *(*JobCreationFunc_t)( void *pvServerParent, void * pvStartParam );
  42. //-----------------------------------------------------------------------------
  43. // Purpose: static job information
  44. // contains information relevant to one type of CJob
  45. //-----------------------------------------------------------------------------
  46. struct JobType_t
  47. {
  48. const char *m_pchName; // name of this type of job
  49. MsgType_t m_eCreationMsg; // network message that creates this job
  50. EServerType m_eServerType; // the server type that responds to this message
  51. JobCreationFunc_t m_pJobFactory; // virtual constructor
  52. };
  53. //-----------------------------------------------------------------------------
  54. // Purpose: reason as to why the current job has yielded to the main thread (paused)
  55. // if this is updated, k_prgchJobPauseReason[] in job.cpp also needs to be updated
  56. //-----------------------------------------------------------------------------
  57. enum EJobPauseReason
  58. {
  59. k_EJobPauseReasonNone,
  60. k_EJobPauseReasonNotStarted,
  61. k_EJobPauseReasonNetworkMsg,
  62. k_EJobPauseReasonSleepForTime,
  63. k_EJobPauseReasonWaitingForLock,
  64. k_EJobPauseReasonYield,
  65. k_EJobPauseReasonSQL,
  66. k_EJobPauseReasonWorkItem,
  67. k_EJobPauseReasonCount
  68. };
  69. //-----------------------------------------------------------------------------
  70. // Purpose: contains information used to route a message to a job, or to
  71. // create a new job from that message type
  72. //-----------------------------------------------------------------------------
  73. struct JobMsgInfo_t
  74. {
  75. MsgType_t m_eMsg;
  76. JobID_t m_JobIDSource;
  77. JobID_t m_JobIDTarget;
  78. EServerType m_eServerType;
  79. JobMsgInfo_t()
  80. {
  81. m_eMsg = (MsgType_t)0;
  82. m_JobIDSource = k_GIDNil;
  83. m_JobIDTarget = k_GIDNil;
  84. m_eServerType = k_EServerTypeInvalid;
  85. }
  86. JobMsgInfo_t( MsgType_t eMsg, JobID_t jobIDSource, JobID_t jobIDTarget, EServerType eServerType )
  87. {
  88. m_eMsg = eMsg;
  89. m_JobIDSource = jobIDSource;
  90. m_JobIDTarget = jobIDTarget;
  91. m_eServerType = eServerType;
  92. }
  93. };
  94. typedef void (CJob::*JobThreadFunc_t)();
  95. #define BYieldingAcquireLock( lock ) _BYieldingAcquireLock( lock, __FILE__, __LINE__ )
  96. #define BAcquireLockImmediate( lock ) _BAcquireLockImmediate( lock, __FILE__, __LINE__ )
  97. #define ReleaseLock( lock ) _ReleaseLock( lock, false, __FILE__, __LINE__ )
  98. //-----------------------------------------------------------------------------
  99. // Purpose: A job is any server operation that requires state. Typically, we use jobs for
  100. // operations that need to pause waiting for responses from other servers. The
  101. // job object persists the state of the operation while it waits, and the reply
  102. // from the remote server re-activates the job.
  103. //-----------------------------------------------------------------------------
  104. class CJob
  105. {
  106. public:
  107. // Constructors & destructors, when overriding job name a static string pointer must be used
  108. explicit CJob( CJobMgr &jobMgr, char const *pchJobName = NULL );
  109. virtual ~CJob();
  110. // starts the job, storing off the network msg and calling it's Run() function
  111. void StartJobFromNetworkMsg( IMsgNetPacket *pNetPacket, const JobID_t &gidJobIDSrc );
  112. // accessors
  113. JobID_t GetJobID() const { return m_JobID; }
  114. // start job immediately
  115. // mostly for CMD jobs, which should immediately Yield() once
  116. // so that they don't do their work until the enclosing JobMbr.Run()
  117. // is called
  118. void StartJob( void * pvStartParam );
  119. // schedules the job for execution, but does not interrup the currently running job. Effectively starts the job on the yielding list as if it had immediately yielded
  120. // although is more efficient than actually doing so
  121. void StartJobDelayed( void * pvStartParam );
  122. // string name of the job
  123. const char *GetName() const;
  124. // return reason why we're paused
  125. EJobPauseReason GetPauseReason() const { return m_ePauseReason; }
  126. // string description of why we're paused
  127. const char *GetPauseReasonDescription() const;
  128. // return time at which this job was last paused or continued
  129. const CJobTime& GetTimeSwitched() const { return m_STimeSwitched; }
  130. // return microseconds run since we were last continued
  131. uint64 GetMicrosecondsRun() const { return m_FastTimerDelta.GetDurationInProgress().GetUlMicroseconds(); }
  132. bool BJobNeedsToHeartbeat() const { return ( m_STimeNextHeartbeat.CServerMicroSecsPassed() >= 0 ); }
  133. // --- locking pointers
  134. bool _BYieldingAcquireLock( CLock *pLock, const char *filename = "unknown", int line = 0 );
  135. bool _BAcquireLockImmediate( CLock *pLock, const char *filename = "unknown", int line = 0 );
  136. void _ReleaseLock( CLock *pLock, bool bForce = false, const char *filename = "unknown", int line = 0 );
  137. bool BHoldsAnyLocks() const { return m_vecLocks.Count() > 0; }
  138. void ReleaseLocks();
  139. /// If we hold any locks, spew about them and release them.
  140. /// This is useful for long running jobs, to make sure they don't leak
  141. /// locks that never get cleaned up
  142. void ShouldNotHoldAnyLocks();
  143. // --- general methods for waiting for events
  144. // Simple yield to other jobs until Run() called again
  145. bool BYield();
  146. // Yield IF JobMgr thinks we need to based on how long we've run and our priority
  147. bool BYieldIfNeeded( bool *pbYielded = NULL );
  148. // waits for a set amount of time
  149. bool BYieldingWaitTime( uint32 m_cMicrosecondsToSleep );
  150. bool BYieldingWaitOneFrame();
  151. // waits for another network msg, returning false if none returns
  152. bool BYieldingWaitForMsg( IMsgNetPacket **ppNetPacket );
  153. bool BYieldingWaitForMsg( CGCMsgBase *pMsg, MsgType_t eMsg );
  154. bool BYieldingWaitForMsg( CProtoBufMsgBase *pMsg, MsgType_t eMsg );
  155. #ifdef GC
  156. bool BYieldingWaitForMsg( CGCMsgBase *pMsg, MsgType_t eMsg, const CSteamID &expectedID );
  157. bool BYieldingWaitForMsg( CProtoBufMsgBase *pMsg, MsgType_t eMsg, const CSteamID &expectedID );
  158. bool BYieldingRunQuery( CGCSQLQueryGroup *pQueryGroup, ESchemaCatalog eSchemaCatalog );
  159. #endif
  160. bool BYieldingWaitTimeWithLimit( uint32 cMicrosecondsToSleep, CJobTime &stimeStarted, int64 nMicroSecLimit );
  161. bool BYieldingWaitTimeWithLimitRealTime( uint32 cMicrosecondsToSleep, int nSecLimit );
  162. void RecordWaitTimeout() { m_flags.m_bits.m_bWaitTimeout = true; }
  163. // wait for pending work items before deleting job
  164. void WaitForThreadFuncWorkItemBlocking();
  165. // waits for a work item completion callback
  166. // You can pass a string that describes what sort of work item you are waiting on.
  167. // WARNING: This function saves the pointer to the string, it doesn't copy the string
  168. bool BYieldingWaitForWorkItem( const char *pszWorkItemName = NULL );
  169. // adds this work item to threaded work pool and waits for it
  170. bool BYieldingWaitForThreadFuncWorkItem( CWorkItem * );
  171. // calls a local function in a thread, and yields until it's done
  172. bool BYieldingWaitForThreadFunc( CFunctor *jobFunctor );
  173. // Used by the DO_NOT_YIELD() macros
  174. int32 GetDoNotYieldDepth() const;
  175. void PushDoNotYield( const char *pchFileAndLine );
  176. void PopDoNotYield();
  177. #ifdef DBGFLAG_VALIDATE
  178. virtual void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures
  179. static void ValidateStatics( CValidator &validator, const char *pchName );
  180. #endif
  181. // creates a job
  182. template <typename JOB_TYPE, typename PARAM_TYPE>
  183. static JOB_TYPE *AllocateJob( PARAM_TYPE *pParam )
  184. {
  185. return new JOB_TYPE( pParam );
  186. }
  187. // delete a job (the job knows what allocator to use)
  188. static void DeleteJob( CJob *pJob );
  189. void SetStartParam( void * pvStartParam ) { Assert( NULL == m_pvStartParam ); m_pvStartParam = pvStartParam; }
  190. void SetFromFromMsg( bool bRunFromMsg ) { m_bRunFromMsg = true; }
  191. void AddPacketToList( IMsgNetPacket *pNetPacket, const JobID_t gidJobIDSrc );
  192. // marks a packet as being finished with, releases the packet and frees the memory
  193. void ReleaseNetPacket( IMsgNetPacket *pNetPacket );
  194. void EndPause( EJobPauseReason eExpectedState );
  195. // Generate an assertion in the coroutine of this job
  196. // (creating a minidump). Useful for inspecting stuck jobs
  197. void GenerateAssert( const char *pchMsg = NULL );
  198. /// Return true if we tried to waited on a message of this type,
  199. /// and failed to receive it. (If so, then this means we could
  200. /// conceivably still receive a reply of that type at any moment.)
  201. bool BHasFailedToReceivedMsgType( MsgType_t m ) const;
  202. /// Mark that we awaited a message of the specified type, but timed out
  203. void MarkFailedToReceivedMsgType( MsgType_t m );
  204. /// Clear flag that we timed out waiting on a message of the specified type.
  205. /// This is used to allow you to wait ont he same message again, even if
  206. /// you know the reply might be a late reply. It's up to you to deal with
  207. /// mismatched replies!
  208. void ClearFailedToReceivedMsgType( MsgType_t m );
  209. protected:
  210. // main job implementation, in the coroutine. Every job must implement at least one of these methods.
  211. virtual bool BYieldingRunJob( void * pvStartParam ) { Assert( false ); return true; } // implement this if your job can be started directly
  212. virtual bool BYieldingRunJobFromMsg( IMsgNetPacket * pNetPacket ) { Assert( false ); return true; } // implement this if your job can be started by a network message
  213. // Can be overridden to return a different timeout per job class
  214. virtual uint32 CHeartbeatsBeforeTimeout();
  215. // Called by CJobMgr to send heartbeat message to our listeners during long operations
  216. void Heartbeat();
  217. // accessor to get access to the JobMgr from the server we belong to
  218. CJobMgr &GetJobMgr();
  219. uint32 m_bRunFromMsg:1,
  220. m_bWorkItemCanceled:1, // true if the work item we were waiting on was canceled
  221. m_bIsTest:1,
  222. m_bIsLongRunning:1;
  223. private:
  224. // starts the coroutine that activates the job
  225. void InitCoroutine();
  226. // continues the current job
  227. void Continue();
  228. // break into this coroutine - can only be called from OUTSIDE this coroutine
  229. void Debug();
  230. // pauses the current job - can only be called from inside a coroutine
  231. void Pause( EJobPauseReason eReason );
  232. static void BRunProxy( void *pvThis );
  233. JobID_t m_JobID; // Our unique identifier.
  234. HCoroutine m_hCoroutine;
  235. void * m_pvStartParam; // Start params for our job, if any
  236. // all these flags indicate some kind of failure and we will want to report them
  237. union {
  238. struct {
  239. uint32
  240. m_bJobFailed:1, // true if BYieldingRunJob returned false
  241. m_bLocksFailed:1,
  242. m_bLocksLongHeld:1,
  243. m_bLocksLongWait:1,
  244. m_bWaitTimeout:1,
  245. m_bLongInterYield:1,
  246. m_bTimeoutNetMsg:1,
  247. m_bTimeoutOther:1,
  248. m_uUnused:24;
  249. } m_bits;
  250. uint32 m_uFlags;
  251. } m_flags;
  252. int m_cLocksAttempted;
  253. int m_cLocksWaitedFor;
  254. EJobPauseReason m_ePauseReason;
  255. MsgType_t m_unWaitMsgType;
  256. CJobTime m_STimeStarted; // time (frame) at which this job started
  257. CJobTime m_STimeSwitched; // time (frame) at which we were last paused or continued
  258. CJobTime m_STimeNextHeartbeat; // Time at which next heartbeat should be performed
  259. CFastTimer m_FastTimerDelta; // How much time we've been running for without yielding
  260. CCycleCount m_cyclecountTotal; // Total runtime
  261. CJob *m_pJobPrev; // the job that launched us
  262. // lock manipulation
  263. void _SetLock( CLock *pLock, const char *filename, int line );
  264. void UnsetLock( CLock *pLock );
  265. void PassLockToJob( CJob *pNewJob, CLock *pLock );
  266. void OnLockDeleted( CLock *pLock );
  267. void AddJobToNotifyOnLockRelease( CJob *pJob );
  268. CUtlVectorFixedGrowable< CLock *, 2 > m_vecLocks;
  269. CLock *m_pWaitingOnLock; // lock we're waiting on, if any
  270. const char *m_pWaitingOnLockFilename;
  271. int m_waitingOnLockLine;
  272. CJob *m_pJobToNotifyOnLockRelease; // other job that wants this lock
  273. CWorkItem *m_pWaitingOnWorkItem; // set if job is waiting for this work item
  274. CJobMgr &m_JobMgr; // our job manager
  275. CUtlVectorFixedGrowable< IMsgNetPacket *, 1 > m_vecNetPackets; // list of tcp packets currently held by this job (ie, needing release on job exit)
  276. /// List of message types that we have waited for, but timed out.
  277. /// once this happens, we currently do not have a good mechanism
  278. /// to recover gracefully. But we at least can detect the situation
  279. /// and avoid getting totally hosed or processing the wrong reply
  280. CUtlVector<MsgType_t> m_vecMsgTypesFailedToReceive;
  281. // pointer to our own static job info
  282. struct JobType_t const *m_pJobType;
  283. // Name of the job for when it's not registered
  284. const char *m_pchJobName;
  285. // A stack of do not yield guards so we can print the right warning if they're nested
  286. CUtlLinkedList<const char *> m_stackDoNotYieldGuards;
  287. // setting the job info
  288. friend void Job_SetJobType( CJob &job, const JobType_t *pJobType );
  289. friend class CJobMgr;
  290. friend class CLock;
  291. // used to store the memory allocation stack
  292. CUtlMemory< unsigned char > m_memAllocStack;
  293. };
  294. // Only one job can be running at a time. We keep a global accessor to it.
  295. extern CJob *g_pJobCur;
  296. inline CJob &GJobCur() { Assert( g_pJobCur != NULL ); return *g_pJobCur; }
  297. #define AssertRunningJob() { Assert( NULL != g_pJobCur ); }
  298. #define AssertRunningThisJob() { Assert( this == g_pJobCur ); }
  299. #define AssertNotRunningThisJob() { Assert( this != g_pJobCur ); }
  300. #define AssertNotRunningJob() { Assert( NULL == g_pJobCur ); }
  301. //-----------------------------------------------------------------------------
  302. // Purpose: simple locking class
  303. // add this object to any classes you want jobs to be able to lock
  304. //-----------------------------------------------------------------------------
  305. class CLock
  306. {
  307. public:
  308. CLock( );
  309. ~CLock();
  310. bool BIsLocked() { return m_pJob != NULL; }
  311. CJob *GetJobLocking() { return m_pJob; }
  312. CJob *GetJobWaitingQueueHead() { return m_pJobToNotifyOnLockRelease; }
  313. CJob *GetJobWaitingQueueTail() { return m_pJobWaitingQueueTail; }
  314. void AddToWaitingQueue( CJob *pJob );
  315. const char *GetName() const;
  316. void SetName( const char *pchName );
  317. void SetName( const char *pchPrefix, uint64 ulID );
  318. void SetName( const CSteamID &steamID );
  319. int16 GetLockType() const { return m_nsLockType; }
  320. void SetLockType( int16 nsLockType ) { m_nsLockType = nsLockType; }
  321. uint64 GetLockSubType() const { return m_unLockSubType; }
  322. void SetLockSubType( uint64 unLockSubType ) { m_unLockSubType = unLockSubType; }
  323. int32 GetWaitingCount() const { return m_nWaitingCount; }
  324. int64 GetMicroSecondsSinceLock() const { return m_sTimeAcquired.CServerMicroSecsPassed(); }
  325. void IncrementReference();
  326. int DecrementReference();
  327. void ClearReference() { m_nRefCount = 0; }
  328. int32 GetReferenceCount() const { return m_nRefCount; }
  329. void Dump( const char *pszPrefix = "\t\t", int nPrintMax = 1, bool bPrintWaiting = true ) const;
  330. private:
  331. enum ENameType
  332. {
  333. k_ENameTypeNone = 0,
  334. k_ENameTypeSteamID = 1,
  335. k_ENameTypeConstStr = 2,
  336. k_ENameTypeConcat = 3,
  337. };
  338. CJob *m_pJob; // the job that's currently locking us
  339. CJob *m_pJobToNotifyOnLockRelease; // Pointer to the first job waiting on us
  340. CJob *m_pJobWaitingQueueTail; // Pointer to the last job waiting on us
  341. int16 m_nsLockType; // Lock priority for safely waiting on multiple locks
  342. int16 m_nsNameType; // Enum that controls how this lock is named
  343. uint64 m_ulID; // ID part of the lock's name
  344. const char *m_pchConstStr; // Prefix part of the lock's name
  345. mutable CUtlString m_strName; // Cached name
  346. int32 m_nRefCount; // # of times locked
  347. int32 m_nWaitingCount; // Count of jobs waiting on the lock
  348. CJobTime m_sTimeAcquired; // Time the lock was last locked
  349. uint64 m_unLockSubType;
  350. const char *m_pFilename; // Filename of the source file who aquired this lock
  351. int m_line; // Line number of the filename
  352. friend class CJob;
  353. };
  354. //-----------------------------------------------------------------------------
  355. // Purpose: automatic locking class
  356. //-----------------------------------------------------------------------------
  357. class CAutoCLock
  358. {
  359. public:
  360. CAutoCLock( CLock &refLock )
  361. : m_pLock ( &refLock)
  362. {
  363. DbgVerify( GJobCur().BYieldingAcquireLock( m_pLock ) );
  364. }
  365. // explicitly unlock before destruction
  366. void Unlock()
  367. {
  368. if ( m_pLock != NULL )
  369. GJobCur().ReleaseLock( m_pLock );
  370. m_pLock = NULL;
  371. }
  372. ~CAutoCLock( )
  373. {
  374. Unlock();
  375. }
  376. private:
  377. CLock *m_pLock;
  378. CJob *m_pJob;
  379. };
  380. } // namespace GCSDK
  381. #include "tier0/memdbgoff.h"
  382. #endif // GC_JOB_H