You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/CrySystem/SystemThreading.cpp

698 lines
26 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "CrySystem_precompiled.h"
#include "System.h"
#include "ThreadConfigManager.h"
#include "IThreadManager.h"
#include <CryCustomTypes.h>
#include "CryUtils.h"
#define INCLUDED_FROM_SYSTEM_THREADING_CPP
#if defined(AZ_RESTRICTED_PLATFORM)
#undef AZ_RESTRICTED_SECTION
#define SYSTEMTHREADING_CPP_SECTION_1 1
#define SYSTEMTHREADING_CPP_SECTION_2 2
#define SYSTEMTHREADING_CPP_SECTION_3 3
#endif
#if defined(WIN32) || defined(WIN64)
#include "CryThreadUtil_win32_thread.h"
#define AZ_RESTRICTED_SECTION_IMPLEMENTED
#elif defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEMTHREADING_CPP_SECTION_1
#include AZ_RESTRICTED_FILE(SystemThreading_cpp)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
#include "CryThreadUtil_pthread.h"
#endif
#undef INCLUDED_FROM_SYSTEM_THREADING_CPP
//////////////////////////////////////////////////////////////////////////
static void ApplyThreadConfig(CryThreadUtil::TThreadHandle pThreadHandle, const SThreadConfig& rThreadDesc)
{
// Apply config
if (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_ThreadName)
{
CryThreadUtil::CrySetThreadName(pThreadHandle, rThreadDesc.szThreadName);
}
if (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_Affinity)
{
CryThreadUtil::CrySetThreadAffinityMask(pThreadHandle, rThreadDesc.affinityFlag);
}
if (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_Priority)
{
CryThreadUtil::CrySetThreadPriority(pThreadHandle, rThreadDesc.priority);
}
if (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_PriorityBoost)
{
CryThreadUtil::CrySetThreadPriorityBoost(pThreadHandle, !rThreadDesc.bDisablePriorityBoost);
}
CryComment("<ThreadInfo> Configured thread \"%s\" %s | AffinityMask: %u %s | Priority: %i %s | PriorityBoost: %s %s",
rThreadDesc.szThreadName, (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_ThreadName) ? "" : "(ignored)",
rThreadDesc.affinityFlag, (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_Affinity) ? "" : "(ignored)",
rThreadDesc.priority, (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_Priority) ? "" : "(ignored)",
!rThreadDesc.bDisablePriorityBoost ? "enabled" : "disabled", (rThreadDesc.paramActivityFlag & SThreadConfig::eThreadParamFlag_PriorityBoost) ? "" : "(ignored)");
}
//////////////////////////////////////////////////////////////////////////
struct SThreadMetaData
: public CMultiThreadRefCount
{
SThreadMetaData()
: m_pThreadTask(0)
, m_threadHandle(0)
, m_threadId(0)
, m_threadName("Cry_UnnamedThread")
, m_isRunning(false)
{
}
IThread* m_pThreadTask; // Pointer to thread task to be executed
CThreadManager* m_pThreadMngr; // Pointer to thread manager
CryThreadUtil::TThreadHandle m_threadHandle; // Thread handle
threadID m_threadId; // The active threadId, 0 = Invalid Id
CryMutex m_threadExitMutex; // Mutex used to safeguard thread exit condition signaling
CryConditionVariable m_threadExitCondition; // Signaled when the thread is about to exit
CryFixedStringT<THREAD_NAME_LENGTH_MAX> m_threadName; // Thread name
volatile bool m_isRunning; // Indicates the thread is not ready to exit yet
};
//////////////////////////////////////////////////////////////////////////
class CThreadManager
: public IThreadManager
{
public:
// <interfuscator:shuffle>
virtual ~CThreadManager()
{
}
virtual bool SpawnThread(IThread* pThread, const char* sThreadName, ...) override;
virtual bool JoinThread(IThread* pThreadTask, EJoinMode eJoinMode) override;
virtual bool RegisterThirdPartyThread(void* pThreadHandle, const char* sThreadName, ...) override;
virtual bool UnRegisterThirdPartyThread(const char* sThreadName, ...) override;
virtual const char* GetThreadName(threadID nThreadId) override;
virtual threadID GetThreadId(const char* sThreadName, ...) override;
virtual void ForEachOtherThread(IThreadManager::ThreadModifFunction fpThreadModiFunction, void* pFuncData = 0) override;
virtual void EnableFloatExceptions(EFPE_Severity eFPESeverity, threadID nThreadId = 0) override;
virtual void EnableFloatExceptionsForEachOtherThread(EFPE_Severity eFPESeverity) override;
virtual uint GetFloatingPointExceptionMask() override;
virtual void SetFloatingPointExceptionMask(uint nMask) override;
IThreadConfigManager* GetThreadConfigManager() override
{
return &m_threadConfigManager;
}
// </interfuscator:shuffle>
private:
#if defined(WIN32) || defined(WIN64)
static unsigned __stdcall RunThread(void* thisPtr);
#define AZ_RESTRICTED_SECTION_IMPLEMENTED
#elif defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEMTHREADING_CPP_SECTION_2
#include AZ_RESTRICTED_FILE(SystemThreading_cpp)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
static void* RunThread(void* thisPtr);
#endif
private:
bool UnregisterThread(IThread* pThreadTask);
bool SpawnThreadImpl(IThread* pThread, const char* sThreadName);
bool RegisterThirdPartyThreadImpl(CryThreadUtil::TThreadHandle pThreadHandle, const char* sThreadName);
bool UnRegisterThirdPartyThreadImpl(const char* sThreadName);
threadID GetThreadIdImpl(const char* sThreadName);
private:
// Note: Guard SThreadMetaData with a _smart_ptr and lock to ensure that a thread waiting to be signaled by another still
// has access to valid SThreadMetaData even though the other thread terminated and as a result unregistered itself from the CThreadManager.
// An example would be the join method. Where one thread waits on a signal from an other thread to terminate and release its SThreadMetaData,
// sharing the same SThreadMetaData condition variable.
typedef std::map<IThread*, _smart_ptr<SThreadMetaData> > SpawnedThreadMap;
typedef std::map<IThread*, _smart_ptr<SThreadMetaData> >::iterator SpawnedThreadMapIter;
typedef std::map<IThread*, _smart_ptr<SThreadMetaData> >::const_iterator SpawnedThreadMapConstIter;
typedef std::pair<IThread*, _smart_ptr<SThreadMetaData> > ThreadMapPair;
typedef std::map<CryFixedStringT<THREAD_NAME_LENGTH_MAX>, _smart_ptr<SThreadMetaData> > SpawnedThirdPartyThreadMap;
typedef std::map<CryFixedStringT<THREAD_NAME_LENGTH_MAX>, _smart_ptr<SThreadMetaData> >::iterator SpawnedThirdPartyThreadMapIter;
typedef std::map<CryFixedStringT<THREAD_NAME_LENGTH_MAX>, _smart_ptr<SThreadMetaData> >::const_iterator SpawnedThirdPartyThreadMapConstIter;
typedef std::pair<CryFixedStringT<THREAD_NAME_LENGTH_MAX>, _smart_ptr<SThreadMetaData> > ThirdPartyThreadMapPair;
CryCriticalSection m_spawnedThreadsLock; // Use lock for the rare occasion a thread is created/destroyed
SpawnedThreadMap m_spawnedThreads; // Holds information of all spawned threads (through this system)
CryCriticalSection m_spawnedThirdPartyThreadsLock; // Use lock for the rare occasion a thread is created/destroyed
SpawnedThirdPartyThreadMap m_spawnedThirdPartyThread; // Holds information of all registered 3rd party threads (through this system)
CThreadConfigManager m_threadConfigManager;
};
//////////////////////////////////////////////////////////////////////////
#if defined(WIN32) || defined(WIN64)
unsigned __stdcall CThreadManager::RunThread(void* thisPtr)
#define AZ_RESTRICTED_SECTION_IMPLEMENTED
#elif defined(AZ_RESTRICTED_PLATFORM)
#define AZ_RESTRICTED_SECTION SYSTEMTHREADING_CPP_SECTION_3
#include AZ_RESTRICTED_FILE(SystemThreading_cpp)
#endif
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
#else
void* CThreadManager::RunThread(void* thisPtr)
#endif
{
// Check that we are not spawning a thread before gEnv->pSystem has been set
// Otherwise we cannot enable floating point exceptions
if (!gEnv || !gEnv->pSystem)
{
CryFatalError("[Error]: CThreadManager::RunThread requires gEnv->pSystem to be initialized.");
}
IThreadConfigManager* pThreadConfigMngr = gEnv->pThreadManager->GetThreadConfigManager();
SThreadMetaData* pThreadData = reinterpret_cast<SThreadMetaData*>(thisPtr);
pThreadData->m_threadId = CryThreadUtil::CryGetCurrentThreadId();
// Apply config
const SThreadConfig* pThreadConfig = pThreadConfigMngr->GetThreadConfig(pThreadData->m_threadName.c_str());
ApplyThreadConfig(pThreadData->m_threadHandle, *pThreadConfig);
// Config not found, append thread name with no config tag
if (pThreadConfig == pThreadConfigMngr->GetDefaultThreadConfig())
{
CryFixedStringT<THREAD_NAME_LENGTH_MAX> tmpString(pThreadData->m_threadName);
const char* cNoConfigAppendix = "(NoCfgFound)";
int nNumCharsToReplace = strlen(cNoConfigAppendix);
// Replace thread name ending
if (pThreadData->m_threadName.size() > THREAD_NAME_LENGTH_MAX - nNumCharsToReplace)
{
tmpString.replace(THREAD_NAME_LENGTH_MAX - nNumCharsToReplace, nNumCharsToReplace, cNoConfigAppendix, nNumCharsToReplace);
}
else
{
tmpString.append(cNoConfigAppendix);
}
// Print to log
if (pThreadConfigMngr->ConfigLoaded())
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> No Thread config found for thread %s using ... default config.", pThreadData->m_threadName.c_str());
}
else
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Thread config not loaded yet. Hence no thread config was found for thread %s ... using default config.", pThreadData->m_threadName.c_str());
}
// Rename Thread
CryThreadUtil::CrySetThreadName(pThreadData->m_threadHandle, tmpString.c_str());
}
// Enable FPEs
gEnv->pThreadManager->EnableFloatExceptions((EFPE_Severity)g_cvars.sys_float_exceptions);
// Execute thread code
pThreadData->m_pThreadTask->ThreadEntry();
// Disable FPEs
gEnv->pThreadManager->EnableFloatExceptions(eFPE_None);
// Signal imminent thread end
pThreadData->m_threadExitMutex.Lock();
pThreadData->m_isRunning = false;
pThreadData->m_threadExitCondition.Notify();
pThreadData->m_threadExitMutex.Unlock();
// Unregister thread
// Note: Unregister after m_threadExitCondition.Notify() to ensure pThreadData is still valid
pThreadData->m_pThreadMngr->UnregisterThread(pThreadData->m_pThreadTask);
CryThreadUtil::CryThreadExitCall();
return NULL;
}
//////////////////////////////////////////////////////////////////////////
bool CThreadManager::JoinThread(IThread* pThreadTask, EJoinMode eJoinMode)
{
// Get thread object
_smart_ptr<SThreadMetaData> pThreadImpl = 0;
{
AUTO_LOCK(m_spawnedThreadsLock);
SpawnedThreadMapIter res = m_spawnedThreads.find(pThreadTask);
if (res == m_spawnedThreads.end())
{
// Thread has already finished and unregistered itself.
// As it is complete we cannot wait for it.
// Hence return true.
return true;
}
pThreadImpl = res->second; // Keep object alive
}
// On try join, exit if the thread is not in a state to exit
if (eJoinMode == eJM_TryJoin && pThreadImpl->m_isRunning)
{
return false;
}
// Wait for completion of the target thread exit condition
pThreadImpl->m_threadExitMutex.Lock();
while (pThreadImpl->m_isRunning)
{
pThreadImpl->m_threadExitCondition.Wait(pThreadImpl->m_threadExitMutex);
}
pThreadImpl->m_threadExitMutex.Unlock();
return true;
}
//////////////////////////////////////////////////////////////////////////
bool CThreadManager::UnregisterThread(IThread* pThreadTask)
{
AUTO_LOCK(m_spawnedThreadsLock);
SpawnedThreadMapIter res = m_spawnedThreads.find(pThreadTask);
if (res == m_spawnedThreads.end())
{
// Duplicate thread deletion
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: UnregisterThread: Unable to unregister thread. Thread name could not be found. Double deletion? IThread pointer: %p", pThreadTask);
return false;
}
m_spawnedThreads.erase(res);
return true;
}
//////////////////////////////////////////////////////////////////////////
const char* CThreadManager::GetThreadName(threadID nThreadId)
{
// Loop over internally spawned threads
{
AUTO_LOCK(m_spawnedThreadsLock);
SpawnedThreadMapConstIter iter = m_spawnedThreads.begin();
SpawnedThreadMapConstIter iterEnd = m_spawnedThreads.end();
for (; iter != iterEnd; ++iter)
{
if (iter->second->m_threadId == nThreadId)
{
return iter->second->m_threadName.c_str();
}
}
}
// Loop over third party threads
{
AUTO_LOCK(m_spawnedThirdPartyThreadsLock);
SpawnedThirdPartyThreadMapConstIter iter = m_spawnedThirdPartyThread.begin();
SpawnedThirdPartyThreadMapConstIter iterEnd = m_spawnedThirdPartyThread.end();
for (; iter != iterEnd; ++iter)
{
if (iter->second->m_threadId == nThreadId)
{
return iter->second->m_threadName.c_str();
}
}
}
return "";
}
//////////////////////////////////////////////////////////////////////////
void CThreadManager::ForEachOtherThread(IThreadManager::ThreadModifFunction fpThreadModiFunction, void* pFuncData)
{
threadID nCurThreadId = CryThreadUtil::CryGetCurrentThreadId();
// Loop over internally spawned threads
{
AUTO_LOCK(m_spawnedThreadsLock);
SpawnedThreadMapConstIter iter = m_spawnedThreads.begin();
SpawnedThreadMapConstIter iterEnd = m_spawnedThreads.end();
for (; iter != iterEnd; ++iter)
{
if (iter->second->m_threadId != nCurThreadId)
{
fpThreadModiFunction(iter->second->m_threadId, pFuncData);
}
}
}
// Loop over third party threads
{
AUTO_LOCK(m_spawnedThirdPartyThreadsLock);
SpawnedThirdPartyThreadMapConstIter iter = m_spawnedThirdPartyThread.begin();
SpawnedThirdPartyThreadMapConstIter iterEnd = m_spawnedThirdPartyThread.end();
for (; iter != iterEnd; ++iter)
{
if (iter->second->m_threadId != nCurThreadId)
{
fpThreadModiFunction(iter->second->m_threadId, pFuncData);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
bool CThreadManager::SpawnThread(IThread* pThreadTask, const char* sThreadName, ...)
{
va_list args;
va_start(args, sThreadName);
// Format thread name
char strThreadName[THREAD_NAME_LENGTH_MAX];
const int cNumCharsNeeded = azvsnprintf(strThreadName, CRY_ARRAY_COUNT(strThreadName), sThreadName, args);
if (cNumCharsNeeded > THREAD_NAME_LENGTH_MAX - 1 || cNumCharsNeeded < 0)
{
strThreadName[THREAD_NAME_LENGTH_MAX - 1] = '\0'; // The WinApi only null terminates if strLen < bufSize
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: ThreadName \"%s\" has been truncated. Max characters allowed: %i. ", strThreadName, THREAD_NAME_LENGTH_MAX - 1);
}
// Spawn thread
bool ret = SpawnThreadImpl(pThreadTask, strThreadName);
if (!ret)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: CSystem::SpawnThread error spawning thread: \"%s\" ", strThreadName);
}
va_end(args);
return ret;
}
//////////////////////////////////////////////////////////////////////////
bool CThreadManager::SpawnThreadImpl(IThread* pThreadTask, const char* sThreadName)
{
if (pThreadTask == NULL)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "<ThreadInfo>: SpawnThread '%s' ThreadTask is NULL : ignoring", sThreadName);
return false;
}
// Init thread meta data
SThreadMetaData* pThreadMetaData = new SThreadMetaData();
pThreadMetaData->m_pThreadTask = pThreadTask;
pThreadMetaData->m_pThreadMngr = this;
pThreadMetaData->m_threadName = sThreadName;
// Add thread to map
{
AUTO_LOCK(m_spawnedThreadsLock);
SpawnedThreadMapIter res = m_spawnedThreads.find(pThreadTask);
if (res != m_spawnedThreads.end())
{
// Thread with same name already spawned
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: SpawnThread: Thread \"%s\" already exists.", sThreadName);
delete pThreadMetaData;
return false;
}
// Insert thread data
m_spawnedThreads.insert(ThreadMapPair(pThreadTask, pThreadMetaData));
}
// Load config if we can and if no config has been defined to be loaded
const SThreadConfig* pThreadConfig = gEnv->pThreadManager->GetThreadConfigManager()->GetThreadConfig(sThreadName);
// Create thread description
CryThreadUtil::SThreadCreationDesc desc = {sThreadName, RunThread, pThreadMetaData, pThreadConfig->paramActivityFlag & SThreadConfig::eThreadParamFlag_StackSize ? pThreadConfig->stackSizeBytes : 0};
// Spawn new thread
pThreadMetaData->m_isRunning = CryThreadUtil::CryCreateThread(&(pThreadMetaData->m_threadHandle), desc);
// Validate thread creation
if (!pThreadMetaData->m_isRunning)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: SpawnThread: Could not spawn thread \"%s\" .", sThreadName);
// Remove thread from map (also releases SThreadMetaData _smart_ptr)
m_spawnedThreads.erase(m_spawnedThreads.find(pThreadTask));
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
bool CThreadManager::RegisterThirdPartyThread(void* pThreadHandle, const char* sThreadName, ...)
{
if (!pThreadHandle)
{
pThreadHandle = reinterpret_cast<void*>(CryThreadUtil::CryGetCurrentThreadHandle());
}
va_list args;
va_start(args, sThreadName);
// Format thread name
char strThreadName[THREAD_NAME_LENGTH_MAX];
const int cNumCharsNeeded = azvsnprintf(strThreadName, CRY_ARRAY_COUNT(strThreadName), sThreadName, args);
if (cNumCharsNeeded > THREAD_NAME_LENGTH_MAX - 1)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: ThreadName \"%s\" has been truncated. Max characters allowed: %i. ", strThreadName, THREAD_NAME_LENGTH_MAX - 1);
}
// Register 3rd party thread
bool ret = RegisterThirdPartyThreadImpl(reinterpret_cast<CryThreadUtil::TThreadHandle>(pThreadHandle), strThreadName);
va_end(args);
return ret;
}
//////////////////////////////////////////////////////////////////////////
bool CThreadManager::RegisterThirdPartyThreadImpl(CryThreadUtil::TThreadHandle threadHandle, const char* sThreadName)
{
if (strcmp(sThreadName, "") == 0)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: CThreadManager::RegisterThirdPartyThread error registering third party thread. No name provided.");
return false;
}
// Init thread meta data
SThreadMetaData* pThreadMetaData = new SThreadMetaData();
pThreadMetaData->m_pThreadTask = 0;
pThreadMetaData->m_pThreadMngr = this;
pThreadMetaData->m_threadName = sThreadName;
pThreadMetaData->m_threadHandle = CryThreadUtil::CryDuplicateThreadHandle(threadHandle); // Ensure that we are not storing a pseudo handle
pThreadMetaData->m_threadId = CryThreadUtil::CryGetThreadId(pThreadMetaData->m_threadHandle);
{
AUTO_LOCK(m_spawnedThirdPartyThreadsLock);
// Check for duplicate
SpawnedThirdPartyThreadMapConstIter res = m_spawnedThirdPartyThread.find(sThreadName);
if (res != m_spawnedThirdPartyThread.end())
{
CryFatalError("CThreadManager::RegisterThirdPartyThread - Unable to register thread \"%s\""
"because another third party thread with the same name \"%s\" has already been registered with ThreadHandle: %p",
sThreadName, res->second->m_threadName.c_str(), reinterpret_cast<void*>(threadHandle));
delete pThreadMetaData;
return false;
}
// Insert thread data
m_spawnedThirdPartyThread.insert(ThirdPartyThreadMapPair(pThreadMetaData->m_threadName.c_str(), pThreadMetaData));
}
// Get thread config
const SThreadConfig* pThreadConfig = gEnv->pThreadManager->GetThreadConfigManager()->GetThreadConfig(sThreadName);
// Apply config (if not default config)
if (strcmp(pThreadConfig->szThreadName, sThreadName) == 0)
{
ApplyThreadConfig(threadHandle, *pThreadConfig);
}
// Update FP exception mask for 3rd party thread
if (pThreadMetaData->m_threadId)
{
CryThreadUtil::EnableFloatExceptions(pThreadMetaData->m_threadId, (EFPE_Severity)g_cvars.sys_float_exceptions);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
bool CThreadManager::UnRegisterThirdPartyThread(const char* sThreadName, ...)
{
va_list args;
va_start(args, sThreadName);
// Format thread name
char strThreadName[THREAD_NAME_LENGTH_MAX];
const int cNumCharsNeeded = azvsnprintf(strThreadName, CRY_ARRAY_COUNT(strThreadName), sThreadName, args);
if (cNumCharsNeeded > THREAD_NAME_LENGTH_MAX - 1)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: ThreadName \"%s\" has been truncated. Max characters allowed: %i. ", strThreadName, THREAD_NAME_LENGTH_MAX - 1);
}
// Unregister 3rd party thread
bool ret = UnRegisterThirdPartyThreadImpl(strThreadName);
va_end(args);
return ret;
}
//////////////////////////////////////////////////////////////////////////
bool CThreadManager::UnRegisterThirdPartyThreadImpl(const char* sThreadName)
{
AUTO_LOCK(m_spawnedThirdPartyThreadsLock);
SpawnedThirdPartyThreadMapIter res = m_spawnedThirdPartyThread.find(sThreadName);
if (res == m_spawnedThirdPartyThread.end())
{
// Duplicate thread deletion
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: UnRegisterThirdPartyThread: Unable to unregister thread. Thread name \"%s\" could not be found. Double deletion? ", sThreadName);
return false;
}
// Close thread handle
CryThreadUtil::CryCloseThreadHandle(res->second->m_threadHandle);
// Delete reference from container
m_spawnedThirdPartyThread.erase(res);
return true;
}
//////////////////////////////////////////////////////////////////////////
threadID CThreadManager::GetThreadId(const char* sThreadName, ...)
{
va_list args;
va_start(args, sThreadName);
// Format thread name
char strThreadName[THREAD_NAME_LENGTH_MAX];
const int cNumCharsNeeded = azvsnprintf(strThreadName, CRY_ARRAY_COUNT(strThreadName), sThreadName, args);
if (cNumCharsNeeded > THREAD_NAME_LENGTH_MAX - 1)
{
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo>: ThreadName \"%s\" has been truncated. Max characters allowed: %i. ", strThreadName, THREAD_NAME_LENGTH_MAX - 1);
}
// Get thread name
threadID ret = GetThreadIdImpl(strThreadName);
va_end(args);
return ret;
}
//////////////////////////////////////////////////////////////////////////
threadID CThreadManager::GetThreadIdImpl(const char* sThreadName)
{
// Loop over internally spawned threads
{
AUTO_LOCK(m_spawnedThreadsLock);
SpawnedThreadMapConstIter iter = m_spawnedThreads.begin();
SpawnedThreadMapConstIter iterEnd = m_spawnedThreads.end();
for (; iter != iterEnd; ++iter)
{
if (iter->second->m_threadName.compare(sThreadName) == 0)
{
return iter->second->m_threadId;
}
}
}
// Loop over third party threads
{
AUTO_LOCK(m_spawnedThirdPartyThreadsLock);
SpawnedThirdPartyThreadMapConstIter iter = m_spawnedThirdPartyThread.begin();
SpawnedThirdPartyThreadMapConstIter iterEnd = m_spawnedThirdPartyThread.end();
for (; iter != iterEnd; ++iter)
{
if (iter->second->m_threadName.compare(sThreadName) == 0)
{
return iter->second->m_threadId;
}
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
static void EnableFPExceptionsForThread(threadID nThreadId, void* pData)
{
EFPE_Severity eFPESeverity = *(EFPE_Severity*)pData;
CryThreadUtil::EnableFloatExceptions(nThreadId, eFPESeverity);
}
//////////////////////////////////////////////////////////////////////////
void CThreadManager::EnableFloatExceptions(EFPE_Severity eFPESeverity, threadID nThreadId /*=0*/)
{
CryThreadUtil::EnableFloatExceptions(nThreadId, eFPESeverity);
}
//////////////////////////////////////////////////////////////////////////
void CThreadManager::EnableFloatExceptionsForEachOtherThread(EFPE_Severity eFPESeverity)
{
ForEachOtherThread(EnableFPExceptionsForThread, &eFPESeverity);
}
//////////////////////////////////////////////////////////////////////////
uint CThreadManager::GetFloatingPointExceptionMask()
{
return CryThreadUtil::GetFloatingPointExceptionMask();
}
//////////////////////////////////////////////////////////////////////////
void CThreadManager::SetFloatingPointExceptionMask(uint nMask)
{
CryThreadUtil::SetFloatingPointExceptionMask(nMask);
}
//////////////////////////////////////////////////////////////////////////
void CSystem::InitThreadSystem()
{
m_pThreadManager = new CThreadManager();
m_env.pThreadManager = m_pThreadManager;
}
//////////////////////////////////////////////////////////////////////////
void CSystem::ShutDownThreadSystem()
{
SAFE_DELETE(m_pThreadManager);
}