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.

1344 lines
42 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: A utility for a discrete job-oriented worker thread.
  4. //
  5. // The class CThreadPool is both the job queue, and the
  6. // worker thread. Except when the main thread attempts to
  7. // synchronously execute a job, most of the inter-thread locking
  8. // on the queue.
  9. //
  10. // The queue threading model uses a manual reset event for optimal
  11. // throughput. Adding to the queue is guarded by a semaphore that
  12. // will block the inserting thread if the queue has overflown.
  13. // This prevents the worker thread from being starved out even if
  14. // not running at a higher priority than the master thread.
  15. //
  16. // The thread function waits for jobs, services jobs, and manages
  17. // communication between the worker and master threads. The nature
  18. // of the work is opaque to the Executer.
  19. //
  20. // CJob instances actually do the work. The base class
  21. // calls virtual methods for job primitives, so derivations don't
  22. // need to worry about threading models. All of the variants of
  23. // job and OS can be expressed in this hierarchy. Instances of
  24. // CJob are the items placed in the queue, and by
  25. // overriding the job primitives they are the manner by which
  26. // users of the Executer control the state of the job.
  27. //
  28. //=============================================================================
  29. #include <limits.h>
  30. #include "tier0/threadtools.h"
  31. #include "tier1/refcount.h"
  32. #include "tier1/utllinkedlist.h"
  33. #include "tier1/utlvector.h"
  34. #include "tier1/functors.h"
  35. #include "tier0/vprof_telemetry.h"
  36. #include "vstdlib/vstdlib.h"
  37. #ifndef JOBTHREAD_H
  38. #define JOBTHREAD_H
  39. #ifdef AddJob // windows.h print function collisions
  40. #undef AddJob
  41. #undef GetJob
  42. #endif
  43. #ifdef VSTDLIB_DLL_EXPORT
  44. #define JOB_INTERFACE DLL_EXPORT
  45. #define JOB_OVERLOAD DLL_GLOBAL_EXPORT
  46. #define JOB_CLASS DLL_CLASS_EXPORT
  47. #else
  48. #define JOB_INTERFACE DLL_IMPORT
  49. #define JOB_OVERLOAD DLL_GLOBAL_IMPORT
  50. #define JOB_CLASS DLL_CLASS_IMPORT
  51. #endif
  52. #if defined( _WIN32 )
  53. #pragma once
  54. #endif
  55. //-----------------------------------------------------------------------------
  56. //
  57. //-----------------------------------------------------------------------------
  58. class CJob;
  59. //-----------------------------------------------------------------------------
  60. //
  61. //-----------------------------------------------------------------------------
  62. enum JobStatusEnum_t
  63. {
  64. // Use negative for errors
  65. JOB_OK, // operation is successful
  66. JOB_STATUS_PENDING, // file is properly queued, waiting for service
  67. JOB_STATUS_INPROGRESS, // file is being accessed
  68. JOB_STATUS_ABORTED, // file was aborted by caller
  69. JOB_STATUS_UNSERVICED, // file is not yet queued
  70. };
  71. typedef int JobStatus_t;
  72. enum JobFlags_t
  73. {
  74. JF_IO = ( 1 << 0 ), // The job primarily blocks on IO or hardware
  75. JF_BOOST_THREAD = ( 1 << 1 ), // Up the thread priority to max allowed while processing task
  76. JF_SERIAL = ( 1 << 2 ), // Job cannot be executed out of order relative to other "strict" jobs
  77. JF_QUEUE = ( 1 << 3 ), // Queue it, even if not an IO job
  78. };
  79. enum JobPriority_t
  80. {
  81. JP_LOW,
  82. JP_NORMAL,
  83. JP_HIGH
  84. };
  85. #define TP_MAX_POOL_THREADS 64
  86. struct ThreadPoolStartParams_t
  87. {
  88. ThreadPoolStartParams_t( bool bIOThreads = false, unsigned nThreads = -1, int *pAffinities = NULL, ThreeState_t fDistribute = TRS_NONE, unsigned nStackSize = -1, int iThreadPriority = SHRT_MIN )
  89. : bIOThreads( bIOThreads ), nThreads( nThreads ), fDistribute( fDistribute ), nStackSize( nStackSize ), iThreadPriority( iThreadPriority ), nThreadsMax( -1 )
  90. {
  91. bExecOnThreadPoolThreadsOnly = false;
  92. bUseAffinityTable = ( pAffinities != NULL ) && ( fDistribute == TRS_TRUE ) && ( nThreads != -1 );
  93. if ( bUseAffinityTable )
  94. {
  95. // user supplied an optional 1:1 affinity mapping to override normal distribute behavior
  96. nThreads = MIN( TP_MAX_POOL_THREADS, nThreads );
  97. for ( unsigned int i = 0; i < nThreads; i++ )
  98. {
  99. iAffinityTable[i] = pAffinities[i];
  100. }
  101. }
  102. }
  103. int nThreads;
  104. int nThreadsMax;
  105. ThreeState_t fDistribute;
  106. int nStackSize;
  107. int iThreadPriority;
  108. int iAffinityTable[TP_MAX_POOL_THREADS];
  109. bool bIOThreads : 1;
  110. bool bUseAffinityTable : 1;
  111. bool bExecOnThreadPoolThreadsOnly : 1;
  112. };
  113. //-----------------------------------------------------------------------------
  114. //
  115. // IThreadPool
  116. //
  117. //-----------------------------------------------------------------------------
  118. typedef bool (*JobFilter_t)( CJob * );
  119. //---------------------------------------------------------
  120. // Messages supported through the CallWorker() method
  121. //---------------------------------------------------------
  122. enum ThreadPoolMessages_t
  123. {
  124. TPM_EXIT, // Exit the thread
  125. TPM_SUSPEND, // Suspend after next operation
  126. TPM_RUNFUNCTOR, // Run functor, reply when done.
  127. };
  128. //---------------------------------------------------------
  129. abstract_class IThreadPool : public IRefCounted
  130. {
  131. public:
  132. virtual ~IThreadPool() {};
  133. //-----------------------------------------------------
  134. // Thread functions
  135. //-----------------------------------------------------
  136. virtual bool Start( const ThreadPoolStartParams_t &startParams = ThreadPoolStartParams_t() ) = 0;
  137. virtual bool Stop( int timeout = TT_INFINITE ) = 0;
  138. //-----------------------------------------------------
  139. // Functions for any thread
  140. //-----------------------------------------------------
  141. virtual unsigned GetJobCount() = 0;
  142. virtual int NumThreads() = 0;
  143. virtual int NumIdleThreads() = 0;
  144. //-----------------------------------------------------
  145. // Pause/resume processing jobs
  146. //-----------------------------------------------------
  147. virtual int SuspendExecution() = 0;
  148. virtual int ResumeExecution() = 0;
  149. //-----------------------------------------------------
  150. // Offer the current thread to the pool
  151. //-----------------------------------------------------
  152. virtual int YieldWait( CThreadEvent **pEvents, int nEvents, bool bWaitAll = true, unsigned timeout = TT_INFINITE ) = 0;
  153. virtual int YieldWait( CJob **, int nJobs, bool bWaitAll = true, unsigned timeout = TT_INFINITE ) = 0;
  154. virtual void Yield( unsigned timeout ) = 0;
  155. bool YieldWait( CThreadEvent &event, unsigned timeout = TT_INFINITE );
  156. bool YieldWait( CJob *, unsigned timeout = TT_INFINITE );
  157. //-----------------------------------------------------
  158. // Add a native job to the queue (master thread)
  159. //-----------------------------------------------------
  160. virtual void AddJob( CJob * ) = 0;
  161. //-----------------------------------------------------
  162. // All threads execute pFunctor asap. Thread will either wake up
  163. // and execute or execute pFunctor right after completing current job and
  164. // before looking for another job.
  165. //-----------------------------------------------------
  166. virtual void ExecuteHighPriorityFunctor( CFunctor *pFunctor ) = 0;
  167. //-----------------------------------------------------
  168. // Add an function object to the queue (master thread)
  169. //-----------------------------------------------------
  170. virtual void AddFunctor( CFunctor *pFunctor, CJob **ppJob = NULL, const char *pszDescription = NULL, unsigned flags = 0 ) { AddFunctorInternal( RetAddRef( pFunctor ), ppJob, pszDescription, flags ); }
  171. //-----------------------------------------------------
  172. // Change the priority of an active job
  173. //-----------------------------------------------------
  174. virtual void ChangePriority( CJob *p, JobPriority_t priority ) = 0;
  175. //-----------------------------------------------------
  176. // Bulk job manipulation (blocking)
  177. //-----------------------------------------------------
  178. int ExecuteAll( JobFilter_t pfnFilter = NULL ) { return ExecuteToPriority( JP_LOW, pfnFilter ); }
  179. virtual int ExecuteToPriority( JobPriority_t toPriority, JobFilter_t pfnFilter = NULL ) = 0;
  180. virtual int AbortAll() = 0;
  181. //-----------------------------------------------------
  182. virtual void Reserved1() = 0;
  183. //-----------------------------------------------------
  184. // Add an arbitrary call to the queue (master thread)
  185. //
  186. // Avert thy eyes! Imagine rather:
  187. //
  188. // CJob *AddCall( <function>, [args1, [arg2,]...]
  189. // CJob *AddCall( <object>, <function>, [args1, [arg2,]...]
  190. // CJob *AddRefCall( <object>, <function>, [args1, [arg2,]...]
  191. // CJob *QueueCall( <function>, [args1, [arg2,]...]
  192. // CJob *QueueCall( <object>, <function>, [args1, [arg2,]...]
  193. //-----------------------------------------------------
  194. #define DEFINE_NONMEMBER_ADD_CALL(N) \
  195. template <typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
  196. CJob *AddCall(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \
  197. { \
  198. CJob *pJob; \
  199. if ( !NumIdleThreads() ) \
  200. { \
  201. pJob = GetDummyJob(); \
  202. FunctorDirectCall( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \
  203. } \
  204. else \
  205. { \
  206. AddFunctorInternal( CreateFunctor( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \
  207. } \
  208. \
  209. return pJob; \
  210. }
  211. //-------------------------------------
  212. #define DEFINE_MEMBER_ADD_CALL(N) \
  213. template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
  214. CJob *AddCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \
  215. { \
  216. CJob *pJob; \
  217. if ( !NumIdleThreads() ) \
  218. { \
  219. pJob = GetDummyJob(); \
  220. FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \
  221. } \
  222. else \
  223. { \
  224. AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \
  225. } \
  226. \
  227. return pJob; \
  228. }
  229. //-------------------------------------
  230. #define DEFINE_CONST_MEMBER_ADD_CALL(N) \
  231. template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
  232. CJob *AddCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \
  233. { \
  234. CJob *pJob; \
  235. if ( !NumIdleThreads() ) \
  236. { \
  237. pJob = GetDummyJob(); \
  238. FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \
  239. } \
  240. else \
  241. { \
  242. AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \
  243. } \
  244. \
  245. return pJob; \
  246. }
  247. //-------------------------------------
  248. #define DEFINE_REF_COUNTING_MEMBER_ADD_CALL(N) \
  249. template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
  250. CJob *AddRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \
  251. { \
  252. CJob *pJob; \
  253. if ( !NumIdleThreads() ) \
  254. { \
  255. pJob = GetDummyJob(); \
  256. FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \
  257. } \
  258. else \
  259. { \
  260. AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \
  261. } \
  262. \
  263. return pJob; \
  264. }
  265. //-------------------------------------
  266. #define DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL(N) \
  267. template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
  268. CJob *AddRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \
  269. { \
  270. CJob *pJob; \
  271. if ( !NumIdleThreads() ) \
  272. { \
  273. pJob = GetDummyJob(); \
  274. FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \
  275. } \
  276. else \
  277. { \
  278. AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \
  279. } \
  280. \
  281. return pJob; \
  282. }
  283. //-----------------------------------------------------------------------------
  284. #define DEFINE_NONMEMBER_QUEUE_CALL(N) \
  285. template <typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
  286. CJob *QueueCall(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \
  287. { \
  288. CJob *pJob; \
  289. AddFunctorInternal( CreateFunctor( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \
  290. return pJob; \
  291. }
  292. //-------------------------------------
  293. #define DEFINE_MEMBER_QUEUE_CALL(N) \
  294. template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
  295. CJob *QueueCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \
  296. { \
  297. CJob *pJob; \
  298. AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \
  299. return pJob; \
  300. }
  301. //-------------------------------------
  302. #define DEFINE_CONST_MEMBER_QUEUE_CALL(N) \
  303. template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
  304. CJob *QueueCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \
  305. { \
  306. CJob *pJob; \
  307. AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \
  308. return pJob; \
  309. }
  310. //-------------------------------------
  311. #define DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL(N) \
  312. template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
  313. CJob *QueueRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \
  314. { \
  315. CJob *pJob; \
  316. AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \
  317. return pJob; \
  318. }
  319. //-------------------------------------
  320. #define DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL(N) \
  321. template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
  322. CJob *QueueRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \
  323. { \
  324. CJob *pJob; \
  325. AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \
  326. \
  327. return pJob; \
  328. }
  329. FUNC_GENERATE_ALL( DEFINE_NONMEMBER_ADD_CALL );
  330. FUNC_GENERATE_ALL( DEFINE_MEMBER_ADD_CALL );
  331. FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_ADD_CALL );
  332. FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_MEMBER_ADD_CALL );
  333. FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL );
  334. FUNC_GENERATE_ALL( DEFINE_NONMEMBER_QUEUE_CALL );
  335. FUNC_GENERATE_ALL( DEFINE_MEMBER_QUEUE_CALL );
  336. FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_QUEUE_CALL );
  337. FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL );
  338. FUNC_GENERATE_ALL( DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL );
  339. #undef DEFINE_NONMEMBER_ADD_CALL
  340. #undef DEFINE_MEMBER_ADD_CALL
  341. #undef DEFINE_CONST_MEMBER_ADD_CALL
  342. #undef DEFINE_REF_COUNTING_MEMBER_ADD_CALL
  343. #undef DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL
  344. #undef DEFINE_NONMEMBER_QUEUE_CALL
  345. #undef DEFINE_MEMBER_QUEUE_CALL
  346. #undef DEFINE_CONST_MEMBER_QUEUE_CALL
  347. #undef DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL
  348. #undef DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL
  349. private:
  350. virtual void AddFunctorInternal( CFunctor *, CJob ** = NULL, const char *pszDescription = NULL, unsigned flags = 0 ) = 0;
  351. //-----------------------------------------------------
  352. // Services for internal use by job instances
  353. //-----------------------------------------------------
  354. friend class CJob;
  355. virtual CJob *GetDummyJob() = 0;
  356. public:
  357. virtual void Distribute( bool bDistribute = true, int *pAffinityTable = NULL ) = 0;
  358. virtual bool Start( const ThreadPoolStartParams_t &startParams, const char *pszNameOverride ) = 0;
  359. };
  360. //-----------------------------------------------------------------------------
  361. JOB_INTERFACE IThreadPool *CreateThreadPool();
  362. JOB_INTERFACE void DestroyThreadPool( IThreadPool *pPool );
  363. //-------------------------------------
  364. JOB_INTERFACE void RunThreadPoolTests();
  365. //-----------------------------------------------------------------------------
  366. JOB_INTERFACE IThreadPool *g_pThreadPool;
  367. //-----------------------------------------------------------------------------
  368. // Class to combine the metadata for an operation and the ability to perform
  369. // the operation. Meant for inheritance. All functions inline, defers to executor
  370. //-----------------------------------------------------------------------------
  371. DECLARE_POINTER_HANDLE( ThreadPoolData_t );
  372. #define JOB_NO_DATA ((ThreadPoolData_t)-1)
  373. class CJob : public CRefCounted1<IRefCounted, CRefCountServiceMT>
  374. {
  375. public:
  376. CJob( JobPriority_t priority = JP_NORMAL )
  377. : m_status( JOB_STATUS_UNSERVICED ),
  378. m_ThreadPoolData( JOB_NO_DATA ),
  379. m_priority( priority ),
  380. m_flags( 0 ),
  381. m_pThreadPool( NULL ),
  382. m_CompleteEvent( true ),
  383. m_iServicingThread( -1 )
  384. {
  385. m_szDescription[ 0 ] = 0;
  386. }
  387. //-----------------------------------------------------
  388. // Priority (not thread safe)
  389. //-----------------------------------------------------
  390. void SetPriority( JobPriority_t priority ) { m_priority = priority; }
  391. JobPriority_t GetPriority() const { return m_priority; }
  392. //-----------------------------------------------------
  393. void SetFlags( unsigned flags ) { m_flags = flags; }
  394. unsigned GetFlags() const { return m_flags; }
  395. //-----------------------------------------------------
  396. void SetServiceThread( int iServicingThread ) { m_iServicingThread = (char)iServicingThread; }
  397. int GetServiceThread() const { return m_iServicingThread; }
  398. void ClearServiceThread() { m_iServicingThread = -1; }
  399. //-----------------------------------------------------
  400. // Fast queries
  401. //-----------------------------------------------------
  402. bool Executed() const { return ( m_status == JOB_OK ); }
  403. bool CanExecute() const { return ( m_status == JOB_STATUS_PENDING || m_status == JOB_STATUS_UNSERVICED ); }
  404. bool IsFinished() const { return ( m_status != JOB_STATUS_PENDING && m_status != JOB_STATUS_INPROGRESS && m_status != JOB_STATUS_UNSERVICED ); }
  405. JobStatus_t GetStatus() const { return m_status; }
  406. /// Slam the status to a particular value. This is named "slam" instead of "set,"
  407. /// to warn you that it should only be used in unusual situations. Otherwise, the
  408. /// job manager really should manage the status for you, and you should not manhandle it.
  409. void SlamStatus(JobStatus_t s) { m_status = s; }
  410. //-----------------------------------------------------
  411. // Try to acquire ownership (to satisfy). If you take the lock, you must either execute or abort.
  412. //-----------------------------------------------------
  413. bool TryLock() { return m_mutex.TryLock(); }
  414. void Lock() { m_mutex.Lock(); }
  415. void Unlock() { m_mutex.Unlock(); }
  416. //-----------------------------------------------------
  417. // Thread event support (safe for NULL this to simplify code )
  418. //-----------------------------------------------------
  419. bool WaitForFinish( uint32 dwTimeout = TT_INFINITE ) { if (!this) return true; return ( !IsFinished() ) ? g_pThreadPool->YieldWait( this, dwTimeout ) : true; }
  420. bool WaitForFinishAndRelease( uint32 dwTimeout = TT_INFINITE ) { if (!this) return true; bool bResult = WaitForFinish( dwTimeout); Release(); return bResult; }
  421. CThreadEvent *AccessEvent() { return &m_CompleteEvent; }
  422. //-----------------------------------------------------
  423. // Perform the job
  424. //-----------------------------------------------------
  425. JobStatus_t Execute();
  426. JobStatus_t TryExecute();
  427. JobStatus_t ExecuteAndRelease() { JobStatus_t status = Execute(); Release(); return status; }
  428. JobStatus_t TryExecuteAndRelease() { JobStatus_t status = TryExecute(); Release(); return status; }
  429. //-----------------------------------------------------
  430. // Terminate the job, discard if partially or wholly fulfilled
  431. //-----------------------------------------------------
  432. JobStatus_t Abort( bool bDiscard = true );
  433. virtual char const *Describe() { return m_szDescription[ 0 ] ? m_szDescription : "Job"; }
  434. virtual void SetDescription( const char *pszDescription )
  435. {
  436. if( pszDescription )
  437. {
  438. Q_strncpy( m_szDescription, pszDescription, sizeof( m_szDescription ) );
  439. }
  440. else
  441. {
  442. m_szDescription[ 0 ] = 0;
  443. }
  444. }
  445. private:
  446. //-----------------------------------------------------
  447. friend class CThreadPool;
  448. JobStatus_t m_status;
  449. JobPriority_t m_priority;
  450. CThreadMutex m_mutex;
  451. unsigned char m_flags;
  452. char m_iServicingThread;
  453. short m_reserved;
  454. ThreadPoolData_t m_ThreadPoolData;
  455. IThreadPool * m_pThreadPool;
  456. CThreadEvent m_CompleteEvent;
  457. char m_szDescription[ 32 ];
  458. private:
  459. //-----------------------------------------------------
  460. CJob( const CJob &fromRequest );
  461. void operator=(const CJob &fromRequest );
  462. virtual JobStatus_t DoExecute() = 0;
  463. virtual JobStatus_t DoAbort( bool bDiscard ) { return JOB_STATUS_ABORTED; }
  464. virtual void DoCleanup() {}
  465. };
  466. //-----------------------------------------------------------------------------
  467. class CFunctorJob : public CJob
  468. {
  469. public:
  470. CFunctorJob( CFunctor *pFunctor, const char *pszDescription = NULL )
  471. : m_pFunctor( pFunctor )
  472. {
  473. if ( pszDescription )
  474. {
  475. Q_strncpy( m_szDescription, pszDescription, sizeof(m_szDescription) );
  476. }
  477. else
  478. {
  479. m_szDescription[0] = 0;
  480. }
  481. }
  482. virtual JobStatus_t DoExecute()
  483. {
  484. (*m_pFunctor)();
  485. return JOB_OK;
  486. }
  487. const char *Describe()
  488. {
  489. return m_szDescription;
  490. }
  491. private:
  492. CRefPtr<CFunctor> m_pFunctor;
  493. char m_szDescription[16];
  494. };
  495. //-----------------------------------------------------------------------------
  496. // Utility for managing multiple jobs
  497. //-----------------------------------------------------------------------------
  498. class CJobSet
  499. {
  500. public:
  501. CJobSet( CJob *pJob = NULL )
  502. {
  503. if ( pJob )
  504. {
  505. m_jobs.AddToTail( pJob );
  506. }
  507. }
  508. CJobSet( CJob **ppJobs, int nJobs )
  509. {
  510. if ( ppJobs )
  511. {
  512. m_jobs.AddMultipleToTail( nJobs, ppJobs );
  513. }
  514. }
  515. ~CJobSet()
  516. {
  517. for ( int i = 0; i < m_jobs.Count(); i++ )
  518. {
  519. m_jobs[i]->Release();
  520. }
  521. }
  522. void operator+=( CJob *pJob )
  523. {
  524. m_jobs.AddToTail( pJob );
  525. }
  526. void operator-=( CJob *pJob )
  527. {
  528. m_jobs.FindAndRemove( pJob );
  529. }
  530. void Execute( bool bRelease = true )
  531. {
  532. for ( int i = 0; i < m_jobs.Count(); i++ )
  533. {
  534. m_jobs[i]->Execute();
  535. if ( bRelease )
  536. {
  537. m_jobs[i]->Release();
  538. }
  539. }
  540. if ( bRelease )
  541. {
  542. m_jobs.RemoveAll();
  543. }
  544. }
  545. void Abort( bool bRelease = true )
  546. {
  547. for ( int i = 0; i < m_jobs.Count(); i++ )
  548. {
  549. m_jobs[i]->Abort();
  550. if ( bRelease )
  551. {
  552. m_jobs[i]->Release();
  553. }
  554. }
  555. if ( bRelease )
  556. {
  557. m_jobs.RemoveAll();
  558. }
  559. }
  560. void WaitForFinish( bool bRelease = true )
  561. {
  562. for ( int i = 0; i < m_jobs.Count(); i++ )
  563. {
  564. m_jobs[i]->WaitForFinish();
  565. if ( bRelease )
  566. {
  567. m_jobs[i]->Release();
  568. }
  569. }
  570. if ( bRelease )
  571. {
  572. m_jobs.RemoveAll();
  573. }
  574. }
  575. void WaitForFinish( IThreadPool *pPool, bool bRelease = true )
  576. {
  577. pPool->YieldWait( m_jobs.Base(), m_jobs.Count() );
  578. if ( bRelease )
  579. {
  580. for ( int i = 0; i < m_jobs.Count(); i++ )
  581. {
  582. m_jobs[i]->Release();
  583. }
  584. m_jobs.RemoveAll();
  585. }
  586. }
  587. private:
  588. CUtlVectorFixed<CJob *, 16> m_jobs;
  589. };
  590. //-----------------------------------------------------------------------------
  591. // Job helpers
  592. //-----------------------------------------------------------------------------
  593. #define ThreadExecute g_pThreadPool->QueueCall
  594. #define ThreadExecuteRef g_pThreadPool->QueueRefCall
  595. #define BeginExecuteParallel() do { CJobSet jobSet
  596. #define EndExecuteParallel() jobSet.WaitForFinish( g_pThreadPool ); } while (0)
  597. #define ExecuteParallel jobSet += g_pThreadPool->QueueCall
  598. #define ExecuteRefParallel jobSet += g_pThreadPool->QueueCallRef
  599. //-----------------------------------------------------------------------------
  600. // Work splitting: array split, best when cost per item is roughly equal
  601. //-----------------------------------------------------------------------------
  602. #ifdef _MSC_VER
  603. #pragma warning(push)
  604. #pragma warning(disable:4389)
  605. #pragma warning(disable:4018)
  606. #pragma warning(disable:4701)
  607. #endif
  608. #define DEFINE_NON_MEMBER_ITER_RANGE_PARALLEL(N) \
  609. template <typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N, typename ITERTYPE1, typename ITERTYPE2> \
  610. void IterRangeParallel(FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( ITERTYPE1, ITERTYPE2 FUNC_ADDL_TEMPLATE_FUNC_PARAMS_##N ), ITERTYPE1 from, ITERTYPE2 to FUNC_ARG_FORMAL_PARAMS_##N ) \
  611. { \
  612. const int MAX_THREADS = 16; \
  613. int nIdle = g_pThreadPool->NumIdleThreads(); \
  614. ITERTYPE1 range = to - from; \
  615. int nThreads = MIN( nIdle + 1, range ); \
  616. if ( nThreads > MAX_THREADS ) \
  617. { \
  618. nThreads = MAX_THREADS; \
  619. } \
  620. if ( nThreads < 2 ) \
  621. { \
  622. FunctorDirectCall( pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \
  623. } \
  624. else \
  625. { \
  626. ITERTYPE1 nIncrement = range / nThreads; \
  627. \
  628. CJobSet jobSet; \
  629. while ( --nThreads ) \
  630. { \
  631. ITERTYPE2 thisTo = from + nIncrement; \
  632. jobSet += g_pThreadPool->AddCall( pfnProxied, from, thisTo FUNC_FUNCTOR_CALL_ARGS_##N ); \
  633. from = thisTo; \
  634. } \
  635. FunctorDirectCall( pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \
  636. jobSet.WaitForFinish( g_pThreadPool ); \
  637. } \
  638. \
  639. }
  640. FUNC_GENERATE_ALL( DEFINE_NON_MEMBER_ITER_RANGE_PARALLEL );
  641. #define DEFINE_MEMBER_ITER_RANGE_PARALLEL(N) \
  642. template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N, typename ITERTYPE1, typename ITERTYPE2> \
  643. void IterRangeParallel(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( ITERTYPE1, ITERTYPE2 FUNC_ADDL_TEMPLATE_FUNC_PARAMS_##N ), ITERTYPE1 from, ITERTYPE2 to FUNC_ARG_FORMAL_PARAMS_##N ) \
  644. { \
  645. const int MAX_THREADS = 16; \
  646. int nIdle = g_pThreadPool->NumIdleThreads(); \
  647. ITERTYPE1 range = to - from; \
  648. int nThreads = MIN( nIdle + 1, range ); \
  649. if ( nThreads > MAX_THREADS ) \
  650. { \
  651. nThreads = MAX_THREADS; \
  652. } \
  653. if ( nThreads < 2 ) \
  654. { \
  655. FunctorDirectCall( pObject, pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \
  656. } \
  657. else \
  658. { \
  659. ITERTYPE1 nIncrement = range / nThreads; \
  660. \
  661. CJobSet jobSet; \
  662. while ( --nThreads ) \
  663. { \
  664. ITERTYPE2 thisTo = from + nIncrement; \
  665. jobSet += g_pThreadPool->AddCall( pObject, pfnProxied, from, thisTo FUNC_FUNCTOR_CALL_ARGS_##N ); \
  666. from = thisTo; \
  667. } \
  668. FunctorDirectCall( pObject, pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \
  669. jobSet.WaitForFinish( g_pThreadPool ); \
  670. } \
  671. \
  672. }
  673. FUNC_GENERATE_ALL( DEFINE_MEMBER_ITER_RANGE_PARALLEL );
  674. //-----------------------------------------------------------------------------
  675. // Work splitting: competitive, best when cost per item varies a lot
  676. //-----------------------------------------------------------------------------
  677. template <typename T>
  678. class CJobItemProcessor
  679. {
  680. public:
  681. typedef T ItemType_t;
  682. void Begin() {}
  683. // void Process( ItemType_t & ) {}
  684. void End() {}
  685. };
  686. template <typename T>
  687. class CFuncJobItemProcessor : public CJobItemProcessor<T>
  688. {
  689. public:
  690. void Init(void (*pfnProcess)( T & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL )
  691. {
  692. m_pfnProcess = pfnProcess;
  693. m_pfnBegin = pfnBegin;
  694. m_pfnEnd = pfnEnd;
  695. }
  696. //CFuncJobItemProcessor(OBJECT_TYPE_PTR pObject, void (FUNCTION_CLASS::*pfnProcess)( ITEM_TYPE & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL );
  697. void Begin() { if ( m_pfnBegin ) (*m_pfnBegin)(); }
  698. void Process( T &item ) { (*m_pfnProcess)( item ); }
  699. void End() { if ( m_pfnEnd ) (*m_pfnEnd)(); }
  700. protected:
  701. void (*m_pfnProcess)( T & );
  702. void (*m_pfnBegin)();
  703. void (*m_pfnEnd)();
  704. };
  705. template <typename T, class OBJECT_TYPE, class FUNCTION_CLASS = OBJECT_TYPE >
  706. class CMemberFuncJobItemProcessor : public CJobItemProcessor<T>
  707. {
  708. public:
  709. void Init( OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( T & ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL )
  710. {
  711. m_pObject = pObject;
  712. m_pfnProcess = pfnProcess;
  713. m_pfnBegin = pfnBegin;
  714. m_pfnEnd = pfnEnd;
  715. }
  716. void Begin() { if ( m_pfnBegin ) ((*m_pObject).*m_pfnBegin)(); }
  717. void Process( T &item ) { ((*m_pObject).*m_pfnProcess)( item ); }
  718. void End() { if ( m_pfnEnd ) ((*m_pObject).*m_pfnEnd)(); }
  719. protected:
  720. OBJECT_TYPE *m_pObject;
  721. void (FUNCTION_CLASS::*m_pfnProcess)( T & );
  722. void (FUNCTION_CLASS::*m_pfnBegin)();
  723. void (FUNCTION_CLASS::*m_pfnEnd)();
  724. };
  725. template <typename ITEM_TYPE, class ITEM_PROCESSOR_TYPE>
  726. class CParallelProcessor
  727. {
  728. public:
  729. CParallelProcessor( const char *pszDescription )
  730. {
  731. m_pItems = m_pLimit= 0;
  732. m_szDescription = pszDescription;
  733. }
  734. void Run( ITEM_TYPE *pItems, unsigned nItems, int nMaxParallel = INT_MAX, IThreadPool *pThreadPool = NULL )
  735. {
  736. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "Run %s %d", m_szDescription, nItems );
  737. if ( nItems == 0 )
  738. return;
  739. if ( !pThreadPool )
  740. {
  741. pThreadPool = g_pThreadPool;
  742. }
  743. m_pItems = pItems;
  744. m_pLimit = pItems + nItems;
  745. int nJobs = nItems - 1;
  746. if ( nJobs > nMaxParallel )
  747. {
  748. nJobs = nMaxParallel;
  749. }
  750. if (! pThreadPool ) // only possible on linux
  751. {
  752. DoExecute( );
  753. return;
  754. }
  755. int nThreads = pThreadPool->NumThreads();
  756. if ( nJobs > nThreads )
  757. {
  758. nJobs = nThreads;
  759. }
  760. if ( nJobs > 1 )
  761. {
  762. CJob **jobs = (CJob **)stackalloc( nJobs * sizeof(CJob **) );
  763. int i = nJobs;
  764. while( i-- )
  765. {
  766. jobs[i] = pThreadPool->QueueCall( this, &CParallelProcessor<ITEM_TYPE, ITEM_PROCESSOR_TYPE>::DoExecute );
  767. jobs[i]->SetDescription( m_szDescription );
  768. }
  769. DoExecute();
  770. for ( i = 0; i < nJobs; i++ )
  771. {
  772. jobs[i]->Abort(); // will either abort ones that never got a thread, or noop on ones that did
  773. jobs[i]->Release();
  774. }
  775. }
  776. else
  777. {
  778. DoExecute();
  779. }
  780. }
  781. ITEM_PROCESSOR_TYPE m_ItemProcessor;
  782. private:
  783. void DoExecute()
  784. {
  785. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "DoExecute %s", m_szDescription );
  786. if ( m_pItems < m_pLimit )
  787. {
  788. m_ItemProcessor.Begin();
  789. ITEM_TYPE *pLimit = m_pLimit;
  790. for (;;)
  791. {
  792. ITEM_TYPE *pCurrent = m_pItems++;
  793. if ( pCurrent < pLimit )
  794. {
  795. m_ItemProcessor.Process( *pCurrent );
  796. }
  797. else
  798. {
  799. break;
  800. }
  801. }
  802. m_ItemProcessor.End();
  803. }
  804. }
  805. CInterlockedPtr<ITEM_TYPE> m_pItems;
  806. ITEM_TYPE * m_pLimit;
  807. const char * m_szDescription;
  808. };
  809. template <typename ITEM_TYPE>
  810. inline void ParallelProcess( const char *pszDescription, ITEM_TYPE *pItems, unsigned nItems, void (*pfnProcess)( ITEM_TYPE & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX )
  811. {
  812. CParallelProcessor<ITEM_TYPE, CFuncJobItemProcessor<ITEM_TYPE> > processor( pszDescription );
  813. processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd );
  814. processor.Run( pItems, nItems, nMaxParallel );
  815. }
  816. template <typename ITEM_TYPE, typename OBJECT_TYPE, typename FUNCTION_CLASS >
  817. inline void ParallelProcess( const char *pszDescription, 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 )
  818. {
  819. CParallelProcessor<ITEM_TYPE, CMemberFuncJobItemProcessor<ITEM_TYPE, OBJECT_TYPE, FUNCTION_CLASS> > processor( pszDescription );
  820. processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd );
  821. processor.Run( pItems, nItems, nMaxParallel );
  822. }
  823. // Parallel Process that lets you specify threadpool
  824. template <typename ITEM_TYPE>
  825. inline void ParallelProcess( const char *pszDescription, IThreadPool *pPool, ITEM_TYPE *pItems, unsigned nItems, void (*pfnProcess)( ITEM_TYPE & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX )
  826. {
  827. CParallelProcessor<ITEM_TYPE, CFuncJobItemProcessor<ITEM_TYPE> > processor( pszDescription );
  828. processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd );
  829. processor.Run( pItems, nItems, nMaxParallel, pPool );
  830. }
  831. template <class ITEM_PROCESSOR_TYPE>
  832. class CParallelLoopProcessor
  833. {
  834. public:
  835. CParallelLoopProcessor( const char *pszDescription )
  836. {
  837. m_lIndex = m_lLimit= 0;
  838. m_nActive = 0;
  839. m_szDescription = pszDescription;
  840. }
  841. void Run( long lBegin, long nItems, int nMaxParallel = INT_MAX )
  842. {
  843. if ( nItems )
  844. {
  845. m_lIndex = lBegin;
  846. m_lLimit = lBegin + nItems;
  847. int i = g_pThreadPool->NumIdleThreads();
  848. if ( nMaxParallel < i)
  849. {
  850. i = nMaxParallel;
  851. }
  852. while( i-- )
  853. {
  854. ++m_nActive;
  855. ThreadExecute( this, &CParallelLoopProcessor<ITEM_PROCESSOR_TYPE>::DoExecute )->Release();
  856. }
  857. ++m_nActive;
  858. DoExecute();
  859. while ( m_nActive )
  860. {
  861. ThreadPause();
  862. }
  863. }
  864. }
  865. ITEM_PROCESSOR_TYPE m_ItemProcessor;
  866. private:
  867. void DoExecute()
  868. {
  869. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "DoExecute %s", m_szDescription );
  870. m_ItemProcessor.Begin();
  871. long lLimit = m_lLimit;
  872. for (;;)
  873. {
  874. long lIndex = m_lIndex ++;
  875. if ( lIndex < lLimit )
  876. {
  877. m_ItemProcessor.Process( lIndex );
  878. }
  879. else
  880. {
  881. break;
  882. }
  883. }
  884. m_ItemProcessor.End();
  885. --m_nActive;
  886. }
  887. CInterlockedInt m_lIndex;
  888. long m_lLimit;
  889. CInterlockedInt m_nActive;
  890. const char * m_szDescription;
  891. };
  892. inline void ParallelLoopProcess( const char *szDescription, long lBegin, unsigned nItems, void (*pfnProcess)( long const & ), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX )
  893. {
  894. CParallelLoopProcessor< CFuncJobItemProcessor< long const > > processor( szDescription );
  895. processor.m_ItemProcessor.Init( pfnProcess, pfnBegin, pfnEnd );
  896. processor.Run( lBegin, nItems, nMaxParallel );
  897. }
  898. template < typename OBJECT_TYPE, typename FUNCTION_CLASS >
  899. inline void ParallelLoopProcess( const char *szDescription, long lBegin, unsigned nItems, OBJECT_TYPE *pObject, void (FUNCTION_CLASS::*pfnProcess)( long const & ), void (FUNCTION_CLASS::*pfnBegin)() = NULL, void (FUNCTION_CLASS::*pfnEnd)() = NULL, int nMaxParallel = INT_MAX )
  900. {
  901. CParallelLoopProcessor< CMemberFuncJobItemProcessor<long const, OBJECT_TYPE, FUNCTION_CLASS> > processor( szDescription );
  902. processor.m_ItemProcessor.Init( pObject, pfnProcess, pfnBegin, pfnEnd );
  903. processor.Run( lBegin, nItems, nMaxParallel );
  904. }
  905. template <class Derived>
  906. class CParallelProcessorBase
  907. {
  908. protected:
  909. typedef CParallelProcessorBase<Derived> ThisParallelProcessorBase_t;
  910. typedef Derived ThisParallelProcessorDerived_t;
  911. public:
  912. CParallelProcessorBase()
  913. {
  914. m_nActive = 0;
  915. m_szDescription = NULL;
  916. }
  917. void SetDescription( const char *pszDescription )
  918. {
  919. m_szDescription = pszDescription;
  920. }
  921. protected:
  922. void Run( int nMaxParallel = INT_MAX, int threadOverride = -1 )
  923. {
  924. int i = g_pThreadPool->NumIdleThreads();
  925. if ( nMaxParallel < i)
  926. {
  927. i = nMaxParallel;
  928. }
  929. while( i -- > 0 )
  930. {
  931. if ( threadOverride == -1 || i == threadOverride - 1 )
  932. {
  933. ++ m_nActive;
  934. ThreadExecute( this, &ThisParallelProcessorBase_t::DoExecute )->Release();
  935. }
  936. }
  937. if ( threadOverride == -1 || threadOverride == 0 )
  938. {
  939. ++ m_nActive;
  940. DoExecute();
  941. }
  942. while ( m_nActive )
  943. {
  944. ThreadPause();
  945. }
  946. }
  947. protected:
  948. void OnBegin() {}
  949. bool OnProcess() { return false; }
  950. void OnEnd() {}
  951. private:
  952. void DoExecute()
  953. {
  954. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "DoExecute %s", m_szDescription );
  955. static_cast<Derived *>( this )->OnBegin();
  956. while ( static_cast<Derived *>( this )->OnProcess() )
  957. continue;
  958. static_cast<Derived *>(this)->OnEnd();
  959. -- m_nActive;
  960. }
  961. CInterlockedInt m_nActive;
  962. const char * m_szDescription;
  963. };
  964. //-----------------------------------------------------------------------------
  965. // Raw thread launching
  966. //-----------------------------------------------------------------------------
  967. inline unsigned FunctorExecuteThread( void *pParam )
  968. {
  969. CFunctor *pFunctor = (CFunctor *)pParam;
  970. (*pFunctor)();
  971. pFunctor->Release();
  972. return 0;
  973. }
  974. inline ThreadHandle_t ThreadExecuteSoloImpl( CFunctor *pFunctor, const char *pszName = NULL )
  975. {
  976. ThreadHandle_t hThread;
  977. ThreadId_t threadId;
  978. hThread = CreateSimpleThread( FunctorExecuteThread, pFunctor, &threadId );
  979. if ( pszName )
  980. {
  981. ThreadSetDebugName( threadId, pszName );
  982. }
  983. return hThread;
  984. }
  985. inline ThreadHandle_t ThreadExecuteSolo( CJob *pJob ) { return ThreadExecuteSoloImpl( CreateFunctor( pJob, &CJob::Execute ), pJob->Describe() ); }
  986. template <typename T1>
  987. inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1 ), pszName ); }
  988. template <typename T1, typename T2>
  989. inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2 ), pszName ); }
  990. template <typename T1, typename T2, typename T3>
  991. inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3 ), pszName ); }
  992. template <typename T1, typename T2, typename T3, typename T4>
  993. inline ThreadHandle_t ThreadExecuteSolo( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4 ) { return ThreadExecuteSoloImpl( CreateFunctor( a1, a2, a3, a4 ), pszName ); }
  994. template <typename T1, typename T2, typename T3, typename T4, typename T5>
  995. 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 ); }
  996. template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
  997. 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 ); }
  998. template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
  999. 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 ); }
  1000. template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
  1001. 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 ); }
  1002. template <typename T1, typename T2>
  1003. inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2 ), pszName ); }
  1004. template <typename T1, typename T2, typename T3>
  1005. inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3 ), pszName ); }
  1006. template <typename T1, typename T2, typename T3, typename T4>
  1007. inline ThreadHandle_t ThreadExecuteSoloRef( const char *pszName, T1 a1, T2 a2, T3 a3, T4 a4 ) { return ThreadExecuteSoloImpl( CreateRefCountingFunctor(a1, a2, a3, a4 ), pszName ); }
  1008. template <typename T1, typename T2, typename T3, typename T4, typename T5>
  1009. 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 ); }
  1010. template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
  1011. 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 ); }
  1012. template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
  1013. 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 ); }
  1014. template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
  1015. 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 ); }
  1016. //-----------------------------------------------------------------------------
  1017. inline bool IThreadPool::YieldWait( CThreadEvent &event, unsigned timeout )
  1018. {
  1019. CThreadEvent *pEvent = &event;
  1020. return ( YieldWait( &pEvent, 1, true, timeout ) != TW_TIMEOUT );
  1021. }
  1022. inline bool IThreadPool::YieldWait( CJob *pJob, unsigned timeout )
  1023. {
  1024. return ( YieldWait( &pJob, 1, true, timeout ) != TW_TIMEOUT );
  1025. }
  1026. //-----------------------------------------------------------------------------
  1027. inline JobStatus_t CJob::Execute()
  1028. {
  1029. if ( IsFinished() )
  1030. {
  1031. return m_status;
  1032. }
  1033. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %s %d", __FUNCTION__, Describe(), m_status );
  1034. AUTO_LOCK( m_mutex );
  1035. AddRef();
  1036. JobStatus_t result;
  1037. switch ( m_status )
  1038. {
  1039. case JOB_STATUS_UNSERVICED:
  1040. case JOB_STATUS_PENDING:
  1041. {
  1042. // Service it
  1043. m_status = JOB_STATUS_INPROGRESS;
  1044. result = m_status = DoExecute();
  1045. DoCleanup();
  1046. m_CompleteEvent.Set();
  1047. break;
  1048. }
  1049. case JOB_STATUS_INPROGRESS:
  1050. AssertMsg(0, "Mutex Should have protected use while processing");
  1051. // fall through...
  1052. case JOB_OK:
  1053. case JOB_STATUS_ABORTED:
  1054. result = m_status;
  1055. break;
  1056. default:
  1057. AssertMsg( m_status < JOB_OK, "Unknown job state");
  1058. result = m_status;
  1059. }
  1060. Release();
  1061. return result;
  1062. }
  1063. //---------------------------------------------------------
  1064. inline JobStatus_t CJob::TryExecute()
  1065. {
  1066. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %s %d", __FUNCTION__, Describe(), m_status );
  1067. // TryLock() would only fail if another thread has entered
  1068. // Execute() or Abort()
  1069. if ( !IsFinished() && TryLock() )
  1070. {
  1071. // ...service the request
  1072. Execute();
  1073. Unlock();
  1074. }
  1075. return m_status;
  1076. }
  1077. //---------------------------------------------------------
  1078. inline JobStatus_t CJob::Abort( bool bDiscard )
  1079. {
  1080. if ( IsFinished() )
  1081. {
  1082. return m_status;
  1083. }
  1084. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %s %d", __FUNCTION__, Describe(), m_status );
  1085. AUTO_LOCK( m_mutex );
  1086. AddRef();
  1087. JobStatus_t result;
  1088. switch ( m_status )
  1089. {
  1090. case JOB_STATUS_UNSERVICED:
  1091. case JOB_STATUS_PENDING:
  1092. {
  1093. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "CJob::DoAbort" );
  1094. result = m_status = DoAbort( bDiscard );
  1095. if ( bDiscard )
  1096. DoCleanup();
  1097. m_CompleteEvent.Set();
  1098. }
  1099. break;
  1100. case JOB_STATUS_ABORTED:
  1101. case JOB_STATUS_INPROGRESS:
  1102. case JOB_OK:
  1103. result = m_status;
  1104. break;
  1105. default:
  1106. AssertMsg( m_status < JOB_OK, "Unknown job state");
  1107. result = m_status;
  1108. }
  1109. Release();
  1110. return result;
  1111. }
  1112. //-----------------------------------------------------------------------------
  1113. #endif // JOBTHREAD_H