summaryrefslogtreecommitdiff
path: root/external/vpc/tier0/threadtools.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /external/vpc/tier0/threadtools.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'external/vpc/tier0/threadtools.cpp')
-rw-r--r--external/vpc/tier0/threadtools.cpp3096
1 files changed, 3096 insertions, 0 deletions
diff --git a/external/vpc/tier0/threadtools.cpp b/external/vpc/tier0/threadtools.cpp
new file mode 100644
index 0000000..735f233
--- /dev/null
+++ b/external/vpc/tier0/threadtools.cpp
@@ -0,0 +1,3096 @@
+//========== Copyright 2005, Valve Corporation, All rights reserved. ========
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "tier0/platform.h"
+
+#if defined( PLATFORM_WINDOWS_PC )
+#define WIN32_LEAN_AND_MEAN
+#define _WIN32_WINNT 0x0403
+#include <windows.h>
+#endif
+
+#ifdef PLATFORM_WINDOWS
+ #include <process.h>
+ #ifdef PLATFORM_WINDOWS_PC
+ #include <Mmsystem.h>
+ #pragma comment(lib, "winmm.lib")
+ #endif
+#elif PLATFORM_PS3
+ #include <sched.h>
+ #include <unistd.h>
+ #include <exception>
+ #include <errno.h>
+ #include <pthread.h>
+ #include <sys/time.h>
+ #include <sys/timer.h>
+ #define GetLastError() errno
+ typedef void *LPVOID;
+#elif PLATFORM_POSIX
+ #include <sched.h>
+ #include <exception>
+ #include <errno.h>
+ #include <signal.h>
+ #include <pthread.h>
+ #include <sys/time.h>
+ #define GetLastError() errno
+ typedef void *LPVOID;
+#if !defined(OSX)
+ #include <sys/fcntl.h>
+ #include <sys/unistd.h>
+ #define sem_unlink( arg )
+#else
+ #define pthread_yield pthread_yield_np
+ #include <mach/thread_act.h>
+#endif // !OSX
+
+#endif
+
+#ifndef _PS3
+#include <memory.h>
+#endif
+#include "tier0/threadtools.h"
+
+#ifdef _X360
+#include "xbox/xbox_win32stubs.h"
+#endif
+
+#include <map>
+
+// Must be last header...
+#include "tier0/memdbgon.h"
+
+#ifdef _PS3
+#include "ps3/ps3_win32stubs.h"
+#define NEW_WAIT_FOR_MULTIPLE_OBJECTS
+bool gbCheckNotMultithreaded = true;
+#endif
+
+#define THREADS_DEBUG 1
+
+#define DEBUG_ERROR(XX) Assert(0)
+
+// Need to ensure initialized before other clients call in for main thread ID
+#ifdef _WIN32
+#pragma warning(disable:4073)
+#pragma init_seg(lib)
+#endif
+
+#ifdef _WIN32
+ASSERT_INVARIANT(TT_SIZEOF_CRITICALSECTION == sizeof(CRITICAL_SECTION));
+ASSERT_INVARIANT(TT_INFINITE == INFINITE);
+#endif
+
+// thread creation counter.
+// this is used to provide a unique threadid for each running thread in g_nThreadID ( a thread local variable ).
+
+const int MAX_THREAD_IDS = 128;
+
+static volatile bool s_bThreadIDAllocated[MAX_THREAD_IDS];
+
+#ifdef LINUX
+
+DLL_CLASS_EXPORT __thread int g_nThreadID;
+
+#elif defined(_PS3)
+ #include "tls_ps3.h"
+#else
+ CTHREADLOCALINT g_nThreadID;
+#endif
+
+
+static CThreadFastMutex s_ThreadIDMutex;
+
+PLATFORM_INTERFACE void AllocateThreadID( void )
+{
+ AUTO_LOCK( s_ThreadIDMutex );
+ for( int i = 1; i < MAX_THREAD_IDS; i++ )
+ {
+ if ( ! s_bThreadIDAllocated[i] )
+ {
+ g_nThreadID = i;
+ s_bThreadIDAllocated[i] = true;
+ return;
+ }
+ }
+ Error( "Out of thread ids. Decrease the number of threads or increase MAX_THREAD_IDS\n" );
+}
+
+PLATFORM_INTERFACE void FreeThreadID( void )
+{
+ AUTO_LOCK( s_ThreadIDMutex );
+ int nThread = g_nThreadID;
+ if ( nThread )
+ s_bThreadIDAllocated[nThread] = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Simple thread functions.
+// Because _beginthreadex uses stdcall, we need to convert to cdecl
+//-----------------------------------------------------------------------------
+struct ThreadProcInfo_t
+{
+ ThreadProcInfo_t( ThreadFunc_t pfnThread, void *pParam )
+ : pfnThread( pfnThread),
+ pParam( pParam )
+ {
+ }
+
+ ThreadFunc_t pfnThread;
+ void * pParam;
+};
+
+//---------------------------------------------------------
+
+#ifdef PLATFORM_WINDOWS
+static DWORD WINAPI ThreadProcConvert( void *pParam )
+{
+ ThreadProcInfo_t info = *((ThreadProcInfo_t *)pParam);
+ AllocateThreadID();
+ delete ((ThreadProcInfo_t *)pParam);
+ unsigned nRet = (*info.pfnThread)(info.pParam);
+ FreeThreadID();
+ return nRet;
+}
+#elif defined( PLATFORM_PS3 )
+union ThreadProcInfoUnion_t
+{
+ struct Val_t
+ {
+ ThreadFunc_t pfnThread;
+ void * pParam;
+ }
+ val;
+ uint64_t val64;
+};
+static void ThreadProcConvertUnion( uint64_t param )
+{
+ COMPILE_TIME_ASSERT( sizeof( ThreadProcInfoUnion_t ) == 8 );
+ ThreadProcInfoUnion_t info;
+ info.val64 = param;
+ AllocateThreadID();
+ unsigned nRet = (*info.val.pfnThread)(info.val.pParam);
+ FreeThreadID();
+ sys_ppu_thread_exit( nRet );
+}
+static void* ThreadProcConvert( void *pParam )
+{
+ ThreadProcInfo_t info = *((ThreadProcInfo_t *)pParam);
+ AllocateThreadID();
+ delete ((ThreadProcInfo_t *)pParam);
+ unsigned nRet = (*info.pfnThread)(info.pParam);
+ FreeThreadID();
+ return ( void * ) nRet;
+}
+
+#else
+static void* ThreadProcConvert( void *pParam )
+{
+ ThreadProcInfo_t info = *((ThreadProcInfo_t *)pParam);
+ AllocateThreadID();
+ delete ((ThreadProcInfo_t *)pParam);
+ unsigned nRet = (*info.pfnThread)(info.pParam);
+ FreeThreadID();
+ return ( void * ) nRet;
+}
+#endif
+
+
+
+#if defined( _PS3 )
+
+/*******************************************************************************
+* Thread Local Storage globals and functions
+*******************************************************************************/
+#ifndef _PS3
+__thread void *gTLSValues[ MAX_TLS_VALUES ] = { NULL };
+__thread bool gTLSFlags[ MAX_TLS_VALUES ] = { false };
+__thread bool gbWaitObjectsCreated = false;
+__thread sys_semaphore_t gWaitObjectsSemaphore;
+#endif // !_PS3
+
+static char gThreadName[28] = "";
+
+// Simple TLS allocator. Linearly searches for a free slot.
+uint32 TlsAlloc()
+{
+ for ( int i = 0; i < MAX_TLS_VALUES; ++i )
+ {
+ if ( !gTLSFlags[i] )
+ {
+ gTLSFlags[i] = true;
+ return i;
+ }
+ }
+
+#ifdef _PS3
+ DEBUG_ERROR("TlsAlloc(): Out of TLS\n");
+#endif
+
+ return 0xFFFFFFFF;
+}
+
+void TlsFree( uint32 index )
+{
+ gTLSValues[ index ] = NULL;
+ gTLSFlags[ index ] = false;
+}
+
+void *TlsGetValue( uint32 index )
+{
+ return gTLSValues[ index ];
+}
+
+void TlsSetValue( uint32 index, void *pValue )
+{
+ gTLSValues[ index ] = pValue;
+}
+#endif //_PS3
+
+
+
+#ifdef PLATFORM_WINDOWS
+class CThreadHandleToIDMap
+{
+public:
+ HANDLE m_hThread;
+ uint m_ThreadID;
+ CThreadHandleToIDMap *m_pNext;
+};
+static CThreadHandleToIDMap *g_pThreadHandleToIDMaps = NULL;
+static CThreadMutex g_ThreadHandleToIDMapMutex;
+static volatile int g_nThreadHandleToIDMaps = 0;
+
+static void AddThreadHandleToIDMap( HANDLE hThread, uint threadID )
+{
+ if ( !hThread )
+ return;
+
+ // Remember this handle/id combo.
+ CThreadHandleToIDMap *pMap = new CThreadHandleToIDMap;
+ pMap->m_hThread = hThread;
+ pMap->m_ThreadID = threadID;
+
+ // Add it to the global list.
+ g_ThreadHandleToIDMapMutex.Lock();
+ pMap->m_pNext = g_pThreadHandleToIDMaps;
+ g_pThreadHandleToIDMaps = pMap;
+ ++g_nThreadHandleToIDMaps;
+
+ g_ThreadHandleToIDMapMutex.Unlock();
+
+ if ( g_nThreadHandleToIDMaps > 500 )
+ Error( "ThreadHandleToIDMap overflow." );
+}
+
+// This assumes you've got g_ThreadHandleToIDMapMutex locked!!
+static bool InternalLookupHandleToThreadIDMap( HANDLE hThread, CThreadHandleToIDMap* &pMap, CThreadHandleToIDMap** &ppPrev )
+{
+ ppPrev = &g_pThreadHandleToIDMaps;
+ for ( pMap=g_pThreadHandleToIDMaps; pMap; pMap=pMap->m_pNext )
+ {
+ if ( pMap->m_hThread == hThread )
+ return true;
+
+ ppPrev = &pMap->m_pNext;
+ }
+
+ return false;
+}
+
+static void RemoveThreadHandleToIDMap( HANDLE hThread )
+{
+ if ( !hThread )
+ return;
+
+ CThreadHandleToIDMap *pMap, **ppPrev;
+
+ g_ThreadHandleToIDMapMutex.Lock();
+
+ if ( g_nThreadHandleToIDMaps <= 0 )
+ Error( "ThreadHandleToIDMap underflow." );
+
+ if ( InternalLookupHandleToThreadIDMap( hThread, pMap, ppPrev ) )
+ {
+ *ppPrev = pMap->m_pNext;
+ delete pMap;
+ --g_nThreadHandleToIDMaps;
+ }
+
+ g_ThreadHandleToIDMapMutex.Unlock();
+}
+
+static uint LookupThreadIDFromHandle( HANDLE hThread )
+{
+ if ( hThread == NULL || hThread == GetCurrentThread() )
+ return GetCurrentThreadId();
+
+ float flStartTime = Plat_FloatTime();
+ while ( Plat_FloatTime() - flStartTime < 2 )
+ {
+ CThreadHandleToIDMap *pMap, **ppPrev;
+
+ g_ThreadHandleToIDMapMutex.Lock();
+ bool bRet = InternalLookupHandleToThreadIDMap( hThread, pMap, ppPrev );
+ g_ThreadHandleToIDMapMutex.Unlock();
+
+ if ( bRet )
+ return pMap->m_ThreadID;
+
+ // We should only get here if a thread that is just starting up is currently in AddThreadHandleToIDMap.
+ // Give up the timeslice and try again.
+ ThreadSleep( 1 );
+ }
+
+ Assert( !"LookupThreadIDFromHandle failed!" );
+ Warning( "LookupThreadIDFromHandle couldn't find thread ID for handle." );
+ return 0;
+}
+#endif
+
+
+//---------------------------------------------------------
+
+ThreadHandle_t * CreateTestThreads( ThreadFunc_t fnThread, int numThreads, int nProcessorsToDistribute )
+{
+ ThreadHandle_t *pHandles = (new ThreadHandle_t[numThreads+1]) + 1;
+ pHandles[-1] = (ThreadHandle_t)INT_TO_POINTER( numThreads );
+ for( int i = 0; i < numThreads; ++i )
+ {
+ //TestThreads();
+ ThreadHandle_t hThread;
+ const unsigned int nDefaultStackSize = 64 * 1024; // this stack size is used in case stackSize == 0
+ hThread = CreateSimpleThread( fnThread, INT_TO_POINTER( i ), nDefaultStackSize );
+
+ if ( nProcessorsToDistribute )
+ {
+ int32 mask = 1 << (i % nProcessorsToDistribute);
+ ThreadSetAffinity( hThread, mask );
+ }
+
+/*
+ ThreadProcInfoUnion_t info;
+ info.val.pfnThread = fnThread;
+ info.val.pParam = (void*)(i);
+ if ( int nError = sys_ppu_thread_create( &hThread, ThreadProcConvertUnion, info.val64, 1001, nDefaultStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, "SimpleThread" ) != CELL_OK )
+ {
+ printf( "PROBLEM!\n" );
+ Error( "Cannot create thread, error %d\n", nError );
+ return 0;
+ }
+*/
+ //ThreadHandle_t hThread = CreateSimpleThread( fnThread, (void*)i );
+ pHandles[i] = hThread;
+ }
+// printf("Finishinged CreateTestThreads(%p,%d)\n", (void*)fnThread, numThreads );
+ return pHandles;
+}
+
+void JoinTestThreads( ThreadHandle_t *pHandles )
+{
+ int nCount = POINTER_TO_INT( pHandles[-1] );
+// printf("Joining test threads @%p[%d]:\n", pHandles, nCount );
+// for( int i = 0; i < nCount; ++i )
+// {
+// printf(" %p,\n", (void*)pHandles[i] );
+// }
+ for( int i = 0; i < nCount; ++i )
+ {
+// printf( "Joining %p", (void*) pHandles[i] );
+// if( !i ) sys_timer_usleep(100000);
+ ThreadJoin( pHandles[i] );
+ ReleaseThreadHandle( pHandles[i] );
+ }
+ delete[]( pHandles - 1 );
+}
+
+
+
+ThreadHandle_t CreateSimpleThread( ThreadFunc_t pfnThread, void *pParam, unsigned stackSize )
+{
+#ifdef PLATFORM_WINDOWS
+ DWORD threadID;
+ HANDLE hThread = (HANDLE)CreateThread( NULL, stackSize, ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ), 0, &threadID );
+ AddThreadHandleToIDMap( hThread, threadID );
+ return (ThreadHandle_t)hThread;
+#elif PLATFORM_PS3
+ //TestThreads();
+ ThreadHandle_t th;
+ ThreadProcInfoUnion_t info;
+ info.val.pfnThread = pfnThread;
+ info.val.pParam = pParam;
+ const unsigned int nDefaultStackSize = 64 * 1024; // this stack size is used in case stackSize == 0
+ if ( sys_ppu_thread_create( &th, ThreadProcConvertUnion, info.val64, 1001, stackSize ? stackSize : nDefaultStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, "SimpleThread" ) != CELL_OK )
+ {
+ AssertMsg1( 0, "Failed to create thread (error 0x%x)", errno );
+ return 0;
+ }
+ return th;
+#elif PLATFORM_POSIX
+ pthread_t tid;
+ pthread_create( &tid, NULL, ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ) );
+ return ( ThreadHandle_t ) tid;
+#else
+ Assert( 0 );
+ DebuggerBreak();
+ return 0;
+#endif
+}
+
+
+bool ReleaseThreadHandle( ThreadHandle_t hThread )
+{
+#ifdef _WIN32
+ bool bRetVal = ( CloseHandle( hThread ) != 0 );
+ RemoveThreadHandleToIDMap( (HANDLE)hThread );
+ return bRetVal;
+#else
+ return true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+//
+// Wrappers for other simple threading operations
+//
+//-----------------------------------------------------------------------------
+
+void ThreadSleep(unsigned nMilliseconds)
+{
+#ifdef _WIN32
+
+#ifdef PLATFORM_WINDOWS_PC
+ static bool bInitialized = false;
+ if ( !bInitialized )
+ {
+ bInitialized = true;
+ // Set the timer resolution to 1 ms (default is 10.0, 15.6, 2.5, 1.0 or
+ // some other value depending on hardware and software) so that we can
+ // use Sleep( 1 ) to avoid wasting CPU time without missing our frame
+ // rate.
+ timeBeginPeriod( 1 );
+ }
+#endif
+
+ Sleep( nMilliseconds );
+#elif PLATFORM_PS3
+ if( nMilliseconds == 0 )
+ {
+ // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead.
+ sys_timer_usleep( 60 );
+ }
+ else
+ {
+ sys_timer_usleep( nMilliseconds * 1000 );
+ }
+#elif defined(POSIX)
+ usleep( nMilliseconds * 1000 );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+#ifndef ThreadGetCurrentId
+ThreadId_t ThreadGetCurrentId()
+{
+#ifdef _WIN32
+ return GetCurrentThreadId();
+#elif defined( _PS3 )
+ sys_ppu_thread_t th = 0;
+ sys_ppu_thread_get_id( &th );
+ return th;
+#elif defined(POSIX)
+ return (ThreadId_t)pthread_self();
+#else
+ Assert(0);
+ DebuggerBreak();
+ return 0;
+#endif
+}
+#endif
+
+//-----------------------------------------------------------------------------
+ThreadHandle_t ThreadGetCurrentHandle()
+{
+#ifdef _WIN32
+ return (ThreadHandle_t)GetCurrentThread();
+#elif defined( _PS3 )
+ sys_ppu_thread_t th = 0;
+ sys_ppu_thread_get_id( &th );
+ return th;
+#elif defined(POSIX)
+ return (ThreadHandle_t)pthread_self();
+#else
+ Assert(0);
+ DebuggerBreak();
+ return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+int ThreadGetPriority( ThreadHandle_t hThread )
+{
+ if ( !hThread )
+ {
+ hThread = ThreadGetCurrentHandle();
+ }
+
+#ifdef _WIN32
+ return ::GetThreadPriority( (HANDLE)hThread );
+#elif defined( _PS3 )
+ int iPri = 0;
+ sys_ppu_thread_get_priority( hThread, &iPri );
+ return iPri;
+#else
+ return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+bool ThreadSetPriority( ThreadHandle_t hThread, int priority )
+{
+ if ( !hThread )
+ {
+ hThread = ThreadGetCurrentHandle();
+ }
+
+#ifdef _WIN32
+ return ( SetThreadPriority(hThread, priority) != 0 );
+#elif defined( _PS3 )
+ int retval = sys_ppu_thread_set_priority( hThread, priority );
+ return retval >= CELL_OK;
+#elif defined(POSIX)
+ struct sched_param thread_param;
+ thread_param.sched_priority = priority;
+ //pthread_setschedparam( (pthread_t ) hThread, SCHED_RR, &thread_param );
+ return true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+void ThreadSetAffinity( ThreadHandle_t hThread, int nAffinityMask )
+{
+ if ( !hThread )
+ {
+ hThread = ThreadGetCurrentHandle();
+ }
+
+#ifdef _WIN32
+ SetThreadAffinityMask( hThread, nAffinityMask );
+#elif defined(POSIX)
+// cpu_set_t cpuSet;
+// CPU_ZERO( cpuSet );
+// for( int i = 0 ; i < 32; i++ )
+// if ( nAffinityMask & ( 1 << i ) )
+// CPU_SET( cpuSet, i );
+// sched_setaffinity( hThread, sizeof( cpuSet ), &cpuSet );
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+
+#ifndef _X360
+uint InitMainThread()
+{
+ ThreadSetDebugName( "MainThrd" );
+
+ return ThreadGetCurrentId();
+}
+
+unsigned long g_ThreadMainThreadID = InitMainThread();
+
+bool ThreadInMainThread()
+{
+ return ( ThreadGetCurrentId() == g_ThreadMainThreadID );
+}
+
+void DeclareCurrentThreadIsMainThread()
+{
+ g_ThreadMainThreadID = ThreadGetCurrentId();
+}
+
+#else
+byte *InitMainThread()
+{
+ byte b;
+
+ return AlignValue( &b, 64*1024 );
+}
+#define STACK_SIZE_360 327680
+byte *g_pBaseMainStack = InitMainThread();
+byte *g_pLimitMainStack = InitMainThread() - STACK_SIZE_360;
+#endif
+
+//-----------------------------------------------------------------------------
+bool ThreadJoin( ThreadHandle_t hThread, unsigned timeout )
+{
+ if ( !hThread )
+ {
+ return false;
+ }
+
+#ifdef _WIN32
+ DWORD dwWait = WaitForSingleObject( (HANDLE)hThread, timeout );
+ if ( dwWait == WAIT_TIMEOUT)
+ return false;
+ if ( dwWait != WAIT_OBJECT_0 && ( dwWait != WAIT_FAILED && GetLastError() != 0 ) )
+ {
+ Assert( 0 );
+ return false;
+ }
+#elif defined( _PS3 )
+ uint64 uiExitCode = 0;
+ int retval = sys_ppu_thread_join( hThread, &uiExitCode );
+ return ( retval >= CELL_OK );
+#elif defined(POSIX)
+ if ( pthread_join( (pthread_t)hThread, NULL ) != 0 )
+ return false;
+#else
+ Assert(0);
+ DebuggerBreak();
+#endif
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+void ThreadSetDebugName( ThreadHandle_t hThread, const char *pszName )
+{
+#ifdef WIN32
+ if ( Plat_IsInDebugSession() )
+ {
+#define MS_VC_EXCEPTION 0x406d1388
+
+ typedef struct tagTHREADNAME_INFO
+ {
+ DWORD dwType; // must be 0x1000
+ LPCSTR szName; // pointer to name (in same addr space)
+ DWORD dwThreadID; // thread ID (-1 caller thread)
+ DWORD dwFlags; // reserved for future use, most be zero
+ } THREADNAME_INFO;
+
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = pszName;
+ info.dwThreadID = LookupThreadIDFromHandle( hThread );
+
+ if ( info.dwThreadID != 0 )
+ {
+ info.dwFlags = 0;
+
+ __try
+ {
+ RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
+ }
+ __except (EXCEPTION_CONTINUE_EXECUTION)
+ {
+ }
+ }
+ }
+#endif
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Used to thread LoadLibrary on the 360
+//-----------------------------------------------------------------------------
+static ThreadedLoadLibraryFunc_t s_ThreadedLoadLibraryFunc = 0;
+PLATFORM_INTERFACE void SetThreadedLoadLibraryFunc( ThreadedLoadLibraryFunc_t func )
+{
+ s_ThreadedLoadLibraryFunc = func;
+}
+
+PLATFORM_INTERFACE ThreadedLoadLibraryFunc_t GetThreadedLoadLibraryFunc()
+{
+ return s_ThreadedLoadLibraryFunc;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// CThreadSyncObject (note nothing uses this directly (I think) )
+//
+//-----------------------------------------------------------------------------
+
+
+#ifdef _PS3
+uint32_t CThreadSyncObject::m_bstaticMutexInitialized = false;
+uint32_t CThreadSyncObject::m_bstaticMutexInitializing = false;
+sys_lwmutex_t CThreadSyncObject::m_staticMutex;
+#endif
+
+
+CThreadSyncObject::CThreadSyncObject()
+#ifdef _WIN32
+ : m_hSyncObject( NULL ), m_bCreatedHandle(false)
+#elif defined(POSIX) && !defined(PLATFORM_PS3)
+ : m_bInitalized( false )
+#endif
+{
+#ifdef _PS3
+ //Do we nee to initialise the staticMutex?
+ if (m_bstaticMutexInitialized) return;
+
+ //If we are the first thread then create the mutex
+ if ( cellAtomicCompareAndSwap32(&m_bstaticMutexInitializing, false, true) == false )
+ {
+ sys_lwmutex_attribute_t mutexAttr;
+ sys_lwmutex_attribute_initialize( mutexAttr );
+ mutexAttr.attr_recursive = SYS_SYNC_RECURSIVE;
+ int err = sys_lwmutex_create( &m_staticMutex, &mutexAttr );
+ Assert(err == CELL_OK);
+ m_bstaticMutexInitialized = true;
+ }
+ else
+ {
+ //Another thread is already in the process of initialising the mutex, wait for it
+ while ( !m_bstaticMutexInitialized )
+ {
+ // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead.
+ sys_timer_usleep( 60 );
+ }
+ }
+#endif
+}
+
+//---------------------------------------------------------
+
+CThreadSyncObject::~CThreadSyncObject()
+{
+#ifdef _WIN32
+ if ( m_hSyncObject && m_bCreatedHandle )
+ {
+ if ( !CloseHandle(m_hSyncObject) )
+ {
+ Assert( 0 );
+ }
+ }
+#elif defined(POSIX) && !defined( PLATFORM_PS3 )
+ if ( m_bInitalized )
+ {
+ pthread_cond_destroy( &m_Condition );
+ pthread_mutex_destroy( &m_Mutex );
+ m_bInitalized = false;
+ }
+#endif
+}
+
+//---------------------------------------------------------
+
+bool CThreadSyncObject::operator!() const
+{
+#if PLATFORM_PS3
+ return m_bstaticMutexInitialized;
+#elif defined( _WIN32 )
+ return !m_hSyncObject;
+#elif defined(POSIX)
+ return !m_bInitalized;
+#endif
+}
+
+//---------------------------------------------------------
+
+void CThreadSyncObject::AssertUseable()
+{
+#ifdef THREADS_DEBUG
+#if PLATFORM_PS3
+ AssertMsg( m_bstaticMutexInitialized, "Thread synchronization object is unuseable" );
+#elif defined( _WIN32 )
+ AssertMsg( m_hSyncObject, "Thread synchronization object is unuseable" );
+#elif defined(POSIX)
+ AssertMsg( m_bInitalized, "Thread synchronization object is unuseable" );
+#endif
+#endif
+}
+
+//---------------------------------------------------------
+
+#if defined(_WIN32) || ( defined(POSIX) && !defined( _PS3 ) )
+bool CThreadSyncObject::Wait( uint32 dwTimeout )
+{
+#ifdef THREADS_DEBUG
+ AssertUseable();
+#endif
+#ifdef _WIN32
+ return ( WaitForSingleObject( m_hSyncObject, dwTimeout ) == WAIT_OBJECT_0 );
+#elif defined( POSIX ) && !defined( PLATFORM_PS3 )
+ pthread_mutex_lock( &m_Mutex );
+ bool bRet = false;
+ if ( m_cSet > 0 )
+ {
+ bRet = true;
+ m_bWakeForEvent = false;
+ }
+ else
+ {
+ volatile int ret = 0;
+
+ while ( !m_bWakeForEvent && ret != ETIMEDOUT )
+ {
+ struct timeval tv;
+ gettimeofday( &tv, NULL );
+ volatile struct timespec tm;
+
+ uint64 actualTimeout = dwTimeout;
+
+ if ( dwTimeout == TT_INFINITE && m_bManualReset )
+ actualTimeout = 10; // just wait 10 msec at most for manual reset events and loop instead
+
+ volatile uint64 nNanoSec = (uint64)tv.tv_usec*1000 + (uint64)actualTimeout*1000000;
+ tm.tv_sec = tv.tv_sec + nNanoSec /1000000000;
+ tm.tv_nsec = nNanoSec % 1000000000;
+
+ do
+ {
+ ret = pthread_cond_timedwait( &m_Condition, &m_Mutex, (const timespec *)&tm );
+ }
+ while( ret == EINTR );
+
+ bRet = ( ret == 0 );
+
+ if ( m_bManualReset )
+ {
+ if ( m_cSet )
+ break;
+ if ( dwTimeout == TT_INFINITE && ret == ETIMEDOUT )
+ ret = 0; // force the loop to spin back around
+ }
+ }
+
+ if ( bRet )
+ m_bWakeForEvent = false;
+ }
+ if ( !m_bManualReset && bRet )
+ m_cSet = 0;
+ pthread_mutex_unlock( &m_Mutex );
+ return bRet;
+#endif
+}
+#endif
+
+#ifdef _WIN32
+#pragma optimize( "", off ) // The compiler optimizer screws up the function below.
+#endif
+
+uint32 CThreadSyncObject::WaitForMultiple( int nObjects, CThreadSyncObject **ppObjects, bool bWaitAll, uint32 dwTimeout )
+{
+#if defined( _WIN32 )
+
+ CThreadSyncObject *pHandles = (CThreadSyncObject*)stackalloc( sizeof(CThreadSyncObject) * nObjects );
+ for ( int i=0; i < nObjects; i++ )
+ {
+ pHandles[i].m_hSyncObject = ppObjects[i]->m_hSyncObject;
+ }
+
+ return WaitForMultiple( nObjects, pHandles, bWaitAll, dwTimeout );
+
+#else
+
+ // TODO: Need a more efficient implementation of this.
+ uint32 dwStartTime = 0;
+
+ if ( dwTimeout != TT_INFINITE )
+ dwStartTime = Plat_MSTime();
+
+ // If bWaitAll = true, then we need to track which ones were triggered.
+ char *pWasTriggered = NULL;
+ int nTriggered = 0;
+ if ( bWaitAll )
+ {
+ pWasTriggered = (char*)stackalloc( nObjects );
+ memset( pWasTriggered, 0, nObjects );
+ }
+
+ while ( 1 )
+ {
+ for ( int i=0; i < nObjects; i++ )
+ {
+ if ( bWaitAll && pWasTriggered[i] )
+ continue;
+
+#ifdef _PS3
+ Assert( !"Not implemented!" );
+ if ( false )
+#else
+ if ( ppObjects[i]->Wait( 0 ) )
+#endif
+ {
+ ++nTriggered;
+ if ( bWaitAll )
+ {
+ if ( nTriggered == nObjects )
+ return 0;
+ else
+ pWasTriggered[i] = 1;
+ }
+ else
+ {
+ return i;
+ }
+ }
+ }
+
+ // Timeout?
+ if ( dwTimeout != TT_INFINITE )
+ {
+ if ( Plat_MSTime() - dwStartTime >= dwTimeout )
+ return TW_TIMEOUT;
+ }
+
+ ThreadSleep( 0 );
+ }
+
+#endif
+}
+
+#ifdef _WIN32
+#pragma optimize( "", on )
+#endif
+
+uint32 CThreadSyncObject::WaitForMultiple( int nObjects, CThreadSyncObject *pObjects, bool bWaitAll, uint32 dwTimeout )
+{
+#if defined(_WIN32 )
+
+ HANDLE *pHandles = (HANDLE*)stackalloc( sizeof(HANDLE) * nObjects );
+ for ( int i=0; i < nObjects; i++ )
+ {
+ pHandles[i] = pObjects[i].m_hSyncObject;
+ }
+
+ DWORD ret = WaitForMultipleObjects( nObjects, pHandles, bWaitAll, dwTimeout );
+ if ( ret == WAIT_TIMEOUT )
+ return TW_TIMEOUT;
+ else if ( ret >= WAIT_OBJECT_0 && (ret-WAIT_OBJECT_0) < (uint32)nObjects )
+ return (int)(ret - WAIT_OBJECT_0);
+ else if ( ret >= WAIT_ABANDONED_0 && (ret - WAIT_ABANDONED_0) < (uint32)nObjects )
+ Error( "Unhandled WAIT_ABANDONED in WaitForMultipleObjects" );
+ else if ( ret == WAIT_FAILED )
+ return TW_FAILED;
+ else
+ Error( "Unknown return value (%lu) from WaitForMultipleObjects", ret );
+
+ // We'll never get here..
+ return 0;
+
+#else
+
+ CThreadSyncObject **ppObjects = (CThreadSyncObject**)stackalloc( sizeof( CThreadSyncObject* ) * nObjects );
+ for ( int i=0; i < nObjects; i++ )
+ {
+ ppObjects[i] = &pObjects[i];
+ }
+
+ return WaitForMultiple( nObjects, ppObjects, bWaitAll, dwTimeout );
+
+#endif
+}
+
+// To implement these, I need to check that casts are safe
+uint32 CThreadEvent::WaitForMultiple( int nObjects, CThreadEvent *pObjects, bool bWaitAll, uint32 dwTimeout )
+{
+ // If data ever gets added to CThreadEvent, then we need a different implementation.
+#ifdef _PS3
+ CThreadEvent **ppObjects = (CThreadEvent**)stackalloc( sizeof( CThreadEvent* ) * nObjects );
+ for ( int i=0; i < nObjects; i++ )
+ {
+ ppObjects[i] = &pObjects[i];
+ }
+ return WaitForMultipleObjects( nObjects, ppObjects, bWaitAll, dwTimeout );
+#else
+ COMPILE_TIME_ASSERT( sizeof( CThreadSyncObject ) == 0 || sizeof( CThreadEvent ) == sizeof( CThreadSyncObject ) );
+ return CThreadSyncObject::WaitForMultiple( nObjects, (CThreadSyncObject*)pObjects, bWaitAll, dwTimeout );
+#endif
+}
+
+
+uint32 CThreadEvent::WaitForMultiple( int nObjects, CThreadEvent **ppObjects, bool bWaitAll, uint32 dwTimeout )
+{
+#ifdef _PS3
+ return WaitForMultipleObjects( nObjects, ppObjects, bWaitAll, dwTimeout );
+#else
+ // If data ever gets added to CThreadEvent, then we need a different implementation.
+ COMPILE_TIME_ASSERT( sizeof( CThreadSyncObject )== 0 || sizeof( CThreadEvent ) == sizeof( CThreadSyncObject ) );
+ return CThreadSyncObject::WaitForMultiple( nObjects, (CThreadSyncObject**)ppObjects, bWaitAll, dwTimeout );
+#endif
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+CThreadEvent::CThreadEvent( bool bManualReset )
+{
+#ifdef _WIN32
+ m_hSyncObject = CreateEvent( NULL, bManualReset, FALSE, NULL );
+ m_bCreatedHandle = true;
+ AssertMsg1(m_hSyncObject, "Failed to create event (error 0x%x)", GetLastError() );
+#elif defined( _PS3 )
+
+ m_bManualReset = bManualReset;
+ m_bSet = 0;
+ m_bInitalized = false;
+ m_numWaitingThread = 0;
+
+ // set up linked list of wait objects
+
+ memset(&m_waitObjects[0], 0, sizeof(m_waitObjects));
+ m_pWaitObjectsList = &m_waitObjects[0];
+ m_pWaitObjectsPool = &m_waitObjects[1];
+
+ for (int i = 2; i < CTHREADEVENT_MAX_WAITING_THREADS + 2; i++)
+ {
+ LLLinkNode(m_pWaitObjectsPool, &m_waitObjects[i]);
+ }
+#elif defined( POSIX )
+ pthread_mutexattr_t Attr;
+ pthread_mutexattr_init( &Attr );
+ pthread_mutex_init( &m_Mutex, &Attr );
+ pthread_mutexattr_destroy( &Attr );
+ pthread_cond_init( &m_Condition, NULL );
+ m_bInitalized = true;
+ m_cSet = 0;
+ m_bWakeForEvent = false;
+ m_bManualReset = bManualReset;
+#else
+#error "Implement me"
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+#ifdef _PS3
+
+//
+// linked list functionality
+//
+
+//-----------------------------------------------------------------------------
+// Purpose: Linked list implementation
+//-----------------------------------------------------------------------------
+
+CThreadEventWaitObject* CThreadEvent::LLUnlinkNode(CThreadEventWaitObject *node)
+{
+ // <sergiy> Note: if you have a null-access crash here, it may mean that CTHREADEVENT_MAX_WAITING_THREADS is not high enough
+ // and the linked list pool is simply exhausted
+ node->m_pPrev->m_pNext = node->m_pNext;
+ if (node->m_pNext) node->m_pNext->m_pPrev = node->m_pPrev;
+ node->m_pNext = node->m_pPrev = NULL;
+
+ return node;
+}
+
+CThreadEventWaitObject* CThreadEvent::LLLinkNode(CThreadEventWaitObject* list, CThreadEventWaitObject *node)
+{
+ node->m_pNext = list->m_pNext;
+ if (node->m_pNext)
+ {
+ node->m_pNext->m_pPrev = node;
+ }
+
+ list->m_pNext = node;
+ node->m_pPrev = list;
+
+ return node;
+}
+
+//-----------------------------------------------------------------------------
+// Helper function to atomically write index into destination and set semaphore
+// This is used by WaitForMultipleObjects(WAIT_ANY) because once the semaphore
+// is set, the waiting thread also needs to know which event triggered it
+// We do NOT need this to be atomic because if a number of events fire it doesn't
+// matter which one of these we pick
+//-----------------------------------------------------------------------------
+void CThreadEventWaitObject::Set()
+{
+ *m_pFlag = m_index;
+ sys_semaphore_post(*m_pSemaphore, 1);
+}
+
+//
+// CThreadEvent::RegisterWaitingThread
+//
+void CThreadEvent::RegisterWaitingThread(sys_semaphore_t *pSemaphore, int index, int *flag)
+{
+ sys_lwmutex_lock(&m_staticMutex, 0);
+
+ // if we are already set, then signal this semaphore
+ if (m_bSet)
+ {
+ CThreadEventWaitObject waitObject;
+ waitObject.Init(pSemaphore, index, flag);
+ waitObject.Set();
+
+ if (!m_bManualReset)
+ {
+ m_bSet = false;
+ }
+ }
+ else
+ {
+ if (!m_pWaitObjectsPool->m_pNext)
+ {
+ DEBUG_ERROR("CThreadEvent: Ran out of events; cannot register waiting thread\n");
+ }
+
+ // add this semaphore to linked list - can be added more than once it doesn't matter
+
+ CThreadEventWaitObject *pWaitObject = LLUnlinkNode(m_pWaitObjectsPool->m_pNext);
+
+ pWaitObject->Init(pSemaphore, index, flag);
+
+ LLLinkNode(m_pWaitObjectsList, pWaitObject);
+ }
+
+ sys_lwmutex_unlock(&m_staticMutex);
+}
+
+//
+// CThreadEvent::UnregisterWaitingThread
+//
+void CThreadEvent::UnregisterWaitingThread(sys_semaphore_t *pSemaphore)
+{
+ // remove all instances of this semaphore from linked list
+
+ sys_lwmutex_lock(&m_staticMutex, 0);
+
+ CThreadEventWaitObject *pWaitObject = m_pWaitObjectsList->m_pNext;
+
+ while (pWaitObject)
+ {
+ CThreadEventWaitObject *pNext = pWaitObject->m_pNext;
+
+ if (pWaitObject->m_pSemaphore == pSemaphore)
+ {
+ LLUnlinkNode(pWaitObject);
+ LLLinkNode(m_pWaitObjectsPool, pWaitObject);
+ }
+
+ pWaitObject = pNext;
+ }
+
+ sys_lwmutex_unlock(&m_staticMutex);
+}
+
+#endif // _PS3
+
+
+#ifdef PLATFORM_WINDOWS
+ CThreadEvent::CThreadEvent( const char *name, bool initialState, bool bManualReset )
+ {
+ m_hSyncObject = CreateEvent( NULL, bManualReset, (BOOL) initialState, name );
+ AssertMsg1( m_hSyncObject, "Failed to create event (error 0x%x)", GetLastError() );
+ }
+
+
+ NamedEventResult_t CThreadEvent::CheckNamedEvent( const char *name, uint32 dwTimeout )
+ {
+ HANDLE eHandle = OpenEvent( SYNCHRONIZE, FALSE, name );
+
+ if ( eHandle == NULL ) return TT_EventDoesntExist;
+
+ DWORD result = WaitForSingleObject( eHandle, dwTimeout );
+
+ return ( result == WAIT_OBJECT_0 ) ? TT_EventSignaled : TT_EventNotSignaled;
+ }
+#endif
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+
+//---------------------------------------------------------
+
+bool CThreadEvent::Set()
+{
+//////////////////////////////////////////////////////////////
+#ifndef NEW_WAIT_FOR_MULTIPLE_OBJECTS
+//////////////////////////////////////////////////////////////
+ AssertUseable();
+#ifdef _WIN32
+ return ( SetEvent( m_hSyncObject ) != 0 );
+#elif defined( _PS3 )
+
+ sys_lwmutex_lock(&m_staticMutex, 0);
+
+ if (m_bManualReset)
+ {
+ //Mark event as set
+ m_bSet = true;
+
+ //If any threads are already waiting then signal them to run
+ if (m_bInitalized)
+ {
+ int err = sys_semaphore_post( m_Semaphore, m_numWaitingThread);
+ Assert(err == CELL_OK);
+ }
+ }
+ else
+ {
+ //If any threads are already waiting then signal ONE to run, else signal next to run
+
+ if (m_numWaitingThread>0)
+ {
+ int err = sys_semaphore_post( m_Semaphore, 1);
+ Assert(err == CELL_OK);
+ }
+ else
+ {
+ m_bSet=true;
+ }
+ }
+
+ sys_lwmutex_unlock(&m_staticMutex);
+
+ return true;
+
+
+#elif defined(POSIX)
+ pthread_mutex_lock( &m_Mutex );
+ m_cSet = 1;
+ m_bWakeForEvent = true;
+ int ret = pthread_cond_signal( &m_Condition );
+ pthread_mutex_unlock( &m_Mutex );
+ return ret == 0;
+#endif
+
+
+//////////////////////////////////////////////////////////////
+#else // NEW_WAIT_FOR_MULTIPLE_OBJECTS
+//////////////////////////////////////////////////////////////
+
+ sys_lwmutex_lock(&m_staticMutex, 0);
+
+ //Mark event as set
+ m_bSet = true;
+
+ // signal registered semaphores
+ while (m_pWaitObjectsList->m_pNext)
+ {
+ CThreadEventWaitObject *pWaitObject = LLUnlinkNode(m_pWaitObjectsList->m_pNext);
+
+ pWaitObject->Set();
+
+ LLLinkNode(m_pWaitObjectsPool, pWaitObject);
+
+ if (!m_bManualReset)
+ {
+ m_bSet = false;
+ break;
+ }
+ }
+
+ sys_lwmutex_unlock(&m_staticMutex);
+
+ return true;
+
+//////////////////////////////////////////////////////////////
+#endif // NEW_WAIT_FOR_MULTIPLE_OBJECTS
+//////////////////////////////////////////////////////////////
+}
+
+//---------------------------------------------------------
+
+bool CThreadEvent::Reset()
+{
+#ifdef THREADS_DEBUG
+ AssertUseable();
+#endif
+#ifdef _WIN32
+ return ( ResetEvent( m_hSyncObject ) != 0 );
+#elif defined( _PS3 )
+
+ //Just mark us as no longer signaled
+ m_bSet = 0;
+
+ return true;
+#elif defined(POSIX)
+ pthread_mutex_lock( &m_Mutex );
+ m_cSet = 0;
+ m_bWakeForEvent = false;
+ pthread_mutex_unlock( &m_Mutex );
+ return true;
+#endif
+}
+
+//---------------------------------------------------------
+
+bool CThreadEvent::Check()
+{
+ #ifdef _PS3
+ return m_bSet; // Please, use for debugging only!
+ #endif
+#ifdef THREADS_DEBUG
+ AssertUseable();
+#endif
+ return Wait( 0 );
+}
+
+
+
+bool CThreadEvent::Wait( uint32 dwTimeout )
+{
+//////////////////////////////////////////////////////////////
+#ifndef NEW_WAIT_FOR_MULTIPLE_OBJECTS
+//////////////////////////////////////////////////////////////
+
+
+#if defined( _WIN32 ) || ( defined( POSIX ) && !defined( _PS3 ) )
+ return CThreadSyncObject::Wait( dwTimeout );
+#elif defined( _PS3 )
+
+ {
+
+ if (dwTimeout == 0)
+ {
+ //If timeout is 0 then just test it now (and reset it if manual )
+ if (m_bSet)
+ {
+ if ( !m_bManualReset ) m_bSet=false;
+ return true;
+ }
+ return false;
+ }
+
+ if (!AddWaitingThread())
+ {
+ //Waiting thread NOT added because m_bSet was already set
+ if ( !m_bManualReset ) m_bSet=false;
+ return true;
+ }
+
+ uint32 timeout;
+ int countTimeout = 0;
+ int ret = ETIMEDOUT;
+ while ( timeout=MIN(1, dwTimeout) )
+ {
+ // on the PS3, "infinite timeout" is specified by zero, not
+ // 0xFFFFFFFF, so we need to perform that ternary here.
+#error Untested code:
+ ret = sys_semaphore_wait( m_Semaphore, timeout == TT_INFINITE ? 0 : timeout * 1000 );
+ Assert( (ret == CELL_OK) || (ret == ETIMEDOUT) );
+
+ if ( ret == CELL_OK )
+ break;
+
+ dwTimeout -= timeout;
+ countTimeout++;
+ if (countTimeout > 30)
+ {
+ printf("WARNING: possible deadlock in CThreadEvent::Wait() !!!\n");
+ }
+ }
+
+ RemoveWaitingThread();
+
+ if ( !m_bManualReset ) m_bSet=false;
+
+ return ret == CELL_OK;
+ }
+
+#endif
+
+//////////////////////////////////////////////////////////////
+#else // NEW_WAIT_FOR_MULTIPLE_OBJECTS
+//////////////////////////////////////////////////////////////
+
+
+ CThreadEvent *pThis = this;
+ DWORD res = WaitForMultipleObjects(1, &pThis, true, dwTimeout);
+ return res == WAIT_OBJECT_0;
+
+
+//////////////////////////////////////////////////////////////
+#endif // NEW_WAIT_FOR_MULTIPLE_OBJECTS
+//////////////////////////////////////////////////////////////
+}
+
+#ifdef _WIN32
+//-----------------------------------------------------------------------------
+//
+// CThreadSemaphore
+//
+// To get Posix implementation, try http://www-128.ibm.com/developerworks/eserver/library/es-win32linux-sem.html
+//
+//-----------------------------------------------------------------------------
+
+CThreadSemaphore::CThreadSemaphore( int32 initialValue, int32 maxValue )
+{
+#ifdef _WIN32
+ if ( maxValue )
+ {
+ AssertMsg( maxValue > 0, "Invalid max value for semaphore" );
+ AssertMsg( initialValue >= 0 && initialValue <= maxValue, "Invalid initial value for semaphore" );
+
+ m_hSyncObject = CreateSemaphore( NULL, initialValue, maxValue, NULL );
+
+ AssertMsg1(m_hSyncObject, "Failed to create semaphore (error 0x%x)", GetLastError());
+ }
+ else
+ {
+ m_hSyncObject = NULL;
+ }
+#elif defined( _PS3 )
+ if ( maxValue )
+ {
+ m_sema_max_val = maxValue;
+ m_semaCount = initialValue;
+ }
+#endif
+}
+
+
+#ifdef _PS3
+//---------------------------------------------------------
+
+bool CThreadSemaphore::AddWaitingThread()
+{
+ bool result;
+
+ sys_lwmutex_lock(&m_staticMutex, 0);
+
+ if (cellAtomicTestAndDecr32(&m_semaCount) > 0)
+ {
+ result=false;
+ }
+ else
+ {
+ result=true;
+ m_numWaitingThread++;
+
+ if ( m_numWaitingThread == 1 )
+ {
+ sys_semaphore_attribute_t semAttr;
+ sys_semaphore_attribute_initialize( semAttr );
+ Assert(m_semaCount == 0);
+ int err = sys_semaphore_create( &m_Semaphore, &semAttr, 0, m_sema_max_val );
+ Assert( err == CELL_OK );
+ m_bInitalized = true;
+ }
+ }
+
+ sys_lwmutex_unlock(&m_staticMutex);
+ return result;
+}
+
+void CThreadSemaphore::RemoveWaitingThread()
+{
+ sys_lwmutex_lock(&m_staticMutex, 0);
+
+ m_numWaitingThread--;
+
+ if ( m_numWaitingThread == 0)
+ {
+ int err = sys_semaphore_destroy( m_Semaphore );
+ Assert( err == CELL_OK );
+ m_bInitalized = false;
+ }
+
+ sys_lwmutex_unlock(&m_staticMutex);
+}
+
+#endif
+
+#ifdef _PS3
+
+bool CThreadSemaphore::Wait( uint32 dwTimeout )
+{
+#ifdef THREADS_DEBUG
+ AssertUseable();
+#endif
+
+
+#ifndef NO_THREAD_SYNC
+ if (!AddWaitingThread())
+ {
+ //Waiting thread NOT added because semaphore was already in a signaled state
+ return true;
+ }
+
+ int ret = sys_semaphore_wait( m_Semaphore, dwTimeout == TT_INFINITE ? 0 : dwTimeout * 1000 );
+ Assert( (ret == CELL_OK) || (ret == ETIMEDOUT) );
+
+ RemoveWaitingThread();
+
+ int old = cellAtomicDecr32(&m_semaCount);
+ Assert(old>0);
+#else
+ int ret = CELL_OK;
+#endif
+
+ // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead.
+ sys_timer_usleep( 60 );
+
+
+
+ return ret == CELL_OK;
+}
+
+#endif
+
+//---------------------------------------------------------
+
+bool CThreadSemaphore::Release( int32 releaseCount, int32 *pPreviousCount )
+{
+#ifdef THRDTOOL_DEBUG
+ AssertUseable();
+#endif
+#ifdef _WIN32
+ return ( ReleaseSemaphore( m_hSyncObject, releaseCount, (LPLONG)pPreviousCount ) != 0 );
+#elif defined( _PS3 )
+
+#ifndef NO_THREAD_SYNC
+
+ if (m_bInitalized)
+ {
+ sys_semaphore_value_t previousVal;
+ sys_semaphore_get_value( m_Semaphore, &previousVal );
+
+ cellAtomicAdd32(&m_semaCount, releaseCount);
+
+ *pPreviousCount = previousVal;
+
+ int err = sys_semaphore_post( m_Semaphore, releaseCount );
+ Assert(err == CELL_OK);
+ }
+
+#endif
+
+ return true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+CThreadFullMutex::CThreadFullMutex( bool bEstablishInitialOwnership, const char *pszName )
+{
+ m_hSyncObject = CreateMutex( NULL, bEstablishInitialOwnership, pszName );
+
+ AssertMsg1( m_hSyncObject, "Failed to create mutex (error 0x%x)", GetLastError() );
+}
+
+//---------------------------------------------------------
+
+bool CThreadFullMutex::Release()
+{
+#ifdef THRDTOOL_DEBUG
+ AssertUseable();
+#endif
+ return ( ReleaseMutex( m_hSyncObject ) != 0 );
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+#if ( defined(WIN32) || defined(OSX) ) && !defined(PLATFORM_PS3)
+namespace GenericThreadLocals
+{
+CThreadLocalBase::CThreadLocalBase()
+{
+#ifdef _WIN32
+ m_index = TlsAlloc();
+ AssertMsg( m_index != 0xFFFFFFFF, "Bad thread local" );
+ if ( m_index == 0xFFFFFFFF )
+ Error( "Out of thread local storage!\n" );
+#elif defined(POSIX)
+ if ( pthread_key_create( (pthread_key_t *)&m_index, NULL ) != 0 )
+ Error( "Out of thread local storage!\n" );
+#endif
+}
+
+//---------------------------------------------------------
+
+CThreadLocalBase::~CThreadLocalBase()
+{
+#ifdef _WIN32
+ if ( m_index != 0xFFFFFFFF )
+ TlsFree( m_index );
+ m_index = 0xFFFFFFFF;
+#elif defined(POSIX)
+ pthread_key_delete( m_index );
+#endif
+}
+
+//---------------------------------------------------------
+
+void * CThreadLocalBase::Get() const
+{
+#ifdef _WIN32
+ if ( m_index != 0xFFFFFFFF )
+ return TlsGetValue( m_index );
+ AssertMsg( 0, "Bad thread local" );
+ return NULL;
+#elif defined(POSIX)
+ void *value = pthread_getspecific( m_index );
+ return value;
+#endif
+}
+
+//---------------------------------------------------------
+
+void CThreadLocalBase::Set( void *value )
+{
+#ifdef _WIN32
+ if (m_index != 0xFFFFFFFF)
+ TlsSetValue(m_index, value);
+ else
+ AssertMsg( 0, "Bad thread local" );
+#elif defined(POSIX)
+ if ( pthread_setspecific( m_index, value ) != 0 )
+ AssertMsg( 0, "Bad thread local" );
+#endif
+}
+} // namespace GenericThreadLocals
+#endif // ( defined(WIN32) || defined(POSIX) ) && !defined(PLATFORM_PS3)
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+
+#ifdef MSVC
+//#ifdef _X360
+#define TO_INTERLOCK_PARAM(p) ((volatile long *)p)
+#define TO_INTERLOCK_PTR_PARAM(p) ((void **)p)
+//#else
+//#define TO_INTERLOCK_PARAM(p) (p)
+//#define TO_INTERLOCK_PTR_PARAM(p) (p)
+//#endif
+
+#if !defined(USE_INTRINSIC_INTERLOCKED) && !defined(_X360)
+int32 ThreadInterlockedIncrement( int32 volatile *pDest )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedIncrement( TO_INTERLOCK_PARAM(pDest) );
+}
+
+int32 ThreadInterlockedDecrement( int32 volatile *pDest )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedDecrement( TO_INTERLOCK_PARAM(pDest) );
+}
+
+int32 ThreadInterlockedExchange( int32 volatile *pDest, int32 value )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedExchange( TO_INTERLOCK_PARAM(pDest), value );
+}
+
+int32 ThreadInterlockedExchangeAdd( int32 volatile *pDest, int32 value )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedExchangeAdd( TO_INTERLOCK_PARAM(pDest), value );
+}
+
+int32 ThreadInterlockedCompareExchange( int32 volatile *pDest, int32 value, int32 comperand )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedCompareExchange( TO_INTERLOCK_PARAM(pDest), value, comperand );
+}
+
+bool ThreadInterlockedAssignIf( int32 volatile *pDest, int32 value, int32 comperand )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+
+#if !(defined(_WIN64) || defined (_X360))
+ __asm
+ {
+ mov eax,comperand
+ mov ecx,pDest
+ mov edx,value
+ lock cmpxchg [ecx],edx
+ mov eax,0
+ setz al
+ }
+#else
+ return ( InterlockedCompareExchange( TO_INTERLOCK_PARAM(pDest), value, comperand ) == comperand );
+#endif
+}
+
+#endif
+
+#if !defined( USE_INTRINSIC_INTERLOCKED ) || defined( _WIN64 )
+void *ThreadInterlockedExchangePointer( void * volatile *pDest, void *value )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedExchangePointer( TO_INTERLOCK_PTR_PARAM(pDest), value );
+}
+
+void *ThreadInterlockedCompareExchangePointer( void * volatile *pDest, void *value, void *comperand )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedCompareExchangePointer( TO_INTERLOCK_PTR_PARAM(pDest), value, comperand );
+}
+
+bool ThreadInterlockedAssignPointerIf( void * volatile *pDest, void *value, void *comperand )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+#if !(defined(_WIN64) || defined (_X360))
+ __asm
+ {
+ mov eax,comperand
+ mov ecx,pDest
+ mov edx,value
+ lock cmpxchg [ecx],edx
+ mov eax,0
+ setz al
+ }
+#else
+ return ( InterlockedCompareExchangePointer( TO_INTERLOCK_PTR_PARAM(pDest), value, comperand ) == comperand );
+#endif
+}
+#endif
+
+int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, int64 comperand )
+{
+ Assert( (size_t)pDest % 8 == 0 );
+
+#if defined(_WIN64) || defined (_X360)
+ return InterlockedCompareExchange64( pDest, value, comperand );
+#else
+ __asm
+ {
+ lea esi,comperand;
+ lea edi,value;
+
+ mov eax,[esi];
+ mov edx,4[esi];
+ mov ebx,[edi];
+ mov ecx,4[edi];
+ mov esi,pDest;
+ lock CMPXCHG8B [esi];
+ }
+#endif
+}
+
+bool ThreadInterlockedAssignIf64(volatile int64 *pDest, int64 value, int64 comperand )
+{
+ Assert( (size_t)pDest % 8 == 0 );
+
+#if defined(_X360) || defined(_WIN64)
+ return ( ThreadInterlockedCompareExchange64( pDest, value, comperand ) == comperand );
+#else
+ __asm
+ {
+ lea esi,comperand;
+ lea edi,value;
+
+ mov eax,[esi];
+ mov edx,4[esi];
+ mov ebx,[edi];
+ mov ecx,4[edi];
+ mov esi,pDest;
+ lock CMPXCHG8B [esi];
+ mov eax,0;
+ setz al;
+ }
+#endif
+}
+
+#elif defined(GNUC)
+
+#ifdef OSX
+#include <libkern/OSAtomic.h>
+#endif
+
+
+long ThreadInterlockedIncrement( long volatile *pDest )
+{
+ return __sync_fetch_and_add( pDest, 1 ) + 1;
+}
+
+long ThreadInterlockedDecrement( long volatile *pDest )
+{
+ return __sync_fetch_and_sub( pDest, 1 ) - 1;
+}
+
+long ThreadInterlockedExchange( long volatile *pDest, long value )
+{
+ return __sync_lock_test_and_set( pDest, value );
+}
+
+long ThreadInterlockedExchangeAdd( long volatile *pDest, long value )
+{
+ return __sync_fetch_and_add( pDest, value );
+}
+
+long ThreadInterlockedCompareExchange( long volatile *pDest, long value, long comperand )
+{
+ return __sync_val_compare_and_swap( pDest, comperand, value );
+}
+
+bool ThreadInterlockedAssignIf( long volatile *pDest, long value, long comperand )
+{
+ return __sync_bool_compare_and_swap( pDest, comperand, value );
+}
+
+#if !defined( USE_INTRINSIC_INTERLOCKED )
+void *ThreadInterlockedExchangePointer( void * volatile *pDest, void *value )
+{
+ return __sync_lock_test_and_set( pDest, value );
+}
+
+void *ThreadInterlockedCompareExchangePointer( void *volatile *pDest, void *value, void *comperand )
+{
+ return __sync_val_compare_and_swap( pDest, comperand, value );
+}
+
+bool ThreadInterlockedAssignPointerIf( void * volatile *pDest, void *value, void *comperand )
+{
+ return __sync_bool_compare_and_swap( pDest, comperand, value );
+}
+#endif
+
+int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, int64 comperand )
+{
+#if defined(OSX)
+ int64 retVal = *pDest;
+ if ( OSAtomicCompareAndSwap64( comperand, value, pDest ) )
+ retVal = *pDest;
+
+ return retVal;
+#else
+ return __sync_val_compare_and_swap( pDest, comperand, value );
+#endif
+}
+
+bool ThreadInterlockedAssignIf64( int64 volatile * pDest, int64 value, int64 comperand )
+{
+ return __sync_bool_compare_and_swap( pDest, comperand, value );
+}
+
+
+#elif defined( _PS3 )
+
+// This is defined in the header!
+
+#else
+// This will perform horribly,
+#error "Falling back to mutexed interlocked operations, you really don't have intrinsics you can use?"ß
+CThreadMutex g_InterlockedMutex;
+
+long ThreadInterlockedIncrement( long volatile *pDest )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ return ++(*pDest);
+}
+
+long ThreadInterlockedDecrement( long volatile *pDest )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ return --(*pDest);
+}
+
+long ThreadInterlockedExchange( long volatile *pDest, long value )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ long retVal = *pDest;
+ *pDest = value;
+ return retVal;
+}
+
+void *ThreadInterlockedExchangePointer( void * volatile *pDest, void *value )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ void *retVal = *pDest;
+ *pDest = value;
+ return retVal;
+}
+
+long ThreadInterlockedExchangeAdd( long volatile *pDest, long value )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ long retVal = *pDest;
+ *pDest += value;
+ return retVal;
+}
+
+long ThreadInterlockedCompareExchange( long volatile *pDest, long value, long comperand )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ long retVal = *pDest;
+ if ( *pDest == comperand )
+ *pDest = value;
+ return retVal;
+}
+
+void *ThreadInterlockedCompareExchangePointer( void * volatile *pDest, void *value, void *comperand )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ void *retVal = *pDest;
+ if ( *pDest == comperand )
+ *pDest = value;
+ return retVal;
+}
+
+
+int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, int64 comperand )
+{
+ Assert( (size_t)pDest % 8 == 0 );
+ AUTO_LOCK( g_InterlockedMutex );
+ int64 retVal = *pDest;
+ if ( *pDest == comperand )
+ *pDest = value;
+ return retVal;
+}
+
+#endif
+
+int64 ThreadInterlockedExchange64( int64 volatile *pDest, int64 value )
+{
+ Assert( (size_t)pDest % 8 == 0 );
+ int64 Old;
+
+ do
+ {
+ Old = *pDest;
+ } while (ThreadInterlockedCompareExchange64(pDest, value, Old) != Old);
+
+ return Old;
+}
+
+
+//-----------------------------------------------------------------------------
+
+#if defined(_WIN32) && defined(THREAD_PROFILER)
+void ThreadNotifySyncNoop(void *p) {}
+
+#define MAP_THREAD_PROFILER_CALL( from, to ) \
+ void from(void *p) \
+ { \
+ static CDynamicFunction<void (*)(void *)> dynFunc( "libittnotify.dll", #to, ThreadNotifySyncNoop ); \
+ (*dynFunc)(p); \
+ }
+
+MAP_THREAD_PROFILER_CALL( ThreadNotifySyncPrepare, __itt_notify_sync_prepare );
+MAP_THREAD_PROFILER_CALL( ThreadNotifySyncCancel, __itt_notify_sync_cancel );
+MAP_THREAD_PROFILER_CALL( ThreadNotifySyncAcquired, __itt_notify_sync_acquired );
+MAP_THREAD_PROFILER_CALL( ThreadNotifySyncReleasing, __itt_notify_sync_releasing );
+
+#endif
+
+//-----------------------------------------------------------------------------
+//
+// CThreadMutex
+//
+//-----------------------------------------------------------------------------
+
+#ifdef _PS3
+CThreadMutex::CThreadMutex()
+{
+ // sys_mutex with recursion enabled is like a win32 critical section
+ sys_mutex_attribute_t mutexAttr;
+ sys_mutex_attribute_initialize( mutexAttr );
+ mutexAttr.attr_recursive = SYS_SYNC_RECURSIVE;
+ sys_mutex_create( &m_Mutex, &mutexAttr );
+}
+CThreadMutex::~CThreadMutex()
+{
+ sys_mutex_destroy( m_Mutex );
+}
+#elif !defined( POSIX )
+CThreadMutex::CThreadMutex()
+{
+#ifdef THREAD_MUTEX_TRACING_ENABLED
+ memset( &m_CriticalSection, 0, sizeof(m_CriticalSection) );
+#endif
+ InitializeCriticalSectionAndSpinCount((CRITICAL_SECTION *)&m_CriticalSection, 4000);
+#ifdef THREAD_MUTEX_TRACING_SUPPORTED
+ // These need to be initialized unconditionally in case mixing release & debug object modules
+ // Lock and unlock may be emitted as COMDATs, in which case may get spurious output
+ m_currentOwnerID = m_lockCount = 0;
+ m_bTrace = false;
+#endif
+}
+
+CThreadMutex::~CThreadMutex()
+{
+ DeleteCriticalSection((CRITICAL_SECTION *)&m_CriticalSection);
+}
+#endif // !POSIX
+
+#ifdef IS_WINDOWS_PC
+typedef BOOL (WINAPI*TryEnterCriticalSectionFunc_t)(LPCRITICAL_SECTION);
+static CDynamicFunction<TryEnterCriticalSectionFunc_t> DynTryEnterCriticalSection( "Kernel32.dll", "TryEnterCriticalSection" );
+#elif defined( _X360 )
+#define DynTryEnterCriticalSection TryEnterCriticalSection
+#endif
+
+bool CThreadMutex::TryLock()
+{
+#if defined( MSVC )
+#ifdef THREAD_MUTEX_TRACING_ENABLED
+ uint thisThreadID = ThreadGetCurrentId();
+ if ( m_bTrace && m_currentOwnerID && ( m_currentOwnerID != thisThreadID ) )
+ Msg( "Thread %u about to try-wait for lock %x owned by %u\n", ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID );
+#endif
+ if ( DynTryEnterCriticalSection != NULL )
+ {
+ if ( (*DynTryEnterCriticalSection )( (CRITICAL_SECTION *)&m_CriticalSection ) != FALSE )
+ {
+#ifdef THREAD_MUTEX_TRACING_ENABLED
+ if (m_lockCount == 0)
+ {
+ // we now own it for the first time. Set owner information
+ m_currentOwnerID = thisThreadID;
+ if ( m_bTrace )
+ Msg( "Thread %u now owns lock 0x%x\n", m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection );
+ }
+ m_lockCount++;
+#endif
+ return true;
+ }
+ return false;
+ }
+ Lock();
+ return true;
+#elif defined( _PS3 )
+
+#ifndef NO_THREAD_SYNC
+ if ( sys_mutex_trylock( m_Mutex ) == CELL_OK )
+#endif
+
+ return true;
+
+ return false; // ?? moved from EA code
+
+#elif defined( POSIX )
+ return pthread_mutex_trylock( &m_Mutex ) == 0;
+#else
+#error "Implement me!"
+ return true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+//
+// CThreadFastMutex
+//
+//-----------------------------------------------------------------------------
+
+#ifdef THREAD_FAST_MUTEX_TIMINGS
+// This is meant to be used in combination with breakpoints and in-debugee, so we turn the optimizer off
+#pragma optimize( "", off )
+CThreadFastMutex *g_pIgnoredMutexes[256]; // Ignore noisy non-problem mutex. Probably could be an array. Right now needed only for sound thread
+float g_MutexTimingTolerance = 5;
+bool g_bMutexTimingOutput;
+
+void TrapMutexTimings( uint32 probableBlocker, uint32 thisThread, volatile CThreadFastMutex *pMutex, CFastTimer &spikeTimer, CAverageCycleCounter &sleepTimer )
+{
+ spikeTimer.End();
+ if ( spikeTimer.GetDuration().GetMillisecondsF() > g_MutexTimingTolerance )
+ {
+ bool bIgnore = false;
+ for ( int j = 0; j < ARRAYSIZE( g_pIgnoredMutexes ) && g_pIgnoredMutexes[j]; j++ )
+ {
+ if ( g_pIgnoredMutexes[j] == pMutex )
+ {
+ bIgnore = true;
+ break;
+ }
+ }
+
+ if ( !bIgnore && spikeTimer.GetDuration().GetMillisecondsF() < 100 )
+ {
+ volatile float FastMutexDuration = spikeTimer.GetDuration().GetMillisecondsF();
+ volatile float average = sleepTimer.GetAverageMilliseconds();
+ volatile float peak = sleepTimer.GetPeakMilliseconds(); volatile int xx = 6;
+ if ( g_bMutexTimingOutput )
+ {
+ char szBuf[256];
+ Msg( "M (%.8x): [%.8x <-- %.8x] (%f,%f,%f)\n", pMutex, probableBlocker, thisThread, FastMutexDuration, average, peak );
+ }
+ }
+ }
+}
+
+#else
+#define TrapMutexTimings( a, b, c, d, e ) ((void)0)
+#endif
+
+//-------------------------------------
+
+#define THREAD_SPIN (8*1024)
+
+void CThreadFastMutex::Lock( const uint32 threadId, unsigned nSpinSleepTime ) volatile
+{
+#ifdef THREAD_FAST_MUTEX_TIMINGS
+ CAverageCycleCounter sleepTimer;
+ CFastTimer spikeTimer;
+ uint32 currentOwner = m_ownerID;
+ spikeTimer.Start();
+ sleepTimer.Init();
+#endif
+ int i;
+ if ( nSpinSleepTime != TT_INFINITE )
+ {
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLock( threadId ) )
+ {
+ TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
+ return;
+ }
+ ThreadPause();
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLock( threadId ) )
+ {
+ TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
+ return;
+ }
+ ThreadPause();
+ if ( i % 1024 == 0 )
+ {
+#ifdef THREAD_FAST_MUTEX_TIMINGS
+ CAverageTimeMarker marker( &sleepTimer );
+#endif
+ ThreadSleep( 0 );
+ }
+ }
+
+#ifdef _WIN32
+ if ( !nSpinSleepTime && GetThreadPriority( GetCurrentThread() ) > THREAD_PRIORITY_NORMAL )
+ {
+ nSpinSleepTime = 1;
+ }
+#endif
+
+ if ( nSpinSleepTime )
+ {
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+#ifdef THREAD_FAST_MUTEX_TIMINGS
+ CAverageTimeMarker marker( &sleepTimer );
+#endif
+ if ( TryLock( threadId ) )
+ {
+ TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 0 );
+ }
+
+ }
+
+ for ( ;; )
+ {
+#ifdef THREAD_FAST_MUTEX_TIMINGS
+ CAverageTimeMarker marker( &sleepTimer );
+#endif
+ if ( TryLock( threadId ) )
+ {
+ TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( nSpinSleepTime );
+ }
+ }
+ else
+ {
+ for ( ;; )
+ {
+ if ( TryLock( threadId ) )
+ {
+ TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
+ return;
+ }
+
+ ThreadPause();
+ }
+ }
+}
+
+#ifdef THREAD_FAST_MUTEX_TIMINGS
+#pragma optimize( "", on )
+#endif
+
+//-----------------------------------------------------------------------------
+//
+// CThreadRWLock
+//
+//-----------------------------------------------------------------------------
+
+void CThreadRWLock::WaitForRead()
+{
+ m_nPendingReaders++;
+
+ do
+ {
+ m_mutex.Unlock();
+ m_CanRead.Wait();
+ m_mutex.Lock();
+ }
+ while (m_nWriters);
+
+ m_nPendingReaders--;
+}
+
+
+void CThreadRWLock::LockForWrite()
+{
+ m_mutex.Lock();
+ bool bWait = ( m_nWriters != 0 || m_nActiveReaders != 0 );
+ m_nWriters++;
+ m_CanRead.Reset();
+ m_mutex.Unlock();
+
+ if ( bWait )
+ {
+ m_CanWrite.Wait();
+ }
+}
+
+void CThreadRWLock::UnlockWrite()
+{
+ m_mutex.Lock();
+ m_nWriters--;
+ if ( m_nWriters == 0)
+ {
+ if ( m_nPendingReaders )
+ {
+ m_CanRead.Set();
+ }
+ }
+ else
+ {
+ m_CanWrite.Set();
+ }
+ m_mutex.Unlock();
+}
+
+//-----------------------------------------------------------------------------
+//
+// CThreadSpinRWLock
+//
+//-----------------------------------------------------------------------------
+#ifndef OLD_SPINRWLOCK
+
+void CThreadSpinRWLock::SpinLockForWrite()
+{
+ int i;
+
+ if ( TryLockForWrite_UnforcedInline() )
+ {
+ return;
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForWrite_UnforcedInline() )
+ {
+ return;
+ }
+ ThreadPause();
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForWrite_UnforcedInline() )
+ {
+ return;
+ }
+ ThreadPause();
+ if ( i % 1024 == 0 )
+ {
+ ThreadSleep( 0 );
+ }
+ }
+
+ for ( i = THREAD_SPIN * 4; i != 0; --i )
+ {
+ if ( TryLockForWrite_UnforcedInline() )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 0 );
+ }
+
+ for ( ;; ) // coded as for instead of while to make easy to breakpoint success
+ {
+ if ( TryLockForWrite_UnforcedInline() )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 1 );
+ }
+}
+
+void CThreadSpinRWLock::SpinLockForRead()
+{
+ int i;
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForRead_UnforcedInline() )
+ {
+ return;
+ }
+ ThreadPause();
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForRead_UnforcedInline() )
+ {
+ return;
+ }
+ ThreadPause();
+ if ( i % 1024 == 0 )
+ {
+ ThreadSleep( 0 );
+ }
+ }
+
+ for ( i = THREAD_SPIN * 4; i != 0; --i )
+ {
+ if ( TryLockForRead_UnforcedInline() )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 0 );
+ }
+
+ for ( ;; ) // coded as for instead of while to make easy to breakpoint success
+ {
+ if ( TryLockForRead_UnforcedInline() )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 1 );
+ }
+}
+
+#else
+/* (commented out to reduce distraction in colorized editor, remove entirely when new implementation settles)
+void CThreadSpinRWLock::SpinLockForWrite( const uint32 threadId )
+{
+ int i;
+
+ if ( TryLockForWrite( threadId ) )
+ {
+ return;
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForWrite( threadId ) )
+ {
+ return;
+ }
+ ThreadPause();
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForWrite( threadId ) )
+ {
+ return;
+ }
+ ThreadPause();
+ if ( i % 1024 == 0 )
+ {
+ ThreadSleep( 0 );
+ }
+ }
+
+ for ( i = THREAD_SPIN * 4; i != 0; --i )
+ {
+ if ( TryLockForWrite( threadId ) )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 0 );
+ }
+
+ for ( ;; ) // coded as for instead of while to make easy to breakpoint success
+ {
+ if ( TryLockForWrite( threadId ) )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 1 );
+ }
+}
+
+void CThreadSpinRWLock::LockForRead()
+{
+ int i;
+ if ( TryLockForRead() )
+ {
+ return;
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForRead() )
+ {
+ return;
+ }
+ ThreadPause();
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForRead() )
+ {
+ return;
+ }
+ ThreadPause();
+ if ( i % 1024 == 0 )
+ {
+ ThreadSleep( 0 );
+ }
+ }
+
+ for ( i = THREAD_SPIN * 4; i != 0; --i )
+ {
+ if ( TryLockForRead() )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 0 );
+ }
+
+ for ( ;; ) // coded as for instead of while to make easy to breakpoint success
+ {
+ if ( TryLockForRead() )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 1 );
+ }
+}
+
+void CThreadSpinRWLock::UnlockRead()
+{
+ int i;
+
+ Assert( m_lockInfo.m_nReaders > 0 && m_lockInfo.m_writerId == 0 );
+
+ //uint32 nLockInfoReaders = m_lockInfo.m_nReaders;
+ LockInfo_t oldValue;
+ LockInfo_t newValue;
+
+ if( IsX360() )
+ {
+ // this is the code equivalent to original code (see below) that doesn't cause LHS on Xbox360
+ // WARNING: This code assumes BIG Endian CPU
+ oldValue.m_i64 = uint32( m_lockInfo.m_nReaders );
+ newValue.m_i64 = oldValue.m_i64 - 1; // NOTE: when we have -1 (or 0xFFFFFFFF) readers, this will result in non-equivalent code
+ }
+ else
+ {
+ // this is the original code that worked here for a while
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ oldValue.m_writerId = 0;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+ newValue.m_writerId = 0;
+ }
+ ThreadMemoryBarrier();
+ if( AssignIf( newValue, oldValue ) )
+ return;
+
+ ThreadPause();
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if( AssignIf( newValue, oldValue ) )
+ return;
+ ThreadPause();
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if( AssignIf( newValue, oldValue ) )
+ return;
+ ThreadPause();
+ if ( i % 512 == 0 )
+ {
+ ThreadSleep( 0 );
+ }
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+ }
+
+ for ( i = THREAD_SPIN * 4; i != 0; --i )
+ {
+ if( AssignIf( newValue, oldValue ) )
+ return;
+ ThreadPause();
+ ThreadSleep( 0 );
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+ }
+
+ for ( ;; ) // coded as for instead of while to make easy to breakpoint success
+ {
+ if( AssignIf( newValue, oldValue ) )
+ return;
+ ThreadPause();
+ ThreadSleep( 1 );
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+ }
+}
+
+void CThreadSpinRWLock::UnlockWrite()
+{
+ Assert( m_lockInfo.m_writerId == ThreadGetCurrentId() && m_lockInfo.m_nReaders == 0 );
+ static const LockInfo_t newValue = { { 0, 0 } };
+ ThreadMemoryBarrier();
+ ThreadInterlockedExchange64( (int64 *)&m_lockInfo, *((int64 *)&newValue) );
+ m_nWriters--;
+}
+*/
+#endif
+
+#if defined( _PS3 )
+// All CThread code is inline in the header for PS3
+
+// This function is implemented here rather than the header because g_pCurThread resolves to GetCurThread() on PS3
+// and we don't want to create a dependency on the ELF stub for everyone who includes the header.
+PLATFORM_INTERFACE CThread *GetCurThreadPS3()
+{
+ return (CThread*)g_pCurThread;
+}
+
+PLATFORM_INTERFACE void SetCurThreadPS3( CThread *pThread )
+{
+ g_pCurThread = pThread;
+}
+#else
+// The CThread implementation needs to be inlined for performance on the PS3 - It makes a difference of more than 1ms/frame
+// for other platforms, we include the .inl in the .cpp file where it existed before
+#include "../public/tier0/threadtools.inl"
+#endif
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CWorkerThread::CWorkerThread()
+: m_EventSend(true), // must be manual-reset for PeekCall()
+ m_EventComplete(true), // must be manual-reset to handle multiple wait with thread properly
+ m_Param(0),
+ m_ReturnVal(0)
+{
+}
+
+//---------------------------------------------------------
+
+int CWorkerThread::CallWorker(unsigned dw, unsigned timeout, bool fBoostWorkerPriorityToMaster)
+{
+ return Call(dw, timeout, fBoostWorkerPriorityToMaster);
+}
+
+//---------------------------------------------------------
+
+int CWorkerThread::CallMaster(unsigned dw, unsigned timeout)
+{
+ return Call(dw, timeout, false);
+}
+
+//---------------------------------------------------------
+
+CThreadEvent &CWorkerThread::GetCallHandle()
+{
+ return m_EventSend;
+}
+
+//---------------------------------------------------------
+
+unsigned CWorkerThread::GetCallParam() const
+{
+ return m_Param;
+}
+
+//---------------------------------------------------------
+
+int CWorkerThread::BoostPriority()
+{
+ int iInitialPriority = GetPriority();
+
+#ifdef WIN32
+ const int iNewPriority = ThreadGetPriority( GetThreadHandle() );
+ if (iNewPriority > iInitialPriority)
+ ThreadSetPriority( GetThreadHandle(), iNewPriority);
+#elif !defined( _PS3 )
+ const int iNewPriority = ThreadGetPriority( (ThreadHandle_t)GetThreadID() );
+ if (iNewPriority > iInitialPriority)
+ ThreadSetPriority( (ThreadHandle_t)GetThreadID(), iNewPriority);
+#endif
+
+ return iInitialPriority;
+}
+
+//---------------------------------------------------------
+static uint32 DefaultWaitFunc( uint32 nHandles, CThreadEvent** ppHandles, int bWaitAll, uint32 timeout )
+{
+ return CThreadEvent::WaitForMultiple( nHandles, ppHandles, bWaitAll!=0, timeout ) ;
+}
+
+
+int CWorkerThread::Call(unsigned dwParam, unsigned timeout, bool fBoostPriority, WaitFunc_t waitFunc)
+{
+ AssertMsg(!m_EventSend.Check(), "Cannot perform call if there's an existing call pending" );
+
+ AUTO_LOCK( m_Lock );
+
+ if (!IsAlive())
+ return WTCR_FAIL;
+
+ int iInitialPriority = 0;
+ if (fBoostPriority)
+ {
+ iInitialPriority = BoostPriority();
+ }
+
+ // set the parameter, signal the worker thread, wait for the completion to be signaled
+ m_Param = dwParam;
+
+ m_EventComplete.Reset();
+ m_EventSend.Set();
+
+ WaitForReply( timeout, waitFunc );
+
+ if (fBoostPriority)
+ SetPriority(iInitialPriority);
+
+ return m_ReturnVal;
+}
+
+//---------------------------------------------------------
+//
+// Wait for a request from the client
+//
+//---------------------------------------------------------
+int CWorkerThread::WaitForReply( unsigned timeout )
+{
+ return WaitForReply( timeout, NULL );
+}
+
+int CWorkerThread::WaitForReply( unsigned timeout, WaitFunc_t pfnWait )
+{
+ if (!pfnWait)
+ {
+ pfnWait = &DefaultWaitFunc;
+ }
+
+ CThreadEvent *waits[] =
+ {
+ &m_EventComplete,
+ &m_ExitEvent
+ };
+
+ unsigned result;
+ bool bInDebugger = Plat_IsInDebugSession();
+
+ uint32 dwActualTimeout = ( (timeout==TT_INFINITE) ? 30000 : timeout );
+
+ do
+ {
+#ifdef WIN32
+ // Make sure the thread handle hasn't been closed
+ if ( !GetThreadHandle() )
+ {
+ result = 1;
+ break;
+ }
+#endif
+
+ result = (*pfnWait)( ARRAYSIZE( waits ), waits, false, dwActualTimeout );
+
+ AssertMsg(timeout != TT_INFINITE || result != TW_TIMEOUT, "Possible hung thread, call to thread timed out");
+
+ } while ( bInDebugger && ( timeout == TT_INFINITE && result == TW_TIMEOUT ) );
+
+ if ( result != 0 )
+ {
+ if (result == TW_TIMEOUT)
+ {
+ m_ReturnVal = WTCR_TIMEOUT;
+ }
+ else if (result == 1)
+ {
+ DevMsg( 2, "Thread failed to respond, probably exited\n");
+ m_EventSend.Reset();
+ m_ReturnVal = WTCR_TIMEOUT;
+ }
+ else
+ {
+ m_EventSend.Reset();
+ m_ReturnVal = WTCR_THREAD_GONE;
+ }
+ }
+
+ return m_ReturnVal;
+}
+
+
+//---------------------------------------------------------
+//
+// Wait for a request from the client
+//
+//---------------------------------------------------------
+
+bool CWorkerThread::WaitForCall(unsigned * pResult)
+{
+ return WaitForCall(TT_INFINITE, pResult);
+}
+
+//---------------------------------------------------------
+
+bool CWorkerThread::WaitForCall(unsigned dwTimeout, unsigned * pResult)
+{
+ bool returnVal = m_EventSend.Wait(dwTimeout);
+ if (pResult)
+ *pResult = m_Param;
+ return returnVal;
+}
+
+//---------------------------------------------------------
+//
+// is there a request?
+//
+
+bool CWorkerThread::PeekCall(unsigned * pParam)
+{
+ if (!m_EventSend.Check())
+ {
+ return false;
+ }
+ else
+ {
+ if (pParam)
+ {
+ *pParam = m_Param;
+ }
+ return true;
+ }
+}
+
+//---------------------------------------------------------
+//
+// Reply to the request
+//
+
+void CWorkerThread::Reply(unsigned dw)
+{
+ m_Param = 0;
+ m_ReturnVal = dw;
+
+ // The request is now complete so PeekCall() should fail from
+ // now on
+ //
+ // This event should be reset BEFORE we signal the client
+ m_EventSend.Reset();
+
+ // Tell the client we're finished
+ m_EventComplete.Set();
+}
+
+
+//-----------------------------------------------------------------------------
+
+
+#if defined( _PS3 )
+
+/*******************************************************************************
+* PS3 equivalent to Win32 function for setting events
+*******************************************************************************/
+BOOL SetEvent( CThreadEvent *pEvent )
+{
+ bool bRetVal = pEvent->Set();
+ if ( !bRetVal )
+ Assert(0);
+
+ return bRetVal;
+}
+
+/*******************************************************************************
+* PS3 equivalent to Win32 function for resetting events
+*******************************************************************************/
+BOOL ResetEvent( CThreadEvent *pEvent )
+{
+ return pEvent->Reset();
+}
+
+#define MAXIMUM_WAIT_OBJECTS 64
+
+/*******************************************************************************
+* Wait for a selection of events to terminate
+*******************************************************************************/
+DWORD WaitForMultipleObjects( DWORD nCount, CThreadEvent **lppHandles, BOOL bWaitAll, DWORD dwMilliseconds )
+{
+ //////////////////////////////////////////////////////////////
+#ifndef NEW_WAIT_FOR_MULTIPLE_OBJECTS
+ //////////////////////////////////////////////////////////////
+
+
+ // Support for a limited amount of events
+ if ( nCount >= MAXIMUM_WAIT_OBJECTS )
+ {
+ Assert(0);
+ return false;
+ }
+
+ bool bRunning = true;
+ unsigned int result = TW_FAILED;
+
+ // For bWaitAll
+ int numEvent = 0;
+ int eventComplete[ MAXIMUM_WAIT_OBJECTS ] = {0};
+
+ uint64_t timeDiffMS = 0;
+ uint64_t startTimeMS = Plat_MSTime();
+ uint64_t endTimeMS = 0;
+
+ while ( bRunning )
+ {
+ // Check for a timeout
+ if ( bRunning && ( dwMilliseconds != INFINITE ) && ( timeDiffMS > dwMilliseconds ) )
+ {
+ result = TW_TIMEOUT;
+ bRunning = false;
+ }
+
+ // Wait for all the events to be set
+ if ( bWaitAll )
+ {
+ for ( int event = 0; event < nCount; ++event )
+ {
+ if ( lppHandles[event]->Wait(1) )
+ {
+ // If an event is complete, mark it as complete in our list
+ if ( eventComplete[ event ] == 0 )
+ {
+ numEvent++;
+ eventComplete[ event ] = 1;
+ }
+ }
+ }
+
+ // If all the events have been set, terminate the function
+ if ( numEvent >= nCount )
+ {
+ result = WAIT_OBJECT_0;
+ bRunning = false;
+ }
+ }
+
+ // Wait for one event to be set
+ else
+ {
+ for ( int event = 0; event < nCount; ++event )
+ {
+ if ( lppHandles[event]->Wait(1) )
+ {
+ result = WAIT_OBJECT_0 + event;
+ bRunning = false;
+ break;
+ }
+ }
+ }
+
+ endTimeMS = Plat_MSTime();
+ timeDiffMS = endTimeMS - startTimeMS;
+ }
+
+ return result;
+
+
+
+ //////////////////////////////////////////////////////////////
+#else // NEW_WAIT_FOR_MULTIPLE_OBJECTS // (expected PS3 only)
+ //////////////////////////////////////////////////////////////
+#ifndef _PS3
+#error This code was written expecting to be run on PS3.
+#endif
+
+ // check if we have a wait objects semaphore
+ if (!gbWaitObjectsCreated)
+ {
+ sys_semaphore_attribute_t semAttr;
+ sys_semaphore_attribute_initialize(semAttr);
+ sys_semaphore_create(&gWaitObjectsSemaphore, &semAttr, 0, 0xFFFF);
+
+ gbWaitObjectsCreated = true;
+ }
+
+ // Support for a limited amount of events
+ if ( nCount >= MAXIMUM_WAIT_OBJECTS )
+ {
+ Assert(0);
+ return false;
+ }
+
+ unsigned int result = WAIT_FAILED;
+ int res = CELL_OK;
+ int event = -1;
+ int numEvent = 0;
+
+ // run through events registering this thread with each one
+ for (int i = 0; i < nCount; i++)
+ {
+ lppHandles[i]->RegisterWaitingThread(&gWaitObjectsSemaphore, i, &event);
+ }
+
+
+ // in the Source API, a timeOut of 0 means very short timeOut, not (as in the PS3 spec) an infinite timeout.
+ // TT_INFINITE is #defined to 2^31-1, which means "infinite timeout" on PC and "72 minutes, 35 seconds" on PS3.
+ // conversely, the code below (around deltaTime) expects to be able to compare against the timeout
+ // value given here, so we cannot just replace 0 with 1 and TT_INFINITE with 0.
+ // So, we replace 0 with 1, meaning "a very short time", and test for the special value TT_INFINITE
+ // at the moment of calling sys_semaphore_wait, where we replace it with the real "infinite timeout"
+ // value. It isn't safe to simply increase the declaration size of TT_INFINITE, because as you can
+ // see it is often assigned to uint32s.
+ // Also, Source timeouts are specified in milliseconds, and PS3 timeouts are in microseconds,
+ // so we need to multiply by one thousand.
+ uint32 timeOut = dwMilliseconds;
+ if ( timeOut == 0 )
+ {
+ timeOut = 1;
+ }
+ else if ( timeOut != TT_INFINITE )
+ {
+ timeOut *= 1000;
+ // note that it's impossible for dwMilliseconds * 1000
+ // to coincidentally equal TT_INFINITE since TT_INFINITE
+ // is not divisible by 1000.
+ COMPILE_TIME_ASSERT( TT_INFINITE % 1000 != 0 );
+ }
+
+ COMPILE_TIME_ASSERT( TT_INFINITE != 0 ); // The code here was written expecting (working around) that TT_INFINITE is
+ // MAXINT, so if you changed this number, please read the comment above and
+ // carefully examine the code here to make sure that timeouts still work
+ // correctly on PS3. Be aware that in many places in Source, a timeout of
+ // 0 has some special meaning other than "infinite timeout", so track those
+ // down too.
+
+
+ // Wait for all the events to be set
+ if ( bWaitAll )
+ {
+ while (numEvent < nCount)
+ {
+ uint64_t deltaTime = Timer_GetTimeUS();
+
+ res = sys_semaphore_wait(gWaitObjectsSemaphore, timeOut == TT_INFINITE ? 0 : timeOut );
+
+ deltaTime = Timer_GetTimeUS() - deltaTime;
+
+ if (res == ETIMEDOUT)
+ {
+ result = TW_TIMEOUT;
+ break;
+ }
+ else if (res == CELL_OK)
+ {
+ numEvent++;
+
+ if (deltaTime >= timeOut)
+ {
+ // note - if this is not truly a time out
+ // then it will be set to WAIT_OBJECT_0
+ // after this loop
+ result = TW_TIMEOUT;
+ break;
+ }
+ else
+ {
+ timeOut -= deltaTime;
+ }
+ }
+ else
+ {
+ result = TW_FAILED;
+ break;
+ }
+ }
+
+ if (numEvent >= nCount)
+ {
+ result = WAIT_OBJECT_0;
+ }
+ }
+ else // Wait for one event to be set
+ {
+ // no event fired yet, wait on semaphore
+ res = sys_semaphore_wait( gWaitObjectsSemaphore, timeOut == TT_INFINITE ? 0 : timeOut );
+
+ if (res == ETIMEDOUT)
+ {
+ result = TW_TIMEOUT;
+ }
+ else if (res == CELL_OK)
+ {
+ if ((event < 0) || (event >= nCount))
+ {
+ DEBUG_ERROR("Bad event\n");
+ }
+
+ result = WAIT_OBJECT_0 + event;
+ }
+ }
+
+ // run through events unregistering this thread, for benefit
+ // of those events that did not fire, or fired before semaphore
+ // was registered
+ for (int i = 0; i < nCount; i++)
+ {
+ lppHandles[i]->UnregisterWaitingThread(&gWaitObjectsSemaphore);
+ }
+
+ // reset semaphore
+ while (sys_semaphore_trywait(gWaitObjectsSemaphore) != EBUSY);
+
+ return result;
+
+
+ //////////////////////////////////////////////////////////////
+#endif // NEW_WAIT_FOR_MULTIPLE_OBJECTS
+ //////////////////////////////////////////////////////////////
+}
+
+#endif