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.

3241 lines
75 KiB

  1. //========== Copyright 2005, Valve Corporation, All rights reserved. ========
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "tier0/platform.h"
  7. #if defined( PLATFORM_WINDOWS_PC )
  8. #define WIN32_LEAN_AND_MEAN
  9. #define _WIN32_WINNT 0x0403
  10. #include <windows.h>
  11. #endif
  12. #ifdef PLATFORM_WINDOWS
  13. #include <process.h>
  14. #ifdef PLATFORM_WINDOWS_PC
  15. #include <Mmsystem.h>
  16. #pragma comment(lib, "winmm.lib")
  17. #endif
  18. #elif PLATFORM_PS3
  19. #include <sched.h>
  20. #include <unistd.h>
  21. #include <exception>
  22. #include <errno.h>
  23. #include <pthread.h>
  24. #include <sys/time.h>
  25. #include <sys/timer.h>
  26. #define GetLastError() errno
  27. typedef void *LPVOID;
  28. #elif PLATFORM_POSIX
  29. #include <sched.h>
  30. #include <exception>
  31. #include <errno.h>
  32. #include <signal.h>
  33. #include <pthread.h>
  34. #include <sys/time.h>
  35. #define GetLastError() errno
  36. typedef void *LPVOID;
  37. #if !defined(OSX)
  38. #include <sys/fcntl.h>
  39. #include <sys/unistd.h>
  40. #define sem_unlink( arg )
  41. #define OS_TO_PTHREAD(x) (x)
  42. #else
  43. #define pthread_yield pthread_yield_np
  44. #include <mach/thread_act.h>
  45. #include <mach/mach.h>
  46. #define OS_TO_PTHREAD(x) pthread_from_mach_thread_np( x )
  47. #endif // !OSX
  48. #endif
  49. #ifndef _PS3
  50. #include <memory.h>
  51. #endif
  52. #include "tier0/minidump.h"
  53. #include "tier0/threadtools.h"
  54. #include "tier0/dynfunction.h"
  55. #ifdef _X360
  56. #include "xbox/xbox_win32stubs.h"
  57. #endif
  58. #include <map>
  59. // Must be last header...
  60. #include "tier0/memdbgon.h"
  61. #ifdef _PS3
  62. #include "ps3/ps3_win32stubs.h"
  63. #define NEW_WAIT_FOR_MULTIPLE_OBJECTS
  64. bool gbCheckNotMultithreaded = true;
  65. extern "C" void(*g_pfnPushMarker)( const char * pName );
  66. extern "C" void(*g_pfnPopMarker)();
  67. #endif
  68. #define THREADS_DEBUG 1
  69. #define DEBUG_ERROR(XX) Assert(0)
  70. // Need to ensure initialized before other clients call in for main thread ID
  71. #ifdef _WIN32
  72. #pragma warning(disable:4073)
  73. #pragma init_seg(lib)
  74. #endif
  75. #ifdef _WIN32
  76. ASSERT_INVARIANT(TT_SIZEOF_CRITICALSECTION == sizeof(CRITICAL_SECTION));
  77. ASSERT_INVARIANT(TT_INFINITE == INFINITE);
  78. #endif
  79. // thread creation counter.
  80. // this is used to provide a unique threadid for each running thread in g_nThreadID ( a thread local variable ).
  81. const int MAX_THREAD_IDS = 128;
  82. static volatile bool s_bThreadIDAllocated[MAX_THREAD_IDS];
  83. #if defined(_LINUX) && defined(DEDICATED)
  84. DLL_CLASS_EXPORT __thread int g_nThreadID;
  85. #elif defined(_PS3)
  86. #include "tls_ps3.h"
  87. #else
  88. DLL_CLASS_EXPORT CTHREADLOCALINT g_nThreadID;
  89. #endif
  90. static CThreadFastMutex s_ThreadIDMutex;
  91. PLATFORM_INTERFACE void AllocateThreadID( void )
  92. {
  93. AUTO_LOCK( s_ThreadIDMutex );
  94. for( int i = 1; i < MAX_THREAD_IDS; i++ )
  95. {
  96. if ( ! s_bThreadIDAllocated[i] )
  97. {
  98. g_nThreadID = i;
  99. s_bThreadIDAllocated[i] = true;
  100. return;
  101. }
  102. }
  103. Error( "Out of thread ids. Decrease the number of threads or increase MAX_THREAD_IDS\n" );
  104. }
  105. PLATFORM_INTERFACE void FreeThreadID( void )
  106. {
  107. AUTO_LOCK( s_ThreadIDMutex );
  108. int nThread = g_nThreadID;
  109. if ( nThread )
  110. s_bThreadIDAllocated[nThread] = false;
  111. }
  112. //-----------------------------------------------------------------------------
  113. // Simple thread functions.
  114. // Because _beginthreadex uses stdcall, we need to convert to cdecl
  115. //-----------------------------------------------------------------------------
  116. struct ThreadProcInfo_t
  117. {
  118. ThreadProcInfo_t( ThreadFunc_t pfnThread, void *pParam )
  119. : pfnThread( pfnThread),
  120. pParam( pParam )
  121. {
  122. }
  123. ThreadFunc_t pfnThread;
  124. void * pParam;
  125. };
  126. //---------------------------------------------------------
  127. #ifdef PLATFORM_WINDOWS
  128. static DWORD WINAPI ThreadProcConvert( void *pParam )
  129. {
  130. ThreadProcInfo_t info = *((ThreadProcInfo_t *)pParam);
  131. AllocateThreadID();
  132. delete ((ThreadProcInfo_t *)pParam);
  133. unsigned nRet = (*info.pfnThread)(info.pParam);
  134. FreeThreadID();
  135. return nRet;
  136. }
  137. #elif defined( PLATFORM_PS3 )
  138. union ThreadProcInfoUnion_t
  139. {
  140. struct Val_t
  141. {
  142. ThreadFunc_t pfnThread;
  143. void * pParam;
  144. }
  145. val;
  146. uint64_t val64;
  147. };
  148. static void ThreadProcConvertUnion( uint64_t param )
  149. {
  150. COMPILE_TIME_ASSERT( sizeof( ThreadProcInfoUnion_t ) == 8 );
  151. ThreadProcInfoUnion_t info;
  152. info.val64 = param;
  153. AllocateThreadID();
  154. unsigned nRet = (*info.val.pfnThread)(info.val.pParam);
  155. FreeThreadID();
  156. sys_ppu_thread_exit( nRet );
  157. }
  158. static void* ThreadProcConvert( void *pParam )
  159. {
  160. ThreadProcInfo_t info = *((ThreadProcInfo_t *)pParam);
  161. AllocateThreadID();
  162. delete ((ThreadProcInfo_t *)pParam);
  163. unsigned nRet = (*info.pfnThread)(info.pParam);
  164. FreeThreadID();
  165. return ( void * ) nRet;
  166. }
  167. #else
  168. static void* ThreadProcConvert( void *pParam )
  169. {
  170. ThreadProcInfo_t info = *((ThreadProcInfo_t *)pParam);
  171. AllocateThreadID();
  172. delete ((ThreadProcInfo_t *)pParam);
  173. unsigned nRet = (*info.pfnThread)(info.pParam);
  174. FreeThreadID();
  175. return ( void * ) (uintp) nRet;
  176. }
  177. #endif
  178. #if defined( _PS3 )
  179. /*******************************************************************************
  180. * Thread Local Storage globals and functions
  181. *******************************************************************************/
  182. #ifndef _PS3
  183. __thread void *gTLSValues[ MAX_TLS_VALUES ] = { NULL };
  184. __thread bool gTLSFlags[ MAX_TLS_VALUES ] = { false };
  185. __thread bool gbWaitObjectsCreated = false;
  186. __thread sys_semaphore_t gWaitObjectsSemaphore;
  187. #endif // !_PS3
  188. static char gThreadName[28] = "";
  189. // Simple TLS allocator. Linearly searches for a free slot.
  190. uint32 TlsAlloc()
  191. {
  192. for ( int i = 0; i < MAX_TLS_VALUES; ++i )
  193. {
  194. if ( !gTLSFlags[i] )
  195. {
  196. gTLSFlags[i] = true;
  197. return i;
  198. }
  199. }
  200. #ifdef _PS3
  201. DEBUG_ERROR("TlsAlloc(): Out of TLS\n");
  202. #endif
  203. return 0xFFFFFFFF;
  204. }
  205. void TlsFree( uint32 index )
  206. {
  207. gTLSValues[ index ] = NULL;
  208. gTLSFlags[ index ] = false;
  209. }
  210. void *TlsGetValue( uint32 index )
  211. {
  212. return gTLSValues[ index ];
  213. }
  214. void TlsSetValue( uint32 index, void *pValue )
  215. {
  216. gTLSValues[ index ] = pValue;
  217. }
  218. #endif //_PS3
  219. #ifdef PLATFORM_WINDOWS
  220. class CThreadHandleToIDMap
  221. {
  222. public:
  223. HANDLE m_hThread;
  224. uint m_ThreadID;
  225. CThreadHandleToIDMap *m_pNext;
  226. };
  227. static CThreadHandleToIDMap *g_pThreadHandleToIDMaps = NULL;
  228. static CThreadMutex g_ThreadHandleToIDMapMutex;
  229. static volatile int g_nThreadHandleToIDMaps = 0;
  230. static void AddThreadHandleToIDMap( HANDLE hThread, uint threadID )
  231. {
  232. if ( !hThread )
  233. return;
  234. // Remember this handle/id combo.
  235. CThreadHandleToIDMap *pMap = new CThreadHandleToIDMap;
  236. pMap->m_hThread = hThread;
  237. pMap->m_ThreadID = threadID;
  238. // Add it to the global list.
  239. g_ThreadHandleToIDMapMutex.Lock();
  240. pMap->m_pNext = g_pThreadHandleToIDMaps;
  241. g_pThreadHandleToIDMaps = pMap;
  242. ++g_nThreadHandleToIDMaps;
  243. g_ThreadHandleToIDMapMutex.Unlock();
  244. if ( g_nThreadHandleToIDMaps > 500 )
  245. Error( "ThreadHandleToIDMap overflow." );
  246. }
  247. // This assumes you've got g_ThreadHandleToIDMapMutex locked!!
  248. static bool InternalLookupHandleToThreadIDMap( HANDLE hThread, CThreadHandleToIDMap* &pMap, CThreadHandleToIDMap** &ppPrev )
  249. {
  250. ppPrev = &g_pThreadHandleToIDMaps;
  251. for ( pMap=g_pThreadHandleToIDMaps; pMap; pMap=pMap->m_pNext )
  252. {
  253. if ( pMap->m_hThread == hThread )
  254. return true;
  255. ppPrev = &pMap->m_pNext;
  256. }
  257. return false;
  258. }
  259. static void RemoveThreadHandleToIDMap( HANDLE hThread )
  260. {
  261. if ( !hThread )
  262. return;
  263. CThreadHandleToIDMap *pMap, **ppPrev;
  264. g_ThreadHandleToIDMapMutex.Lock();
  265. if ( g_nThreadHandleToIDMaps <= 0 )
  266. Error( "ThreadHandleToIDMap underflow." );
  267. if ( InternalLookupHandleToThreadIDMap( hThread, pMap, ppPrev ) )
  268. {
  269. *ppPrev = pMap->m_pNext;
  270. delete pMap;
  271. --g_nThreadHandleToIDMaps;
  272. }
  273. g_ThreadHandleToIDMapMutex.Unlock();
  274. }
  275. static uint LookupThreadIDFromHandle( HANDLE hThread )
  276. {
  277. if ( hThread == NULL || hThread == GetCurrentThread() )
  278. return GetCurrentThreadId();
  279. float flStartTime = Plat_FloatTime();
  280. while ( Plat_FloatTime() - flStartTime < 2 )
  281. {
  282. CThreadHandleToIDMap *pMap, **ppPrev;
  283. g_ThreadHandleToIDMapMutex.Lock();
  284. bool bRet = InternalLookupHandleToThreadIDMap( hThread, pMap, ppPrev );
  285. g_ThreadHandleToIDMapMutex.Unlock();
  286. if ( bRet )
  287. return pMap->m_ThreadID;
  288. // We should only get here if a thread that is just starting up is currently in AddThreadHandleToIDMap.
  289. // Give up the timeslice and try again.
  290. ThreadSleep( 1 );
  291. }
  292. Assert( !"LookupThreadIDFromHandle failed!" );
  293. Warning( "LookupThreadIDFromHandle couldn't find thread ID for handle." );
  294. return 0;
  295. }
  296. #endif
  297. //---------------------------------------------------------
  298. ThreadHandle_t * CreateTestThreads( ThreadFunc_t fnThread, int numThreads, int nProcessorsToDistribute )
  299. {
  300. ThreadHandle_t *pHandles = (new ThreadHandle_t[numThreads+1]) + 1;
  301. pHandles[-1] = (ThreadHandle_t)INT_TO_POINTER( numThreads );
  302. for( int i = 0; i < numThreads; ++i )
  303. {
  304. //TestThreads();
  305. ThreadHandle_t hThread;
  306. const unsigned int nDefaultStackSize = 64 * 1024; // this stack size is used in case stackSize == 0
  307. hThread = CreateSimpleThread( fnThread, INT_TO_POINTER( i ), nDefaultStackSize );
  308. if ( nProcessorsToDistribute )
  309. {
  310. int32 mask = 1 << (i % nProcessorsToDistribute);
  311. ThreadSetAffinity( hThread, mask );
  312. }
  313. /*
  314. ThreadProcInfoUnion_t info;
  315. info.val.pfnThread = fnThread;
  316. info.val.pParam = (void*)(i);
  317. if ( int nError = sys_ppu_thread_create( &hThread, ThreadProcConvertUnion, info.val64, 1001, nDefaultStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, "SimpleThread" ) != CELL_OK )
  318. {
  319. printf( "PROBLEM!\n" );
  320. Error( "Cannot create thread, error %d\n", nError );
  321. return 0;
  322. }
  323. */
  324. //ThreadHandle_t hThread = CreateSimpleThread( fnThread, (void*)i );
  325. pHandles[i] = hThread;
  326. }
  327. // printf("Finishinged CreateTestThreads(%p,%d)\n", (void*)fnThread, numThreads );
  328. return pHandles;
  329. }
  330. void JoinTestThreads( ThreadHandle_t *pHandles )
  331. {
  332. int nCount = POINTER_TO_INT( (uintp)pHandles[-1] );
  333. // printf("Joining test threads @%p[%d]:\n", pHandles, nCount );
  334. // for( int i = 0; i < nCount; ++i )
  335. // {
  336. // printf(" %p,\n", (void*)pHandles[i] );
  337. // }
  338. for( int i = 0; i < nCount; ++i )
  339. {
  340. // printf( "Joining %p", (void*) pHandles[i] );
  341. // if( !i ) sys_timer_usleep(100000);
  342. ThreadJoin( pHandles[i] );
  343. ReleaseThreadHandle( pHandles[i] );
  344. }
  345. delete[]( pHandles - 1 );
  346. }
  347. ThreadHandle_t CreateSimpleThread( ThreadFunc_t pfnThread, void *pParam, unsigned stackSize )
  348. {
  349. #ifdef PLATFORM_WINDOWS
  350. DWORD threadID;
  351. HANDLE hThread = (HANDLE)CreateThread( NULL, stackSize, ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ), stackSize ? STACK_SIZE_PARAM_IS_A_RESERVATION : 0, &threadID );
  352. AddThreadHandleToIDMap( hThread, threadID );
  353. return (ThreadHandle_t)hThread;
  354. #elif PLATFORM_PS3
  355. //TestThreads();
  356. ThreadHandle_t th;
  357. ThreadProcInfoUnion_t info;
  358. info.val.pfnThread = pfnThread;
  359. info.val.pParam = pParam;
  360. const unsigned int nDefaultStackSize = 64 * 1024; // this stack size is used in case stackSize == 0
  361. if ( sys_ppu_thread_create( &th, ThreadProcConvertUnion, info.val64, 1001, stackSize ? stackSize : nDefaultStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, "SimpleThread" ) != CELL_OK )
  362. {
  363. AssertMsg1( 0, "Failed to create thread (error 0x%x)", errno );
  364. return 0;
  365. }
  366. return th;
  367. #elif PLATFORM_POSIX
  368. pthread_t tid;
  369. pthread_create( &tid, NULL, ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ) );
  370. return ( ThreadHandle_t ) tid;
  371. #else
  372. Assert( 0 );
  373. DebuggerBreak();
  374. return 0;
  375. #endif
  376. }
  377. bool ReleaseThreadHandle( ThreadHandle_t hThread )
  378. {
  379. #ifdef _WIN32
  380. bool bRetVal = ( CloseHandle( hThread ) != 0 );
  381. RemoveThreadHandleToIDMap( (HANDLE)hThread );
  382. return bRetVal;
  383. #else
  384. return true;
  385. #endif
  386. }
  387. //-----------------------------------------------------------------------------
  388. //
  389. // Wrappers for other simple threading operations
  390. //
  391. //-----------------------------------------------------------------------------
  392. void ThreadSleep(unsigned nMilliseconds)
  393. {
  394. #ifdef _WIN32
  395. #ifdef PLATFORM_WINDOWS_PC
  396. static bool bInitialized = false;
  397. if ( !bInitialized )
  398. {
  399. bInitialized = true;
  400. // Set the timer resolution to 1 ms (default is 10.0, 15.6, 2.5, 1.0 or
  401. // some other value depending on hardware and software) so that we can
  402. // use Sleep( 1 ) to avoid wasting CPU time without missing our frame
  403. // rate.
  404. timeBeginPeriod( 1 );
  405. }
  406. #endif
  407. Sleep( nMilliseconds );
  408. #elif PLATFORM_PS3
  409. if( nMilliseconds == 0 )
  410. {
  411. // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead.
  412. // sys_timer_usleep( 60 );
  413. sys_ppu_thread_yield();
  414. }
  415. else
  416. {
  417. sys_timer_usleep( nMilliseconds * 1000 );
  418. }
  419. #elif defined(POSIX)
  420. usleep( nMilliseconds * 1000 );
  421. #endif
  422. }
  423. //-----------------------------------------------------------------------------
  424. void ThreadNanoSleep(unsigned ns)
  425. {
  426. #ifdef _WIN32
  427. // ceil
  428. Sleep( ( ns + 999 ) / 1000 );
  429. #elif PLATFORM_PS3
  430. sys_timer_usleep( ns );
  431. #elif defined(POSIX)
  432. struct timespec tm;
  433. tm.tv_sec = 0;
  434. tm.tv_nsec = ns;
  435. nanosleep( &tm, NULL );
  436. #endif
  437. }
  438. //-----------------------------------------------------------------------------
  439. #ifndef ThreadGetCurrentId
  440. ThreadId_t ThreadGetCurrentId()
  441. {
  442. #ifdef _WIN32
  443. return GetCurrentThreadId();
  444. #elif defined( _PS3 )
  445. sys_ppu_thread_t th = 0;
  446. sys_ppu_thread_get_id( &th );
  447. return th;
  448. #elif defined(POSIX)
  449. return (ThreadId_t)pthread_self();
  450. #else
  451. Assert(0);
  452. DebuggerBreak();
  453. return 0;
  454. #endif
  455. }
  456. #endif
  457. //-----------------------------------------------------------------------------
  458. ThreadHandle_t ThreadGetCurrentHandle()
  459. {
  460. #ifdef _WIN32
  461. return (ThreadHandle_t)GetCurrentThread();
  462. #elif defined( _PS3 )
  463. sys_ppu_thread_t th = 0;
  464. sys_ppu_thread_get_id( &th );
  465. return th;
  466. #elif defined(POSIX)
  467. return (ThreadHandle_t)pthread_self();
  468. #else
  469. Assert(0);
  470. DebuggerBreak();
  471. return 0;
  472. #endif
  473. }
  474. // On PS3, this will return true for zombie threads
  475. bool ThreadIsThreadIdRunning( ThreadId_t uThreadId )
  476. {
  477. #ifdef _WIN32
  478. bool bRunning = true;
  479. HANDLE hThread = ::OpenThread( THREAD_QUERY_INFORMATION , false, uThreadId );
  480. if ( hThread )
  481. {
  482. DWORD dwExitCode;
  483. if( !::GetExitCodeThread( hThread, &dwExitCode ) || dwExitCode != STILL_ACTIVE )
  484. bRunning = false;
  485. CloseHandle( hThread );
  486. }
  487. else
  488. {
  489. bRunning = false;
  490. }
  491. return bRunning;
  492. #elif defined( _PS3 )
  493. // will return CELL_OK for zombie threads
  494. int priority;
  495. return (sys_ppu_thread_get_priority( uThreadId, &priority ) == CELL_OK );
  496. #elif defined(POSIX)
  497. int iResult = pthread_kill( OS_TO_PTHREAD(uThreadId), 0 );
  498. if ( iResult == 0 )
  499. return true;
  500. return false;
  501. #endif
  502. }
  503. //-----------------------------------------------------------------------------
  504. int ThreadGetPriority( ThreadHandle_t hThread )
  505. {
  506. if ( !hThread )
  507. {
  508. hThread = ThreadGetCurrentHandle();
  509. }
  510. #ifdef _WIN32
  511. return ::GetThreadPriority( (HANDLE)hThread );
  512. #elif defined( _PS3 )
  513. int iPri = 0;
  514. sys_ppu_thread_get_priority( hThread, &iPri );
  515. return iPri;
  516. #else
  517. return 0;
  518. #endif
  519. }
  520. //-----------------------------------------------------------------------------
  521. bool ThreadSetPriority( ThreadHandle_t hThread, int priority )
  522. {
  523. if ( !hThread )
  524. {
  525. hThread = ThreadGetCurrentHandle();
  526. }
  527. #ifdef _WIN32
  528. return ( SetThreadPriority(hThread, priority) != 0 );
  529. #elif defined( _PS3 )
  530. int retval = sys_ppu_thread_set_priority( hThread, priority );
  531. return retval >= CELL_OK;
  532. #elif defined(POSIX)
  533. struct sched_param thread_param;
  534. thread_param.sched_priority = priority;
  535. //pthread_setschedparam( (pthread_t ) hThread, SCHED_RR, &thread_param );
  536. return true;
  537. #endif
  538. }
  539. //-----------------------------------------------------------------------------
  540. void ThreadSetAffinity( ThreadHandle_t hThread, int nAffinityMask )
  541. {
  542. if ( !hThread )
  543. {
  544. hThread = ThreadGetCurrentHandle();
  545. }
  546. #ifdef _WIN32
  547. SetThreadAffinityMask( hThread, nAffinityMask );
  548. #elif defined(POSIX)
  549. // cpu_set_t cpuSet;
  550. // CPU_ZERO( cpuSet );
  551. // for( int i = 0 ; i < 32; i++ )
  552. // if ( nAffinityMask & ( 1 << i ) )
  553. // CPU_SET( cpuSet, i );
  554. // sched_setaffinity( hThread, sizeof( cpuSet ), &cpuSet );
  555. #endif
  556. }
  557. //-----------------------------------------------------------------------------
  558. #ifndef _X360
  559. ThreadId_t InitMainThread()
  560. {
  561. ThreadSetDebugName( "MainThrd" );
  562. return ThreadGetCurrentId();
  563. }
  564. ThreadId_t g_ThreadMainThreadID = InitMainThread();
  565. bool ThreadInMainThread()
  566. {
  567. return ( ThreadGetCurrentId() == g_ThreadMainThreadID );
  568. }
  569. void DeclareCurrentThreadIsMainThread()
  570. {
  571. g_ThreadMainThreadID = ThreadGetCurrentId();
  572. }
  573. #else
  574. byte *InitMainThread()
  575. {
  576. byte b;
  577. return AlignValue( &b, 64*1024 );
  578. }
  579. #define STACK_SIZE_360 327680
  580. byte *g_pBaseMainStack = InitMainThread();
  581. byte *g_pLimitMainStack = InitMainThread() - STACK_SIZE_360;
  582. #endif
  583. //-----------------------------------------------------------------------------
  584. bool ThreadJoin( ThreadHandle_t hThread, unsigned timeout )
  585. {
  586. if ( !hThread )
  587. {
  588. return false;
  589. }
  590. #ifdef _WIN32
  591. DWORD dwWait = WaitForSingleObject( (HANDLE)hThread, timeout );
  592. if ( dwWait == WAIT_TIMEOUT)
  593. return false;
  594. if ( dwWait != WAIT_OBJECT_0 && ( dwWait != WAIT_FAILED && GetLastError() != 0 ) )
  595. {
  596. Assert( 0 );
  597. return false;
  598. }
  599. #elif defined( _PS3 )
  600. uint64 uiExitCode = 0;
  601. int retval = sys_ppu_thread_join( hThread, &uiExitCode );
  602. return ( retval >= CELL_OK );
  603. #elif defined(POSIX)
  604. if ( pthread_join( (pthread_t)hThread, NULL ) != 0 )
  605. return false;
  606. #else
  607. Assert(0);
  608. DebuggerBreak();
  609. #endif
  610. return true;
  611. }
  612. //-----------------------------------------------------------------------------
  613. void ThreadSetDebugName( ThreadHandle_t hThread, const char *pszName )
  614. {
  615. #ifdef WIN32
  616. if ( Plat_IsInDebugSession() )
  617. {
  618. #define MS_VC_EXCEPTION 0x406d1388
  619. typedef struct tagTHREADNAME_INFO
  620. {
  621. DWORD dwType; // must be 0x1000
  622. LPCSTR szName; // pointer to name (in same addr space)
  623. DWORD dwThreadID; // thread ID (-1 caller thread)
  624. DWORD dwFlags; // reserved for future use, most be zero
  625. } THREADNAME_INFO;
  626. THREADNAME_INFO info;
  627. info.dwType = 0x1000;
  628. info.szName = pszName;
  629. info.dwThreadID = LookupThreadIDFromHandle( hThread );
  630. if ( info.dwThreadID != 0 )
  631. {
  632. info.dwFlags = 0;
  633. __try
  634. {
  635. RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
  636. }
  637. __except (EXCEPTION_CONTINUE_EXECUTION)
  638. {
  639. }
  640. }
  641. }
  642. #endif
  643. }
  644. //-----------------------------------------------------------------------------
  645. // Used to thread LoadLibrary on the 360
  646. //-----------------------------------------------------------------------------
  647. static ThreadedLoadLibraryFunc_t s_ThreadedLoadLibraryFunc = 0;
  648. PLATFORM_INTERFACE void SetThreadedLoadLibraryFunc( ThreadedLoadLibraryFunc_t func )
  649. {
  650. s_ThreadedLoadLibraryFunc = func;
  651. }
  652. PLATFORM_INTERFACE ThreadedLoadLibraryFunc_t GetThreadedLoadLibraryFunc()
  653. {
  654. return s_ThreadedLoadLibraryFunc;
  655. }
  656. //-----------------------------------------------------------------------------
  657. //
  658. // CThreadSyncObject (note nothing uses this directly (I think) )
  659. //
  660. //-----------------------------------------------------------------------------
  661. #ifdef _PS3
  662. uint32_t CThreadSyncObject::m_bstaticMutexInitialized = false;
  663. uint32_t CThreadSyncObject::m_bstaticMutexInitializing = false;
  664. sys_lwmutex_t CThreadSyncObject::m_staticMutex;
  665. #endif
  666. CThreadSyncObject::CThreadSyncObject()
  667. #ifdef _WIN32
  668. : m_hSyncObject( NULL ), m_bCreatedHandle(false)
  669. #elif defined(POSIX) && !defined(PLATFORM_PS3)
  670. : m_bInitalized( false )
  671. #endif
  672. {
  673. #ifdef _PS3
  674. //Do we nee to initialise the staticMutex?
  675. if (m_bstaticMutexInitialized) return;
  676. //If we are the first thread then create the mutex
  677. if ( cellAtomicCompareAndSwap32(&m_bstaticMutexInitializing, false, true) == false )
  678. {
  679. sys_lwmutex_attribute_t mutexAttr;
  680. sys_lwmutex_attribute_initialize( mutexAttr );
  681. mutexAttr.attr_recursive = SYS_SYNC_RECURSIVE;
  682. int err = sys_lwmutex_create( &m_staticMutex, &mutexAttr );
  683. Assert(err == CELL_OK);
  684. m_bstaticMutexInitialized = true;
  685. }
  686. else
  687. {
  688. //Another thread is already in the process of initialising the mutex, wait for it
  689. while ( !m_bstaticMutexInitialized )
  690. {
  691. // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead.
  692. // sys_timer_usleep( 60 );
  693. sys_ppu_thread_yield();
  694. }
  695. }
  696. #endif
  697. }
  698. //---------------------------------------------------------
  699. CThreadSyncObject::~CThreadSyncObject()
  700. {
  701. #ifdef _WIN32
  702. if ( m_hSyncObject && m_bCreatedHandle )
  703. {
  704. if ( !CloseHandle(m_hSyncObject) )
  705. {
  706. Assert( 0 );
  707. }
  708. }
  709. #elif defined(POSIX) && !defined( PLATFORM_PS3 )
  710. if ( m_bInitalized )
  711. {
  712. pthread_cond_destroy( &m_Condition );
  713. pthread_mutex_destroy( &m_Mutex );
  714. m_bInitalized = false;
  715. }
  716. #endif
  717. }
  718. //---------------------------------------------------------
  719. bool CThreadSyncObject::operator!() const
  720. {
  721. #if PLATFORM_PS3
  722. return m_bstaticMutexInitialized;
  723. #elif defined( _WIN32 )
  724. return !m_hSyncObject;
  725. #elif defined(POSIX)
  726. return !m_bInitalized;
  727. #endif
  728. }
  729. //---------------------------------------------------------
  730. void CThreadSyncObject::AssertUseable()
  731. {
  732. #ifdef THREADS_DEBUG
  733. #if PLATFORM_PS3
  734. AssertMsg( m_bstaticMutexInitialized, "Thread synchronization object is unuseable" );
  735. #elif defined( _WIN32 )
  736. AssertMsg( m_hSyncObject, "Thread synchronization object is unuseable" );
  737. #elif defined(POSIX)
  738. AssertMsg( m_bInitalized, "Thread synchronization object is unuseable" );
  739. #endif
  740. #endif
  741. }
  742. //---------------------------------------------------------
  743. #if defined(_WIN32) || ( defined(POSIX) && !defined( _PS3 ) )
  744. bool CThreadSyncObject::Wait( uint32 dwTimeout )
  745. {
  746. #ifdef THREADS_DEBUG
  747. AssertUseable();
  748. #endif
  749. #ifdef _WIN32
  750. return ( WaitForSingleObject( m_hSyncObject, dwTimeout ) == WAIT_OBJECT_0 );
  751. #elif defined( POSIX ) && !defined( PLATFORM_PS3 )
  752. pthread_mutex_lock( &m_Mutex );
  753. bool bRet = false;
  754. if ( m_cSet > 0 )
  755. {
  756. bRet = true;
  757. m_bWakeForEvent = false;
  758. }
  759. else
  760. {
  761. volatile int ret = 0;
  762. while ( !m_bWakeForEvent && ret != ETIMEDOUT )
  763. {
  764. struct timeval tv;
  765. gettimeofday( &tv, NULL );
  766. volatile struct timespec tm;
  767. uint64 actualTimeout = dwTimeout;
  768. if ( dwTimeout == TT_INFINITE && m_bManualReset )
  769. actualTimeout = 10; // just wait 10 msec at most for manual reset events and loop instead
  770. volatile uint64 nNanoSec = (uint64)tv.tv_usec*1000 + (uint64)actualTimeout*1000000;
  771. tm.tv_sec = tv.tv_sec + nNanoSec /1000000000;
  772. tm.tv_nsec = nNanoSec % 1000000000;
  773. do
  774. {
  775. ret = pthread_cond_timedwait( &m_Condition, &m_Mutex, (const timespec *)&tm );
  776. }
  777. while( ret == EINTR );
  778. bRet = ( ret == 0 );
  779. if ( m_bManualReset )
  780. {
  781. if ( m_cSet )
  782. break;
  783. if ( dwTimeout == TT_INFINITE && ret == ETIMEDOUT )
  784. ret = 0; // force the loop to spin back around
  785. }
  786. }
  787. if ( bRet )
  788. m_bWakeForEvent = false;
  789. }
  790. if ( !m_bManualReset && bRet )
  791. m_cSet = 0;
  792. pthread_mutex_unlock( &m_Mutex );
  793. return bRet;
  794. #endif
  795. }
  796. #endif
  797. uint32 CThreadSyncObject::WaitForMultiple( int nObjects, CThreadSyncObject **ppObjects, bool bWaitAll, uint32 dwTimeout )
  798. {
  799. #if defined( _WIN32 )
  800. CThreadSyncObject *pHandles = (CThreadSyncObject*)stackalloc( sizeof(CThreadSyncObject) * nObjects );
  801. for ( int i=0; i < nObjects; i++ )
  802. {
  803. pHandles[i].m_hSyncObject = ppObjects[i]->m_hSyncObject;
  804. }
  805. return WaitForMultiple( nObjects, pHandles, bWaitAll, dwTimeout );
  806. #else
  807. // TODO: Need a more efficient implementation of this.
  808. uint32 dwStartTime = 0;
  809. if ( dwTimeout != TT_INFINITE )
  810. dwStartTime = Plat_MSTime();
  811. // If bWaitAll = true, then we need to track which ones were triggered.
  812. char *pWasTriggered = NULL;
  813. int nTriggered = 0;
  814. if ( bWaitAll )
  815. {
  816. pWasTriggered = (char*)stackalloc( nObjects );
  817. memset( pWasTriggered, 0, nObjects );
  818. }
  819. while ( 1 )
  820. {
  821. for ( int i=0; i < nObjects; i++ )
  822. {
  823. if ( bWaitAll && pWasTriggered[i] )
  824. continue;
  825. #ifdef _PS3
  826. Assert( !"Not implemented!" );
  827. if ( false )
  828. #else
  829. if ( ppObjects[i]->Wait( 0 ) )
  830. #endif
  831. {
  832. ++nTriggered;
  833. if ( bWaitAll )
  834. {
  835. if ( nTriggered == nObjects )
  836. return 0;
  837. else
  838. pWasTriggered[i] = 1;
  839. }
  840. else
  841. {
  842. return i;
  843. }
  844. }
  845. }
  846. // Timeout?
  847. if ( dwTimeout != TT_INFINITE )
  848. {
  849. if ( Plat_MSTime() - dwStartTime >= dwTimeout )
  850. return TW_TIMEOUT;
  851. }
  852. ThreadSleep( 0 );
  853. }
  854. #endif
  855. }
  856. uint32 CThreadSyncObject::WaitForMultiple( int nObjects, CThreadSyncObject *pObjects, bool bWaitAll, uint32 dwTimeout )
  857. {
  858. #if defined(_WIN32 )
  859. HANDLE *pHandles = (HANDLE*)stackalloc( sizeof(HANDLE) * nObjects );
  860. for ( int i=0; i < nObjects; i++ )
  861. {
  862. pHandles[i] = pObjects[i].m_hSyncObject;
  863. }
  864. DWORD ret = WaitForMultipleObjects( nObjects, pHandles, bWaitAll, dwTimeout );
  865. if ( ret == WAIT_TIMEOUT )
  866. return TW_TIMEOUT;
  867. else if ( ret >= WAIT_OBJECT_0 && (ret-WAIT_OBJECT_0) < (uint32)nObjects )
  868. return (int)(ret - WAIT_OBJECT_0);
  869. else if ( ret >= WAIT_ABANDONED_0 && (ret - WAIT_ABANDONED_0) < (uint32)nObjects )
  870. Error( "Unhandled WAIT_ABANDONED in WaitForMultipleObjects" );
  871. else if ( ret == WAIT_FAILED )
  872. return TW_FAILED;
  873. else
  874. Error( "Unknown return value (%lu) from WaitForMultipleObjects", ret );
  875. // We'll never get here..
  876. return 0;
  877. #else
  878. CThreadSyncObject **ppObjects = (CThreadSyncObject**)stackalloc( sizeof( CThreadSyncObject* ) * nObjects );
  879. for ( int i=0; i < nObjects; i++ )
  880. {
  881. ppObjects[i] = &pObjects[i];
  882. }
  883. return WaitForMultiple( nObjects, ppObjects, bWaitAll, dwTimeout );
  884. #endif
  885. }
  886. // To implement these, I need to check that casts are safe
  887. uint32 CThreadEvent::WaitForMultiple( int nObjects, CThreadEvent *pObjects, bool bWaitAll, uint32 dwTimeout )
  888. {
  889. // If data ever gets added to CThreadEvent, then we need a different implementation.
  890. #ifdef _PS3
  891. CThreadEvent **ppObjects = (CThreadEvent**)stackalloc( sizeof( CThreadEvent* ) * nObjects );
  892. for ( int i=0; i < nObjects; i++ )
  893. {
  894. ppObjects[i] = &pObjects[i];
  895. }
  896. return WaitForMultipleObjects( nObjects, ppObjects, bWaitAll, dwTimeout );
  897. #else
  898. COMPILE_TIME_ASSERT( sizeof( CThreadSyncObject ) == 0 || sizeof( CThreadEvent ) == sizeof( CThreadSyncObject ) );
  899. return CThreadSyncObject::WaitForMultiple( nObjects, (CThreadSyncObject*)pObjects, bWaitAll, dwTimeout );
  900. #endif
  901. }
  902. uint32 CThreadEvent::WaitForMultiple( int nObjects, CThreadEvent **ppObjects, bool bWaitAll, uint32 dwTimeout )
  903. {
  904. #ifdef _PS3
  905. return WaitForMultipleObjects( nObjects, ppObjects, bWaitAll, dwTimeout );
  906. #else
  907. // If data ever gets added to CThreadEvent, then we need a different implementation.
  908. COMPILE_TIME_ASSERT( sizeof( CThreadSyncObject )== 0 || sizeof( CThreadEvent ) == sizeof( CThreadSyncObject ) );
  909. return CThreadSyncObject::WaitForMultiple( nObjects, (CThreadSyncObject**)ppObjects, bWaitAll, dwTimeout );
  910. #endif
  911. }
  912. //-----------------------------------------------------------------------------
  913. //
  914. //-----------------------------------------------------------------------------
  915. CThreadEvent::CThreadEvent( bool bManualReset )
  916. {
  917. #ifdef _WIN32
  918. m_hSyncObject = CreateEvent( NULL, bManualReset, FALSE, NULL );
  919. m_bCreatedHandle = true;
  920. AssertMsg1(m_hSyncObject, "Failed to create event (error 0x%x)", GetLastError() );
  921. #elif defined( _PS3 )
  922. m_bManualReset = bManualReset;
  923. m_bSet = 0;
  924. m_bInitalized = false;
  925. m_numWaitingThread = 0;
  926. // set up linked list of wait objects
  927. memset(&m_waitObjects[0], 0, sizeof(m_waitObjects));
  928. m_pWaitObjectsList = &m_waitObjects[0];
  929. m_pWaitObjectsPool = &m_waitObjects[1];
  930. for (int i = 2; i < CTHREADEVENT_MAX_WAITING_THREADS + 2; i++)
  931. {
  932. LLLinkNode(m_pWaitObjectsPool, &m_waitObjects[i]);
  933. }
  934. #elif defined( POSIX )
  935. pthread_mutexattr_t Attr;
  936. pthread_mutexattr_init( &Attr );
  937. pthread_mutex_init( &m_Mutex, &Attr );
  938. pthread_mutexattr_destroy( &Attr );
  939. pthread_cond_init( &m_Condition, NULL );
  940. m_bInitalized = true;
  941. m_cSet = 0;
  942. m_bWakeForEvent = false;
  943. m_bManualReset = bManualReset;
  944. #else
  945. #error "Implement me"
  946. #endif
  947. }
  948. //-----------------------------------------------------------------------------
  949. //
  950. //-----------------------------------------------------------------------------
  951. #ifdef _PS3
  952. //
  953. // linked list functionality
  954. //
  955. //-----------------------------------------------------------------------------
  956. // Purpose: Linked list implementation
  957. //-----------------------------------------------------------------------------
  958. CThreadEventWaitObject* CThreadEvent::LLUnlinkNode(CThreadEventWaitObject *node)
  959. {
  960. // <sergiy> Note: if you have a null-access crash here, it may mean that CTHREADEVENT_MAX_WAITING_THREADS is not high enough
  961. // and the linked list pool is simply exhausted
  962. node->m_pPrev->m_pNext = node->m_pNext;
  963. if (node->m_pNext) node->m_pNext->m_pPrev = node->m_pPrev;
  964. node->m_pNext = node->m_pPrev = NULL;
  965. return node;
  966. }
  967. CThreadEventWaitObject* CThreadEvent::LLLinkNode(CThreadEventWaitObject* list, CThreadEventWaitObject *node)
  968. {
  969. node->m_pNext = list->m_pNext;
  970. if (node->m_pNext)
  971. {
  972. node->m_pNext->m_pPrev = node;
  973. }
  974. list->m_pNext = node;
  975. node->m_pPrev = list;
  976. return node;
  977. }
  978. //-----------------------------------------------------------------------------
  979. // Helper function to atomically write index into destination and set semaphore
  980. // This is used by WaitForMultipleObjects(WAIT_ANY) because once the semaphore
  981. // is set, the waiting thread also needs to know which event triggered it
  982. // We do NOT need this to be atomic because if a number of events fire it doesn't
  983. // matter which one of these we pick
  984. //-----------------------------------------------------------------------------
  985. void CThreadEventWaitObject::Set()
  986. {
  987. *m_pFlag = m_index;
  988. sys_semaphore_post(*m_pSemaphore, 1);
  989. }
  990. //
  991. // CThreadEvent::RegisterWaitingThread
  992. //
  993. void CThreadEvent::RegisterWaitingThread(sys_semaphore_t *pSemaphore, int index, int *flag)
  994. {
  995. sys_lwmutex_lock(&m_staticMutex, 0);
  996. // if we are already set, then signal this semaphore
  997. if (m_bSet)
  998. {
  999. CThreadEventWaitObject waitObject;
  1000. waitObject.Init(pSemaphore, index, flag);
  1001. waitObject.Set();
  1002. if (!m_bManualReset)
  1003. {
  1004. m_bSet = false;
  1005. }
  1006. }
  1007. else
  1008. {
  1009. if (!m_pWaitObjectsPool->m_pNext)
  1010. {
  1011. DEBUG_ERROR("CThreadEvent: Ran out of events; cannot register waiting thread\n");
  1012. }
  1013. // add this semaphore to linked list - can be added more than once it doesn't matter
  1014. CThreadEventWaitObject *pWaitObject = LLUnlinkNode(m_pWaitObjectsPool->m_pNext);
  1015. pWaitObject->Init(pSemaphore, index, flag);
  1016. LLLinkNode(m_pWaitObjectsList, pWaitObject);
  1017. }
  1018. sys_lwmutex_unlock(&m_staticMutex);
  1019. }
  1020. //
  1021. // CThreadEvent::UnregisterWaitingThread
  1022. //
  1023. void CThreadEvent::UnregisterWaitingThread(sys_semaphore_t *pSemaphore)
  1024. {
  1025. // remove all instances of this semaphore from linked list
  1026. sys_lwmutex_lock(&m_staticMutex, 0);
  1027. CThreadEventWaitObject *pWaitObject = m_pWaitObjectsList->m_pNext;
  1028. while (pWaitObject)
  1029. {
  1030. CThreadEventWaitObject *pNext = pWaitObject->m_pNext;
  1031. if (pWaitObject->m_pSemaphore == pSemaphore)
  1032. {
  1033. LLUnlinkNode(pWaitObject);
  1034. LLLinkNode(m_pWaitObjectsPool, pWaitObject);
  1035. }
  1036. pWaitObject = pNext;
  1037. }
  1038. sys_lwmutex_unlock(&m_staticMutex);
  1039. }
  1040. #endif // _PS3
  1041. #ifdef PLATFORM_WINDOWS
  1042. CThreadEvent::CThreadEvent( const char *name, bool initialState, bool bManualReset )
  1043. {
  1044. m_hSyncObject = CreateEvent( NULL, bManualReset, (BOOL) initialState, name );
  1045. AssertMsg1( m_hSyncObject, "Failed to create event (error 0x%x)", GetLastError() );
  1046. }
  1047. NamedEventResult_t CThreadEvent::CheckNamedEvent( const char *name, uint32 dwTimeout )
  1048. {
  1049. HANDLE eHandle = OpenEvent( SYNCHRONIZE, FALSE, name );
  1050. if ( eHandle == NULL ) return TT_EventDoesntExist;
  1051. DWORD result = WaitForSingleObject( eHandle, dwTimeout );
  1052. return ( result == WAIT_OBJECT_0 ) ? TT_EventSignaled : TT_EventNotSignaled;
  1053. }
  1054. #endif
  1055. //-----------------------------------------------------------------------------
  1056. //
  1057. //-----------------------------------------------------------------------------
  1058. //---------------------------------------------------------
  1059. bool CThreadEvent::Set()
  1060. {
  1061. //////////////////////////////////////////////////////////////
  1062. #ifndef NEW_WAIT_FOR_MULTIPLE_OBJECTS
  1063. //////////////////////////////////////////////////////////////
  1064. AssertUseable();
  1065. #ifdef _WIN32
  1066. return ( SetEvent( m_hSyncObject ) != 0 );
  1067. #elif defined( _PS3 )
  1068. sys_lwmutex_lock(&m_staticMutex, 0);
  1069. if (m_bManualReset)
  1070. {
  1071. //Mark event as set
  1072. m_bSet = true;
  1073. //If any threads are already waiting then signal them to run
  1074. if (m_bInitalized)
  1075. {
  1076. int err = sys_semaphore_post( m_Semaphore, m_numWaitingThread);
  1077. Assert(err == CELL_OK);
  1078. }
  1079. }
  1080. else
  1081. {
  1082. //If any threads are already waiting then signal ONE to run, else signal next to run
  1083. if (m_numWaitingThread>0)
  1084. {
  1085. int err = sys_semaphore_post( m_Semaphore, 1);
  1086. Assert(err == CELL_OK);
  1087. }
  1088. else
  1089. {
  1090. m_bSet=true;
  1091. }
  1092. }
  1093. sys_lwmutex_unlock(&m_staticMutex);
  1094. return true;
  1095. #elif defined(POSIX)
  1096. pthread_mutex_lock( &m_Mutex );
  1097. m_cSet = 1;
  1098. m_bWakeForEvent = true;
  1099. int ret = pthread_cond_signal( &m_Condition );
  1100. pthread_mutex_unlock( &m_Mutex );
  1101. return ret == 0;
  1102. #endif
  1103. //////////////////////////////////////////////////////////////
  1104. #else // NEW_WAIT_FOR_MULTIPLE_OBJECTS
  1105. //////////////////////////////////////////////////////////////
  1106. sys_lwmutex_lock(&m_staticMutex, 0);
  1107. //Mark event as set
  1108. m_bSet = true;
  1109. // signal registered semaphores
  1110. while (m_pWaitObjectsList->m_pNext)
  1111. {
  1112. CThreadEventWaitObject *pWaitObject = LLUnlinkNode(m_pWaitObjectsList->m_pNext);
  1113. pWaitObject->Set();
  1114. LLLinkNode(m_pWaitObjectsPool, pWaitObject);
  1115. g_pfnPopMarker();
  1116. if (!m_bManualReset)
  1117. {
  1118. m_bSet = false;
  1119. break;
  1120. }
  1121. }
  1122. sys_lwmutex_unlock(&m_staticMutex);
  1123. return true;
  1124. //////////////////////////////////////////////////////////////
  1125. #endif // NEW_WAIT_FOR_MULTIPLE_OBJECTS
  1126. //////////////////////////////////////////////////////////////
  1127. }
  1128. //---------------------------------------------------------
  1129. bool CThreadEvent::Reset()
  1130. {
  1131. #ifdef THREADS_DEBUG
  1132. AssertUseable();
  1133. #endif
  1134. #ifdef _WIN32
  1135. return ( ResetEvent( m_hSyncObject ) != 0 );
  1136. #elif defined( _PS3 )
  1137. //Just mark us as no longer signaled
  1138. m_bSet = 0;
  1139. return true;
  1140. #elif defined(POSIX)
  1141. pthread_mutex_lock( &m_Mutex );
  1142. m_cSet = 0;
  1143. m_bWakeForEvent = false;
  1144. pthread_mutex_unlock( &m_Mutex );
  1145. return true;
  1146. #endif
  1147. }
  1148. //---------------------------------------------------------
  1149. bool CThreadEvent::Check()
  1150. {
  1151. #ifdef _PS3
  1152. return m_bSet; // Please, use for debugging only!
  1153. #endif
  1154. #ifdef THREADS_DEBUG
  1155. AssertUseable();
  1156. #endif
  1157. return Wait( 0 );
  1158. }
  1159. bool CThreadEvent::Wait( uint32 dwTimeout )
  1160. {
  1161. //////////////////////////////////////////////////////////////
  1162. #ifndef NEW_WAIT_FOR_MULTIPLE_OBJECTS
  1163. //////////////////////////////////////////////////////////////
  1164. #if defined( _WIN32 ) || ( defined( POSIX ) && !defined( _PS3 ) )
  1165. return CThreadSyncObject::Wait( dwTimeout );
  1166. #elif defined( _PS3 )
  1167. {
  1168. if (dwTimeout == 0)
  1169. {
  1170. //If timeout is 0 then just test it now (and reset it if manual )
  1171. if (m_bSet)
  1172. {
  1173. if ( !m_bManualReset ) m_bSet=false;
  1174. return true;
  1175. }
  1176. return false;
  1177. }
  1178. if (!AddWaitingThread())
  1179. {
  1180. //Waiting thread NOT added because m_bSet was already set
  1181. if ( !m_bManualReset ) m_bSet=false;
  1182. return true;
  1183. }
  1184. uint32 timeout;
  1185. int countTimeout = 0;
  1186. int ret = ETIMEDOUT;
  1187. while ( timeout=MIN(1, dwTimeout) )
  1188. {
  1189. // on the PS3, "infinite timeout" is specified by zero, not
  1190. // 0xFFFFFFFF, so we need to perform that ternary here.
  1191. //#error Untested code:
  1192. ret = sys_semaphore_wait( m_Semaphore, timeout == TT_INFINITE ? 0 : timeout * 1000 );
  1193. Assert( (ret == CELL_OK) || (ret == ETIMEDOUT) );
  1194. if ( ret == CELL_OK )
  1195. break;
  1196. dwTimeout -= timeout;
  1197. countTimeout++;
  1198. if (countTimeout > 30)
  1199. {
  1200. // printf("WARNING: possible deadlock in CThreadEvent::Wait() !!!\n");
  1201. }
  1202. }
  1203. RemoveWaitingThread();
  1204. if ( !m_bManualReset ) m_bSet=false;
  1205. return ret == CELL_OK;
  1206. }
  1207. #endif
  1208. //////////////////////////////////////////////////////////////
  1209. #else // NEW_WAIT_FOR_MULTIPLE_OBJECTS
  1210. //////////////////////////////////////////////////////////////
  1211. CThreadEvent *pThis = this;
  1212. DWORD res = WaitForMultipleObjects(1, &pThis, true, dwTimeout);
  1213. return res == WAIT_OBJECT_0;
  1214. //////////////////////////////////////////////////////////////
  1215. #endif // NEW_WAIT_FOR_MULTIPLE_OBJECTS
  1216. //////////////////////////////////////////////////////////////
  1217. }
  1218. #ifdef _WIN32
  1219. //-----------------------------------------------------------------------------
  1220. //
  1221. // CThreadSemaphore
  1222. //
  1223. // To get Posix implementation, try http://www-128.ibm.com/developerworks/eserver/library/es-win32linux-sem.html
  1224. //
  1225. //-----------------------------------------------------------------------------
  1226. CThreadSemaphore::CThreadSemaphore( int32 initialValue, int32 maxValue )
  1227. {
  1228. #ifdef _WIN32
  1229. if ( maxValue )
  1230. {
  1231. AssertMsg( maxValue > 0, "Invalid max value for semaphore" );
  1232. AssertMsg( initialValue >= 0 && initialValue <= maxValue, "Invalid initial value for semaphore" );
  1233. m_hSyncObject = CreateSemaphore( NULL, initialValue, maxValue, NULL );
  1234. AssertMsg1(m_hSyncObject, "Failed to create semaphore (error 0x%x)", GetLastError());
  1235. }
  1236. else
  1237. {
  1238. m_hSyncObject = NULL;
  1239. }
  1240. #elif defined( _PS3 )
  1241. if ( maxValue )
  1242. {
  1243. m_sema_max_val = maxValue;
  1244. m_semaCount = initialValue;
  1245. }
  1246. #endif
  1247. }
  1248. #ifdef _PS3
  1249. //---------------------------------------------------------
  1250. bool CThreadSemaphore::AddWaitingThread()
  1251. {
  1252. bool result;
  1253. sys_lwmutex_lock(&m_staticMutex, 0);
  1254. if (cellAtomicTestAndDecr32(&m_semaCount) > 0)
  1255. {
  1256. result=false;
  1257. }
  1258. else
  1259. {
  1260. result=true;
  1261. m_numWaitingThread++;
  1262. if ( m_numWaitingThread == 1 )
  1263. {
  1264. sys_semaphore_attribute_t semAttr;
  1265. sys_semaphore_attribute_initialize( semAttr );
  1266. Assert(m_semaCount == 0);
  1267. int err = sys_semaphore_create( &m_Semaphore, &semAttr, 0, m_sema_max_val );
  1268. Assert( err == CELL_OK );
  1269. m_bInitalized = true;
  1270. }
  1271. }
  1272. sys_lwmutex_unlock(&m_staticMutex);
  1273. return result;
  1274. }
  1275. void CThreadSemaphore::RemoveWaitingThread()
  1276. {
  1277. sys_lwmutex_lock(&m_staticMutex, 0);
  1278. m_numWaitingThread--;
  1279. if ( m_numWaitingThread == 0)
  1280. {
  1281. int err = sys_semaphore_destroy( m_Semaphore );
  1282. Assert( err == CELL_OK );
  1283. m_bInitalized = false;
  1284. }
  1285. sys_lwmutex_unlock(&m_staticMutex);
  1286. }
  1287. #endif
  1288. #ifdef _PS3
  1289. bool CThreadSemaphore::Wait( uint32 dwTimeout )
  1290. {
  1291. #ifdef THREADS_DEBUG
  1292. AssertUseable();
  1293. #endif
  1294. #ifndef NO_THREAD_SYNC
  1295. if (!AddWaitingThread())
  1296. {
  1297. //Waiting thread NOT added because semaphore was already in a signaled state
  1298. return true;
  1299. }
  1300. int ret = sys_semaphore_wait( m_Semaphore, dwTimeout == TT_INFINITE ? 0 : dwTimeout * 1000 );
  1301. Assert( (ret == CELL_OK) || (ret == ETIMEDOUT) );
  1302. RemoveWaitingThread();
  1303. int old = cellAtomicDecr32(&m_semaCount);
  1304. Assert(old>0);
  1305. #else
  1306. int ret = CELL_OK;
  1307. #endif
  1308. // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead.
  1309. // sys_timer_usleep( 60 );
  1310. sys_ppu_thread_yield();
  1311. return ret == CELL_OK;
  1312. }
  1313. #endif
  1314. //---------------------------------------------------------
  1315. bool CThreadSemaphore::Release( int32 releaseCount, int32 *pPreviousCount )
  1316. {
  1317. #ifdef THRDTOOL_DEBUG
  1318. AssertUseable();
  1319. #endif
  1320. #ifdef _WIN32
  1321. return ( ReleaseSemaphore( m_hSyncObject, releaseCount, (LPLONG)pPreviousCount ) != 0 );
  1322. #elif defined( _PS3 )
  1323. #ifndef NO_THREAD_SYNC
  1324. if (m_bInitalized)
  1325. {
  1326. sys_semaphore_value_t previousVal;
  1327. sys_semaphore_get_value( m_Semaphore, &previousVal );
  1328. cellAtomicAdd32(&m_semaCount, releaseCount);
  1329. *pPreviousCount = previousVal;
  1330. int err = sys_semaphore_post( m_Semaphore, releaseCount );
  1331. Assert(err == CELL_OK);
  1332. }
  1333. #endif
  1334. return true;
  1335. #endif
  1336. }
  1337. //-----------------------------------------------------------------------------
  1338. //
  1339. //-----------------------------------------------------------------------------
  1340. CThreadFullMutex::CThreadFullMutex( bool bEstablishInitialOwnership, const char *pszName )
  1341. {
  1342. m_hSyncObject = CreateMutex( NULL, bEstablishInitialOwnership, pszName );
  1343. AssertMsg1( m_hSyncObject, "Failed to create mutex (error 0x%x)", GetLastError() );
  1344. }
  1345. //---------------------------------------------------------
  1346. bool CThreadFullMutex::Release()
  1347. {
  1348. #ifdef THRDTOOL_DEBUG
  1349. AssertUseable();
  1350. #endif
  1351. return ( ReleaseMutex( m_hSyncObject ) != 0 );
  1352. }
  1353. #endif
  1354. //-----------------------------------------------------------------------------
  1355. //
  1356. //-----------------------------------------------------------------------------
  1357. #if defined( WIN32 ) || defined( _PS3 ) || defined( _OSX ) || ( defined (_LINUX) && !defined(DEDICATED) )
  1358. #if !defined(_PS3)
  1359. namespace GenericThreadLocals
  1360. {
  1361. #endif
  1362. CThreadLocalBase::CThreadLocalBase()
  1363. {
  1364. #if defined(_WIN32) || defined(_PS3)
  1365. m_index = TlsAlloc();
  1366. AssertMsg( m_index != 0xFFFFFFFF, "Bad thread local" );
  1367. if ( m_index == 0xFFFFFFFF )
  1368. Error( "Out of thread local storage!\n" );
  1369. #elif defined(POSIX)
  1370. if ( pthread_key_create( (pthread_key_t *)&m_index, NULL ) != 0 )
  1371. Error( "Out of thread local storage!\n" );
  1372. #endif
  1373. }
  1374. //---------------------------------------------------------
  1375. CThreadLocalBase::~CThreadLocalBase()
  1376. {
  1377. #if defined(_WIN32) || defined(_PS3)
  1378. if ( m_index != 0xFFFFFFFF )
  1379. TlsFree( m_index );
  1380. m_index = 0xFFFFFFFF;
  1381. #elif defined(POSIX)
  1382. pthread_key_delete( m_index );
  1383. #endif
  1384. }
  1385. //---------------------------------------------------------
  1386. void * CThreadLocalBase::Get() const
  1387. {
  1388. #if defined(_WIN32) || defined(_PS3)
  1389. if ( m_index != 0xFFFFFFFF )
  1390. return TlsGetValue( m_index );
  1391. AssertMsg( 0, "Bad thread local" );
  1392. return NULL;
  1393. #elif defined(POSIX)
  1394. void *value = pthread_getspecific( m_index );
  1395. return value;
  1396. #endif
  1397. }
  1398. //---------------------------------------------------------
  1399. void CThreadLocalBase::Set( void *value )
  1400. {
  1401. #if defined(_WIN32) || defined(_PS3)
  1402. if (m_index != 0xFFFFFFFF)
  1403. TlsSetValue(m_index, value);
  1404. else
  1405. AssertMsg( 0, "Bad thread local" );
  1406. #elif defined(POSIX)
  1407. if ( pthread_setspecific( m_index, value ) != 0 )
  1408. AssertMsg( 0, "Bad thread local" );
  1409. #endif
  1410. }
  1411. #if !defined(_PS3)
  1412. } // namespace GenericThreadLocals
  1413. #endif
  1414. #endif // ( defined(WIN32) )
  1415. //-----------------------------------------------------------------------------
  1416. //-----------------------------------------------------------------------------
  1417. #ifdef MSVC
  1418. //#ifdef _X360
  1419. #define TO_INTERLOCK_PARAM(p) ((volatile long *)p)
  1420. #define TO_INTERLOCK_PTR_PARAM(p) ((void **)p)
  1421. //#else
  1422. //#define TO_INTERLOCK_PARAM(p) (p)
  1423. //#define TO_INTERLOCK_PTR_PARAM(p) (p)
  1424. //#endif
  1425. #if !defined(USE_INTRINSIC_INTERLOCKED) && !defined(_X360)
  1426. int32 ThreadInterlockedIncrement( int32 volatile *pDest )
  1427. {
  1428. Assert( (size_t)pDest % 4 == 0 );
  1429. return InterlockedIncrement( TO_INTERLOCK_PARAM(pDest) );
  1430. }
  1431. int32 ThreadInterlockedDecrement( int32 volatile *pDest )
  1432. {
  1433. Assert( (size_t)pDest % 4 == 0 );
  1434. return InterlockedDecrement( TO_INTERLOCK_PARAM(pDest) );
  1435. }
  1436. int32 ThreadInterlockedExchange( int32 volatile *pDest, int32 value )
  1437. {
  1438. Assert( (size_t)pDest % 4 == 0 );
  1439. return InterlockedExchange( TO_INTERLOCK_PARAM(pDest), value );
  1440. }
  1441. int32 ThreadInterlockedExchangeAdd( int32 volatile *pDest, int32 value )
  1442. {
  1443. Assert( (size_t)pDest % 4 == 0 );
  1444. return InterlockedExchangeAdd( TO_INTERLOCK_PARAM(pDest), value );
  1445. }
  1446. int32 ThreadInterlockedCompareExchange( int32 volatile *pDest, int32 value, int32 comperand )
  1447. {
  1448. Assert( (size_t)pDest % 4 == 0 );
  1449. return InterlockedCompareExchange( TO_INTERLOCK_PARAM(pDest), value, comperand );
  1450. }
  1451. bool ThreadInterlockedAssignIf( int32 volatile *pDest, int32 value, int32 comperand )
  1452. {
  1453. Assert( (size_t)pDest % 4 == 0 );
  1454. #if !(defined(_WIN64) || defined (_X360))
  1455. __asm
  1456. {
  1457. mov eax,comperand
  1458. mov ecx,pDest
  1459. mov edx,value
  1460. lock cmpxchg [ecx],edx
  1461. mov eax,0
  1462. setz al
  1463. }
  1464. #else
  1465. return ( InterlockedCompareExchange( TO_INTERLOCK_PARAM(pDest), value, comperand ) == comperand );
  1466. #endif
  1467. }
  1468. #endif
  1469. #if !defined( USE_INTRINSIC_INTERLOCKED ) || defined( _WIN64 )
  1470. void *ThreadInterlockedExchangePointer( void * volatile *pDest, void *value )
  1471. {
  1472. Assert( (size_t)pDest % 4 == 0 );
  1473. return InterlockedExchangePointer( TO_INTERLOCK_PTR_PARAM(pDest), value );
  1474. }
  1475. void *ThreadInterlockedCompareExchangePointer( void * volatile *pDest, void *value, void *comperand )
  1476. {
  1477. Assert( (size_t)pDest % 4 == 0 );
  1478. return InterlockedCompareExchangePointer( TO_INTERLOCK_PTR_PARAM(pDest), value, comperand );
  1479. }
  1480. bool ThreadInterlockedAssignPointerIf( void * volatile *pDest, void *value, void *comperand )
  1481. {
  1482. Assert( (size_t)pDest % 4 == 0 );
  1483. #if !(defined(_WIN64) || defined (_X360))
  1484. __asm
  1485. {
  1486. mov eax,comperand
  1487. mov ecx,pDest
  1488. mov edx,value
  1489. lock cmpxchg [ecx],edx
  1490. mov eax,0
  1491. setz al
  1492. }
  1493. #else
  1494. return ( InterlockedCompareExchangePointer( TO_INTERLOCK_PTR_PARAM(pDest), value, comperand ) == comperand );
  1495. #endif
  1496. }
  1497. #endif
  1498. #ifdef COMPILER_MSVC32
  1499. int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, int64 comperand )
  1500. {
  1501. Assert( (size_t)pDest % 8 == 0 );
  1502. __asm
  1503. {
  1504. lea esi,comperand;
  1505. lea edi,value;
  1506. mov eax,[esi];
  1507. mov edx,4[esi];
  1508. mov ebx,[edi];
  1509. mov ecx,4[edi];
  1510. mov esi,pDest;
  1511. lock CMPXCHG8B [esi];
  1512. }
  1513. }
  1514. #endif
  1515. bool ThreadInterlockedAssignIf64(volatile int64 *pDest, int64 value, int64 comperand )
  1516. {
  1517. Assert( (size_t)pDest % 8 == 0 );
  1518. #if defined(_X360) || defined(_WIN64)
  1519. return ( ThreadInterlockedCompareExchange64( pDest, value, comperand ) == comperand );
  1520. #else
  1521. __asm
  1522. {
  1523. lea esi,comperand;
  1524. lea edi,value;
  1525. mov eax,[esi];
  1526. mov edx,4[esi];
  1527. mov ebx,[edi];
  1528. mov ecx,4[edi];
  1529. mov esi,pDest;
  1530. lock CMPXCHG8B [esi];
  1531. mov eax,0;
  1532. setz al;
  1533. }
  1534. #endif
  1535. }
  1536. #ifdef _WIN64
  1537. bool ThreadInterlockedAssignIf128( volatile int128 *pDest, const int128 &value, const int128 &comperand )
  1538. {
  1539. DbgAssert( ( (size_t)pDest % 16 ) == 0 );
  1540. // Must copy comperand to stack because the intrinsic uses it as an in/out param
  1541. int64 comperandInOut[2] = { comperand.m128i_i64[0], comperand.m128i_i64[1] };
  1542. // Description:
  1543. // The CMPXCHG16B instruction compares the 128-bit value in the RDX:RAX and RCX:RBX registers
  1544. // with a 128-bit memory location. If the values are equal, the zero flag (ZF) is set,
  1545. // and the RCX:RBX value is copied to the memory location.
  1546. // Otherwise, the ZF flag is cleared, and the memory value is copied to RDX:RAX.
  1547. // _InterlockedCompareExchange128: http://msdn.microsoft.com/en-us/library/bb514094.aspx
  1548. if ( _InterlockedCompareExchange128( ( volatile int64 * )pDest, value.m128i_i64[1], value.m128i_i64[0], comperandInOut ) )
  1549. return true;
  1550. return false;
  1551. }
  1552. #endif
  1553. #elif defined(GNUC)
  1554. #ifdef OSX
  1555. #include <libkern/OSAtomic.h>
  1556. #endif
  1557. long ThreadInterlockedIncrement( long volatile *pDest )
  1558. {
  1559. return __sync_fetch_and_add( pDest, 1 ) + 1;
  1560. }
  1561. long ThreadInterlockedDecrement( long volatile *pDest )
  1562. {
  1563. return __sync_fetch_and_sub( pDest, 1 ) - 1;
  1564. }
  1565. long ThreadInterlockedExchange( long volatile *pDest, long value )
  1566. {
  1567. return __sync_lock_test_and_set( pDest, value );
  1568. }
  1569. long ThreadInterlockedExchangeAdd( long volatile *pDest, long value )
  1570. {
  1571. return __sync_fetch_and_add( pDest, value );
  1572. }
  1573. long ThreadInterlockedCompareExchange( long volatile *pDest, long value, long comperand )
  1574. {
  1575. return __sync_val_compare_and_swap( pDest, comperand, value );
  1576. }
  1577. bool ThreadInterlockedAssignIf( long volatile *pDest, long value, long comperand )
  1578. {
  1579. return __sync_bool_compare_and_swap( pDest, comperand, value );
  1580. }
  1581. #if !defined( USE_INTRINSIC_INTERLOCKED )
  1582. void *ThreadInterlockedCompareExchangePointer( void *volatile *pDest, void *value, void *comperand )
  1583. {
  1584. return __sync_val_compare_and_swap( pDest, comperand, value );
  1585. }
  1586. bool ThreadInterlockedAssignPointerIf( void * volatile *pDest, void *value, void *comperand )
  1587. {
  1588. return __sync_bool_compare_and_swap( pDest, comperand, value );
  1589. }
  1590. #elif defined( PLATFORM_64BITS )
  1591. void *ThreadInterlockedExchangePointer( void * volatile *pDest, void *value )
  1592. {
  1593. return __sync_lock_test_and_set( pDest, value );
  1594. }
  1595. void *ThreadInterlockedCompareExchangePointer( void * volatile *p, void *value, void *comparand ) {
  1596. return (void *)( ( intp )ThreadInterlockedCompareExchange64( reinterpret_cast<intp volatile *>(p), reinterpret_cast<intp>(value), reinterpret_cast<intp>(comparand) ) );
  1597. }
  1598. #endif
  1599. int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, int64 comperand )
  1600. {
  1601. return __sync_val_compare_and_swap( pDest, comperand, value );
  1602. }
  1603. bool ThreadInterlockedAssignIf64( int64 volatile * pDest, int64 value, int64 comperand )
  1604. {
  1605. return __sync_bool_compare_and_swap( pDest, comperand, value );
  1606. }
  1607. #elif defined( _PS3 )
  1608. // This is defined in the header!
  1609. #else
  1610. // This will perform horribly,
  1611. #error "Falling back to mutexed interlocked operations, you really don't have intrinsics you can use?"ß
  1612. CThreadMutex g_InterlockedMutex;
  1613. long ThreadInterlockedIncrement( long volatile *pDest )
  1614. {
  1615. AUTO_LOCK( g_InterlockedMutex );
  1616. return ++(*pDest);
  1617. }
  1618. long ThreadInterlockedDecrement( long volatile *pDest )
  1619. {
  1620. AUTO_LOCK( g_InterlockedMutex );
  1621. return --(*pDest);
  1622. }
  1623. long ThreadInterlockedExchange( long volatile *pDest, long value )
  1624. {
  1625. AUTO_LOCK( g_InterlockedMutex );
  1626. long retVal = *pDest;
  1627. *pDest = value;
  1628. return retVal;
  1629. }
  1630. void *ThreadInterlockedExchangePointer( void * volatile *pDest, void *value )
  1631. {
  1632. AUTO_LOCK( g_InterlockedMutex );
  1633. void *retVal = *pDest;
  1634. *pDest = value;
  1635. return retVal;
  1636. }
  1637. long ThreadInterlockedExchangeAdd( long volatile *pDest, long value )
  1638. {
  1639. AUTO_LOCK( g_InterlockedMutex );
  1640. long retVal = *pDest;
  1641. *pDest += value;
  1642. return retVal;
  1643. }
  1644. long ThreadInterlockedCompareExchange( long volatile *pDest, long value, long comperand )
  1645. {
  1646. AUTO_LOCK( g_InterlockedMutex );
  1647. long retVal = *pDest;
  1648. if ( *pDest == comperand )
  1649. *pDest = value;
  1650. return retVal;
  1651. }
  1652. void *ThreadInterlockedCompareExchangePointer( void * volatile *pDest, void *value, void *comperand )
  1653. {
  1654. AUTO_LOCK( g_InterlockedMutex );
  1655. void *retVal = *pDest;
  1656. if ( *pDest == comperand )
  1657. *pDest = value;
  1658. return retVal;
  1659. }
  1660. int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, int64 comperand )
  1661. {
  1662. Assert( (size_t)pDest % 8 == 0 );
  1663. AUTO_LOCK( g_InterlockedMutex );
  1664. int64 retVal = *pDest;
  1665. if ( *pDest == comperand )
  1666. *pDest = value;
  1667. return retVal;
  1668. }
  1669. #endif
  1670. #ifdef COMPILER_MSVC32
  1671. PLATFORM_INTERFACE int64 ThreadInterlockedOr64( int64 volatile *pDest, int64 value )
  1672. {
  1673. int64 Old;
  1674. do
  1675. {
  1676. Old = *pDest;
  1677. } while ( ThreadInterlockedCompareExchange64( pDest, Old | value, Old ) != Old );
  1678. return Old;
  1679. }
  1680. PLATFORM_INTERFACE int64 ThreadInterlockedAnd64( int64 volatile *pDest, int64 value )
  1681. {
  1682. int64 Old;
  1683. do
  1684. {
  1685. Old = *pDest;
  1686. } while ( ThreadInterlockedCompareExchange64( pDest, Old & value, Old ) != Old );
  1687. return Old;
  1688. }
  1689. PLATFORM_INTERFACE int64 ThreadInterlockedIncrement64( int64 volatile *pDest )
  1690. {
  1691. int64 Old;
  1692. do
  1693. {
  1694. Old = *pDest;
  1695. } while ( ThreadInterlockedCompareExchange64( pDest, Old + 1, Old ) != Old );
  1696. return Old + 1;
  1697. }
  1698. PLATFORM_INTERFACE int64 ThreadInterlockedDecrement64( int64 volatile *pDest )
  1699. {
  1700. int64 Old;
  1701. do
  1702. {
  1703. Old = *pDest;
  1704. } while ( ThreadInterlockedCompareExchange64( pDest, Old - 1, Old ) != Old );
  1705. return Old - 1;
  1706. }
  1707. PLATFORM_INTERFACE int64 ThreadInterlockedExchangeAdd64( int64 volatile *pDest, int64 value )
  1708. {
  1709. int64 Old;
  1710. do
  1711. {
  1712. Old = *pDest;
  1713. } while ( ThreadInterlockedCompareExchange64( pDest, Old + value, Old ) != Old );
  1714. return Old;
  1715. }
  1716. #endif
  1717. int64 ThreadInterlockedExchange64( int64 volatile *pDest, int64 value )
  1718. {
  1719. Assert( (size_t)pDest % 8 == 0 );
  1720. int64 Old;
  1721. do
  1722. {
  1723. Old = *pDest;
  1724. } while (ThreadInterlockedCompareExchange64(pDest, value, Old) != Old);
  1725. return Old;
  1726. }
  1727. //-----------------------------------------------------------------------------
  1728. #if defined(_WIN32) && defined(THREAD_PROFILER)
  1729. void ThreadNotifySyncNoop(void *p) {}
  1730. #define MAP_THREAD_PROFILER_CALL( from, to ) \
  1731. void from(void *p) \
  1732. { \
  1733. static CDynamicFunction<void (*)(void *)> dynFunc( "libittnotify.dll", #to, ThreadNotifySyncNoop ); \
  1734. (*dynFunc)(p); \
  1735. }
  1736. MAP_THREAD_PROFILER_CALL( ThreadNotifySyncPrepare, __itt_notify_sync_prepare );
  1737. MAP_THREAD_PROFILER_CALL( ThreadNotifySyncCancel, __itt_notify_sync_cancel );
  1738. MAP_THREAD_PROFILER_CALL( ThreadNotifySyncAcquired, __itt_notify_sync_acquired );
  1739. MAP_THREAD_PROFILER_CALL( ThreadNotifySyncReleasing, __itt_notify_sync_releasing );
  1740. #endif
  1741. //-----------------------------------------------------------------------------
  1742. //
  1743. // CThreadMutex
  1744. //
  1745. //-----------------------------------------------------------------------------
  1746. #ifdef _PS3
  1747. CThreadMutex::CThreadMutex()
  1748. {
  1749. // sys_mutex with recursion enabled is like a win32 critical section
  1750. sys_mutex_attribute_t mutexAttr;
  1751. sys_mutex_attribute_initialize( mutexAttr );
  1752. mutexAttr.attr_recursive = SYS_SYNC_RECURSIVE;
  1753. sys_mutex_create( &m_Mutex, &mutexAttr );
  1754. }
  1755. CThreadMutex::~CThreadMutex()
  1756. {
  1757. sys_mutex_destroy( m_Mutex );
  1758. }
  1759. #elif !defined( POSIX )
  1760. CThreadMutex::CThreadMutex()
  1761. {
  1762. #ifdef THREAD_MUTEX_TRACING_ENABLED
  1763. memset( &m_CriticalSection, 0, sizeof(m_CriticalSection) );
  1764. #endif
  1765. InitializeCriticalSectionAndSpinCount((CRITICAL_SECTION *)&m_CriticalSection, 4000);
  1766. #ifdef THREAD_MUTEX_TRACING_SUPPORTED
  1767. // These need to be initialized unconditionally in case mixing release & debug object modules
  1768. // Lock and unlock may be emitted as COMDATs, in which case may get spurious output
  1769. m_currentOwnerID = m_lockCount = 0;
  1770. m_bTrace = false;
  1771. #endif
  1772. }
  1773. CThreadMutex::~CThreadMutex()
  1774. {
  1775. DeleteCriticalSection((CRITICAL_SECTION *)&m_CriticalSection);
  1776. }
  1777. #endif // !POSIX
  1778. #ifdef IS_WINDOWS_PC
  1779. typedef BOOL (WINAPI*TryEnterCriticalSectionFunc_t)(LPCRITICAL_SECTION);
  1780. static CDynamicFunction<TryEnterCriticalSectionFunc_t> DynTryEnterCriticalSection( "Kernel32.dll", "TryEnterCriticalSection" );
  1781. #elif defined( _X360 )
  1782. #define DynTryEnterCriticalSection TryEnterCriticalSection
  1783. #endif
  1784. bool CThreadMutex::TryLock()
  1785. {
  1786. #if defined( MSVC )
  1787. #ifdef THREAD_MUTEX_TRACING_ENABLED
  1788. uint thisThreadID = ThreadGetCurrentId();
  1789. if ( m_bTrace && m_currentOwnerID && ( m_currentOwnerID != thisThreadID ) )
  1790. Msg( "Thread %u about to try-wait for lock %p owned by %u\n", ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID );
  1791. #endif
  1792. if ( DynTryEnterCriticalSection != NULL )
  1793. {
  1794. if ( (*DynTryEnterCriticalSection )( (CRITICAL_SECTION *)&m_CriticalSection ) != FALSE )
  1795. {
  1796. #ifdef THREAD_MUTEX_TRACING_ENABLED
  1797. if (m_lockCount == 0)
  1798. {
  1799. // we now own it for the first time. Set owner information
  1800. m_currentOwnerID = thisThreadID;
  1801. if ( m_bTrace )
  1802. Msg( "Thread %u now owns lock %p\n", m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection );
  1803. }
  1804. m_lockCount++;
  1805. #endif
  1806. return true;
  1807. }
  1808. return false;
  1809. }
  1810. Lock();
  1811. return true;
  1812. #elif defined( _PS3 )
  1813. #ifndef NO_THREAD_SYNC
  1814. if ( sys_mutex_trylock( m_Mutex ) == CELL_OK )
  1815. #endif
  1816. return true;
  1817. return false; // ?? moved from EA code
  1818. #elif defined( POSIX )
  1819. return pthread_mutex_trylock( &m_Mutex ) == 0;
  1820. #else
  1821. #error "Implement me!"
  1822. return true;
  1823. #endif
  1824. }
  1825. //-----------------------------------------------------------------------------
  1826. //
  1827. // CThreadFastMutex
  1828. //
  1829. //-----------------------------------------------------------------------------
  1830. #ifdef THREAD_FAST_MUTEX_TIMINGS
  1831. // This is meant to be used in combination with breakpoints and in-debugee, so we turn the optimizer off
  1832. #pragma optimize( "", off )
  1833. CThreadFastMutex *g_pIgnoredMutexes[256]; // Ignore noisy non-problem mutex. Probably could be an array. Right now needed only for sound thread
  1834. float g_MutexTimingTolerance = 5;
  1835. bool g_bMutexTimingOutput;
  1836. void TrapMutexTimings( uint32 probableBlocker, uint32 thisThread, volatile CThreadFastMutex *pMutex, CFastTimer &spikeTimer, CAverageCycleCounter &sleepTimer )
  1837. {
  1838. spikeTimer.End();
  1839. if ( spikeTimer.GetDuration().GetMillisecondsF() > g_MutexTimingTolerance )
  1840. {
  1841. bool bIgnore = false;
  1842. for ( int j = 0; j < ARRAYSIZE( g_pIgnoredMutexes ) && g_pIgnoredMutexes[j]; j++ )
  1843. {
  1844. if ( g_pIgnoredMutexes[j] == pMutex )
  1845. {
  1846. bIgnore = true;
  1847. break;
  1848. }
  1849. }
  1850. if ( !bIgnore && spikeTimer.GetDuration().GetMillisecondsF() < 100 )
  1851. {
  1852. volatile float FastMutexDuration = spikeTimer.GetDuration().GetMillisecondsF();
  1853. volatile float average = sleepTimer.GetAverageMilliseconds();
  1854. volatile float peak = sleepTimer.GetPeakMilliseconds(); volatile int xx = 6;
  1855. if ( g_bMutexTimingOutput )
  1856. {
  1857. char szBuf[256];
  1858. Msg( "M (%.8x): [%.8x <-- %.8x] (%f,%f,%f)\n", pMutex, probableBlocker, thisThread, FastMutexDuration, average, peak );
  1859. }
  1860. }
  1861. }
  1862. }
  1863. #else
  1864. #define TrapMutexTimings( a, b, c, d, e ) ((void)0)
  1865. #endif
  1866. //-------------------------------------
  1867. #define THREAD_SPIN (8*1024)
  1868. void CThreadFastMutex::Lock( const uint32 threadId, unsigned nSpinSleepTime ) volatile
  1869. {
  1870. #ifdef THREAD_FAST_MUTEX_TIMINGS
  1871. CAverageCycleCounter sleepTimer;
  1872. CFastTimer spikeTimer;
  1873. uint32 currentOwner = m_ownerID;
  1874. spikeTimer.Start();
  1875. sleepTimer.Init();
  1876. #endif
  1877. int i;
  1878. if ( nSpinSleepTime != TT_INFINITE )
  1879. {
  1880. for ( i = THREAD_SPIN; i != 0; --i )
  1881. {
  1882. if ( TryLock( threadId ) )
  1883. {
  1884. TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
  1885. return;
  1886. }
  1887. ThreadPause();
  1888. }
  1889. for ( i = THREAD_SPIN; i != 0; --i )
  1890. {
  1891. if ( TryLock( threadId ) )
  1892. {
  1893. TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
  1894. return;
  1895. }
  1896. ThreadPause();
  1897. if ( i % 1024 == 0 )
  1898. {
  1899. #ifdef THREAD_FAST_MUTEX_TIMINGS
  1900. CAverageTimeMarker marker( &sleepTimer );
  1901. #endif
  1902. ThreadSleep( 0 );
  1903. }
  1904. }
  1905. #ifdef _WIN32
  1906. if ( !nSpinSleepTime && GetThreadPriority( GetCurrentThread() ) > THREAD_PRIORITY_NORMAL )
  1907. {
  1908. nSpinSleepTime = 1;
  1909. }
  1910. #endif
  1911. if ( nSpinSleepTime )
  1912. {
  1913. for ( i = THREAD_SPIN; i != 0; --i )
  1914. {
  1915. #ifdef THREAD_FAST_MUTEX_TIMINGS
  1916. CAverageTimeMarker marker( &sleepTimer );
  1917. #endif
  1918. if ( TryLock( threadId ) )
  1919. {
  1920. TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
  1921. return;
  1922. }
  1923. ThreadPause();
  1924. ThreadSleep( 0 );
  1925. }
  1926. }
  1927. for ( ;; )
  1928. {
  1929. #ifdef THREAD_FAST_MUTEX_TIMINGS
  1930. CAverageTimeMarker marker( &sleepTimer );
  1931. #endif
  1932. if ( TryLock( threadId ) )
  1933. {
  1934. TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
  1935. return;
  1936. }
  1937. ThreadPause();
  1938. ThreadSleep( nSpinSleepTime );
  1939. }
  1940. }
  1941. else
  1942. {
  1943. for ( ;; )
  1944. {
  1945. if ( TryLock( threadId ) )
  1946. {
  1947. TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
  1948. return;
  1949. }
  1950. ThreadPause();
  1951. }
  1952. }
  1953. }
  1954. #ifdef THREAD_FAST_MUTEX_TIMINGS
  1955. #pragma optimize( "", on )
  1956. #endif
  1957. //-----------------------------------------------------------------------------
  1958. //
  1959. // CThreadRWLock
  1960. //
  1961. //-----------------------------------------------------------------------------
  1962. void CThreadRWLock::WaitForRead()
  1963. {
  1964. m_nPendingReaders++;
  1965. do
  1966. {
  1967. m_mutex.Unlock();
  1968. m_CanRead.Wait();
  1969. m_mutex.Lock();
  1970. }
  1971. while (m_nWriters);
  1972. m_nPendingReaders--;
  1973. }
  1974. void CThreadRWLock::LockForWrite()
  1975. {
  1976. m_mutex.Lock();
  1977. bool bWait = ( m_nWriters != 0 || m_nActiveReaders != 0 );
  1978. m_nWriters++;
  1979. m_CanRead.Reset();
  1980. m_mutex.Unlock();
  1981. if ( bWait )
  1982. {
  1983. m_CanWrite.Wait();
  1984. }
  1985. }
  1986. void CThreadRWLock::UnlockWrite()
  1987. {
  1988. m_mutex.Lock();
  1989. m_nWriters--;
  1990. if ( m_nWriters == 0)
  1991. {
  1992. if ( m_nPendingReaders )
  1993. {
  1994. m_CanRead.Set();
  1995. }
  1996. }
  1997. else
  1998. {
  1999. m_CanWrite.Set();
  2000. }
  2001. m_mutex.Unlock();
  2002. }
  2003. //-----------------------------------------------------------------------------
  2004. //
  2005. // CThreadSpinRWLock
  2006. //
  2007. //-----------------------------------------------------------------------------
  2008. #ifndef OLD_SPINRWLOCK
  2009. void CThreadSpinRWLock::SpinLockForWrite()
  2010. {
  2011. int i;
  2012. if ( TryLockForWrite_UnforcedInline() )
  2013. {
  2014. return;
  2015. }
  2016. for ( i = THREAD_SPIN; i != 0; --i )
  2017. {
  2018. if ( TryLockForWrite_UnforcedInline() )
  2019. {
  2020. return;
  2021. }
  2022. ThreadPause();
  2023. }
  2024. for ( i = THREAD_SPIN; i != 0; --i )
  2025. {
  2026. if ( TryLockForWrite_UnforcedInline() )
  2027. {
  2028. return;
  2029. }
  2030. ThreadPause();
  2031. if ( i % 1024 == 0 )
  2032. {
  2033. ThreadSleep( 0 );
  2034. }
  2035. }
  2036. for ( i = THREAD_SPIN * 4; i != 0; --i )
  2037. {
  2038. if ( TryLockForWrite_UnforcedInline() )
  2039. {
  2040. return;
  2041. }
  2042. ThreadPause();
  2043. ThreadSleep( 0 );
  2044. }
  2045. for ( ;; ) // coded as for instead of while to make easy to breakpoint success
  2046. {
  2047. if ( TryLockForWrite_UnforcedInline() )
  2048. {
  2049. return;
  2050. }
  2051. ThreadPause();
  2052. ThreadSleep( 1 );
  2053. }
  2054. }
  2055. void CThreadSpinRWLock::SpinLockForRead()
  2056. {
  2057. int i;
  2058. for ( i = THREAD_SPIN; i != 0; --i )
  2059. {
  2060. if ( TryLockForRead_UnforcedInline() )
  2061. {
  2062. return;
  2063. }
  2064. ThreadPause();
  2065. }
  2066. for ( i = THREAD_SPIN; i != 0; --i )
  2067. {
  2068. if ( TryLockForRead_UnforcedInline() )
  2069. {
  2070. return;
  2071. }
  2072. ThreadPause();
  2073. if ( i % 1024 == 0 )
  2074. {
  2075. ThreadSleep( 0 );
  2076. }
  2077. }
  2078. for ( i = THREAD_SPIN * 4; i != 0; --i )
  2079. {
  2080. if ( TryLockForRead_UnforcedInline() )
  2081. {
  2082. return;
  2083. }
  2084. ThreadPause();
  2085. ThreadSleep( 0 );
  2086. }
  2087. for ( ;; ) // coded as for instead of while to make easy to breakpoint success
  2088. {
  2089. if ( TryLockForRead_UnforcedInline() )
  2090. {
  2091. return;
  2092. }
  2093. ThreadPause();
  2094. ThreadSleep( 1 );
  2095. }
  2096. }
  2097. #else
  2098. /* (commented out to reduce distraction in colorized editor, remove entirely when new implementation settles)
  2099. void CThreadSpinRWLock::SpinLockForWrite( const uint32 threadId )
  2100. {
  2101. int i;
  2102. if ( TryLockForWrite( threadId ) )
  2103. {
  2104. return;
  2105. }
  2106. for ( i = THREAD_SPIN; i != 0; --i )
  2107. {
  2108. if ( TryLockForWrite( threadId ) )
  2109. {
  2110. return;
  2111. }
  2112. ThreadPause();
  2113. }
  2114. for ( i = THREAD_SPIN; i != 0; --i )
  2115. {
  2116. if ( TryLockForWrite( threadId ) )
  2117. {
  2118. return;
  2119. }
  2120. ThreadPause();
  2121. if ( i % 1024 == 0 )
  2122. {
  2123. ThreadSleep( 0 );
  2124. }
  2125. }
  2126. for ( i = THREAD_SPIN * 4; i != 0; --i )
  2127. {
  2128. if ( TryLockForWrite( threadId ) )
  2129. {
  2130. return;
  2131. }
  2132. ThreadPause();
  2133. ThreadSleep( 0 );
  2134. }
  2135. for ( ;; ) // coded as for instead of while to make easy to breakpoint success
  2136. {
  2137. if ( TryLockForWrite( threadId ) )
  2138. {
  2139. return;
  2140. }
  2141. ThreadPause();
  2142. ThreadSleep( 1 );
  2143. }
  2144. }
  2145. void CThreadSpinRWLock::LockForRead()
  2146. {
  2147. int i;
  2148. if ( TryLockForRead() )
  2149. {
  2150. return;
  2151. }
  2152. for ( i = THREAD_SPIN; i != 0; --i )
  2153. {
  2154. if ( TryLockForRead() )
  2155. {
  2156. return;
  2157. }
  2158. ThreadPause();
  2159. }
  2160. for ( i = THREAD_SPIN; i != 0; --i )
  2161. {
  2162. if ( TryLockForRead() )
  2163. {
  2164. return;
  2165. }
  2166. ThreadPause();
  2167. if ( i % 1024 == 0 )
  2168. {
  2169. ThreadSleep( 0 );
  2170. }
  2171. }
  2172. for ( i = THREAD_SPIN * 4; i != 0; --i )
  2173. {
  2174. if ( TryLockForRead() )
  2175. {
  2176. return;
  2177. }
  2178. ThreadPause();
  2179. ThreadSleep( 0 );
  2180. }
  2181. for ( ;; ) // coded as for instead of while to make easy to breakpoint success
  2182. {
  2183. if ( TryLockForRead() )
  2184. {
  2185. return;
  2186. }
  2187. ThreadPause();
  2188. ThreadSleep( 1 );
  2189. }
  2190. }
  2191. void CThreadSpinRWLock::UnlockRead()
  2192. {
  2193. int i;
  2194. Assert( m_lockInfo.m_nReaders > 0 && m_lockInfo.m_writerId == 0 );
  2195. //uint32 nLockInfoReaders = m_lockInfo.m_nReaders;
  2196. LockInfo_t oldValue;
  2197. LockInfo_t newValue;
  2198. if( IsX360() )
  2199. {
  2200. // this is the code equivalent to original code (see below) that doesn't cause LHS on Xbox360
  2201. // WARNING: This code assumes BIG Endian CPU
  2202. oldValue.m_i64 = uint32( m_lockInfo.m_nReaders );
  2203. newValue.m_i64 = oldValue.m_i64 - 1; // NOTE: when we have -1 (or 0xFFFFFFFF) readers, this will result in non-equivalent code
  2204. }
  2205. else
  2206. {
  2207. // this is the original code that worked here for a while
  2208. oldValue.m_nReaders = m_lockInfo.m_nReaders;
  2209. oldValue.m_writerId = 0;
  2210. newValue.m_nReaders = oldValue.m_nReaders - 1;
  2211. newValue.m_writerId = 0;
  2212. }
  2213. ThreadMemoryBarrier();
  2214. if( AssignIf( newValue, oldValue ) )
  2215. return;
  2216. ThreadPause();
  2217. oldValue.m_nReaders = m_lockInfo.m_nReaders;
  2218. newValue.m_nReaders = oldValue.m_nReaders - 1;
  2219. for ( i = THREAD_SPIN; i != 0; --i )
  2220. {
  2221. if( AssignIf( newValue, oldValue ) )
  2222. return;
  2223. ThreadPause();
  2224. oldValue.m_nReaders = m_lockInfo.m_nReaders;
  2225. newValue.m_nReaders = oldValue.m_nReaders - 1;
  2226. }
  2227. for ( i = THREAD_SPIN; i != 0; --i )
  2228. {
  2229. if( AssignIf( newValue, oldValue ) )
  2230. return;
  2231. ThreadPause();
  2232. if ( i % 512 == 0 )
  2233. {
  2234. ThreadSleep( 0 );
  2235. }
  2236. oldValue.m_nReaders = m_lockInfo.m_nReaders;
  2237. newValue.m_nReaders = oldValue.m_nReaders - 1;
  2238. }
  2239. for ( i = THREAD_SPIN * 4; i != 0; --i )
  2240. {
  2241. if( AssignIf( newValue, oldValue ) )
  2242. return;
  2243. ThreadPause();
  2244. ThreadSleep( 0 );
  2245. oldValue.m_nReaders = m_lockInfo.m_nReaders;
  2246. newValue.m_nReaders = oldValue.m_nReaders - 1;
  2247. }
  2248. for ( ;; ) // coded as for instead of while to make easy to breakpoint success
  2249. {
  2250. if( AssignIf( newValue, oldValue ) )
  2251. return;
  2252. ThreadPause();
  2253. ThreadSleep( 1 );
  2254. oldValue.m_nReaders = m_lockInfo.m_nReaders;
  2255. newValue.m_nReaders = oldValue.m_nReaders - 1;
  2256. }
  2257. }
  2258. void CThreadSpinRWLock::UnlockWrite()
  2259. {
  2260. Assert( m_lockInfo.m_writerId == ThreadGetCurrentId() && m_lockInfo.m_nReaders == 0 );
  2261. static const LockInfo_t newValue = { { 0, 0 } };
  2262. ThreadMemoryBarrier();
  2263. ThreadInterlockedExchange64( (int64 *)&m_lockInfo, *((int64 *)&newValue) );
  2264. m_nWriters--;
  2265. }
  2266. */
  2267. #endif
  2268. #if defined( _PS3 )
  2269. // All CThread code is inline in the header for PS3
  2270. // This function is implemented here rather than the header because g_pCurThread resolves to GetCurThread() on PS3
  2271. // and we don't want to create a dependency on the ELF stub for everyone who includes the header.
  2272. PLATFORM_INTERFACE CThread *GetCurThreadPS3()
  2273. {
  2274. return (CThread*)g_pCurThread;
  2275. }
  2276. PLATFORM_INTERFACE void SetCurThreadPS3( CThread *pThread )
  2277. {
  2278. g_pCurThread = pThread;
  2279. }
  2280. #else
  2281. // The CThread implementation needs to be inlined for performance on the PS3 - It makes a difference of more than 1ms/frame
  2282. // for other platforms, we include the .inl in the .cpp file where it existed before
  2283. #include "../public/tier0/threadtools.inl"
  2284. #endif
  2285. //-----------------------------------------------------------------------------
  2286. //
  2287. //-----------------------------------------------------------------------------
  2288. CWorkerThread::CWorkerThread()
  2289. : m_EventSend(true), // must be manual-reset for PeekCall()
  2290. m_EventComplete(true), // must be manual-reset to handle multiple wait with thread properly
  2291. m_Param(0),
  2292. m_ReturnVal(0)
  2293. {
  2294. }
  2295. //---------------------------------------------------------
  2296. int CWorkerThread::CallWorker(unsigned dw, unsigned timeout, bool fBoostWorkerPriorityToMaster)
  2297. {
  2298. return Call(dw, timeout, fBoostWorkerPriorityToMaster);
  2299. }
  2300. //---------------------------------------------------------
  2301. int CWorkerThread::CallMaster(unsigned dw, unsigned timeout)
  2302. {
  2303. return Call(dw, timeout, false);
  2304. }
  2305. //---------------------------------------------------------
  2306. CThreadEvent &CWorkerThread::GetCallHandle()
  2307. {
  2308. return m_EventSend;
  2309. }
  2310. //---------------------------------------------------------
  2311. unsigned CWorkerThread::GetCallParam() const
  2312. {
  2313. return m_Param;
  2314. }
  2315. //---------------------------------------------------------
  2316. int CWorkerThread::BoostPriority()
  2317. {
  2318. int iInitialPriority = GetPriority();
  2319. #ifdef WIN32
  2320. const int iNewPriority = ThreadGetPriority( GetThreadHandle() );
  2321. if (iNewPriority > iInitialPriority)
  2322. ThreadSetPriority( GetThreadHandle(), iNewPriority);
  2323. #elif !defined( _PS3 )
  2324. const int iNewPriority = ThreadGetPriority( (ThreadHandle_t)GetThreadID() );
  2325. if (iNewPriority > iInitialPriority)
  2326. ThreadSetPriority( (ThreadHandle_t)GetThreadID(), iNewPriority);
  2327. #endif
  2328. return iInitialPriority;
  2329. }
  2330. //---------------------------------------------------------
  2331. static uint32 DefaultWaitFunc( uint32 nHandles, CThreadEvent** ppHandles, int bWaitAll, uint32 timeout )
  2332. {
  2333. return CThreadEvent::WaitForMultiple( nHandles, ppHandles, bWaitAll!=0, timeout ) ;
  2334. }
  2335. int CWorkerThread::Call(unsigned dwParam, unsigned timeout, bool fBoostPriority, WaitFunc_t waitFunc)
  2336. {
  2337. AssertMsg(!m_EventSend.Check(), "Cannot perform call if there's an existing call pending" );
  2338. AUTO_LOCK( m_Lock );
  2339. if (!IsAlive())
  2340. return WTCR_FAIL;
  2341. int iInitialPriority = 0;
  2342. if (fBoostPriority)
  2343. {
  2344. iInitialPriority = BoostPriority();
  2345. }
  2346. // set the parameter, signal the worker thread, wait for the completion to be signaled
  2347. m_Param = dwParam;
  2348. m_EventComplete.Reset();
  2349. m_EventSend.Set();
  2350. WaitForReply( timeout, waitFunc );
  2351. if (fBoostPriority)
  2352. SetPriority(iInitialPriority);
  2353. return m_ReturnVal;
  2354. }
  2355. //---------------------------------------------------------
  2356. //
  2357. // Wait for a request from the client
  2358. //
  2359. //---------------------------------------------------------
  2360. int CWorkerThread::WaitForReply( unsigned timeout )
  2361. {
  2362. return WaitForReply( timeout, NULL );
  2363. }
  2364. int CWorkerThread::WaitForReply( unsigned timeout, WaitFunc_t pfnWait )
  2365. {
  2366. if (!pfnWait)
  2367. {
  2368. pfnWait = &DefaultWaitFunc;
  2369. }
  2370. CThreadEvent *waits[] =
  2371. {
  2372. &m_EventComplete,
  2373. &m_ExitEvent
  2374. };
  2375. unsigned result;
  2376. bool bInDebugger = Plat_IsInDebugSession();
  2377. uint32 dwActualTimeout = ( (timeout==TT_INFINITE) ? 30000 : timeout );
  2378. do
  2379. {
  2380. #ifdef WIN32
  2381. // Make sure the thread handle hasn't been closed
  2382. if ( !GetThreadHandle() )
  2383. {
  2384. result = 1;
  2385. break;
  2386. }
  2387. #endif
  2388. result = (*pfnWait)( ARRAYSIZE( waits ), waits, false, dwActualTimeout );
  2389. AssertMsg(timeout != TT_INFINITE || result != TW_TIMEOUT, "Possible hung thread, call to thread timed out");
  2390. } while ( bInDebugger && ( timeout == TT_INFINITE && result == TW_TIMEOUT ) );
  2391. if ( result != 0 )
  2392. {
  2393. if (result == TW_TIMEOUT)
  2394. {
  2395. m_ReturnVal = WTCR_TIMEOUT;
  2396. }
  2397. else if (result == 1)
  2398. {
  2399. DevMsg( 2, "Thread failed to respond, probably exited\n");
  2400. m_EventSend.Reset();
  2401. m_ReturnVal = WTCR_TIMEOUT;
  2402. }
  2403. else
  2404. {
  2405. m_EventSend.Reset();
  2406. m_ReturnVal = WTCR_THREAD_GONE;
  2407. }
  2408. }
  2409. return m_ReturnVal;
  2410. }
  2411. //---------------------------------------------------------
  2412. //
  2413. // Wait for a request from the client
  2414. //
  2415. //---------------------------------------------------------
  2416. bool CWorkerThread::WaitForCall(unsigned * pResult)
  2417. {
  2418. return WaitForCall(TT_INFINITE, pResult);
  2419. }
  2420. //---------------------------------------------------------
  2421. bool CWorkerThread::WaitForCall(unsigned dwTimeout, unsigned * pResult)
  2422. {
  2423. bool returnVal = m_EventSend.Wait(dwTimeout);
  2424. if (pResult)
  2425. *pResult = m_Param;
  2426. return returnVal;
  2427. }
  2428. //---------------------------------------------------------
  2429. //
  2430. // is there a request?
  2431. //
  2432. bool CWorkerThread::PeekCall(unsigned * pParam)
  2433. {
  2434. if (!m_EventSend.Check())
  2435. {
  2436. return false;
  2437. }
  2438. else
  2439. {
  2440. if (pParam)
  2441. {
  2442. *pParam = m_Param;
  2443. }
  2444. return true;
  2445. }
  2446. }
  2447. //---------------------------------------------------------
  2448. //
  2449. // Reply to the request
  2450. //
  2451. void CWorkerThread::Reply(unsigned dw)
  2452. {
  2453. m_Param = 0;
  2454. m_ReturnVal = dw;
  2455. // The request is now complete so PeekCall() should fail from
  2456. // now on
  2457. //
  2458. // This event should be reset BEFORE we signal the client
  2459. m_EventSend.Reset();
  2460. // Tell the client we're finished
  2461. m_EventComplete.Set();
  2462. }
  2463. //-----------------------------------------------------------------------------
  2464. #if defined( _PS3 )
  2465. /*******************************************************************************
  2466. * PS3 equivalent to Win32 function for setting events
  2467. *******************************************************************************/
  2468. BOOL SetEvent( CThreadEvent *pEvent )
  2469. {
  2470. bool bRetVal = pEvent->Set();
  2471. if ( !bRetVal )
  2472. Assert(0);
  2473. return bRetVal;
  2474. }
  2475. /*******************************************************************************
  2476. * PS3 equivalent to Win32 function for resetting events
  2477. *******************************************************************************/
  2478. BOOL ResetEvent( CThreadEvent *pEvent )
  2479. {
  2480. return pEvent->Reset();
  2481. }
  2482. #define MAXIMUM_WAIT_OBJECTS 64
  2483. /*******************************************************************************
  2484. * Wait for a selection of events to terminate
  2485. *******************************************************************************/
  2486. DWORD WaitForMultipleObjects( DWORD nCount, CThreadEvent **lppHandles, BOOL bWaitAll, DWORD dwMilliseconds )
  2487. {
  2488. //////////////////////////////////////////////////////////////
  2489. #ifndef NEW_WAIT_FOR_MULTIPLE_OBJECTS
  2490. //////////////////////////////////////////////////////////////
  2491. // Support for a limited amount of events
  2492. if ( nCount >= MAXIMUM_WAIT_OBJECTS )
  2493. {
  2494. Assert(0);
  2495. return false;
  2496. }
  2497. bool bRunning = true;
  2498. unsigned int result = TW_FAILED;
  2499. // For bWaitAll
  2500. int numEvent = 0;
  2501. int eventComplete[ MAXIMUM_WAIT_OBJECTS ] = {0};
  2502. uint64_t timeDiffMS = 0;
  2503. uint64_t startTimeMS = Plat_MSTime();
  2504. uint64_t endTimeMS = 0;
  2505. while ( bRunning )
  2506. {
  2507. // Check for a timeout
  2508. if ( bRunning && ( dwMilliseconds != INFINITE ) && ( timeDiffMS > dwMilliseconds ) )
  2509. {
  2510. result = TW_TIMEOUT;
  2511. bRunning = false;
  2512. }
  2513. // Wait for all the events to be set
  2514. if ( bWaitAll )
  2515. {
  2516. for ( int event = 0; event < nCount; ++event )
  2517. {
  2518. if ( lppHandles[event]->Wait(1) )
  2519. {
  2520. // If an event is complete, mark it as complete in our list
  2521. if ( eventComplete[ event ] == 0 )
  2522. {
  2523. numEvent++;
  2524. eventComplete[ event ] = 1;
  2525. }
  2526. }
  2527. }
  2528. // If all the events have been set, terminate the function
  2529. if ( numEvent >= nCount )
  2530. {
  2531. result = WAIT_OBJECT_0;
  2532. bRunning = false;
  2533. }
  2534. }
  2535. // Wait for one event to be set
  2536. else
  2537. {
  2538. for ( int event = 0; event < nCount; ++event )
  2539. {
  2540. if ( lppHandles[event]->Wait(1) )
  2541. {
  2542. result = WAIT_OBJECT_0 + event;
  2543. bRunning = false;
  2544. break;
  2545. }
  2546. }
  2547. }
  2548. endTimeMS = Plat_MSTime();
  2549. timeDiffMS = endTimeMS - startTimeMS;
  2550. }
  2551. return result;
  2552. //////////////////////////////////////////////////////////////
  2553. #else // NEW_WAIT_FOR_MULTIPLE_OBJECTS // (expected PS3 only)
  2554. //////////////////////////////////////////////////////////////
  2555. #ifndef _PS3
  2556. #error This code was written expecting to be run on PS3.
  2557. #endif
  2558. // check if we have a wait objects semaphore
  2559. if (!gbWaitObjectsCreated)
  2560. {
  2561. sys_semaphore_attribute_t semAttr;
  2562. sys_semaphore_attribute_initialize(semAttr);
  2563. sys_semaphore_create(&gWaitObjectsSemaphore, &semAttr, 0, 0xFFFF);
  2564. gbWaitObjectsCreated = true;
  2565. }
  2566. // Support for a limited amount of events
  2567. if ( nCount >= MAXIMUM_WAIT_OBJECTS )
  2568. {
  2569. Assert(0);
  2570. return false;
  2571. }
  2572. unsigned int result = WAIT_FAILED;
  2573. int res = CELL_OK;
  2574. int event = -1;
  2575. int numEvent = 0;
  2576. // run through events registering this thread with each one
  2577. for (int i = 0; i < nCount; i++)
  2578. {
  2579. lppHandles[i]->RegisterWaitingThread(&gWaitObjectsSemaphore, i, &event);
  2580. }
  2581. // in the Source API, a timeOut of 0 means very short timeOut, not (as in the PS3 spec) an infinite timeout.
  2582. // TT_INFINITE is #defined to 2^31-1, which means "infinite timeout" on PC and "72 minutes, 35 seconds" on PS3.
  2583. // conversely, the code below (around deltaTime) expects to be able to compare against the timeout
  2584. // value given here, so we cannot just replace 0 with 1 and TT_INFINITE with 0.
  2585. // So, we replace 0 with 1, meaning "a very short time", and test for the special value TT_INFINITE
  2586. // at the moment of calling sys_semaphore_wait, where we replace it with the real "infinite timeout"
  2587. // value. It isn't safe to simply increase the declaration size of TT_INFINITE, because as you can
  2588. // see it is often assigned to uint32s.
  2589. // Also, Source timeouts are specified in milliseconds, and PS3 timeouts are in microseconds,
  2590. // so we need to multiply by one thousand.
  2591. uint32 timeOut = dwMilliseconds;
  2592. if ( timeOut == 0 )
  2593. {
  2594. timeOut = 1;
  2595. }
  2596. else if ( timeOut != TT_INFINITE )
  2597. {
  2598. timeOut *= 1000;
  2599. // note that it's impossible for dwMilliseconds * 1000
  2600. // to coincidentally equal TT_INFINITE since TT_INFINITE
  2601. // is not divisible by 1000.
  2602. COMPILE_TIME_ASSERT( TT_INFINITE % 1000 != 0 );
  2603. }
  2604. COMPILE_TIME_ASSERT( TT_INFINITE != 0 ); // The code here was written expecting (working around) that TT_INFINITE is
  2605. // MAXINT, so if you changed this number, please read the comment above and
  2606. // carefully examine the code here to make sure that timeouts still work
  2607. // correctly on PS3. Be aware that in many places in Source, a timeout of
  2608. // 0 has some special meaning other than "infinite timeout", so track those
  2609. // down too.
  2610. // Wait for all the events to be set
  2611. if ( bWaitAll )
  2612. {
  2613. while (numEvent < nCount)
  2614. {
  2615. uint64_t deltaTime = Plat_USTime();
  2616. res = sys_semaphore_wait(gWaitObjectsSemaphore, timeOut == TT_INFINITE ? 0 : timeOut );
  2617. deltaTime = Plat_USTime() - deltaTime;
  2618. if (res == ETIMEDOUT)
  2619. {
  2620. result = TW_TIMEOUT;
  2621. break;
  2622. }
  2623. else if (res == CELL_OK)
  2624. {
  2625. numEvent++;
  2626. if (deltaTime >= timeOut)
  2627. {
  2628. // note - if this is not truly a time out
  2629. // then it will be set to WAIT_OBJECT_0
  2630. // after this loop
  2631. result = TW_TIMEOUT;
  2632. break;
  2633. }
  2634. else
  2635. {
  2636. timeOut -= deltaTime;
  2637. }
  2638. }
  2639. else
  2640. {
  2641. result = TW_FAILED;
  2642. break;
  2643. }
  2644. }
  2645. if (numEvent >= nCount)
  2646. {
  2647. result = WAIT_OBJECT_0;
  2648. }
  2649. }
  2650. else // Wait for one event to be set
  2651. {
  2652. // no event fired yet, wait on semaphore
  2653. res = sys_semaphore_wait( gWaitObjectsSemaphore, timeOut == TT_INFINITE ? 0 : timeOut );
  2654. if (res == ETIMEDOUT)
  2655. {
  2656. result = TW_TIMEOUT;
  2657. }
  2658. else if (res == CELL_OK)
  2659. {
  2660. if ((event < 0) || (event >= nCount))
  2661. {
  2662. DEBUG_ERROR("Bad event\n");
  2663. }
  2664. result = WAIT_OBJECT_0 + event;
  2665. }
  2666. }
  2667. // run through events unregistering this thread, for benefit
  2668. // of those events that did not fire, or fired before semaphore
  2669. // was registered
  2670. for (int i = 0; i < nCount; i++)
  2671. {
  2672. lppHandles[i]->UnregisterWaitingThread(&gWaitObjectsSemaphore);
  2673. }
  2674. // reset semaphore
  2675. while (sys_semaphore_trywait(gWaitObjectsSemaphore) != EBUSY);
  2676. return result;
  2677. //////////////////////////////////////////////////////////////
  2678. #endif // NEW_WAIT_FOR_MULTIPLE_OBJECTS
  2679. //////////////////////////////////////////////////////////////
  2680. }
  2681. #endif