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.

1614 lines
39 KiB

  1. //========== Copyright 2005, Valve Corporation, All rights reserved. ========
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #if defined( _WIN32 ) && !defined( _X360 )
  7. #define WIN32_LEAN_AND_MEAN
  8. #include <windows.h>
  9. #endif
  10. #include "tier0/dbg.h"
  11. #include "tier0/tslist.h"
  12. #include "tier0/icommandline.h"
  13. #include "tier0/threadtools.h"
  14. #include "vstdlib/jobthread.h"
  15. #include "vstdlib/random.h"
  16. #include "tier1/functors.h"
  17. #include "tier1/fmtstr.h"
  18. #include "tier1/utlvector.h"
  19. #include "tier1/generichash.h"
  20. #include "tier0/fasttimer.h"
  21. #if defined( _X360 )
  22. #include "xbox/xbox_win32stubs.h"
  23. #endif
  24. #ifdef _PS3
  25. #include "tls_ps3.h"
  26. #include "ps3/ps3_win32stubs.h"
  27. #endif
  28. #include "tier0/memdbgon.h"
  29. class CJobThread;
  30. //-----------------------------------------------------------------------------
  31. inline void ServiceJobAndRelease( CJob *pJob, int iThread = -1 )
  32. {
  33. // TryLock() would only fail if another thread has entered
  34. // Execute() or Abort()
  35. if ( !pJob->IsFinished() && pJob->TryLock() )
  36. {
  37. // ...service the request
  38. pJob->SetServiceThread( iThread );
  39. pJob->Execute();
  40. pJob->Unlock();
  41. }
  42. pJob->Release();
  43. }
  44. //-----------------------------------------------------------------------------
  45. #pragma pack(push)
  46. #pragma pack(8)
  47. class ALIGN16 CJobQueue
  48. {
  49. public:
  50. CJobQueue() :
  51. m_nItems( 0 ),
  52. m_nMaxItems( INT_MAX )
  53. {
  54. }
  55. int Count()
  56. {
  57. return m_nItems;
  58. }
  59. int Count( JobPriority_t priority )
  60. {
  61. return m_queues[priority].Count();
  62. }
  63. static JobPriority_t GetMinPriority()
  64. {
  65. return (JobPriority_t)m_MinPriority;
  66. }
  67. static void SetMinPriority( JobPriority_t priority )
  68. {
  69. m_MinPriority = priority;
  70. }
  71. CJob *PrePush()
  72. {
  73. if ( m_nItems >= m_nMaxItems )
  74. {
  75. CJob *pOverflowJob;
  76. if ( Pop( &pOverflowJob ) )
  77. {
  78. return pOverflowJob;
  79. }
  80. }
  81. return NULL;
  82. }
  83. int Push( CJob *pJob, int iThread = -1 )
  84. {
  85. pJob->AddRef();
  86. CJob *pOverflowJob;
  87. int nOverflow = 0;
  88. while ( ( pOverflowJob = PrePush() ) != NULL )
  89. {
  90. ServiceJobAndRelease( pJob );
  91. nOverflow++;
  92. }
  93. m_queues[pJob->GetPriority()].PushItem( pJob );
  94. m_mutex.Lock();
  95. if ( ++m_nItems == 1 )
  96. {
  97. m_JobAvailableEvent.Set();
  98. }
  99. m_mutex.Unlock();
  100. return nOverflow;
  101. }
  102. bool Pop( CJob **ppJob )
  103. {
  104. m_mutex.Lock();
  105. if ( !m_nItems )
  106. {
  107. m_mutex.Unlock();
  108. *ppJob = NULL;
  109. return false;
  110. }
  111. if ( --m_nItems == 0 )
  112. {
  113. m_JobAvailableEvent.Reset();
  114. }
  115. m_mutex.Unlock();
  116. for ( int i = JP_NUM_PRIORITIES - 1; i >= m_MinPriority; --i )
  117. {
  118. if ( m_queues[i].PopItem( ppJob ) )
  119. {
  120. return true;
  121. }
  122. }
  123. m_mutex.Lock();
  124. AssertMsg( m_MinPriority != JP_LOW, "Expected at least one queue item" );
  125. if ( ++m_nItems == 1 )
  126. {
  127. m_JobAvailableEvent.Set();
  128. ThreadSleep();
  129. }
  130. m_mutex.Unlock();
  131. *ppJob = NULL;
  132. return false;
  133. }
  134. CThreadEvent &GetEventHandle()
  135. {
  136. return m_JobAvailableEvent;
  137. }
  138. void Flush()
  139. {
  140. // Only safe to call when system is suspended
  141. m_mutex.Lock();
  142. m_nItems = 0;
  143. m_JobAvailableEvent.Reset();
  144. CJob *pJob = NULL;
  145. for ( int i = JP_NUM_PRIORITIES - 1; i >= 0; --i )
  146. {
  147. while ( m_queues[i].PopItem( &pJob ) )
  148. {
  149. pJob->Abort();
  150. pJob->Release();
  151. }
  152. }
  153. m_mutex.Unlock();
  154. }
  155. private:
  156. CTSQueue<CJob *> m_queues[JP_NUM_PRIORITIES];
  157. int m_nItems;
  158. int m_nMaxItems;
  159. CThreadMutex m_mutex;
  160. CThreadManualEvent m_JobAvailableEvent;
  161. static int m_MinPriority;
  162. } ALIGN16_POST;
  163. int CJobQueue::m_MinPriority = JP_LOW;
  164. #pragma pack(pop)
  165. class CJobThread;
  166. //-----------------------------------------------------------------------------
  167. //
  168. // CThreadPool
  169. //
  170. //-----------------------------------------------------------------------------
  171. class CThreadPool : public CRefCounted1<IThreadPool, CRefCountServiceMT>
  172. {
  173. public:
  174. CThreadPool();
  175. ~CThreadPool();
  176. //-----------------------------------------------------
  177. // Thread functions
  178. //-----------------------------------------------------
  179. bool Start( const ThreadPoolStartParams_t &startParams = ThreadPoolStartParams_t() ) { return Start( startParams, NULL ); }
  180. bool Start( const ThreadPoolStartParams_t &startParams, const char *pszNameOverride );
  181. bool Stop( int timeout = TT_INFINITE );
  182. void Distribute( bool bDistribute = true, int *pAffinityTable = NULL );
  183. //-----------------------------------------------------
  184. // Functions for any thread
  185. //-----------------------------------------------------
  186. unsigned GetJobCount() { return m_nJobs; }
  187. int NumThreads();
  188. int NumIdleThreads();
  189. //-----------------------------------------------------
  190. // Pause/resume processing jobs
  191. //-----------------------------------------------------
  192. int SuspendExecution();
  193. int ResumeExecution();
  194. //-----------------------------------------------------
  195. // Offer the current thread to the pool
  196. //-----------------------------------------------------
  197. virtual int YieldWait( CThreadEvent **pEvents, int nEvents, bool bWaitAll = true, unsigned timeout = TT_INFINITE );
  198. virtual int YieldWait( CJob **, int nJobs, bool bWaitAll = true, unsigned timeout = TT_INFINITE );
  199. void Yield( unsigned timeout );
  200. virtual int YieldWaitPerFrameJobs( );
  201. //-----------------------------------------------------
  202. // Add a native job to the queue (master thread)
  203. //-----------------------------------------------------
  204. void AddJob( CJob * );
  205. virtual void AddPerFrameJob( CJob * );
  206. void InsertJobInQueue( CJob * );
  207. //-----------------------------------------------------
  208. // Add an function object to the queue (master thread)
  209. //-----------------------------------------------------
  210. void AddFunctorInternal( CFunctor *, CJob ** = NULL, const char *pszDescription = NULL, unsigned flags = 0 );
  211. //-----------------------------------------------------
  212. // Remove a job from the queue (master thread)
  213. //-----------------------------------------------------
  214. virtual void ChangePriority( CJob *p, JobPriority_t priority );
  215. //-----------------------------------------------------
  216. // Bulk job manipulation (blocking)
  217. //-----------------------------------------------------
  218. int ExecuteToPriority( JobPriority_t toPriority, JobFilter_t pfnFilter = NULL );
  219. int AbortAll();
  220. private:
  221. enum
  222. {
  223. IO_STACKSIZE = ( 64 * 1024 ),
  224. COMPUTATION_STACKSIZE = 0,
  225. };
  226. //-----------------------------------------------------
  227. //
  228. //-----------------------------------------------------
  229. CJob *PeekJob();
  230. CJob *GetDummyJob();
  231. //-----------------------------------------------------
  232. // Thread functions
  233. //-----------------------------------------------------
  234. int Run();
  235. private:
  236. friend class CJobThread;
  237. CJobQueue m_SharedQueue;
  238. CInterlockedInt m_nIdleThreads;
  239. CUtlVector<CJobThread *> m_Threads;
  240. CThreadMutex m_SuspendMutex;
  241. int m_nSuspend;
  242. CInterlockedInt m_nJobs;
  243. // Some jobs should only be executed on the threadpool thread(s). Ie: the rendering thread has the GL context
  244. // and the main thread coming in and "helping" with jobs breaks that pretty nicely. This flag states that
  245. // only the threadpool threads should execute these jobs.
  246. bool m_bExecOnThreadPoolThreadsOnly;
  247. CThreadMutex m_PerFrameJobListMutex;
  248. CUtlVectorFixedGrowable< CJob *, 2048 > m_PerFrameJobs;
  249. };
  250. //-----------------------------------------------------------------------------
  251. JOB_INTERFACE IThreadPool *CreateNewThreadPool()
  252. {
  253. return new CThreadPool;
  254. }
  255. JOB_INTERFACE void DestroyThreadPool( IThreadPool *pPool )
  256. {
  257. delete static_cast<CThreadPool*>( pPool );
  258. }
  259. //-----------------------------------------------------------------------------
  260. class CGlobalThreadPool : public CThreadPool
  261. {
  262. public:
  263. virtual bool Start( const ThreadPoolStartParams_t &startParamsIn )
  264. {
  265. int nThreads = ( CommandLine()->ParmValue( "-threads", -1 ) - 1 );
  266. ThreadPoolStartParams_t startParams = startParamsIn;
  267. if ( nThreads >= 0 )
  268. {
  269. startParams.nThreads = nThreads;
  270. }
  271. else
  272. {
  273. // Cap the GlobPool threads at 4.
  274. startParams.nThreadsMax = 4;
  275. }
  276. return CThreadPool::Start( startParams, "GlobPool" );
  277. }
  278. virtual bool OnFinalRelease()
  279. {
  280. AssertMsg( 0, "Releasing global thread pool object!" );
  281. return false;
  282. }
  283. };
  284. //-----------------------------------------------------------------------------
  285. class CJobThread : public CWorkerThread
  286. {
  287. public:
  288. CJobThread( CThreadPool *pOwner, int iThread ) :
  289. m_SharedQueue( pOwner->m_SharedQueue ),
  290. m_pOwner( pOwner ),
  291. m_iThread( iThread )
  292. {
  293. }
  294. CThreadEvent &GetIdleEvent()
  295. {
  296. return m_IdleEvent;
  297. }
  298. CJobQueue &AccessDirectQueue()
  299. {
  300. return m_DirectQueue;
  301. }
  302. private:
  303. unsigned Wait()
  304. {
  305. unsigned waitResult;
  306. #ifdef WIN32
  307. enum Event_t
  308. {
  309. CALL_FROM_MASTER,
  310. SHARED_QUEUE,
  311. DIRECT_QUEUE,
  312. NUM_EVENTS
  313. };
  314. CThreadEvent *waitHandles[NUM_EVENTS];
  315. waitHandles[CALL_FROM_MASTER] = &GetCallHandle();
  316. waitHandles[SHARED_QUEUE] = &m_SharedQueue.GetEventHandle();
  317. waitHandles[DIRECT_QUEUE] = &m_DirectQueue.GetEventHandle();
  318. #ifdef _DEBUG
  319. while ( ( waitResult = CThreadEvent::WaitForMultiple( ARRAYSIZE(waitHandles), waitHandles , FALSE, 10 ) ) == TW_TIMEOUT )
  320. {
  321. waitResult = waitResult; // break here
  322. }
  323. #else
  324. waitResult = CThreadEvent::WaitForMultiple( ARRAYSIZE(waitHandles), waitHandles , FALSE, TT_INFINITE );
  325. #endif
  326. #else // !win32
  327. bool bSet = false;
  328. int nWaitTime = 100;
  329. while ( !bSet )
  330. {
  331. // jobs are typically enqueued to the shared job queue so wait on it
  332. bSet = m_SharedQueue.GetEventHandle().Wait( nWaitTime );
  333. if ( !bSet )
  334. bSet = m_DirectQueue.GetEventHandle().Wait( 0 );
  335. if ( !bSet )
  336. bSet = GetCallHandle().Wait( 0 );
  337. }
  338. if ( !bSet )
  339. waitResult = WAIT_TIMEOUT;
  340. else
  341. waitResult = WAIT_OBJECT_0;
  342. #endif
  343. return waitResult;
  344. }
  345. int Run()
  346. {
  347. // Wait for either a call from the master thread, or an item in the queue...
  348. unsigned waitResult;
  349. bool bExit = false;
  350. m_pOwner->m_nIdleThreads++;
  351. m_IdleEvent.Set();
  352. while ( !bExit && ( waitResult = Wait() ) != TW_FAILED )
  353. {
  354. if ( PeekCall() )
  355. {
  356. switch ( GetCallParam() )
  357. {
  358. case TPM_EXIT:
  359. Reply( true );
  360. bExit = TRUE;
  361. break;
  362. case TPM_SUSPEND:
  363. Reply( true );
  364. Suspend();
  365. break;
  366. default:
  367. AssertMsg( 0, "Unknown call to thread" );
  368. Reply( false );
  369. break;
  370. }
  371. }
  372. else
  373. {
  374. CJob *pJob;
  375. bool bTookJob = false;
  376. do
  377. {
  378. if ( !m_DirectQueue.Pop( &pJob) )
  379. {
  380. if ( !m_SharedQueue.Pop( &pJob ) )
  381. {
  382. // Nothing to process, return to wait state
  383. break;
  384. }
  385. }
  386. if ( !bTookJob )
  387. {
  388. m_IdleEvent.Reset();
  389. m_pOwner->m_nIdleThreads--;
  390. bTookJob = true;
  391. }
  392. ServiceJobAndRelease( pJob, m_iThread );
  393. m_pOwner->m_nJobs--;
  394. } while ( !PeekCall() );
  395. if ( bTookJob )
  396. {
  397. m_pOwner->m_nIdleThreads++;
  398. m_IdleEvent.Set();
  399. }
  400. }
  401. }
  402. m_pOwner->m_nIdleThreads--;
  403. m_IdleEvent.Reset();
  404. return 0;
  405. }
  406. CJobQueue m_DirectQueue;
  407. CJobQueue & m_SharedQueue;
  408. CThreadPool * m_pOwner;
  409. CThreadManualEvent m_IdleEvent;
  410. int m_iThread;
  411. };
  412. //-----------------------------------------------------------------------------
  413. CGlobalThreadPool g_ThreadPool;
  414. IThreadPool *g_pThreadPool = &g_ThreadPool;
  415. IThreadPool *g_pAlternateThreadPool;
  416. //-----------------------------------------------------------------------------
  417. //
  418. // CThreadPool
  419. //
  420. //-----------------------------------------------------------------------------
  421. CThreadPool::CThreadPool() :
  422. m_nIdleThreads( 0 ),
  423. m_nJobs( 0 ),
  424. m_nSuspend( 0 )
  425. {
  426. }
  427. //---------------------------------------------------------
  428. CThreadPool::~CThreadPool()
  429. {
  430. }
  431. //---------------------------------------------------------
  432. //
  433. //---------------------------------------------------------
  434. int CThreadPool::NumThreads()
  435. {
  436. return m_Threads.Count();
  437. }
  438. //---------------------------------------------------------
  439. //
  440. //---------------------------------------------------------
  441. int CThreadPool::NumIdleThreads()
  442. {
  443. return m_nIdleThreads;
  444. }
  445. //---------------------------------------------------------
  446. // Pause/resume processing jobs
  447. //---------------------------------------------------------
  448. int CThreadPool::SuspendExecution()
  449. {
  450. AUTO_LOCK( m_SuspendMutex );
  451. // If not already suspended
  452. if ( m_nSuspend == 0 )
  453. {
  454. int i;
  455. for ( i = 0; i < m_Threads.Count(); i++ )
  456. {
  457. m_Threads[i]->CallWorker( TPM_SUSPEND, 0 );
  458. }
  459. for ( i = 0; i < m_Threads.Count(); i++ )
  460. {
  461. m_Threads[i]->WaitForReply();
  462. }
  463. // Because worker must signal before suspending, we could reach
  464. // here with the thread not actually suspended
  465. for ( i = 0; i < m_Threads.Count(); i++ )
  466. {
  467. while ( !m_Threads[i]->IsSuspended() )
  468. {
  469. ThreadSleep();
  470. }
  471. }
  472. }
  473. return m_nSuspend++;
  474. }
  475. //---------------------------------------------------------
  476. int CThreadPool::ResumeExecution()
  477. {
  478. AUTO_LOCK( m_SuspendMutex );
  479. AssertMsg( m_nSuspend >= 1, "Attempted resume when not suspended");
  480. int result = m_nSuspend--;
  481. if (m_nSuspend == 0 )
  482. {
  483. for ( int i = 0; i < m_Threads.Count(); i++ )
  484. {
  485. m_Threads[i]->Resume();
  486. }
  487. }
  488. return result;
  489. }
  490. //---------------------------------------------------------
  491. int CThreadPool::YieldWait( CThreadEvent **pEvents, int nEvents, bool bWaitAll, unsigned timeout )
  492. {
  493. Assert( timeout == TT_INFINITE ); // unimplemented
  494. int result;
  495. CJob *pJob;
  496. // Always wait for zero milliseconds initially, to let us process jobs on this thread.
  497. timeout = 0;
  498. while ( ( result = CThreadEvent::WaitForMultiple( nEvents, pEvents, bWaitAll, timeout ) ) == TW_TIMEOUT )
  499. {
  500. if (!m_bExecOnThreadPoolThreadsOnly && m_SharedQueue.Pop( &pJob ) )
  501. {
  502. ServiceJobAndRelease( pJob );
  503. m_nJobs--;
  504. }
  505. else
  506. {
  507. // Since there are no jobs for the main thread set the timeout to infinite.
  508. // The only disadvantage to this is that if a job thread creates a new job
  509. // then the main thread will not be available to pick it up, but if that
  510. // is a problem you can just create more worker threads. Debugging test runs
  511. // of TF2 suggests that jobs are only ever added from the main thread which
  512. // means that there is no disadvantage.
  513. // Waiting on the events instead of busy spinning has multiple advantages.
  514. // It avoids wasting CPU time/electricity, it makes it more obvious in profiles
  515. // when the main thread is idle versus busy, and it allows ready thread analysis
  516. // in xperf to find out what woke up a waiting thread.
  517. // It also avoids unnecessary CPU starvation -- seen on customer traces of TF2.
  518. timeout = TT_INFINITE;
  519. }
  520. }
  521. return result;
  522. }
  523. //---------------------------------------------------------
  524. int CThreadPool::YieldWait( CJob **ppJobs, int nJobs, bool bWaitAll, unsigned timeout )
  525. {
  526. CUtlVectorFixed<CThreadEvent*, 64> handles;
  527. if ( nJobs > handles.NumAllocated() - 2 )
  528. {
  529. return TW_FAILED;
  530. }
  531. for ( int i = 0; i < nJobs; i++ )
  532. {
  533. handles.AddToTail( ppJobs[i]->AccessEvent() );
  534. }
  535. return YieldWait( (CThreadEvent **)handles.Base(), handles.Count(), bWaitAll, timeout);
  536. }
  537. //---------------------------------------------------------
  538. void CThreadPool::Yield( unsigned timeout )
  539. {
  540. // @MULTICORE (toml 10/24/2006): not implemented
  541. Assert( ThreadInMainThread() );
  542. if ( !ThreadInMainThread() )
  543. {
  544. ThreadSleep( timeout );
  545. return;
  546. }
  547. ThreadSleep( timeout );
  548. }
  549. //---------------------------------------------------------
  550. // Block until all per-frame jobs are done
  551. //---------------------------------------------------------
  552. int CThreadPool::YieldWaitPerFrameJobs( )
  553. {
  554. // NOTE: Per-Frame jobs may spawn other per-frame jobs.
  555. // Therefore we must copy the list off and block on that list
  556. // because more jobs may be added while we're yielding
  557. int nRetVal = 0;
  558. while ( true )
  559. {
  560. m_PerFrameJobListMutex.Lock();
  561. int nCount = m_PerFrameJobs.Count();
  562. if ( nCount == 0 )
  563. {
  564. m_PerFrameJobListMutex.Unlock();
  565. break;
  566. }
  567. size_t nSize = nCount * sizeof( CJob* );
  568. CJob **ppJobs = ( CJob** )stackalloc( nSize );
  569. memcpy( ppJobs, m_PerFrameJobs.Base(), nSize );
  570. m_PerFrameJobs.RemoveAll();
  571. m_PerFrameJobListMutex.Unlock();
  572. nRetVal = YieldWait( ppJobs, nCount );
  573. for ( int i = 0; i < nCount; ++i )
  574. {
  575. ppJobs[i]->Release();
  576. }
  577. }
  578. return nRetVal;
  579. }
  580. //---------------------------------------------------------
  581. // Add a job to the queue
  582. //---------------------------------------------------------
  583. void CThreadPool::AddJob( CJob *pJob )
  584. {
  585. if ( !pJob )
  586. {
  587. return;
  588. }
  589. if ( pJob->m_ThreadPoolData != JOB_NO_DATA )
  590. {
  591. Warning( "Cannot add a thread job already committed to another thread pool\n" );
  592. return;
  593. }
  594. if ( m_Threads.Count() == 0 )
  595. {
  596. // So only threadpool jobs are supposed to execute the jobs, but there are no threadpool threads?
  597. Assert( !m_bExecOnThreadPoolThreadsOnly );
  598. pJob->Execute();
  599. return;
  600. }
  601. int flags = pJob->GetFlags();
  602. if ( !m_bExecOnThreadPoolThreadsOnly && ( ( flags & ( JF_IO | JF_QUEUE ) ) == 0 ) /* @TBD && !m_queue.Count() */ )
  603. {
  604. if ( !NumIdleThreads() )
  605. {
  606. pJob->Execute();
  607. return;
  608. }
  609. pJob->SetPriority( (JobPriority_t)(JP_NUM_PRIORITIES - 1) );
  610. }
  611. if ( !pJob->CanExecute() )
  612. {
  613. // Already handled
  614. ExecuteOnce( Warning( "Attempted to add job to job queue that has already been completed\n" ) );
  615. return;
  616. }
  617. pJob->m_pThreadPool = this;
  618. pJob->m_status = JOB_STATUS_PENDING;
  619. #if defined( THREAD_PARENT_STACK_TRACE_ENABLED )
  620. {
  621. int iValidEntries = GetCallStack_Fast( pJob->m_ParentStackTrace, ARRAYSIZE( pJob->m_ParentStackTrace ), 0 );
  622. for( int i = iValidEntries; i < ARRAYSIZE( pJob->m_ParentStackTrace ); ++i )
  623. {
  624. pJob->m_ParentStackTrace[i] = NULL;
  625. }
  626. }
  627. #endif
  628. InsertJobInQueue( pJob );
  629. ++m_nJobs;
  630. }
  631. //-----------------------------------------------------
  632. // Add a native job to the queue (master thread)
  633. // Call YieldWaitPerFrameJobs() to wait only until all per-frame jobs are done
  634. //-----------------------------------------------------
  635. void CThreadPool::AddPerFrameJob( CJob *pJob )
  636. {
  637. m_PerFrameJobListMutex.Lock();
  638. pJob->AddRef();
  639. m_PerFrameJobs.AddToTail( pJob );
  640. m_PerFrameJobListMutex.Unlock();
  641. AddJob( pJob );
  642. }
  643. //---------------------------------------------------------
  644. //
  645. //---------------------------------------------------------
  646. void CThreadPool::InsertJobInQueue( CJob *pJob )
  647. {
  648. CJobQueue *pQueue;
  649. if ( !( pJob->GetFlags() & JF_SERIAL ) )
  650. {
  651. int iThread = pJob->GetServiceThread();
  652. if ( iThread == -1 || !m_Threads.IsValidIndex( iThread ) )
  653. {
  654. pQueue = &m_SharedQueue;
  655. }
  656. else
  657. {
  658. pQueue = &(m_Threads[iThread]->AccessDirectQueue());
  659. }
  660. }
  661. else
  662. {
  663. pQueue = &(m_Threads[0]->AccessDirectQueue());
  664. }
  665. #ifdef SN_TARGET_PS3
  666. // GSidhu
  667. // Make sure render job goes on shared q rather than direct q.
  668. // Direct q jobs get picked up only after jobs appear on shared q or
  669. // wait times out on shared q.
  670. // This is a fix for Eurogamer, look at this in more detail later
  671. pQueue = &m_SharedQueue;
  672. #endif
  673. m_nJobs -= pQueue->Push( pJob );
  674. }
  675. //---------------------------------------------------------
  676. // Add an function object to the queue (master thread)
  677. //---------------------------------------------------------
  678. void CThreadPool::AddFunctorInternal( CFunctor *pFunctor, CJob **ppJob, const char *pszDescription, unsigned flags )
  679. {
  680. // Note: assumes caller has handled refcount
  681. CJob *pJob = new CFunctorJob( pFunctor, pszDescription );
  682. pJob->SetFlags( flags );
  683. AddJob( pJob );
  684. if ( ppJob )
  685. {
  686. *ppJob = pJob;
  687. }
  688. else
  689. {
  690. pJob->Release();
  691. }
  692. }
  693. //---------------------------------------------------------
  694. // Remove a job from the queue
  695. //---------------------------------------------------------
  696. void CThreadPool::ChangePriority( CJob *pJob, JobPriority_t priority )
  697. {
  698. // Right now, only support upping the priority
  699. if ( pJob->GetPriority() < priority )
  700. {
  701. pJob->SetPriority( priority );
  702. m_SharedQueue.Push( pJob );
  703. }
  704. else
  705. {
  706. ExecuteOnce( if ( pJob->GetPriority() != priority ) DevMsg( "CThreadPool::RemoveJob not implemented right now" ) );
  707. }
  708. }
  709. //---------------------------------------------------------
  710. // Execute to a specified priority
  711. //---------------------------------------------------------
  712. #define THREADED_EXECUTETOPRIORITY 0 // Not ready for general consumption [8/4/2010 tom]
  713. int CThreadPool::ExecuteToPriority( JobPriority_t iToPriority, JobFilter_t pfnFilter )
  714. {
  715. if ( !THREADED_EXECUTETOPRIORITY || pfnFilter )
  716. {
  717. SuspendExecution();
  718. CJob *pJob;
  719. int i;
  720. int nExecuted = 0;
  721. int nJobsTotal = GetJobCount();
  722. CUtlVector<CJob *> jobsToPutBack;
  723. for ( int iCurPriority = JP_NUM_PRIORITIES - 1; iCurPriority >= iToPriority; --iCurPriority )
  724. {
  725. for ( i = 0; i < m_Threads.Count(); i++ )
  726. {
  727. CJobQueue &queue = m_Threads[i]->AccessDirectQueue();
  728. while ( queue.Count( (JobPriority_t)iCurPriority ) )
  729. {
  730. queue.Pop( &pJob );
  731. if ( pfnFilter && !(*pfnFilter)( pJob ) )
  732. {
  733. if ( pJob->CanExecute() )
  734. {
  735. jobsToPutBack.EnsureCapacity( nJobsTotal );
  736. jobsToPutBack.AddToTail( pJob );
  737. }
  738. else
  739. {
  740. m_nJobs--;
  741. pJob->Release(); // an already serviced job in queue, may as well ditch it (as in, main thread probably force executed)
  742. }
  743. continue;
  744. }
  745. ServiceJobAndRelease( pJob );
  746. m_nJobs--;
  747. nExecuted++;
  748. }
  749. }
  750. while ( m_SharedQueue.Count( (JobPriority_t)iCurPriority ) )
  751. {
  752. m_SharedQueue.Pop( &pJob );
  753. if ( pfnFilter && !(*pfnFilter)( pJob ) )
  754. {
  755. if ( pJob->CanExecute() )
  756. {
  757. jobsToPutBack.EnsureCapacity( nJobsTotal );
  758. jobsToPutBack.AddToTail( pJob );
  759. }
  760. else
  761. {
  762. m_nJobs--;
  763. pJob->Release(); // see above
  764. }
  765. continue;
  766. }
  767. ServiceJobAndRelease( pJob );
  768. m_nJobs--;
  769. nExecuted++;
  770. }
  771. }
  772. for ( i = 0; i < jobsToPutBack.Count(); i++ )
  773. {
  774. InsertJobInQueue( jobsToPutBack[i] );
  775. jobsToPutBack[i]->Release();
  776. }
  777. ResumeExecution();
  778. return nExecuted;
  779. }
  780. else
  781. {
  782. JobPriority_t prevPriority = CJobQueue::GetMinPriority();
  783. CJobQueue::SetMinPriority( iToPriority );
  784. CUtlVectorFixedGrowable<CThreadEvent*, 64> handles;
  785. for ( int i = 0; i < m_Threads.Count(); i++ )
  786. {
  787. handles.AddToTail( &m_Threads[i]->GetIdleEvent() );
  788. }
  789. CJob *pJob = NULL;
  790. do
  791. {
  792. YieldWait( (CThreadEvent **)handles.Base(), handles.Count(), true, TT_INFINITE );
  793. if ( m_SharedQueue.Pop( &pJob ) )
  794. {
  795. ServiceJobAndRelease( pJob );
  796. m_nJobs--;
  797. }
  798. } while ( pJob );
  799. CJobQueue::SetMinPriority( prevPriority );
  800. return 1;
  801. }
  802. }
  803. //---------------------------------------------------------
  804. //
  805. //---------------------------------------------------------
  806. int CThreadPool::AbortAll()
  807. {
  808. SuspendExecution();
  809. CJob *pJob;
  810. int iAborted = 0;
  811. while ( m_SharedQueue.Pop( &pJob ) )
  812. {
  813. pJob->Abort();
  814. pJob->Release();
  815. iAborted++;
  816. }
  817. for ( int i = 0; i < m_Threads.Count(); i++ )
  818. {
  819. CJobQueue &queue = m_Threads[i]->AccessDirectQueue();
  820. while ( queue.Pop( &pJob ) )
  821. {
  822. pJob->Abort();
  823. pJob->Release();
  824. iAborted++;
  825. }
  826. }
  827. m_nJobs = 0;
  828. ResumeExecution();
  829. return iAborted;
  830. }
  831. //---------------------------------------------------------
  832. // CThreadPool thread functions
  833. //---------------------------------------------------------
  834. bool CThreadPool::Start( const ThreadPoolStartParams_t &startParams, const char *pszName )
  835. {
  836. #if defined( DEDICATED ) && IsPlatformLinux()
  837. if ( !startParams.bEnableOnLinuxDedicatedServer )
  838. return false;
  839. #endif
  840. int nThreads = startParams.nThreads;
  841. m_bExecOnThreadPoolThreadsOnly = startParams.bExecOnThreadPoolThreadsOnly;
  842. if ( nThreads < 0 )
  843. {
  844. const CPUInformation &ci = GetCPUInformation();
  845. if ( startParams.bIOThreads )
  846. {
  847. nThreads = ci.m_nLogicalProcessors;
  848. }
  849. else
  850. {
  851. // One worker thread per logical processor minus main thread and graphic driver
  852. nThreads = ci.m_nLogicalProcessors - 2;
  853. if ( IsPC() )
  854. {
  855. if ( nThreads > 3 )
  856. {
  857. DevMsg( "Defaulting to limit of 3 worker threads, use -threads on command line if want more\n" ); // Current >4 processor configs don't really work so well, probably due to cache issues? (toml 7/12/2007)
  858. nThreads = 3;
  859. }
  860. }
  861. }
  862. if ( ( startParams.nThreadsMax >= 0 ) && ( nThreads > startParams.nThreadsMax ) )
  863. {
  864. nThreads = startParams.nThreadsMax;
  865. }
  866. }
  867. if ( nThreads <= 0 )
  868. {
  869. return true;
  870. }
  871. int nStackSize = startParams.nStackSize;
  872. if ( nStackSize < 0 )
  873. {
  874. if ( startParams.bIOThreads )
  875. {
  876. nStackSize = IO_STACKSIZE;
  877. }
  878. else
  879. {
  880. nStackSize = COMPUTATION_STACKSIZE;
  881. }
  882. }
  883. int priority = startParams.iThreadPriority;
  884. if ( priority == SHRT_MIN )
  885. {
  886. if ( startParams.bIOThreads )
  887. {
  888. #if defined( _WIN32 )
  889. priority = THREAD_PRIORITY_HIGHEST;
  890. #endif
  891. }
  892. else
  893. {
  894. priority = ThreadGetPriority();
  895. }
  896. }
  897. if ( IsPS3() && priority < ThreadGetPriority() )
  898. {
  899. // On PS3 all thread pools should be the same priority as creator or less demanding
  900. priority = ThreadGetPriority();
  901. }
  902. bool bDistribute;
  903. if ( startParams.fDistribute != TRS_NONE )
  904. {
  905. bDistribute = ( startParams.fDistribute == TRS_TRUE );
  906. }
  907. else
  908. {
  909. bDistribute = !startParams.bIOThreads;
  910. }
  911. //--------------------------------------------------------
  912. m_Threads.EnsureCapacity( nThreads );
  913. if ( !pszName )
  914. {
  915. pszName = ( startParams.bIOThreads ) ? "IOJob" : "CmpJob";
  916. }
  917. while ( nThreads-- )
  918. {
  919. int iThread = m_Threads.AddToTail();
  920. m_Threads[iThread] = new CJobThread( this, iThread );
  921. CFmtStr formattedName( "%s%d", pszName, iThread );
  922. m_Threads[iThread]->SetName( formattedName );
  923. m_Threads[iThread]->Start( nStackSize );
  924. m_Threads[iThread]->GetIdleEvent().Wait();
  925. ThreadSetDebugName( m_Threads[iThread]->GetThreadHandle(), formattedName );
  926. ThreadSetPriority( (ThreadHandle_t)m_Threads[iThread]->GetThreadHandle(), priority );
  927. }
  928. Distribute( bDistribute, startParams.bUseAffinityTable ? (int *)startParams.iAffinityTable : NULL );
  929. return true;
  930. }
  931. //---------------------------------------------------------
  932. void CThreadPool::Distribute( bool bDistribute, int *pAffinityTable )
  933. {
  934. if ( bDistribute )
  935. {
  936. const CPUInformation &ci = GetCPUInformation();
  937. int nHwThreadsPer = (( ci.m_bHT ) ? 2 : 1);
  938. if ( ci.m_nLogicalProcessors > 1 )
  939. {
  940. if ( !pAffinityTable )
  941. {
  942. #if defined( IS_WINDOWS_PC )
  943. // no affinity table, distribution is cycled across all available
  944. HINSTANCE hInst = LoadLibrary( "kernel32.dll" );
  945. if ( hInst )
  946. {
  947. typedef DWORD (WINAPI *SetThreadIdealProcessorFn)(ThreadHandle_t hThread, DWORD dwIdealProcessor);
  948. SetThreadIdealProcessorFn Thread_SetIdealProcessor = (SetThreadIdealProcessorFn)GetProcAddress( hInst, "SetThreadIdealProcessor" );
  949. if ( Thread_SetIdealProcessor )
  950. {
  951. ThreadHandle_t hMainThread = ThreadGetCurrentHandle();
  952. Thread_SetIdealProcessor( hMainThread, 0 );
  953. int iProc = 0;
  954. for ( int i = 0; i < m_Threads.Count(); i++ )
  955. {
  956. iProc += nHwThreadsPer;
  957. if ( iProc >= ci.m_nLogicalProcessors )
  958. {
  959. iProc %= ci.m_nLogicalProcessors;
  960. if ( nHwThreadsPer > 1 )
  961. {
  962. iProc = ( iProc + 1 ) % nHwThreadsPer;
  963. }
  964. }
  965. Thread_SetIdealProcessor((ThreadHandle_t)m_Threads[i]->GetThreadHandle(), iProc);
  966. }
  967. }
  968. FreeLibrary( hInst );
  969. }
  970. #else
  971. // no affinity table, distribution is cycled across all available
  972. int iProc = 0;
  973. for ( int i = 0; i < m_Threads.Count(); i++ )
  974. {
  975. iProc += nHwThreadsPer;
  976. if ( iProc >= ci.m_nLogicalProcessors )
  977. {
  978. iProc %= ci.m_nLogicalProcessors;
  979. if ( nHwThreadsPer > 1 )
  980. {
  981. iProc = ( iProc + 1 ) % nHwThreadsPer;
  982. }
  983. }
  984. ThreadSetAffinity( (ThreadHandle_t)m_Threads[i]->GetThreadHandle(), 1 << iProc );
  985. }
  986. #endif
  987. }
  988. else
  989. {
  990. // distribution is from affinity table
  991. for ( int i = 0; i < m_Threads.Count(); i++ )
  992. {
  993. ThreadSetAffinity( (ThreadHandle_t)m_Threads[i]->GetThreadHandle(), pAffinityTable[i] );
  994. }
  995. }
  996. }
  997. }
  998. else
  999. {
  1000. #if defined( _WIN32 )
  1001. DWORD_PTR dwProcessAffinity, dwSystemAffinity;
  1002. if ( GetProcessAffinityMask( GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity ) )
  1003. {
  1004. for ( int i = 0; i < m_Threads.Count(); i++ )
  1005. {
  1006. ThreadSetAffinity( (ThreadHandle_t)m_Threads[i]->GetThreadHandle(), dwProcessAffinity );
  1007. }
  1008. }
  1009. #endif
  1010. }
  1011. }
  1012. //---------------------------------------------------------
  1013. bool CThreadPool::Stop( int timeout )
  1014. {
  1015. #ifdef _PS3
  1016. if ( GetTLSGlobals()->bNormalQuitRequested )
  1017. {
  1018. // When PS3 system requests a quit we
  1019. // might leave some of our threads suspended
  1020. // we need to resume them so that they could
  1021. // receive TPM_EXIT command
  1022. AUTO_LOCK( m_SuspendMutex );
  1023. if ( m_nSuspend >= 1 )
  1024. {
  1025. m_nSuspend = 1;
  1026. ResumeExecution();
  1027. }
  1028. }
  1029. #endif
  1030. CUtlVector< ThreadHandle_t > arrHandles;
  1031. arrHandles.SetCount( m_Threads.Count() );
  1032. for ( int i = 0; i < m_Threads.Count(); i++ )
  1033. {
  1034. arrHandles[i] = m_Threads[i]->GetThreadHandle();
  1035. #ifdef _WIN32
  1036. if( arrHandles[i] )
  1037. {
  1038. // due to weird legacy reasons, the worker thread keeps its handle ownership.
  1039. // it closes the handle BEFORE exiting, which renders that handle useless to join on Win32
  1040. // this leads to (a) invalid handle before the thread exits (b) potentially unmapping the code running worker thread before the thread exits
  1041. // The worker thread even has a hacky workaround: it sets an event before exiting! Obviously, that event is useless to fix this race condition
  1042. // The right solution would be to have the worker NOT close its own handle, rendering that handle useful. And make ThreadJoin close that handle,
  1043. // mimicking pthreads semantics (joinable thread enters zombie state until it's properly joined). But it leads to another problem,
  1044. // namely handle leak in cases when we potentially stop the workers in other systems, and potentially incorrect joins on closed handles
  1045. // (because in Win32, it's valid to wait for thread handle twice, but it's not valid in pthreads to join the same thread twice).
  1046. // So for now, I'm just fixing it with local fix here.
  1047. //
  1048. HANDLE hDup;
  1049. DuplicateHandle( GetCurrentProcess(), arrHandles[i], GetCurrentProcess(), &hDup, DUPLICATE_SAME_ACCESS, FALSE, 0 );
  1050. arrHandles[i] = (ThreadHandle_t)hDup;
  1051. }
  1052. #endif
  1053. m_Threads[i]->CallWorker( TPM_EXIT );
  1054. }
  1055. for ( int i = 0; i < m_Threads.Count(); ++i )
  1056. {
  1057. if ( arrHandles[i] )
  1058. {
  1059. ThreadJoin( arrHandles[i] );
  1060. #ifdef WIN32
  1061. Assert( !m_Threads[i]->GetThreadHandle() );
  1062. // because we duplicated the handle above, due to the historical reasons described above, we have to close this handle on Win32
  1063. CloseHandle( arrHandles[i] );
  1064. #else
  1065. Assert( !m_Threads[i]->IsAlive() );
  1066. #endif
  1067. }
  1068. #ifdef WIN32
  1069. while( m_Threads[i]->GetThreadHandle() )
  1070. #else
  1071. while( m_Threads[i]->IsAlive() )
  1072. #endif
  1073. {
  1074. ThreadSleep( 0 );
  1075. }
  1076. delete m_Threads[i];
  1077. }
  1078. m_nJobs = 0;
  1079. m_SharedQueue.Flush();
  1080. m_nIdleThreads = 0;
  1081. m_Threads.RemoveAll();
  1082. return true;
  1083. }
  1084. //---------------------------------------------------------
  1085. CJob *CThreadPool::GetDummyJob()
  1086. {
  1087. class CDummyJob : public CJob
  1088. {
  1089. public:
  1090. CDummyJob()
  1091. {
  1092. Execute();
  1093. }
  1094. virtual JobStatus_t DoExecute() { return JOB_OK; }
  1095. };
  1096. static CDummyJob dummyJob;
  1097. dummyJob.AddRef();
  1098. return &dummyJob;
  1099. }
  1100. namespace ThreadPoolTest
  1101. {
  1102. int g_iSleep;
  1103. CThreadEvent g_done;
  1104. int g_nTotalToComplete;
  1105. CThreadPool *g_pTestThreadPool;
  1106. class CCountJob : public CJob
  1107. {
  1108. public:
  1109. virtual JobStatus_t DoExecute()
  1110. {
  1111. m_nCount++;
  1112. ThreadPause();
  1113. if ( g_iSleep >= 0)
  1114. ThreadSleep( g_iSleep );
  1115. if ( bDoWork )
  1116. {
  1117. byte pMemory[1024];
  1118. int i;
  1119. for ( i = 0; i < 1024; i++ )
  1120. {
  1121. pMemory[i] = rand();
  1122. }
  1123. for ( i = 0; i < 50; i++ )
  1124. {
  1125. sqrt( (float)HashBlock( pMemory, 1024 ) + HashBlock( pMemory, 1024 ) + 10.0 );
  1126. }
  1127. bDoWork = false;
  1128. }
  1129. if ( m_nCount == g_nTotalToComplete )
  1130. g_done.Set();
  1131. return 0;
  1132. }
  1133. static CInterlockedInt m_nCount;
  1134. bool bDoWork;
  1135. };
  1136. CInterlockedInt CCountJob::m_nCount;
  1137. int g_nTotalAtFinish;
  1138. void Test( bool bDistribute, bool bSleep = true, bool bFinishExecute = false, bool bDoWork = false, bool bIncludeMain = false, bool bPrioritized = false )
  1139. {
  1140. int nJobCount = 4000;
  1141. CCountJob *jobs = new CCountJob[4000];
  1142. for ( int bInterleavePushPop = 0; bInterleavePushPop < 2; bInterleavePushPop++ )
  1143. {
  1144. for ( g_iSleep = -10; g_iSleep <= 10; g_iSleep += 10 )
  1145. {
  1146. Msg( "ThreadPoolTest: Testing! Sleep %d, interleave %d, prioritized %d \n", g_iSleep, bInterleavePushPop, bPrioritized );
  1147. int nMaxThreads = ( IsX360() ) ? 6 : 8;
  1148. int nIncrement = ( IsX360() ) ? 1 : 2;
  1149. for ( int i = 1; i <= nMaxThreads; i += nIncrement )
  1150. {
  1151. CCountJob::m_nCount = 0;
  1152. g_nTotalAtFinish = 0;
  1153. ThreadPoolStartParams_t params;
  1154. params.nThreads = i;
  1155. params.fDistribute = ( bDistribute) ? TRS_TRUE : TRS_FALSE;
  1156. g_pTestThreadPool->Start( params, "Tst" );
  1157. if ( !bInterleavePushPop )
  1158. {
  1159. g_pTestThreadPool->SuspendExecution();
  1160. }
  1161. g_nTotalToComplete = nJobCount;
  1162. CFastTimer timer, suspendTimer;
  1163. suspendTimer.Start();
  1164. timer.Start();
  1165. for ( int j = 0; j < nJobCount; j++ )
  1166. {
  1167. jobs[j].SetFlags( JF_QUEUE );
  1168. jobs[j].bDoWork = bDoWork;
  1169. if ( bPrioritized )
  1170. {
  1171. jobs[j].SetPriority( (JobPriority_t)RandomInt( JP_LOW, JP_IMMEDIATE ) );
  1172. }
  1173. g_pTestThreadPool->AddJob( &jobs[j] );
  1174. if ( bSleep && j % 16 == 0 )
  1175. {
  1176. ThreadSleep( 0 );
  1177. }
  1178. }
  1179. if ( !bInterleavePushPop )
  1180. {
  1181. g_pTestThreadPool->ResumeExecution();
  1182. }
  1183. if ( bFinishExecute && g_iSleep <= 1 )
  1184. {
  1185. if ( bDoWork && bIncludeMain )
  1186. {
  1187. while ( g_pTestThreadPool->GetJobCount() && g_pTestThreadPool->NumIdleThreads() == g_pTestThreadPool->NumIdleThreads() )
  1188. {
  1189. }
  1190. CThreadEvent *pEvent = &g_done;
  1191. g_pTestThreadPool->YieldWait( &pEvent, 1 );
  1192. }
  1193. else
  1194. {
  1195. g_done.Wait();
  1196. }
  1197. }
  1198. g_nTotalAtFinish = CCountJob::m_nCount;
  1199. timer.End();
  1200. g_pTestThreadPool->SuspendExecution();
  1201. suspendTimer.End();
  1202. g_pTestThreadPool->ResumeExecution();
  1203. g_pTestThreadPool->Stop();
  1204. g_done.Reset();
  1205. int counts[9] = { 0 };
  1206. for ( int j = 0; j < nJobCount; j++ )
  1207. {
  1208. if ( jobs[j].GetServiceThread() != -1 )
  1209. {
  1210. counts[jobs[j].GetServiceThread()+1]++;
  1211. jobs[j].ClearServiceThread();
  1212. }
  1213. else if ( jobs[j].GetStatus() == JOB_OK )
  1214. {
  1215. counts[0]++;
  1216. }
  1217. }
  1218. Msg( "ThreadPoolTest: %d threads -- %d (%d) jobs processed in %fms, %fms to suspend (%f/%f) [ (main) %d, %d, %d, %d, %d, %d, %d, %d, %d]\n",
  1219. i, (int)g_nTotalAtFinish, (int)CCountJob::m_nCount, timer.GetDuration().GetMillisecondsF(), suspendTimer.GetDuration().GetMillisecondsF() - timer.GetDuration().GetMillisecondsF(),
  1220. timer.GetDuration().GetMillisecondsF() / (float)CCountJob::m_nCount, (suspendTimer.GetDuration().GetMillisecondsF())/(float)g_nTotalAtFinish,
  1221. counts[0], counts[1], counts[2], counts[3], counts[4], counts[5], counts[6], counts[7], counts[8] );
  1222. }
  1223. }
  1224. }
  1225. delete[]jobs;
  1226. }
  1227. bool g_bOutputError;
  1228. volatile int g_ReadyToExecute;
  1229. CInterlockedInt g_nReady;
  1230. class CExecuteTestJob : public CJob
  1231. {
  1232. public:
  1233. virtual JobStatus_t DoExecute()
  1234. {
  1235. byte pMemory[1024];
  1236. int i;
  1237. for ( i = 0; i < 1024; i++ )
  1238. {
  1239. pMemory[i] = rand();
  1240. }
  1241. for ( i = 0; i < 50; i++ )
  1242. {
  1243. sqrt( (float)HashBlock( pMemory, 1024 ) + HashBlock( pMemory, 1024 ) + 10.0 );
  1244. }
  1245. if ( AccessEvent()->Check() || IsFinished() )
  1246. {
  1247. if ( !g_bOutputError )
  1248. {
  1249. Msg( "Forced execute test failed!\n" );
  1250. DebuggerBreakIfDebugging();
  1251. }
  1252. }
  1253. return 0;
  1254. }
  1255. };
  1256. class CExecuteTestExecuteJob : public CJob
  1257. {
  1258. public:
  1259. virtual JobStatus_t DoExecute()
  1260. {
  1261. bool bAbort = ( RandomInt( 1, 10 ) == 1 );
  1262. g_nReady++;
  1263. while ( !g_ReadyToExecute )
  1264. {
  1265. ThreadPause();
  1266. }
  1267. if ( !bAbort )
  1268. m_pTestJob->Execute();
  1269. else
  1270. m_pTestJob->Abort();
  1271. g_nReady--;
  1272. return 0;
  1273. }
  1274. CExecuteTestJob *m_pTestJob;
  1275. };
  1276. void TestForcedExecute()
  1277. {
  1278. Msg( "TestForcedExecute\n" );
  1279. for ( int tests = 0; tests < 30; tests++ )
  1280. {
  1281. for ( int i = 1; i <= 5; i += 2 )
  1282. {
  1283. g_nReady = 0;
  1284. ThreadPoolStartParams_t params;
  1285. params.nThreads = i;
  1286. params.fDistribute = TRS_TRUE;
  1287. g_pTestThreadPool->Start( params, "Tst" );
  1288. int nJobCount = 4000;
  1289. CExecuteTestJob *jobs = new CExecuteTestJob[nJobCount];
  1290. for ( int j = 0; j < nJobCount; j++ )
  1291. {
  1292. g_ReadyToExecute = false;
  1293. for ( int k = 0; k < i; k++ )
  1294. {
  1295. CExecuteTestExecuteJob *pJob = new CExecuteTestExecuteJob;
  1296. pJob->SetFlags( JF_QUEUE );
  1297. pJob->m_pTestJob = &jobs[j];
  1298. g_pTestThreadPool->AddJob( pJob );
  1299. pJob->Release();
  1300. }
  1301. while ( g_nReady < i )
  1302. {
  1303. ThreadPause();
  1304. }
  1305. g_ReadyToExecute = true;
  1306. ThreadSleep();
  1307. jobs[j].Execute();
  1308. while ( g_nReady > 0 )
  1309. {
  1310. ThreadPause();
  1311. }
  1312. }
  1313. g_pTestThreadPool->Stop();
  1314. delete []jobs;
  1315. }
  1316. }
  1317. Msg( "TestForcedExecute DONE\n" );
  1318. }
  1319. } // namespace ThreadPoolTest
  1320. void RunThreadPoolTests()
  1321. {
  1322. CThreadPool pool;
  1323. ThreadPoolTest::g_pTestThreadPool = &pool;
  1324. RunTSQueueTests(10000);
  1325. RunTSListTests(10000);
  1326. intp mask1=-1;
  1327. #ifdef _WIN32
  1328. intp mask2 = -1;
  1329. GetProcessAffinityMask( GetCurrentProcess(), (DWORD_PTR *) &mask1, (DWORD_PTR *) &mask2 );
  1330. #endif
  1331. Msg( "ThreadPoolTest: Job distribution speed\n" );
  1332. for ( int i = 0; i < 2; i++ )
  1333. {
  1334. bool bToCompletion = ( i % 2 != 0 );
  1335. Msg( bToCompletion ? "ThreadPoolTest: To completion\n" : "ThreadPoolTest: NOT to completion\n" );
  1336. if ( !IsX360() )
  1337. {
  1338. Msg( "ThreadPoolTest: Non-distribute\n" );
  1339. ThreadPoolTest::Test( false, true, bToCompletion );
  1340. }
  1341. Msg( "ThreadPoolTest: Distribute\n" );
  1342. ThreadPoolTest::Test( true, true, bToCompletion );
  1343. // Msg( "ThreadPoolTest: One core\n" );
  1344. // ThreadSetAffinity( 0, 1 );
  1345. // ThreadPoolTest::Test( false, true, bToCompletion );
  1346. // ThreadSetAffinity( 0, mask1 );
  1347. Msg( "ThreadPoolTest: NO Sleep\n" );
  1348. ThreadPoolTest::Test( false, false, bToCompletion );
  1349. Msg( "ThreadPoolTest: Distribute NO Sleep\n" );
  1350. ThreadPoolTest::Test( true, false, bToCompletion );
  1351. // Not plumbed correctly
  1352. // Msg( "ThreadPoolTest: One core\n" );
  1353. // ThreadSetAffinity( 0, 1 );
  1354. // ThreadPoolTest::Test( false, false, bToCompletion );
  1355. // ThreadSetAffinity( 0, mask1 );
  1356. }
  1357. for ( int bMain = 0; bMain < 2; bMain++ )
  1358. {
  1359. Msg( "ThreadPoolTest: Jobs doing work, %s main thread\n", bMain ? "WITH" : "without" );
  1360. for ( int i = 0; i < 2; i++ )
  1361. {
  1362. bool bToCompletion = true;// = ( i % 2 != 0 );
  1363. if ( !IsX360() )
  1364. {
  1365. Msg( "ThreadPoolTest: Non-distribute\n" );
  1366. ThreadPoolTest::Test( false, true, bToCompletion, true, !!bMain );
  1367. }
  1368. Msg( "ThreadPoolTest: Distribute\n" );
  1369. ThreadPoolTest::Test( true, true, bToCompletion, true, !!bMain );
  1370. // Msg( "ThreadPoolTest: One core\n" );
  1371. // ThreadSetAffinity( 0, 1 );
  1372. // ThreadPoolTest::Test( false, true, bToCompletion, true, !!bMain );
  1373. // ThreadSetAffinity( 0, mask1 );
  1374. Msg( "ThreadPoolTest: NO Sleep\n" );
  1375. ThreadPoolTest::Test( false, false, bToCompletion, true, !!bMain );
  1376. Msg( "ThreadPoolTest: Distribute NO Sleep\n" );
  1377. ThreadPoolTest::Test( true, false, bToCompletion, true, !!bMain );
  1378. // Msg( "ThreadPoolTest: One core\n" );
  1379. // ThreadSetAffinity( 0, 1 );
  1380. // ThreadPoolTest::Test( false, false, bToCompletion, true, !!bMain );
  1381. // ThreadSetAffinity( 0, mask1 );
  1382. }
  1383. }
  1384. #ifdef _WIN32
  1385. GetProcessAffinityMask( GetCurrentProcess(), (DWORD_PTR *) &mask1, (DWORD_PTR *) &mask2 );
  1386. #endif
  1387. ThreadPoolTest::TestForcedExecute();
  1388. }