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.

649 lines
15 KiB

  1. #ifndef THREADTOOLS_INL
  2. #define THREADTOOLS_INL
  3. // This file is included in threadtools.h for PS3 and threadtools.cpp for all other platforms
  4. //
  5. // Do not #include other files here
  6. #ifndef _PS3
  7. // this is defined in the .cpp for the PS3 to avoid introducing a dependency for files including the header
  8. CTHREADLOCALPTR(CThread) g_pCurThread;
  9. #define INLINE_ON_PS3
  10. #else
  11. // Inlining these functions on PS3 (which are called across PRX boundaries) saves us over 1ms per frame
  12. #define INLINE_ON_PS3 inline
  13. #endif
  14. INLINE_ON_PS3 CThread::CThread() :
  15. #ifdef _WIN32
  16. m_hThread( NULL ),
  17. m_threadId( 0 ),
  18. #elif defined( _PS3 ) || defined(_POSIX)
  19. m_threadId( 0 ),
  20. m_threadZombieId( 0 ) ,
  21. #endif
  22. m_result( 0 ),
  23. m_flags( 0 )
  24. {
  25. m_szName[0] = 0;
  26. m_NotSuspendedEvent.Set();
  27. }
  28. //---------------------------------------------------------
  29. INLINE_ON_PS3 CThread::~CThread()
  30. {
  31. #ifdef MSVC
  32. if (m_hThread)
  33. #elif defined(POSIX) && !defined( _PS3 )
  34. if ( m_threadId )
  35. #endif
  36. {
  37. if ( IsAlive() )
  38. {
  39. Msg( "Illegal termination of worker thread! Threads must negotiate an end to the thread before the CThread object is destroyed.\n" );
  40. #ifdef _WIN32
  41. DoNewAssertDialog( __FILE__, __LINE__, "Illegal termination of worker thread! Threads must negotiate an end to the thread before the CThread object is destroyed.\n" );
  42. #endif
  43. if ( GetCurrentCThread() == this )
  44. {
  45. Stop(); // BUGBUG: Alfred - this doesn't make sense, this destructor fires from the hosting thread not the thread itself!!
  46. }
  47. }
  48. }
  49. #if defined(POSIX) || defined( _PS3 )
  50. if ( m_threadZombieId )
  51. {
  52. // just clean up zombie threads immediately (the destructor is fired from the hosting thread)
  53. Join();
  54. }
  55. #endif
  56. }
  57. //---------------------------------------------------------
  58. INLINE_ON_PS3 const char *CThread::GetName()
  59. {
  60. AUTO_LOCK( m_Lock );
  61. if ( !m_szName[0] )
  62. {
  63. #if defined( _WIN32 )
  64. _snprintf( m_szName, sizeof(m_szName) - 1, "Thread(%p/%p)", this, m_hThread );
  65. #elif defined( _PS3 )
  66. snprintf( m_szName, sizeof(m_szName) - 1, "Thread(%p)", this );
  67. #elif defined( POSIX )
  68. _snprintf( m_szName, sizeof(m_szName) - 1, "Thread(%p/0x%p)", this, m_threadId );
  69. #endif
  70. m_szName[sizeof(m_szName) - 1] = 0;
  71. }
  72. return m_szName;
  73. }
  74. //---------------------------------------------------------
  75. INLINE_ON_PS3 void CThread::SetName(const char *pszName)
  76. {
  77. AUTO_LOCK( m_Lock );
  78. strncpy( m_szName, pszName, sizeof(m_szName) - 1 );
  79. m_szName[sizeof(m_szName) - 1] = 0;
  80. }
  81. //-----------------------------------------------------
  82. // Functions for the other threads
  83. //-----------------------------------------------------
  84. // Start thread running - error if already running
  85. INLINE_ON_PS3 bool CThread::Start( unsigned nBytesStack, ThreadPriorityEnum_t nPriority )
  86. {
  87. AUTO_LOCK( m_Lock );
  88. if ( IsAlive() )
  89. {
  90. AssertMsg( 0, "Tried to create a thread that has already been created!" );
  91. return false;
  92. }
  93. bool bInitSuccess = false;
  94. CThreadEvent createComplete;
  95. ThreadInit_t init = { this, &createComplete, &bInitSuccess };
  96. #if defined( THREAD_PARENT_STACK_TRACE_ENABLED )
  97. {
  98. int iValidEntries = GetCallStack_Fast( init.ParentStackTrace, ARRAYSIZE( init.ParentStackTrace ), 0 );
  99. for( int i = iValidEntries; i < ARRAYSIZE( init.ParentStackTrace ); ++i )
  100. {
  101. init.ParentStackTrace[i] = NULL;
  102. }
  103. }
  104. #endif
  105. #ifdef PLATFORM_WINDOWS
  106. m_hThread = (HANDLE)CreateThread( NULL,
  107. nBytesStack,
  108. (LPTHREAD_START_ROUTINE)GetThreadProc(),
  109. new ThreadInit_t(init),
  110. nBytesStack ? STACK_SIZE_PARAM_IS_A_RESERVATION : 0,
  111. (LPDWORD)&m_threadId );
  112. if( nPriority != TP_PRIORITY_DEFAULT )
  113. {
  114. SetThreadPriority( m_hThread, nPriority );
  115. }
  116. if ( !m_hThread )
  117. {
  118. AssertMsg1( 0, "Failed to create thread (error 0x%x)", GetLastError() );
  119. return false;
  120. }
  121. #elif PLATFORM_PS3
  122. // On the PS3, a stack size of 0 doesn't imply a default stack size, so we need to force it to our
  123. // own default size.
  124. if ( nBytesStack == 0 )
  125. {
  126. nBytesStack = PS3_SYS_PPU_THREAD_COMMON_STACK_SIZE;
  127. }
  128. //The thread is about to begin
  129. m_threadEnd.Reset();
  130. // sony documentation:
  131. // "If the PPU thread is not joined by sys_ppu_thread_join() after exit,
  132. // it should always be created as non-joinable (not specifying
  133. // SYS_PPU_THREAD_CREATE_JOINABLE). Otherwise, some resources are left
  134. // allocated after termination of the PPU thread as if memory leaks."
  135. const char* threadName=m_szName;
  136. if ( sys_ppu_thread_create( &m_threadId,
  137. (void(*)(uint64_t))GetThreadProc(),
  138. (uint64_t)(new ThreadInit_t( init )),
  139. nPriority,
  140. nBytesStack,
  141. SYS_PPU_THREAD_CREATE_JOINABLE ,
  142. threadName ) != CELL_OK )
  143. {
  144. AssertMsg1( 0, "Failed to create thread (error 0x%x)", errno );
  145. return false;
  146. }
  147. bInitSuccess = true;
  148. #elif PLATFORM_POSIX
  149. pthread_attr_t attr;
  150. pthread_attr_init( &attr );
  151. pthread_attr_setstacksize( &attr, MAX( nBytesStack, 1024u*1024 ) );
  152. if ( pthread_create( &m_threadId, &attr, (void *(*)(void *))GetThreadProc(), new ThreadInit_t( init ) ) != 0 )
  153. {
  154. AssertMsg1( 0, "Failed to create thread (error 0x%x)", GetLastError() );
  155. return false;
  156. }
  157. bInitSuccess = true;
  158. #endif
  159. if ( !WaitForCreateComplete( &createComplete ) )
  160. {
  161. Msg( "Thread failed to initialize\n" );
  162. #ifdef _WIN32
  163. CloseHandle( m_hThread );
  164. m_hThread = NULL;
  165. #elif defined( _PS3 )
  166. m_threadEnd.Set();
  167. m_threadId = NULL;
  168. m_threadZombieId = 0;
  169. #endif
  170. return false;
  171. }
  172. if ( !bInitSuccess )
  173. {
  174. Msg( "Thread failed to initialize\n" );
  175. #ifdef _WIN32
  176. CloseHandle( m_hThread );
  177. m_hThread = NULL;
  178. #elif defined(POSIX) && !defined( _PS3 )
  179. m_threadId = 0;
  180. m_threadZombieId = 0;
  181. #endif
  182. return false;
  183. }
  184. #ifdef _WIN32
  185. if ( !m_hThread )
  186. {
  187. Msg( "Thread exited immediately\n" );
  188. }
  189. #endif
  190. #ifdef _WIN32
  191. AddThreadHandleToIDMap( m_hThread, m_threadId );
  192. return !!m_hThread;
  193. #elif defined(POSIX)
  194. return !!m_threadId;
  195. #endif
  196. }
  197. //---------------------------------------------------------
  198. //
  199. // Return true if the thread has been created and hasn't yet exited
  200. //
  201. INLINE_ON_PS3 bool CThread::IsAlive()
  202. {
  203. #ifdef PLATFORM_WINDOWS
  204. DWORD dwExitCode;
  205. return (
  206. m_hThread
  207. && GetExitCodeThread(m_hThread, &dwExitCode)
  208. && dwExitCode == STILL_ACTIVE );
  209. #elif defined(POSIX)
  210. return !!m_threadId;
  211. #endif
  212. }
  213. // This method causes the current thread to wait until this thread
  214. // is no longer alive.
  215. INLINE_ON_PS3 bool CThread::Join( unsigned timeout )
  216. {
  217. #ifdef _WIN32
  218. if ( m_hThread )
  219. #elif defined(POSIX)
  220. if ( m_threadId || m_threadZombieId )
  221. #endif
  222. {
  223. AssertMsg(GetCurrentCThread() != this, _T("Thread cannot be joined with self"));
  224. #ifdef _WIN32
  225. return ThreadJoin( (ThreadHandle_t)m_hThread, timeout );
  226. #elif defined(POSIX)
  227. bool ret = ThreadJoin( (ThreadHandle_t)(m_threadId ? m_threadId : m_threadZombieId), timeout );
  228. m_threadZombieId = 0;
  229. return ret;
  230. #endif
  231. }
  232. return true;
  233. }
  234. //---------------------------------------------------------
  235. INLINE_ON_PS3 ThreadHandle_t CThread::GetThreadHandle()
  236. {
  237. #ifdef _WIN32
  238. return (ThreadHandle_t)m_hThread;
  239. #else
  240. return (ThreadHandle_t)m_threadId;
  241. #endif
  242. }
  243. //---------------------------------------------------------
  244. INLINE_ON_PS3 int CThread::GetResult()
  245. {
  246. return m_result;
  247. }
  248. //-----------------------------------------------------
  249. // Functions for both this, and maybe, and other threads
  250. //-----------------------------------------------------
  251. // Forcibly, abnormally, but relatively cleanly stop the thread
  252. //
  253. INLINE_ON_PS3 void CThread::Stop(int exitCode)
  254. {
  255. if ( !IsAlive() )
  256. return;
  257. if ( GetCurrentCThread() == this )
  258. {
  259. #if !defined( _PS3 )
  260. m_result = exitCode;
  261. if ( !( m_flags & SUPPORT_STOP_PROTOCOL ) )
  262. {
  263. OnExit();
  264. g_pCurThread = NULL;
  265. #ifdef _WIN32
  266. CloseHandle( m_hThread );
  267. RemoveThreadHandleToIDMap( m_hThread );
  268. m_hThread = NULL;
  269. #else
  270. m_threadId = 0;
  271. m_threadZombieId = 0;
  272. #endif
  273. }
  274. else
  275. {
  276. throw exitCode;
  277. }
  278. #else
  279. AssertMsg( false, "Called CThread::Stop() for a platform that doesn't have it!\n");
  280. #endif
  281. }
  282. else
  283. AssertMsg( 0, "Only thread can stop self: Use a higher-level protocol");
  284. }
  285. //---------------------------------------------------------
  286. // Get the priority
  287. INLINE_ON_PS3 int CThread::GetPriority() const
  288. {
  289. #ifdef _WIN32
  290. return GetThreadPriority(m_hThread);
  291. #elif defined( _PS3 )
  292. return ThreadGetPriority( (ThreadHandle_t) m_threadId );
  293. #elif defined(POSIX)
  294. struct sched_param thread_param;
  295. int policy;
  296. pthread_getschedparam( m_threadId, &policy, &thread_param );
  297. return thread_param.sched_priority;
  298. #endif
  299. }
  300. //---------------------------------------------------------
  301. // Set the priority
  302. INLINE_ON_PS3 bool CThread::SetPriority(int priority)
  303. {
  304. #ifdef WIN32
  305. return ThreadSetPriority( (ThreadHandle_t)m_hThread, priority );
  306. #else
  307. return ThreadSetPriority( (ThreadHandle_t)m_threadId, priority );
  308. #endif
  309. }
  310. //---------------------------------------------------------
  311. // Suspend a thread
  312. INLINE_ON_PS3 unsigned CThread::Suspend()
  313. {
  314. AssertMsg( ThreadGetCurrentId() == (ThreadId_t)m_threadId, "Cannot call CThread::Suspend from outside thread" );
  315. if ( ThreadGetCurrentId() != (ThreadId_t)m_threadId )
  316. {
  317. DebuggerBreakIfDebugging();
  318. }
  319. m_NotSuspendedEvent.Reset();
  320. m_NotSuspendedEvent.Wait();
  321. return 0;
  322. }
  323. //---------------------------------------------------------
  324. INLINE_ON_PS3 unsigned CThread::Resume()
  325. {
  326. if ( m_NotSuspendedEvent.Check() )
  327. {
  328. DevWarning( "Called Resume() on a thread that is not suspended!\n" );
  329. }
  330. m_NotSuspendedEvent.Set();
  331. return 0;
  332. }
  333. //---------------------------------------------------------
  334. // Force hard-termination of thread. Used for critical failures.
  335. INLINE_ON_PS3 bool CThread::Terminate(int exitCode)
  336. {
  337. #if defined( _X360 )
  338. AssertMsg( 0, "Cannot terminate a thread on the Xbox!" );
  339. return false;
  340. #elif defined( _WIN32 )
  341. // I hope you know what you're doing!
  342. if (!TerminateThread(m_hThread, exitCode))
  343. return false;
  344. CloseHandle( m_hThread );
  345. RemoveThreadHandleToIDMap( m_hThread );
  346. m_hThread = NULL;
  347. #elif defined( _PS3 )
  348. m_threadEnd.Set();
  349. m_threadId = NULL;
  350. #elif defined(POSIX)
  351. pthread_kill( m_threadId, SIGKILL );
  352. m_threadId = 0;
  353. #endif
  354. return true;
  355. }
  356. //-----------------------------------------------------
  357. // Global methods
  358. //-----------------------------------------------------
  359. // Get the Thread object that represents the current thread, if any.
  360. // Can return NULL if the current thread was not created using
  361. // CThread
  362. //
  363. INLINE_ON_PS3 CThread *CThread::GetCurrentCThread()
  364. {
  365. #ifdef _PS3
  366. return GetCurThreadPS3();
  367. #else
  368. return g_pCurThread;
  369. #endif
  370. }
  371. //---------------------------------------------------------
  372. //
  373. // Offer a context switch. Under Win32, equivalent to Sleep(0)
  374. //
  375. #ifdef Yield
  376. #undef Yield
  377. #endif
  378. INLINE_ON_PS3 void CThread::Yield()
  379. {
  380. #ifdef _WIN32
  381. ::Sleep(0);
  382. #elif defined( _PS3 )
  383. // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead.
  384. sys_timer_usleep( 60 );
  385. #elif defined(POSIX)
  386. pthread_yield();
  387. #endif
  388. }
  389. //---------------------------------------------------------
  390. //
  391. // This method causes the current thread to yield and not to be
  392. // scheduled for further execution until a certain amount of real
  393. // time has elapsed, more or less. Duration is in milliseconds
  394. INLINE_ON_PS3 void CThread::Sleep( unsigned duration )
  395. {
  396. #ifdef _WIN32
  397. ::Sleep(duration);
  398. #elif defined (_PS3)
  399. sys_timer_usleep( duration * 1000 );
  400. #elif defined(POSIX)
  401. usleep( duration * 1000 );
  402. #endif
  403. }
  404. //---------------------------------------------------------
  405. // Optional pre-run call, with ability to fail-create. Note Init()
  406. // is forced synchronous with Start()
  407. INLINE_ON_PS3 bool CThread::Init()
  408. {
  409. return true;
  410. }
  411. //---------------------------------------------------------
  412. #if defined( _PS3 )
  413. INLINE_ON_PS3 int CThread::Run()
  414. {
  415. return -1;
  416. }
  417. #endif // _PS3
  418. // Called when the thread exits
  419. INLINE_ON_PS3 void CThread::OnExit() { }
  420. // Allow for custom start waiting
  421. INLINE_ON_PS3 bool CThread::WaitForCreateComplete( CThreadEvent *pEvent )
  422. {
  423. // Force serialized thread creation...
  424. if (!pEvent->Wait(60000))
  425. {
  426. AssertMsg( 0, "Probably deadlock or failure waiting for thread to initialize." );
  427. return false;
  428. }
  429. return true;
  430. }
  431. INLINE_ON_PS3 bool CThread::IsThreadRunning()
  432. {
  433. #ifdef _PS3
  434. // ThreadIsThreadIdRunning() doesn't work on PS3 if the thread is in a zombie state
  435. return m_eventTheadExit.Check();
  436. #else
  437. return ThreadIsThreadIdRunning( (ThreadId_t)m_threadId );
  438. #endif
  439. }
  440. //---------------------------------------------------------
  441. INLINE_ON_PS3 CThread::ThreadProc_t CThread::GetThreadProc()
  442. {
  443. return ThreadProc;
  444. }
  445. INLINE_ON_PS3 void CThread::ThreadProcRunWithMinidumpHandler( void *pv )
  446. {
  447. ThreadInit_t *pInit = reinterpret_cast<ThreadInit_t*>(pv);
  448. pInit->pThread->m_result = pInit->pThread->Run();
  449. }
  450. #ifdef PLATFORM_WINDOWS
  451. unsigned long STDCALL CThread::ThreadProc(LPVOID pv)
  452. #else
  453. INLINE_ON_PS3 void* CThread::ThreadProc(LPVOID pv)
  454. #endif
  455. {
  456. #if defined( POSIX ) || defined( _PS3 )
  457. ThreadInit_t *pInit = reinterpret_cast<ThreadInit_t*>(pv);
  458. #else
  459. std::auto_ptr<ThreadInit_t> pInit((ThreadInit_t *)pv);
  460. #endif
  461. #ifdef _X360
  462. // Make sure all threads are consistent w.r.t floating-point math
  463. SetupFPUControlWord();
  464. #endif
  465. AllocateThreadID();
  466. CThread *pThread = pInit->pThread;
  467. #ifdef _PS3
  468. SetCurThreadPS3( pThread );
  469. #else
  470. g_pCurThread = pThread;
  471. #endif
  472. pThread->m_pStackBase = AlignValue( &pThread, 4096 );
  473. pInit->pThread->m_result = -1;
  474. #if defined( THREAD_PARENT_STACK_TRACE_ENABLED )
  475. CStackTop_ReferenceParentStack stackTop( pInit->ParentStackTrace, ARRAYSIZE( pInit->ParentStackTrace ) );
  476. #endif
  477. bool bInitSuccess = true;
  478. if ( pInit->pfInitSuccess )
  479. *(pInit->pfInitSuccess) = false;
  480. #ifdef _PS3
  481. *(pInit->pfInitSuccess) = pInit->pThread->Init();
  482. #else
  483. try
  484. {
  485. bInitSuccess = pInit->pThread->Init();
  486. }
  487. catch (...)
  488. {
  489. pInit->pInitCompleteEvent->Set();
  490. throw;
  491. }
  492. #endif // _PS3
  493. if ( pInit->pfInitSuccess )
  494. *(pInit->pfInitSuccess) = bInitSuccess;
  495. pInit->pInitCompleteEvent->Set();
  496. if (!bInitSuccess)
  497. return 0;
  498. if ( !Plat_IsInDebugSession() && (pInit->pThread->m_flags & SUPPORT_STOP_PROTOCOL) )
  499. {
  500. #ifndef _PS3
  501. try
  502. #endif
  503. {
  504. pInit->pThread->m_result = pInit->pThread->Run();
  505. }
  506. #ifndef _PS3
  507. catch (...)
  508. {
  509. }
  510. #endif
  511. }
  512. else
  513. {
  514. #if defined( _WIN32 )
  515. CatchAndWriteMiniDumpForVoidPtrFn( ThreadProcRunWithMinidumpHandler, pv, false );
  516. #else
  517. pInit->pThread->m_result = pInit->pThread->Run();
  518. #endif
  519. }
  520. pInit->pThread->OnExit();
  521. #ifdef _PS3
  522. SetCurThreadPS3( NULL );
  523. #else
  524. g_pCurThread = NULL;
  525. #endif
  526. FreeThreadID();
  527. AUTO_LOCK( pThread->m_Lock );
  528. #ifdef _WIN32
  529. CloseHandle( pThread->m_hThread );
  530. RemoveThreadHandleToIDMap( pThread->m_hThread );
  531. pThread->m_hThread = NULL;
  532. #elif defined( _PS3 )
  533. pThread->m_threadZombieId = pThread->m_threadId;
  534. pThread->m_threadEnd.Set();
  535. pThread->m_threadId = 0;
  536. #elif defined(POSIX)
  537. pThread->m_threadZombieId = pThread->m_threadId;
  538. pThread->m_threadId = 0;
  539. #else
  540. #error
  541. #endif
  542. pThread->m_ExitEvent.Set();
  543. #ifdef _PS3
  544. {
  545. pThread->m_Lock.Unlock();
  546. sys_ppu_thread_exit( pInit->pThread->m_result );
  547. // reacquire the lock in case thread exit didn't actually exit the thread, so that
  548. // AUTO_LOCK won't double-unlock the lock (to keep it paired)
  549. pThread->m_Lock.Lock();
  550. }
  551. #endif
  552. #if defined( POSIX )|| defined( _PS3 )
  553. return (void*)(uintp)pInit->pThread->m_result;
  554. #else
  555. return pInit->pThread->m_result;
  556. #endif
  557. }
  558. #endif // THREADTOOLS_INL