Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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