diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /external/vpc/public/tier0/threadtools.inl | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'external/vpc/public/tier0/threadtools.inl')
| -rw-r--r-- | external/vpc/public/tier0/threadtools.inl | 629 |
1 files changed, 629 insertions, 0 deletions
diff --git a/external/vpc/public/tier0/threadtools.inl b/external/vpc/public/tier0/threadtools.inl new file mode 100644 index 0000000..0afe97a --- /dev/null +++ b/external/vpc/public/tier0/threadtools.inl @@ -0,0 +1,629 @@ +#ifndef THREADTOOLS_INL +#define THREADTOOLS_INL + +// This file is included in threadtools.h for PS3 and threadtools.cpp for all other platforms +// +// Do not #include other files here + +#ifndef _PS3 +// this is defined in the .cpp for the PS3 to avoid introducing a dependency for files including the header +CTHREADLOCALPTR(CThread) g_pCurThread; + +#define INLINE_ON_PS3 +#else +// Inlining these functions on PS3 (which are called across PRX boundaries) saves us over 1ms per frame +#define INLINE_ON_PS3 inline +#endif + +INLINE_ON_PS3 CThread::CThread() : +#ifdef _WIN32 +m_hThread( NULL ), +m_threadId( 0 ), +#elif defined( _PS3 ) || defined(_POSIX) +m_threadId( 0 ), +m_threadZombieId( 0 ) , +#endif +m_result( 0 ), +m_flags( 0 ) +{ + m_szName[0] = 0; + m_NotSuspendedEvent.Set(); +} + +//--------------------------------------------------------- + +INLINE_ON_PS3 CThread::~CThread() +{ +#ifdef MSVC + if (m_hThread) +#elif defined(POSIX) && !defined( _PS3 ) + 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!! + } + } + } +#if defined(POSIX) || defined( _PS3 ) + if ( m_threadZombieId ) + { + // just clean up zombie threads immediately (the destructor is fired from the hosting thread) + Join(); + } +#endif +} + + +//--------------------------------------------------------- + +INLINE_ON_PS3 const char *CThread::GetName() +{ + AUTO_LOCK( m_Lock ); + if ( !m_szName[0] ) + { +#if defined( _WIN32 ) + _snprintf( m_szName, sizeof(m_szName) - 1, "Thread(%p/%p)", this, m_hThread ); +#elif defined( _PS3 ) + snprintf( m_szName, sizeof(m_szName) - 1, "Thread(%p)", this ); +#elif defined( POSIX ) + _snprintf( m_szName, sizeof(m_szName) - 1, "Thread(%p/0x%x)", this, (uint)m_threadId ); +#endif + m_szName[sizeof(m_szName) - 1] = 0; + } + return m_szName; +} + +//--------------------------------------------------------- + +INLINE_ON_PS3 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; +} + +//----------------------------------------------------- +// Functions for the other threads +//----------------------------------------------------- + +// Start thread running - error if already running +INLINE_ON_PS3 bool CThread::Start( unsigned nBytesStack, ThreadPriorityEnum_t nPriority ) +{ + 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 }; + +#if defined( THREAD_PARENT_STACK_TRACE_ENABLED ) + { + int iValidEntries = GetCallStack_Fast( init.ParentStackTrace, ARRAYSIZE( init.ParentStackTrace ), 0 ); + for( int i = iValidEntries; i < ARRAYSIZE( init.ParentStackTrace ); ++i ) + { + init.ParentStackTrace[i] = NULL; + } + } +#endif + +#ifdef PLATFORM_WINDOWS + m_hThread = (HANDLE)CreateThread( NULL, + nBytesStack, + (LPTHREAD_START_ROUTINE)GetThreadProc(), + new ThreadInit_t(init), + 0, + (LPDWORD)&m_threadId ); + + if( nPriority != PRIORITY_DEFAULT ) + { + SetThreadPriority( m_hThread, nPriority ); + } + + if ( !m_hThread ) + { + AssertMsg1( 0, "Failed to create thread (error 0x%x)", GetLastError() ); + return false; + } +#elif PLATFORM_PS3 + // On the PS3, a stack size of 0 doesn't imply a default stack size, so we need to force it to our + // own default size. + if ( nBytesStack == 0 ) + { + nBytesStack = PS3_SYS_PPU_THREAD_COMMON_STACK_SIZE; + } + + //The thread is about to begin + m_threadEnd.Reset(); + + // sony documentation: + // "If the PPU thread is not joined by sys_ppu_thread_join() after exit, + // it should always be created as non-joinable (not specifying + // SYS_PPU_THREAD_CREATE_JOINABLE). Otherwise, some resources are left + // allocated after termination of the PPU thread as if memory leaks." + const char* threadName=m_szName; + if ( sys_ppu_thread_create( &m_threadId, + (void(*)(uint64_t))GetThreadProc(), + (uint64_t)(new ThreadInit_t( init )), + nPriority, + nBytesStack, + SYS_PPU_THREAD_CREATE_JOINABLE , + threadName ) != CELL_OK ) + { + AssertMsg1( 0, "Failed to create thread (error 0x%x)", errno ); + return false; + } + + bInitSuccess = true; +#elif PLATFORM_POSIX + pthread_attr_t attr; + pthread_attr_init( &attr ); + 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; + } + bInitSuccess = true; +#endif + + + + if ( !WaitForCreateComplete( &createComplete ) ) + { + Msg( "Thread failed to initialize\n" ); +#ifdef _WIN32 + CloseHandle( m_hThread ); + m_hThread = NULL; +#elif defined( _PS3 ) + m_threadEnd.Set(); + m_threadId = NULL; + m_threadZombieId = 0; +#endif + + return false; + } + + if ( !bInitSuccess ) + { + Msg( "Thread failed to initialize\n" ); +#ifdef _WIN32 + CloseHandle( m_hThread ); + m_hThread = NULL; +#elif defined(POSIX) && !defined( _PS3 ) + m_threadId = 0; + m_threadZombieId = 0; +#endif + return false; + } + +#ifdef _WIN32 + if ( !m_hThread ) + { + Msg( "Thread exited immediately\n" ); + } +#endif + +#ifdef _WIN32 + AddThreadHandleToIDMap( m_hThread, m_threadId ); + return !!m_hThread; +#elif defined(POSIX) + return !!m_threadId; +#endif +} + +//--------------------------------------------------------- +// +// Return true if the thread has been created and hasn't yet exited +// + +INLINE_ON_PS3 bool CThread::IsAlive() +{ +#ifdef PLATFORM_WINDOWS + DWORD dwExitCode; + return ( + m_hThread + && GetExitCodeThread(m_hThread, &dwExitCode) + && dwExitCode == STILL_ACTIVE ); +#elif defined(POSIX) + return !!m_threadId; +#endif +} + +// This method causes the current thread to wait until this thread +// is no longer alive. +INLINE_ON_PS3 bool CThread::Join( unsigned timeout ) +{ +#ifdef _WIN32 + if ( m_hThread ) +#elif defined(POSIX) + if ( m_threadId || m_threadZombieId ) +#endif + { + AssertMsg(GetCurrentCThread() != this, _T("Thread cannot be joined with self")); + +#ifdef _WIN32 + return ThreadJoin( (ThreadHandle_t)m_hThread, timeout ); +#elif defined(POSIX) + bool ret = ThreadJoin( (ThreadHandle_t)(m_threadId ? m_threadId : m_threadZombieId), timeout ); + m_threadZombieId = 0; + return ret; +#endif + } + return true; +} + +//--------------------------------------------------------- + +INLINE_ON_PS3 ThreadHandle_t CThread::GetThreadHandle() +{ +#ifdef _WIN32 + return (ThreadHandle_t)m_hThread; +#else + return (ThreadHandle_t)m_threadId; +#endif +} + + +//--------------------------------------------------------- + +INLINE_ON_PS3 int CThread::GetResult() +{ + return m_result; +} + +//----------------------------------------------------- +// Functions for both this, and maybe, and other threads +//----------------------------------------------------- + +// Forcibly, abnormally, but relatively cleanly stop the thread +// + +INLINE_ON_PS3 void CThread::Stop(int exitCode) +{ + if ( !IsAlive() ) + return; + + if ( GetCurrentCThread() == this ) + { +#if !defined( _PS3 ) + m_result = exitCode; + if ( !( m_flags & SUPPORT_STOP_PROTOCOL ) ) + { + OnExit(); + g_pCurThread = NULL; + +#ifdef _WIN32 + CloseHandle( m_hThread ); + RemoveThreadHandleToIDMap( m_hThread ); + m_hThread = NULL; +#else + m_threadId = 0; + m_threadZombieId = 0; +#endif + } + else + { + throw exitCode; + } +#else + AssertMsg( false, "Called CThread::Stop() for a platform that doesn't have it!\n"); +#endif + } + else + AssertMsg( 0, "Only thread can stop self: Use a higher-level protocol"); +} + +//--------------------------------------------------------- + +// Get the priority +INLINE_ON_PS3 int CThread::GetPriority() const +{ +#ifdef _WIN32 + return GetThreadPriority(m_hThread); +#elif defined( _PS3 ) + return ThreadGetPriority( (ThreadHandle_t) m_threadId ); +#elif defined(POSIX) + struct sched_param thread_param; + int policy; + pthread_getschedparam( m_threadId, &policy, &thread_param ); + return thread_param.sched_priority; +#endif +} + +//--------------------------------------------------------- + +// Set the priority +INLINE_ON_PS3 bool CThread::SetPriority(int priority) +{ +#ifdef WIN32 + return ThreadSetPriority( (ThreadHandle_t)m_hThread, priority ); +#else + return ThreadSetPriority( (ThreadHandle_t)m_threadId, priority ); +#endif +} + +//--------------------------------------------------------- + +// Suspend a thread +INLINE_ON_PS3 unsigned CThread::Suspend() +{ + AssertMsg( ThreadGetCurrentId() == (ThreadId_t)m_threadId, "Cannot call CThread::Suspend from outside thread" ); + + if ( ThreadGetCurrentId() != (ThreadId_t)m_threadId ) + { + DebuggerBreakIfDebugging(); + } + + m_NotSuspendedEvent.Reset(); + m_NotSuspendedEvent.Wait(); + + return 0; +} + + +//--------------------------------------------------------- + +INLINE_ON_PS3 unsigned CThread::Resume() +{ + if ( m_NotSuspendedEvent.Check() ) + { + DevWarning( "Called Resume() on a thread that is not suspended!\n" ); + } + m_NotSuspendedEvent.Set(); + return 0; +} + +//--------------------------------------------------------- + +// Force hard-termination of thread. Used for critical failures. +INLINE_ON_PS3 bool CThread::Terminate(int exitCode) +{ +#if defined( _X360 ) + AssertMsg( 0, "Cannot terminate a thread on the Xbox!" ); + return false; +#elif defined( _WIN32 ) + // I hope you know what you're doing! + if (!TerminateThread(m_hThread, exitCode)) + return false; + CloseHandle( m_hThread ); + RemoveThreadHandleToIDMap( m_hThread ); + m_hThread = NULL; +#elif defined( _PS3 ) + m_threadEnd.Set(); + m_threadId = NULL; +#elif defined(POSIX) + pthread_kill( m_threadId, SIGKILL ); + m_threadId = 0; +#endif + return true; +} + +//----------------------------------------------------- +// Global methods +//----------------------------------------------------- + +// Get the Thread object that represents the current thread, if any. +// Can return NULL if the current thread was not created using +// CThread +// + +INLINE_ON_PS3 CThread *CThread::GetCurrentCThread() +{ +#ifdef _PS3 + return GetCurThreadPS3(); +#else + return g_pCurThread; +#endif +} + +//--------------------------------------------------------- +// +// Offer a context switch. Under Win32, equivalent to Sleep(0) +// + +#ifdef Yield +#undef Yield +#endif +INLINE_ON_PS3 void CThread::Yield() +{ +#ifdef _WIN32 + ::Sleep(0); +#elif defined( _PS3 ) + // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead. + sys_timer_usleep( 60 ); +#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. Duration is in milliseconds + +INLINE_ON_PS3 void CThread::Sleep( unsigned duration ) +{ +#ifdef _WIN32 + ::Sleep(duration); +#elif defined (_PS3) + sys_timer_usleep( duration * 1000 ); +#elif defined(POSIX) + usleep( duration * 1000 ); +#endif +} + +//--------------------------------------------------------- + +// Optional pre-run call, with ability to fail-create. Note Init() +// is forced synchronous with Start() +INLINE_ON_PS3 bool CThread::Init() +{ + return true; +} + +//--------------------------------------------------------- + +#if defined( _PS3 ) +INLINE_ON_PS3 int CThread::Run() +{ + return -1; +} +#endif // _PS3 + +// Called when the thread exits +INLINE_ON_PS3 void CThread::OnExit() { } + +// Allow for custom start waiting +INLINE_ON_PS3 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; +} + +//--------------------------------------------------------- +INLINE_ON_PS3 CThread::ThreadProc_t CThread::GetThreadProc() +{ + return ThreadProc; +} + +#ifdef PLATFORM_WINDOWS +unsigned long STDCALL CThread::ThreadProc(LPVOID pv) +#else +INLINE_ON_PS3 void* CThread::ThreadProc(LPVOID pv) +#endif +{ +#if defined( POSIX ) || defined( _PS3 ) + ThreadInit_t *pInit = reinterpret_cast<ThreadInit_t*>(pv); +#else + std::auto_ptr<ThreadInit_t> pInit((ThreadInit_t *)pv); +#endif + +#ifdef _X360 + // Make sure all threads are consistent w.r.t floating-point math + SetupFPUControlWord(); +#endif + AllocateThreadID(); + + CThread *pThread = pInit->pThread; +#ifdef _PS3 + SetCurThreadPS3( pThread ); +#else + g_pCurThread = pThread; +#endif + + pThread->m_pStackBase = AlignValue( &pThread, 4096 ); + + pInit->pThread->m_result = -1; + +#if defined( THREAD_PARENT_STACK_TRACE_ENABLED ) + CStackTop_ReferenceParentStack stackTop( pInit->ParentStackTrace, ARRAYSIZE( pInit->ParentStackTrace ) ); +#endif + + bool bInitSuccess = true; + if ( pInit->pfInitSuccess ) + *(pInit->pfInitSuccess) = false; + +#ifdef _PS3 + *(pInit->pfInitSuccess) = pInit->pThread->Init(); +#else + try + { + bInitSuccess = pInit->pThread->Init(); + } + + catch (...) + { + pInit->pInitCompleteEvent->Set(); + throw; + } +#endif // _PS3 + + if ( pInit->pfInitSuccess ) + *(pInit->pfInitSuccess) = bInitSuccess; + pInit->pInitCompleteEvent->Set(); + if (!bInitSuccess) + return 0; + + if ( !Plat_IsInDebugSession() && (pInit->pThread->m_flags & SUPPORT_STOP_PROTOCOL) ) + { +#ifndef _PS3 + try +#endif + { + pInit->pThread->m_result = pInit->pThread->Run(); + } + +#ifndef _PS3 + catch (...) + { + } +#endif + } + else + { + pInit->pThread->m_result = pInit->pThread->Run(); + } + + pInit->pThread->OnExit(); +#ifdef _PS3 + SetCurThreadPS3( NULL ); +#else + g_pCurThread = NULL; +#endif + FreeThreadID(); + + AUTO_LOCK( pThread->m_Lock ); +#ifdef _WIN32 + CloseHandle( pThread->m_hThread ); + RemoveThreadHandleToIDMap( pThread->m_hThread ); + pThread->m_hThread = NULL; +#elif defined( _PS3 ) + pThread->m_threadZombieId = pThread->m_threadId; + pThread->m_threadEnd.Set(); + pThread->m_threadId = 0; +#elif defined(POSIX) + pThread->m_threadZombieId = pThread->m_threadId; + pThread->m_threadId = 0; +#else +#error +#endif + + pThread->m_ExitEvent.Set(); +#ifdef _PS3 + { + pThread->m_Lock.Unlock(); + sys_ppu_thread_exit( pInit->pThread->m_result ); + // reacquire the lock in case thread exit didn't actually exit the thread, so that + // AUTO_LOCK won't double-unlock the lock (to keep it paired) + pThread->m_Lock.Lock(); + } +#endif + +#if defined( POSIX )|| defined( _PS3 ) + return (void*)pInit->pThread->m_result; +#else + return pInit->pThread->m_result; +#endif +} + +#endif // THREADTOOLS_INL |