summaryrefslogtreecommitdiff
path: root/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 /tier0/threadtools.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'tier0/threadtools.cpp')
-rw-r--r--tier0/threadtools.cpp2404
1 files changed, 2404 insertions, 0 deletions
diff --git a/tier0/threadtools.cpp b/tier0/threadtools.cpp
new file mode 100644
index 0000000..001e703
--- /dev/null
+++ b/tier0/threadtools.cpp
@@ -0,0 +1,2404 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "pch_tier0.h"
+
+#include "tier1/strtools.h"
+#include "tier0/dynfunction.h"
+#if defined( _WIN32 ) && !defined( _X360 )
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+#ifdef _WIN32
+ #include <process.h>
+
+#ifdef IS_WINDOWS_PC
+ #include <Mmsystem.h>
+ #pragma comment(lib, "winmm.lib")
+#endif // IS_WINDOWS_PC
+
+#elif defined(POSIX)
+
+#if !defined(OSX)
+ #include <sys/fcntl.h>
+ #include <sys/unistd.h>
+ #define sem_unlink( arg )
+ #define OS_TO_PTHREAD(x) (x)
+#else
+ #define pthread_yield pthread_yield_np
+ #include <mach/thread_act.h>
+ #include <mach/mach.h>
+ #define OS_TO_PTHREAD(x) pthread_from_mach_thread_np( x )
+#endif // !OSX
+
+#ifdef LINUX
+#include <dlfcn.h> // RTLD_NEXT
+#endif
+
+typedef int (*PTHREAD_START_ROUTINE)(
+ void *lpThreadParameter
+ );
+typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
+#include <sched.h>
+#include <exception>
+#include <errno.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/time.h>
+#define GetLastError() errno
+typedef void *LPVOID;
+#endif
+
+#include "tier0/valve_minmax_off.h"
+#include <memory>
+#include "tier0/valve_minmax_on.h"
+
+#include "tier0/threadtools.h"
+#include "tier0/vcrmode.h"
+
+#ifdef _X360
+#include "xbox/xbox_win32stubs.h"
+#endif
+
+#include "tier0/vprof_telemetry.h"
+
+// Must be last header...
+#include "tier0/memdbgon.h"
+
+#define THREADS_DEBUG 1
+
+// 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
+
+//-----------------------------------------------------------------------------
+// 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 _WIN32
+static unsigned __stdcall ThreadProcConvert( void *pParam )
+#elif defined(POSIX)
+static void *ThreadProcConvert( void *pParam )
+#else
+#error
+#endif
+{
+ ThreadProcInfo_t info = *((ThreadProcInfo_t *)pParam);
+ delete ((ThreadProcInfo_t *)pParam);
+#ifdef _WIN32
+ return (*info.pfnThread)(info.pParam);
+#elif defined(POSIX)
+ return (void *)(*info.pfnThread)(info.pParam);
+#else
+#error
+#endif
+}
+
+
+//---------------------------------------------------------
+
+ThreadHandle_t CreateSimpleThread( ThreadFunc_t pfnThread, void *pParam, ThreadId_t *pID, unsigned stackSize )
+{
+#ifdef _WIN32
+ ThreadId_t idIgnored;
+ if ( !pID )
+ pID = &idIgnored;
+ HANDLE h = VCRHook_CreateThread(NULL, stackSize, (LPTHREAD_START_ROUTINE)ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ), CREATE_SUSPENDED, pID);
+ if ( h != INVALID_HANDLE_VALUE )
+ {
+ Plat_ApplyHardwareDataBreakpointsToNewThread( *pID );
+ ResumeThread( h );
+ }
+ return (ThreadHandle_t)h;
+#elif defined(POSIX)
+ pthread_t tid;
+
+ // If we need to create threads that are detached right out of the gate, we would need to do something like this:
+ // pthread_attr_t attr;
+ // int rc = pthread_attr_init(&attr);
+ // rc = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ // ... pthread_create( &tid, &attr, ... ) ...
+ // rc = pthread_attr_destroy(&attr);
+ // ... pthread_join will now fail
+
+ int ret = pthread_create( &tid, NULL, ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ) );
+ if ( ret )
+ {
+ // There are only PTHREAD_THREADS_MAX number of threads, and we're probably leaking handles if ret == EAGAIN here?
+ Error( "CreateSimpleThread: pthread_create failed. Someone not calling pthread_detach() or pthread_join. Ret:%d\n", ret );
+ }
+ if ( pID )
+ *pID = (ThreadId_t)tid;
+ Plat_ApplyHardwareDataBreakpointsToNewThread( (long unsigned int)tid );
+ return (ThreadHandle_t)tid;
+#endif
+}
+
+ThreadHandle_t CreateSimpleThread( ThreadFunc_t pfnThread, void *pParam, unsigned stackSize )
+{
+ return CreateSimpleThread( pfnThread, pParam, NULL, stackSize );
+}
+
+PLATFORM_INTERFACE void ThreadDetach( ThreadHandle_t hThread )
+{
+#if defined( POSIX )
+ // The resources of this thread will be freed immediately when it terminates,
+ // instead of waiting for another thread to perform PTHREAD_JOIN.
+ pthread_t tid = ( pthread_t )hThread;
+
+ pthread_detach( tid );
+#endif
+}
+
+bool ReleaseThreadHandle( ThreadHandle_t hThread )
+{
+#ifdef _WIN32
+ return ( CloseHandle( hThread ) != 0 );
+#else
+ return true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+//
+// Wrappers for other simple threading operations
+//
+//-----------------------------------------------------------------------------
+
+void ThreadSleep(unsigned nMilliseconds)
+{
+#ifdef _WIN32
+
+#ifdef IS_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 // IS_WINDOWS_PC
+
+ Sleep( nMilliseconds );
+#elif defined(POSIX)
+ usleep( nMilliseconds * 1000 );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+#ifndef ThreadGetCurrentId
+uint ThreadGetCurrentId()
+{
+#ifdef _WIN32
+ return GetCurrentThreadId();
+#elif defined(POSIX)
+ return (uint)pthread_self();
+#endif
+}
+#endif
+
+//-----------------------------------------------------------------------------
+ThreadHandle_t ThreadGetCurrentHandle()
+{
+#ifdef _WIN32
+ return (ThreadHandle_t)GetCurrentThread();
+#elif defined(POSIX)
+ return (ThreadHandle_t)pthread_self();
+#endif
+}
+
+// On PS3, this will return true for zombie threads
+bool ThreadIsThreadIdRunning( ThreadId_t uThreadId )
+{
+#ifdef _WIN32
+ bool bRunning = true;
+ HANDLE hThread = ::OpenThread( THREAD_QUERY_INFORMATION , false, uThreadId );
+ if ( hThread )
+ {
+ DWORD dwExitCode;
+ if( !::GetExitCodeThread( hThread, &dwExitCode ) || dwExitCode != STILL_ACTIVE )
+ bRunning = false;
+
+ CloseHandle( hThread );
+ }
+ else
+ {
+ bRunning = false;
+ }
+ return bRunning;
+#elif defined( _PS3 )
+
+ // will return CELL_OK for zombie threads
+ int priority;
+ return (sys_ppu_thread_get_priority( uThreadId, &priority ) == CELL_OK );
+
+#elif defined(POSIX)
+ pthread_t thread = OS_TO_PTHREAD(uThreadId);
+ if ( thread )
+ {
+ int iResult = pthread_kill( thread, 0 );
+ if ( iResult == 0 )
+ return true;
+ }
+ else
+ {
+ // We really ought not to be passing NULL in to here
+ AssertMsg( false, "ThreadIsThreadIdRunning received a null thread ID" );
+ }
+
+ return false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+int ThreadGetPriority( ThreadHandle_t hThread )
+{
+ if ( !hThread )
+ {
+ hThread = ThreadGetCurrentHandle();
+ }
+
+#ifdef _WIN32
+ return ::GetThreadPriority( (HANDLE)hThread );
+#else
+ struct sched_param thread_param;
+ int policy;
+ pthread_getschedparam( (pthread_t)hThread, &policy, &thread_param );
+ return thread_param.sched_priority;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+bool ThreadSetPriority( ThreadHandle_t hThread, int priority )
+{
+ if ( !hThread )
+ {
+ hThread = ThreadGetCurrentHandle();
+ }
+
+#ifdef _WIN32
+ return ( SetThreadPriority(hThread, priority) != 0 );
+#elif defined(POSIX)
+ struct sched_param thread_param;
+ thread_param.sched_priority = priority;
+ pthread_setschedparam( (pthread_t)hThread, SCHED_OTHER, &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
+
+}
+
+//-----------------------------------------------------------------------------
+
+uint InitMainThread()
+{
+#ifndef LINUX
+ // Skip doing the setname on Linux for the main thread. Here is why...
+
+ // From Pierre-Loup e-mail about why pthread_setname_np() on the main thread
+ // in Linux will cause some tools to display "MainThrd" as the executable name:
+ //
+ // You have two things in procfs, comm and cmdline. Each of the threads have
+ // a different `comm`, which is the value you set through pthread_setname_np
+ // or prctl(PR_SET_NAME). Top can either display cmdline or comm; it
+ // switched to display comm by default; htop still displays cmdline by
+ // default. Top -c will output cmdline rather than comm.
+ //
+ // If you press 'H' while top is running it will display each thread as a
+ // separate process, so you will have different entries for MainThrd,
+ // MatQueue0, etc with their own CPU usage. But when that mode isn't enabled
+ // it just displays the 'comm' name from the first thread.
+ ThreadSetDebugName( "MainThrd" );
+#endif
+
+#ifdef _WIN32
+ return ThreadGetCurrentId();
+#elif defined(POSIX)
+ return (uint)pthread_self();
+#endif
+}
+
+uint g_ThreadMainThreadID = InitMainThread();
+
+bool ThreadInMainThread()
+{
+ return ( ThreadGetCurrentId() == g_ThreadMainThreadID );
+}
+
+//-----------------------------------------------------------------------------
+void DeclareCurrentThreadIsMainThread()
+{
+ g_ThreadMainThreadID = ThreadGetCurrentId();
+}
+
+bool ThreadJoin( ThreadHandle_t hThread, unsigned timeout )
+{
+ // You should really never be calling this with a NULL thread handle. If you
+ // are then that probably implies a race condition or threading misunderstanding.
+ Assert( hThread );
+ if ( !hThread )
+ {
+ return false;
+ }
+
+#ifdef _WIN32
+ DWORD dwWait = VCRHook_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(POSIX)
+ if ( pthread_join( (pthread_t)hThread, NULL ) != 0 )
+ return false;
+#endif
+ return true;
+}
+
+#ifdef RAD_TELEMETRY_ENABLED
+void TelemetryThreadSetDebugName( ThreadId_t id, const char *pszName );
+#endif
+
+//-----------------------------------------------------------------------------
+
+void ThreadSetDebugName( ThreadId_t id, const char *pszName )
+{
+ if( !pszName )
+ return;
+
+#ifdef RAD_TELEMETRY_ENABLED
+ TelemetryThreadSetDebugName( id, pszName );
+#endif
+
+#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 = id;
+ info.dwFlags = 0;
+
+ __try
+ {
+ RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
+ }
+ __except (EXCEPTION_CONTINUE_EXECUTION)
+ {
+ }
+ }
+#elif defined( _LINUX )
+ // As of glibc v2.12, we can use pthread_setname_np.
+ typedef int (pthread_setname_np_func)(pthread_t, const char *);
+ static pthread_setname_np_func *s_pthread_setname_np_func = (pthread_setname_np_func *)dlsym(RTLD_DEFAULT, "pthread_setname_np");
+
+ if ( s_pthread_setname_np_func )
+ {
+ if ( id == (uint32)-1 )
+ id = pthread_self();
+
+ /*
+ pthread_setname_np() in phthread_setname.c has the following code:
+
+ #define TASK_COMM_LEN 16
+ size_t name_len = strlen (name);
+ if (name_len >= TASK_COMM_LEN)
+ return ERANGE;
+
+ So we need to truncate the threadname to 16 or the call will just fail.
+ */
+ char szThreadName[ 16 ];
+ strncpy( szThreadName, pszName, ARRAYSIZE( szThreadName ) );
+ szThreadName[ ARRAYSIZE( szThreadName ) - 1 ] = 0;
+ (*s_pthread_setname_np_func)( id, szThreadName );
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+
+#ifdef _WIN32
+ASSERT_INVARIANT( TW_FAILED == WAIT_FAILED );
+ASSERT_INVARIANT( TW_TIMEOUT == WAIT_TIMEOUT );
+ASSERT_INVARIANT( WAIT_OBJECT_0 == 0 );
+
+int ThreadWaitForObjects( int nEvents, const HANDLE *pHandles, bool bWaitAll, unsigned timeout )
+{
+ return VCRHook_WaitForMultipleObjects( nEvents, pHandles, bWaitAll, timeout );
+}
+#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::CThreadSyncObject()
+#ifdef _WIN32
+ : m_hSyncObject( NULL ), m_bCreatedHandle(false)
+#elif defined(POSIX)
+ : m_bInitalized( false )
+#endif
+{
+}
+
+//---------------------------------------------------------
+
+CThreadSyncObject::~CThreadSyncObject()
+{
+#ifdef _WIN32
+ if ( m_hSyncObject && m_bCreatedHandle )
+ {
+ if ( !CloseHandle(m_hSyncObject) )
+ {
+ Assert( 0 );
+ }
+ }
+#elif defined(POSIX)
+ if ( m_bInitalized )
+ {
+ pthread_cond_destroy( &m_Condition );
+ pthread_mutex_destroy( &m_Mutex );
+ m_bInitalized = false;
+ }
+#endif
+}
+
+//---------------------------------------------------------
+
+bool CThreadSyncObject::operator!() const
+{
+#ifdef _WIN32
+ return !m_hSyncObject;
+#elif defined(POSIX)
+ return !m_bInitalized;
+#endif
+}
+
+//---------------------------------------------------------
+
+void CThreadSyncObject::AssertUseable()
+{
+#ifdef THREADS_DEBUG
+#ifdef _WIN32
+ AssertMsg( m_hSyncObject, "Thread synchronization object is unuseable" );
+#elif defined(POSIX)
+ AssertMsg( m_bInitalized, "Thread synchronization object is unuseable" );
+#endif
+#endif
+}
+
+//---------------------------------------------------------
+
+bool CThreadSyncObject::Wait( uint32 dwTimeout )
+{
+#ifdef THREADS_DEBUG
+ AssertUseable();
+#endif
+#ifdef _WIN32
+ return ( VCRHook_WaitForSingleObject( m_hSyncObject, dwTimeout ) == WAIT_OBJECT_0 );
+#elif defined(POSIX)
+ 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
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+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( 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 _WIN32
+CThreadEvent::CThreadEvent( HANDLE hHandle )
+{
+ m_hSyncObject = hHandle;
+ m_bCreatedHandle = false;
+ AssertMsg(m_hSyncObject, "Null event passed into constructor" );
+}
+#endif
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+
+//---------------------------------------------------------
+
+bool CThreadEvent::Set()
+{
+ AssertUseable();
+#ifdef _WIN32
+ return ( SetEvent( m_hSyncObject ) != 0 );
+#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
+}
+
+//---------------------------------------------------------
+
+bool CThreadEvent::Reset()
+{
+#ifdef THREADS_DEBUG
+ AssertUseable();
+#endif
+#ifdef _WIN32
+ return ( ResetEvent( m_hSyncObject ) != 0 );
+#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 THREADS_DEBUG
+ AssertUseable();
+#endif
+ return Wait( 0 );
+}
+
+
+
+bool CThreadEvent::Wait( uint32 dwTimeout )
+{
+ return CThreadSyncObject::Wait( dwTimeout );
+}
+
+#ifdef _WIN32
+//-----------------------------------------------------------------------------
+//
+// CThreadSemaphore
+//
+// To get Posix implementation, try http://www-128.ibm.com/developerworks/eserver/library/es-win32linux-sem.html
+//
+//-----------------------------------------------------------------------------
+
+CThreadSemaphore::CThreadSemaphore( long initialValue, long maxValue )
+{
+ 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;
+ }
+}
+
+//---------------------------------------------------------
+
+bool CThreadSemaphore::Release( long releaseCount, long *pPreviousCount )
+{
+#ifdef THRDTOOL_DEBUG
+ AssertUseable();
+#endif
+ return ( ReleaseSemaphore( m_hSyncObject, releaseCount, pPreviousCount ) != 0 );
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+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
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+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( &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
+}
+
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+
+#ifdef _WIN32
+#ifdef _X360
+#define TO_INTERLOCK_PARAM(p) ((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
+
+#ifndef USE_INTRINSIC_INTERLOCKED
+long ThreadInterlockedIncrement( long volatile *pDest )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedIncrement( TO_INTERLOCK_PARAM(pDest) );
+}
+
+long ThreadInterlockedDecrement( long volatile *pDest )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedDecrement( TO_INTERLOCK_PARAM(pDest) );
+}
+
+long ThreadInterlockedExchange( long volatile *pDest, long value )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedExchange( TO_INTERLOCK_PARAM(pDest), value );
+}
+
+long ThreadInterlockedExchangeAdd( long volatile *pDest, long value )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedExchangeAdd( TO_INTERLOCK_PARAM(pDest), value );
+}
+
+long ThreadInterlockedCompareExchange( long volatile *pDest, long value, long comperand )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedCompareExchange( TO_INTERLOCK_PARAM(pDest), value, comperand );
+}
+
+bool ThreadInterlockedAssignIf( long volatile *pDest, long value, long 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_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(PLATFORM_WINDOWS_PC32 )
+ __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;
+ }
+#else
+ return ( ThreadInterlockedCompareExchange64( pDest, value, comperand ) == comperand );
+#endif
+}
+
+#if defined( PLATFORM_64BITS )
+
+#if _MSC_VER < 1500
+// This intrinsic isn't supported on VS2005.
+extern "C" unsigned char _InterlockedCompareExchange128( int64 volatile * Destination, int64 ExchangeHigh, int64 ExchangeLow, int64 * ComparandResult );
+#endif
+
+bool ThreadInterlockedAssignIf128( volatile int128 *pDest, const int128 &value, const int128 &comperand )
+{
+ Assert( ( (size_t)pDest % 16 ) == 0 );
+
+ volatile int64 *pDest64 = ( volatile int64 * )pDest;
+ int64 *pValue64 = ( int64 * )&value;
+ int64 *pComperand64 = ( int64 * )&comperand;
+
+ // Description:
+ // The CMPXCHG16B instruction compares the 128-bit value in the RDX:RAX and RCX:RBX registers
+ // with a 128-bit memory location. If the values are equal, the zero flag (ZF) is set,
+ // and the RCX:RBX value is copied to the memory location.
+ // Otherwise, the ZF flag is cleared, and the memory value is copied to RDX:RAX.
+
+ // _InterlockedCompareExchange128: http://msdn.microsoft.com/en-us/library/bb514094.aspx
+ return _InterlockedCompareExchange128( pDest64, pValue64[1], pValue64[0], pComperand64 ) == 1;
+}
+
+#endif // PLATFORM_64BITS
+
+int64 ThreadInterlockedIncrement64( int64 volatile *pDest )
+{
+ Assert( (size_t)pDest % 8 == 0 );
+
+ int64 Old;
+
+ do
+ {
+ Old = *pDest;
+ } while (ThreadInterlockedCompareExchange64(pDest, Old + 1, Old) != Old);
+
+ return Old + 1;
+}
+
+int64 ThreadInterlockedDecrement64( int64 volatile *pDest )
+{
+ Assert( (size_t)pDest % 8 == 0 );
+ int64 Old;
+
+ do
+ {
+ Old = *pDest;
+ } while (ThreadInterlockedCompareExchange64(pDest, Old - 1, Old) != Old);
+
+ return Old - 1;
+}
+
+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;
+}
+
+int64 ThreadInterlockedExchangeAdd64( int64 volatile *pDest, int64 value )
+{
+ Assert( (size_t)pDest % 8 == 0 );
+ int64 Old;
+
+ do
+ {
+ Old = *pDest;
+ } while (ThreadInterlockedCompareExchange64(pDest, Old + value, Old) != Old);
+
+ return Old;
+}
+
+#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 );
+}
+
+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 );
+}
+
+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 );
+}
+
+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;
+}
+
+
+#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;
+}
+
+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;
+}
+
+bool ThreadInterlockedAssignIf64(volatile int64 *pDest, int64 value, int64 comperand )
+{
+ Assert( (size_t)pDest % 8 == 0 );
+ return ( ThreadInterlockedCompareExchange64( pDest, value, comperand ) == comperand );
+}
+
+bool ThreadInterlockedAssignIf( long volatile *pDest, long value, long comperand )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return ( ThreadInterlockedCompareExchange( pDest, value, comperand ) == comperand );
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+
+#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
+//
+//-----------------------------------------------------------------------------
+
+#ifndef 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
+
+#if defined( _WIN32 ) && !defined( _X360 )
+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( _WIN32 )
+#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 %p 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%p\n", m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection );
+ }
+ m_lockCount++;
+#endif
+ return true;
+ }
+ return false;
+ }
+ Lock();
+ return true;
+#elif defined( POSIX )
+ return pthread_mutex_trylock( &m_Mutex ) == 0;
+#else
+#error "Implement me!"
+ return true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+//
+// CThreadFastMutex
+//
+//-----------------------------------------------------------------------------
+
+#define THREAD_SPIN (8*1024)
+
+void CThreadFastMutex::Lock( const uint32 threadId, unsigned nSpinSleepTime ) volatile
+{
+ int i;
+ if ( nSpinSleepTime != TT_INFINITE )
+ {
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLock( threadId ) )
+ {
+ return;
+ }
+ ThreadPause();
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLock( threadId ) )
+ {
+ return;
+ }
+ ThreadPause();
+ if ( i % 1024 == 0 )
+ {
+ ThreadSleep( 0 );
+ }
+ }
+
+#ifdef _WIN32
+ if ( !nSpinSleepTime && GetThreadPriority( GetCurrentThread() ) > THREAD_PRIORITY_NORMAL )
+ {
+ nSpinSleepTime = 1;
+ }
+ else
+#endif
+
+ if ( nSpinSleepTime )
+ {
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLock( threadId ) )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 0 );
+ }
+
+ }
+
+ for ( ;; ) // coded as for instead of while to make easy to breakpoint success
+ {
+ if ( TryLock( threadId ) )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( nSpinSleepTime );
+ }
+ }
+ else
+ {
+ for ( ;; ) // coded as for instead of while to make easy to breakpoint success
+ {
+ if ( TryLock( threadId ) )
+ {
+ return;
+ }
+
+ ThreadPause();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+// 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
+//
+//-----------------------------------------------------------------------------
+
+void CThreadSpinRWLock::SpinLockForWrite( const uint32 threadId )
+{
+ int i;
+
+ for ( i = 1000; i != 0; --i )
+ {
+ if ( TryLockForWrite( threadId ) )
+ {
+ return;
+ }
+ ThreadPause();
+ }
+
+ for ( i = 20000; 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;
+
+ // In order to grab a read lock, the number of readers must not change and no thread can own the write lock
+ LockInfo_t oldValue;
+ LockInfo_t newValue;
+
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ oldValue.m_writerId = 0;
+ newValue.m_nReaders = oldValue.m_nReaders + 1;
+ newValue.m_writerId = 0;
+
+ if( m_nWriters == 0 && AssignIf( newValue, oldValue ) )
+ return;
+ ThreadPause();
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders + 1;
+
+ for ( i = 1000; i != 0; --i )
+ {
+ if( m_nWriters == 0 && AssignIf( newValue, oldValue ) )
+ return;
+ ThreadPause();
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders + 1;
+ }
+
+ for ( i = 20000; i != 0; --i )
+ {
+ if( m_nWriters == 0 && 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( m_nWriters == 0 && AssignIf( newValue, oldValue ) )
+ return;
+ ThreadPause();
+ ThreadSleep( 1 );
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders + 1;
+ }
+}
+
+void CThreadSpinRWLock::UnlockRead()
+{
+ int i;
+
+ Assert( m_lockInfo.m_nReaders > 0 && m_lockInfo.m_writerId == 0 );
+ LockInfo_t oldValue;
+ LockInfo_t newValue;
+
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ oldValue.m_writerId = 0;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+ newValue.m_writerId = 0;
+
+ if( AssignIf( newValue, oldValue ) )
+ return;
+ ThreadPause();
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+
+ for ( i = 500; 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 = 20000; 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 };
+#if defined(_X360)
+ // X360TBD: Serious Perf implications, not yet. __sync();
+#endif
+ ThreadInterlockedExchange64( (int64 *)&m_lockInfo, *((int64 *)&newValue) );
+ m_nWriters--;
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+// CThread
+//
+//-----------------------------------------------------------------------------
+
+CThreadLocalPtr<CThread> g_pCurThread;
+
+//---------------------------------------------------------
+
+CThread::CThread()
+:
+#ifdef _WIN32
+ m_hThread( NULL ),
+#endif
+ m_threadId( 0 ),
+ m_result( 0 ),
+ m_flags( 0 )
+{
+ m_szName[0] = 0;
+}
+
+//---------------------------------------------------------
+
+CThread::~CThread()
+{
+#ifdef _WIN32
+ if (m_hThread)
+#elif defined(POSIX)
+ if ( m_threadId )
+#endif
+ {
+ if ( IsAlive() )
+ {
+ Msg( "Illegal termination of worker thread! Threads must negotiate an end to the thread before the CThread object is destroyed.\n" );
+#ifdef _WIN32
+
+ DoNewAssertDialog( __FILE__, __LINE__, "Illegal termination of worker thread! Threads must negotiate an end to the thread before the CThread object is destroyed.\n" );
+#endif
+ if ( GetCurrentCThread() == this )
+ {
+ Stop(); // BUGBUG: Alfred - this doesn't make sense, this destructor fires from the hosting thread not the thread itself!!
+ }
+ }
+
+#ifdef _WIN32
+ // Now that the worker thread has exited (which we know because we presumably waited
+ // on the thread handle for it to exit) we can finally close the thread handle. We
+ // cannot do this any earlier, and certainly not in CThread::ThreadProc().
+ CloseHandle( m_hThread );
+#endif
+ }
+}
+
+
+//---------------------------------------------------------
+
+const char *CThread::GetName()
+{
+ AUTO_LOCK( m_Lock );
+ if ( !m_szName[0] )
+ {
+#ifdef _WIN32
+ _snprintf( m_szName, sizeof(m_szName) - 1, "Thread(%p/%p)", this, m_hThread );
+#elif defined(POSIX)
+ _snprintf( m_szName, sizeof(m_szName) - 1, "Thread(0x%x/0x%x)", (uint)this, (uint)m_threadId );
+#endif
+ m_szName[sizeof(m_szName) - 1] = 0;
+ }
+ return m_szName;
+}
+
+//---------------------------------------------------------
+
+void CThread::SetName(const char *pszName)
+{
+ AUTO_LOCK( m_Lock );
+ strncpy( m_szName, pszName, sizeof(m_szName) - 1 );
+ m_szName[sizeof(m_szName) - 1] = 0;
+}
+
+//---------------------------------------------------------
+
+bool CThread::Start( unsigned nBytesStack )
+{
+ AUTO_LOCK( m_Lock );
+
+ if ( IsAlive() )
+ {
+ AssertMsg( 0, "Tried to create a thread that has already been created!" );
+ return false;
+ }
+
+ bool bInitSuccess = false;
+ CThreadEvent createComplete;
+ ThreadInit_t init = { this, &createComplete, &bInitSuccess };
+
+#ifdef _WIN32
+ HANDLE hThread;
+ m_hThread = hThread = (HANDLE)VCRHook_CreateThread( NULL,
+ nBytesStack,
+ (LPTHREAD_START_ROUTINE)GetThreadProc(),
+ new ThreadInit_t(init),
+ CREATE_SUSPENDED,
+ &m_threadId );
+ if ( !hThread )
+ {
+ AssertMsg1( 0, "Failed to create thread (error 0x%x)", GetLastError() );
+ return false;
+ }
+ Plat_ApplyHardwareDataBreakpointsToNewThread( m_threadId );
+ ResumeThread( hThread );
+
+#elif defined(POSIX)
+ pthread_attr_t attr;
+ pthread_attr_init( &attr );
+ // From http://www.kernel.org/doc/man-pages/online/pages/man3/pthread_attr_setstacksize.3.html
+ // A thread's stack size is fixed at the time of thread creation. Only the main thread can dynamically grow its stack.
+ pthread_attr_setstacksize( &attr, MAX( nBytesStack, 1024u*1024 ) );
+ if ( pthread_create( &m_threadId, &attr, (void *(*)(void *))GetThreadProc(), new ThreadInit_t( init ) ) != 0 )
+ {
+ AssertMsg1( 0, "Failed to create thread (error 0x%x)", GetLastError() );
+ return false;
+ }
+ Plat_ApplyHardwareDataBreakpointsToNewThread( (long unsigned int)m_threadId );
+ bInitSuccess = true;
+#endif
+
+#if !defined( OSX )
+ ThreadSetDebugName( m_threadId, m_szName );
+#endif
+
+ if ( !WaitForCreateComplete( &createComplete ) )
+ {
+ Msg( "Thread failed to initialize\n" );
+#ifdef _WIN32
+ CloseHandle( m_hThread );
+ m_hThread = NULL;
+ m_threadId = 0;
+#elif defined(POSIX)
+ m_threadId = 0;
+#endif
+ return false;
+ }
+
+ if ( !bInitSuccess )
+ {
+ Msg( "Thread failed to initialize\n" );
+#ifdef _WIN32
+ CloseHandle( m_hThread );
+ m_hThread = NULL;
+ m_threadId = 0;
+#elif defined(POSIX)
+ m_threadId = 0;
+#endif
+ return false;
+ }
+
+#ifdef _WIN32
+ if ( !m_hThread )
+ {
+ Msg( "Thread exited immediately\n" );
+ }
+#endif
+
+#ifdef _WIN32
+ return !!m_hThread;
+#elif defined(POSIX)
+ return !!m_threadId;
+#endif
+}
+
+//---------------------------------------------------------
+//
+// Return true if the thread exists. false otherwise
+//
+
+bool CThread::IsAlive()
+{
+#ifdef _WIN32
+ DWORD dwExitCode;
+
+ return ( m_hThread &&
+ GetExitCodeThread( m_hThread, &dwExitCode ) &&
+ dwExitCode == STILL_ACTIVE );
+#elif defined(POSIX)
+ return m_threadId;
+#endif
+}
+
+//---------------------------------------------------------
+
+bool CThread::Join(unsigned timeout)
+{
+#ifdef _WIN32
+ if ( m_hThread )
+#elif defined(POSIX)
+ if ( m_threadId )
+#endif
+ {
+ AssertMsg(GetCurrentCThread() != this, _T("Thread cannot be joined with self"));
+
+#ifdef _WIN32
+ return ThreadJoin( (ThreadHandle_t)m_hThread );
+#elif defined(POSIX)
+ return ThreadJoin( (ThreadHandle_t)m_threadId );
+#endif
+ }
+ return true;
+}
+
+//---------------------------------------------------------
+
+#ifdef _WIN32
+
+HANDLE CThread::GetThreadHandle()
+{
+ return m_hThread;
+}
+
+#endif
+
+#if defined( _WIN32 ) || defined( LINUX )
+
+//---------------------------------------------------------
+
+uint CThread::GetThreadId()
+{
+ return m_threadId;
+}
+
+#endif
+
+//---------------------------------------------------------
+
+int CThread::GetResult()
+{
+ return m_result;
+}
+
+//---------------------------------------------------------
+//
+// Forcibly, abnormally, but relatively cleanly stop the thread
+//
+
+void CThread::Stop(int exitCode)
+{
+ if ( !IsAlive() )
+ return;
+
+ if ( GetCurrentCThread() == this )
+ {
+ m_result = exitCode;
+ if ( !( m_flags & SUPPORT_STOP_PROTOCOL ) )
+ {
+ OnExit();
+ g_pCurThread = (int)NULL;
+
+#ifdef _WIN32
+ CloseHandle( m_hThread );
+ m_hThread = NULL;
+#endif
+ Cleanup();
+ }
+ throw exitCode;
+ }
+ else
+ AssertMsg( 0, "Only thread can stop self: Use a higher-level protocol");
+}
+
+//---------------------------------------------------------
+
+int CThread::GetPriority() const
+{
+#ifdef _WIN32
+ return GetThreadPriority(m_hThread);
+#elif defined(POSIX)
+ struct sched_param thread_param;
+ int policy;
+ pthread_getschedparam( m_threadId, &policy, &thread_param );
+ return thread_param.sched_priority;
+#endif
+}
+
+//---------------------------------------------------------
+
+bool CThread::SetPriority(int priority)
+{
+#ifdef _WIN32
+ return ThreadSetPriority( (ThreadHandle_t)m_hThread, priority );
+#else
+ return ThreadSetPriority( (ThreadHandle_t)m_threadId, priority );
+#endif
+}
+
+
+//---------------------------------------------------------
+
+void CThread::SuspendCooperative()
+{
+ if ( ThreadGetCurrentId() == (ThreadId_t)m_threadId )
+ {
+ m_SuspendEventSignal.Set();
+ m_nSuspendCount = 1;
+ m_SuspendEvent.Wait();
+ m_nSuspendCount = 0;
+ }
+ else
+ {
+ Assert( !"Suspend not called from worker thread, this would be a bug" );
+ }
+}
+
+//---------------------------------------------------------
+
+void CThread::ResumeCooperative()
+{
+ Assert( m_nSuspendCount == 1 );
+ m_SuspendEvent.Set();
+}
+
+
+void CThread::BWaitForThreadSuspendCooperative()
+{
+ m_SuspendEventSignal.Wait();
+}
+
+
+#ifndef LINUX
+//---------------------------------------------------------
+
+unsigned int CThread::Suspend()
+{
+#ifdef _WIN32
+ return ( SuspendThread(m_hThread) != 0 );
+#elif defined(OSX)
+ int susCount = m_nSuspendCount++;
+ while ( thread_suspend( pthread_mach_thread_np(m_threadId) ) != KERN_SUCCESS )
+ {
+ };
+ return ( susCount) != 0;
+#else
+#error
+#endif
+}
+
+//---------------------------------------------------------
+
+unsigned int CThread::Resume()
+{
+#ifdef _WIN32
+ return ( ResumeThread(m_hThread) != 0 );
+#elif defined(OSX)
+ int susCount = m_nSuspendCount++;
+ while ( thread_resume( pthread_mach_thread_np(m_threadId) ) != KERN_SUCCESS )
+ {
+ };
+ return ( susCount - 1) != 0;
+#else
+#error
+#endif
+}
+#endif
+
+
+//---------------------------------------------------------
+
+bool CThread::Terminate(int exitCode)
+{
+#ifndef _X360
+#ifdef _WIN32
+ // I hope you know what you're doing!
+ if (!TerminateThread(m_hThread, exitCode))
+ return false;
+ CloseHandle( m_hThread );
+ m_hThread = NULL;
+ Cleanup();
+#elif defined(POSIX)
+ pthread_kill( m_threadId, SIGKILL );
+ Cleanup();
+#endif
+
+ return true;
+#else
+ AssertMsg( 0, "Cannot terminate a thread on the Xbox!" );
+ return false;
+#endif
+}
+
+//---------------------------------------------------------
+//
+// Get the Thread object that represents the current thread, if any.
+// Can return NULL if the current thread was not created using
+// CThread
+//
+
+CThread *CThread::GetCurrentCThread()
+{
+ return g_pCurThread;
+}
+
+//---------------------------------------------------------
+//
+// Offer a context switch. Under Win32, equivalent to Sleep(0)
+//
+
+void CThread::Yield()
+{
+#ifdef _WIN32
+ ::Sleep(0);
+#elif defined(POSIX)
+ pthread_yield();
+#endif
+}
+
+//---------------------------------------------------------
+//
+// This method causes the current thread to yield and not to be
+// scheduled for further execution until a certain amount of real
+// time has elapsed, more or less.
+//
+
+void CThread::Sleep(unsigned duration)
+{
+#ifdef _WIN32
+ ::Sleep(duration);
+#elif defined(POSIX)
+ usleep( duration * 1000 );
+#endif
+}
+
+//---------------------------------------------------------
+
+bool CThread::Init()
+{
+ return true;
+}
+
+//---------------------------------------------------------
+
+void CThread::OnExit()
+{
+}
+
+//---------------------------------------------------------
+
+void CThread::Cleanup()
+{
+ m_threadId = 0;
+}
+
+//---------------------------------------------------------
+bool CThread::WaitForCreateComplete(CThreadEvent * pEvent)
+{
+ // Force serialized thread creation...
+ if (!pEvent->Wait(60000))
+ {
+ AssertMsg( 0, "Probably deadlock or failure waiting for thread to initialize." );
+ return false;
+ }
+ return true;
+}
+
+//---------------------------------------------------------
+
+bool CThread::IsThreadRunning()
+{
+#ifdef _PS3
+ // ThreadIsThreadIdRunning() doesn't work on PS3 if the thread is in a zombie state
+ return m_eventTheadExit.Check();
+#else
+ return ThreadIsThreadIdRunning( (ThreadId_t)m_threadId );
+#endif
+}
+
+//---------------------------------------------------------
+
+CThread::ThreadProc_t CThread::GetThreadProc()
+{
+ return ThreadProc;
+}
+
+//---------------------------------------------------------
+
+unsigned __stdcall CThread::ThreadProc(LPVOID pv)
+{
+ std::auto_ptr<ThreadInit_t> pInit((ThreadInit_t *)pv);
+
+#ifdef _X360
+ // Make sure all threads are consistent w.r.t floating-point math
+ SetupFPUControlWord();
+#endif
+
+ CThread *pThread = pInit->pThread;
+ g_pCurThread = pThread;
+
+ g_pCurThread->m_pStackBase = AlignValue( &pThread, 4096 );
+
+ pInit->pThread->m_result = -1;
+
+ bool bInitSuccess = true;
+ if ( pInit->pfInitSuccess )
+ *(pInit->pfInitSuccess) = false;
+
+ try
+ {
+ bInitSuccess = pInit->pThread->Init();
+ }
+
+ catch (...)
+ {
+ pInit->pInitCompleteEvent->Set();
+ throw;
+ }
+
+ if ( pInit->pfInitSuccess )
+ *(pInit->pfInitSuccess) = bInitSuccess;
+ pInit->pInitCompleteEvent->Set();
+ if (!bInitSuccess)
+ return 0;
+
+ if ( pInit->pThread->m_flags & SUPPORT_STOP_PROTOCOL )
+ {
+ try
+ {
+ pInit->pThread->m_result = pInit->pThread->Run();
+ }
+
+ catch (...)
+ {
+ }
+ }
+ else
+ {
+ pInit->pThread->m_result = pInit->pThread->Run();
+ }
+
+ pInit->pThread->OnExit();
+ g_pCurThread = (int)NULL;
+ pInit->pThread->Cleanup();
+
+ return pInit->pThread->m_result;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+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_pParamFunctor(NULL),
+ m_ReturnVal(0)
+{
+}
+
+//---------------------------------------------------------
+
+int CWorkerThread::CallWorker(unsigned dw, unsigned timeout, bool fBoostWorkerPriorityToMaster, CFunctor *pParamFunctor)
+{
+ return Call(dw, timeout, fBoostWorkerPriorityToMaster, NULL, pParamFunctor);
+}
+
+//---------------------------------------------------------
+
+int CWorkerThread::CallMaster(unsigned dw, unsigned timeout)
+{
+ return Call(dw, timeout, false);
+}
+
+//---------------------------------------------------------
+
+CThreadEvent &CWorkerThread::GetCallHandle()
+{
+ return m_EventSend;
+}
+
+//---------------------------------------------------------
+
+unsigned CWorkerThread::GetCallParam( CFunctor **ppParamFunctor ) const
+{
+ if( ppParamFunctor )
+ *ppParamFunctor = m_pParamFunctor;
+ return m_Param;
+}
+
+//---------------------------------------------------------
+
+int CWorkerThread::BoostPriority()
+{
+ int iInitialPriority = GetPriority();
+ const int iNewPriority = ThreadGetPriority( (ThreadHandle_t)GetThreadID() );
+ if (iNewPriority > iInitialPriority)
+ ThreadSetPriority( (ThreadHandle_t)GetThreadID(), iNewPriority);
+ return iInitialPriority;
+}
+
+//---------------------------------------------------------
+
+static uint32 __stdcall DefaultWaitFunc( int nEvents, CThreadEvent * const *pEvents, int bWaitAll, uint32 timeout )
+{
+ return ThreadWaitForEvents( nEvents, pEvents, bWaitAll!=0, timeout );
+// return VCRHook_WaitForMultipleObjects( nHandles, (const void **)pHandles, bWaitAll, timeout );
+}
+
+
+int CWorkerThread::Call(unsigned dwParam, unsigned timeout, bool fBoostPriority, WaitFunc_t pfnWait, CFunctor *pParamFunctor)
+{
+ 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_pParamFunctor = pParamFunctor;
+
+ m_EventComplete.Reset();
+ m_EventSend.Set();
+
+ WaitForReply( timeout, pfnWait );
+
+ // MWD: Investigate why setting thread priorities is killing the 360
+#ifndef _X360
+ if (fBoostPriority)
+ SetPriority(iInitialPriority);
+#endif
+
+ 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;
+ }
+
+#ifdef WIN32
+ CThreadEvent threadEvent( GetThreadHandle() );
+#endif
+
+ CThreadEvent *waits[] =
+ {
+#ifdef WIN32
+ &threadEvent,
+#endif
+ &m_EventComplete
+ };
+
+
+ unsigned result;
+ bool bInDebugger = Plat_IsInDebugSession();
+
+ do
+ {
+#ifdef WIN32
+ // Make sure the thread handle hasn't been closed
+ if ( !GetThreadHandle() )
+ {
+ result = WAIT_OBJECT_0 + 1;
+ break;
+ }
+#endif
+ result = (*pfnWait)((sizeof(waits) / sizeof(waits[0])), waits, false,
+ (timeout != TT_INFINITE) ? timeout : 30000);
+
+ AssertMsg(timeout != TT_INFINITE || result != WAIT_TIMEOUT, "Possible hung thread, call to thread timed out");
+
+ } while ( bInDebugger && ( timeout == TT_INFINITE && result == WAIT_TIMEOUT ) );
+
+ if ( result != WAIT_OBJECT_0 + 1 )
+ {
+ if (result == WAIT_TIMEOUT)
+ m_ReturnVal = WTCR_TIMEOUT;
+ else if (result == WAIT_OBJECT_0)
+ {
+ 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, CFunctor **ppParamFunctor)
+{
+ if (!m_EventSend.Check())
+ {
+ return false;
+ }
+ else
+ {
+ if (pParam)
+ {
+ *pParam = m_Param;
+ }
+ if( ppParamFunctor )
+ {
+ *ppParamFunctor = m_pParamFunctor;
+ }
+ 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();
+}
+
+//-----------------------------------------------------------------------------