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.
433 lines
16 KiB
C++
433 lines
16 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.
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// NOTE: INTERNAL HEADER NOT FOR PUBLIC USE
|
|
// This header should only be include by SystemThreading.cpp only
|
|
// It provides an interface for WinApi intrinsics
|
|
// It's only client should be CThreadManager which should manage all thread interaction
|
|
|
|
#pragma once
|
|
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#undef AZ_RESTRICTED_SECTION
|
|
#define CRYTHREADUTIL_WIN32_THREAD_H_SECTION_1 1
|
|
#define CRYTHREADUTIL_WIN32_THREAD_H_SECTION_2 2
|
|
#endif
|
|
|
|
#if !defined(INCLUDED_FROM_SYSTEM_THREADING_CPP)
|
|
# error "CRYTEK INTERNAL HEADER. ONLY INCLUDE FROM SYSTEMTHRADING.CPP."
|
|
#endif
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#define DEFAULT_THREAD_STACK_SIZE_KB 0
|
|
|
|
// Returns the last Win32 error, in string format. Returns an empty string if there is no error.
|
|
static string GetLastErrorAsString()
|
|
{
|
|
// Get the error message, if any.
|
|
DWORD errorMessageID = GetLastError();
|
|
if (errorMessageID == 0)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION CRYTHREADUTIL_WIN32_THREAD_H_SECTION_1
|
|
#include AZ_RESTRICTED_FILE(CryThreadUtil_win32_thread_h)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#else
|
|
LPSTR messageBuffer = nullptr;
|
|
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), messageBuffer, 0, NULL);
|
|
|
|
string message(messageBuffer, size);
|
|
|
|
// Free the buffer.
|
|
LocalFree(messageBuffer);
|
|
|
|
return message;
|
|
#endif
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// THREAD CREATION AND MANAGMENT
|
|
//////////////////////////////////////////////////////////////////////////
|
|
namespace CryThreadUtil
|
|
{
|
|
// Define type for platform specific thread handle
|
|
typedef THREAD_HANDLE TThreadHandle;
|
|
|
|
struct SThreadCreationDesc
|
|
{
|
|
// Define platform specific thread entry function functor type
|
|
typedef unsigned int(_stdcall * EntryFunc)(void*);
|
|
|
|
const char* szThreadName;
|
|
EntryFunc fpEntryFunc;
|
|
void* pArgList;
|
|
uint32 nStackSizeInBytes;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
TThreadHandle CryGetCurrentThreadHandle()
|
|
{
|
|
return GetCurrentThread(); // most likely returns pseudo handle (0xfffffffe)
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Note: Handle must be closed lated via CryCloseThreadHandle()
|
|
TThreadHandle CryDuplicateThreadHandle(const TThreadHandle& hThreadHandle)
|
|
{
|
|
// NOTES:
|
|
// GetCurrentThread() may return a psydo handle to the current thread
|
|
// to avoid going into the slower kernel mode.
|
|
// Hence the handle is useless when being used from an other thread.
|
|
// - GetCurrentThread() -> 0xfffffffe
|
|
// - GetCurrentProcess() -> 0xffffffff
|
|
|
|
HANDLE hRealHandle = 0;
|
|
DuplicateHandle(GetCurrentProcess(), // Source Process Handle.
|
|
hThreadHandle, // Source Handle to dup.
|
|
GetCurrentProcess(), // Target Process Handle.
|
|
&hRealHandle, // Target Handle pointer.
|
|
0, // Options flag.
|
|
TRUE, // Inheritable flag
|
|
DUPLICATE_SAME_ACCESS); // Options
|
|
|
|
return (TThreadHandle)hRealHandle;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CryCloseThreadHandle(TThreadHandle& hThreadHandle)
|
|
{
|
|
if (hThreadHandle)
|
|
{
|
|
CloseHandle(hThreadHandle);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
threadID CryGetCurrentThreadId()
|
|
{
|
|
return GetCurrentThreadId();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
threadID CryGetThreadId(TThreadHandle hThreadHandle)
|
|
{
|
|
return GetThreadId(hThreadHandle);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CrySetThreadName(TThreadHandle pThreadHandle, const char* sThreadName)
|
|
{
|
|
const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
|
|
|
struct SThreadNameDesc
|
|
{
|
|
DWORD dwType; // Must be 0x1000.
|
|
LPCSTR szName; // Pointer to name (in user addr space).
|
|
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
|
DWORD dwFlags; // Reserved for future use, must be zero.
|
|
};
|
|
|
|
SThreadNameDesc info;
|
|
info.dwType = 0x1000;
|
|
info.szName = sThreadName;
|
|
info.dwThreadID = GetThreadId(pThreadHandle);
|
|
info.dwFlags = 0;
|
|
AZ_PUSH_DISABLE_WARNING(6312 6322, "-Wunknown-warning-option")
|
|
// warning C6312: Possible infinite loop: use of the constant EXCEPTION_CONTINUE_EXECUTION in the exception-filter expression of a try-except
|
|
// warning C6322: empty _except block
|
|
__try
|
|
{
|
|
// Raise exception to set thread name for attached debugger
|
|
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR*)&info);
|
|
}
|
|
__except (GetExceptionCode() == MS_VC_EXCEPTION ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
AZ_POP_DISABLE_WARNING
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CrySetThreadAffinityMask(TThreadHandle pThreadHandle, DWORD dwAffinityMask)
|
|
{
|
|
SetThreadAffinityMask(pThreadHandle, dwAffinityMask);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CrySetThreadPriority(TThreadHandle pThreadHandle, DWORD dwPriority)
|
|
{
|
|
if (!SetThreadPriority(pThreadHandle, dwPriority))
|
|
{
|
|
string errMsg = GetLastErrorAsString();
|
|
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to set thread priority. System Error Msg: \"%s\"", errMsg.c_str());
|
|
return;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CrySetThreadPriorityBoost(TThreadHandle pThreadHandle, bool bEnabled)
|
|
{
|
|
SetThreadPriorityBoost(pThreadHandle, !bEnabled);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
bool CryCreateThread(TThreadHandle* pThreadHandle, const SThreadCreationDesc& threadDesc)
|
|
{
|
|
const uint32 nStackSize = threadDesc.nStackSizeInBytes != 0 ? threadDesc.nStackSizeInBytes : DEFAULT_THREAD_STACK_SIZE_KB * 1024;
|
|
|
|
// Create thread
|
|
unsigned int threadId = 0;
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION CRYTHREADUTIL_WIN32_THREAD_H_SECTION_2
|
|
#include AZ_RESTRICTED_FILE(CryThreadUtil_win32_thread_h)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#else
|
|
*pThreadHandle = (void*)_beginthreadex(NULL, nStackSize, threadDesc.fpEntryFunc, threadDesc.pArgList, CREATE_SUSPENDED, &threadId);
|
|
#endif
|
|
|
|
if (!(*pThreadHandle))
|
|
{
|
|
string errMsg = GetLastErrorAsString();
|
|
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "<ThreadInfo> Unable to create thread \"%s\". System Error Msg: \"%s\"", threadDesc.szThreadName, errMsg.c_str());
|
|
return false;
|
|
}
|
|
|
|
// Start thread
|
|
ResumeThread(*pThreadHandle);
|
|
|
|
// Print info to log
|
|
CryComment("<ThreadInfo>: New thread \"%s\" | StackSize: %u(KB)", threadDesc.szThreadName, threadDesc.nStackSizeInBytes / 1024);
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CryThreadExitCall()
|
|
{
|
|
// Note on: ExitThread() (from MSDN)
|
|
// ExitThread is the preferred method of exiting a thread in C code.
|
|
// However, in C++ code, the thread is exited before any destructor can be called or any other automatic cleanup can be performed.
|
|
// Therefore, in C++ code, you should return from your thread function.
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FLOATING POINT EXCEPTIONS
|
|
//////////////////////////////////////////////////////////////////////////
|
|
namespace CryThreadUtil
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////
|
|
void EnableFloatExceptions([[maybe_unused]] EFPE_Severity eFPESeverity)
|
|
{
|
|
AZ_PUSH_DISABLE_WARNING(4996, "-Wunknown-warning-option")
|
|
|
|
// Optimization
|
|
// Enable DAZ/FZ
|
|
// Denormals Are Zeros
|
|
// Flush-to-Zero
|
|
_controlfp(_DN_FLUSH, _MCW_DN);
|
|
_mm_setcsr(_mm_getcsr() | _MM_FLUSH_ZERO_ON);
|
|
|
|
#ifndef _RELEASE
|
|
if (eFPESeverity == eFPE_None)
|
|
{
|
|
// mask all floating exceptions off.
|
|
_controlfp(_MCW_EM, _MCW_EM);
|
|
_mm_setcsr(_mm_getcsr() | _MM_MASK_MASK);
|
|
}
|
|
else
|
|
{
|
|
// Clear pending exceptions
|
|
_fpreset();
|
|
|
|
if (eFPESeverity == eFPE_Basic)
|
|
{
|
|
// Enable:
|
|
// - _EM_ZERODIVIDE
|
|
// - _EM_INVALID
|
|
//
|
|
// Disable:
|
|
// - _EM_DENORMAL
|
|
// - _EM_OVERFLOW
|
|
// - _EM_UNDERFLOW
|
|
// - _EM_INEXACT
|
|
|
|
_controlfp(_EM_INEXACT | _EM_DENORMAL | _EM_UNDERFLOW | _EM_OVERFLOW, _MCW_EM);
|
|
_mm_setcsr((_mm_getcsr() & ~_MM_MASK_MASK) | (_MM_MASK_DENORM | _MM_MASK_INEXACT | _MM_MASK_UNDERFLOW | _MM_MASK_OVERFLOW));
|
|
|
|
//_mm_setcsr(_mm_getcsr() & ~0x280);
|
|
}
|
|
|
|
if (eFPESeverity == eFPE_All)
|
|
{
|
|
// Enable:
|
|
// - _EM_ZERODIVIDE
|
|
// - _EM_INVALID
|
|
// - _EM_UNDERFLOW
|
|
// - _EM_OVERFLOW
|
|
//
|
|
// Disable:
|
|
// - _EM_INEXACT
|
|
// - _EM_DENORMAL
|
|
|
|
_controlfp(_EM_INEXACT | _EM_DENORMAL, _MCW_EM);
|
|
_mm_setcsr((_mm_getcsr() & ~_MM_MASK_MASK) | (_MM_MASK_INEXACT | _MM_MASK_DENORM));
|
|
}
|
|
}
|
|
#endif // _RELEASE
|
|
|
|
AZ_POP_DISABLE_WARNING
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void EnableFloatExceptions(threadID nThreadId, EFPE_Severity eFPESeverity)
|
|
{
|
|
if (eFPESeverity >= eFPE_LastEntry)
|
|
{
|
|
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Floating Point Exception (FPE) severity is out of range. (%i)", eFPESeverity);
|
|
}
|
|
|
|
// Check if the thread ID matches the current thread
|
|
if (nThreadId == 0 || nThreadId == CryGetCurrentThreadId())
|
|
{
|
|
EnableFloatExceptions(eFPESeverity);
|
|
return;
|
|
}
|
|
|
|
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, true, nThreadId);
|
|
|
|
if (hThread == 0)
|
|
{
|
|
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Unable to open thread. %p", hThread);
|
|
return;
|
|
}
|
|
|
|
SuspendThread(hThread);
|
|
|
|
CONTEXT ctx;
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
ctx.ContextFlags = CONTEXT_ALL;
|
|
if (GetThreadContext(hThread, &ctx) == 0)
|
|
{
|
|
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Unable to get thread context");
|
|
ResumeThread(hThread);
|
|
CloseHandle(hThread);
|
|
return;
|
|
}
|
|
|
|
#ifdef PLATFORM_64BIT
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Note:
|
|
// DO NOT USE ctx.FltSave.MxCsr ... SetThreadContext() will copy the value of ctx.MxCsr into it
|
|
//////////////////////////////////////////////////////////////////////////
|
|
DWORD& floatMxCsr = ctx.MxCsr; // Hold FPE Mask and Status for MMX (SSE) floating point registers
|
|
WORD& floatControlWord = ctx.FltSave.ControlWord; // Hold FPE Mask for floating point registers
|
|
#ifndef _RELEASE
|
|
WORD& floatStatuslWord = ctx.FltSave.StatusWord; // Holds FPE Status for floating point registers
|
|
#endif
|
|
#else
|
|
DWORD& floatMxCsr = *(DWORD*)(&ctx.ExtendedRegisters[24]); // Hold FPE Mask and Status for MMX (SSE) floating point registers
|
|
DWORD& floatControlWord = ctx.FloatSave.ControlWord; // Hold FPE Mask for floating point registers
|
|
DWORD& floatStatuslWord = ctx.FloatSave.StatusWord; // Holds FPE Status for floating point registers
|
|
#endif
|
|
|
|
// Flush-To-Zero Mode
|
|
// Two conditions must be met for FTZ processing to occur:
|
|
// - The FTZ bit (bit 15) in the MXCSR register must be masked (value = 1).
|
|
// - The underflow exception (bit 11) needs to be masked (value = 1).
|
|
|
|
// Set flush mode to zero mode
|
|
floatControlWord = (floatControlWord & ~_MCW_DN) | _DN_FLUSH;
|
|
floatMxCsr = (floatMxCsr & ~_MM_FLUSH_ZERO_MASK) | (_MM_FLUSH_ZERO_ON);
|
|
|
|
#ifndef _RELEASE
|
|
|
|
// Reset FPE bits
|
|
floatControlWord = floatControlWord | _MCW_EM;
|
|
floatMxCsr = floatMxCsr | _MM_MASK_MASK;
|
|
|
|
// Clear pending exceptions
|
|
floatStatuslWord = floatStatuslWord & ~(_SW_INEXACT | _SW_UNDERFLOW | _SW_OVERFLOW | _SW_ZERODIVIDE | _SW_INVALID | _SW_DENORMAL);
|
|
floatMxCsr = floatMxCsr & ~(_MM_EXCEPT_INEXACT | _MM_EXCEPT_UNDERFLOW | _MM_EXCEPT_OVERFLOW | _MM_EXCEPT_DIV_ZERO | _MM_EXCEPT_INVALID | _MM_EXCEPT_DENORM);
|
|
|
|
if (eFPESeverity == eFPE_Basic)
|
|
{
|
|
// Enable:
|
|
// - _EM_ZERODIVIDE
|
|
// - _EM_INVALID
|
|
//
|
|
// Disable:
|
|
// - _EM_DENORMAL
|
|
// - _EM_OVERFLOW
|
|
// - _EM_UNDERFLOW
|
|
// - _EM_INEXACT
|
|
|
|
floatControlWord = (floatControlWord & ~_MCW_EM) | (_EM_DENORMAL | _EM_INEXACT | EM_UNDERFLOW | _EM_OVERFLOW);
|
|
floatMxCsr = (floatMxCsr & ~_MM_MASK_MASK) | (_MM_MASK_DENORM | _MM_MASK_INEXACT | _MM_MASK_UNDERFLOW | _MM_MASK_OVERFLOW);
|
|
}
|
|
|
|
if (eFPESeverity == eFPE_All)
|
|
{
|
|
// Enable:
|
|
// - _EM_ZERODIVIDE
|
|
// - _EM_INVALID
|
|
// - _EM_UNDERFLOW
|
|
// - _EM_OVERFLOW
|
|
//
|
|
// Disable:
|
|
// - _EM_INEXACT
|
|
// - _EM_DENORMAL
|
|
|
|
floatControlWord = (floatControlWord & ~_MCW_EM) | (_EM_INEXACT | _EM_DENORMAL);
|
|
floatMxCsr = (floatMxCsr & ~_MM_MASK_MASK) | (_MM_MASK_INEXACT | _MM_MASK_DENORM);
|
|
}
|
|
#endif
|
|
|
|
ctx.ContextFlags = CONTEXT_ALL;
|
|
if (SetThreadContext(hThread, &ctx) == 0)
|
|
{
|
|
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_ERROR, "Error setting ThreadContext for ThreadID: %u", nThreadId);
|
|
ResumeThread(hThread);
|
|
CloseHandle(hThread);
|
|
return;
|
|
}
|
|
|
|
ResumeThread(hThread);
|
|
CloseHandle(hThread);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
uint GetFloatingPointExceptionMask()
|
|
{
|
|
uint nMask = 0;
|
|
_clearfp();
|
|
_controlfp_s(&nMask, 0, 0);
|
|
return nMask;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void SetFloatingPointExceptionMask(uint nMask)
|
|
{
|
|
uint temp = 0;
|
|
_clearfp();
|
|
const unsigned int kAllowedBits = _MCW_DN | _MCW_EM | _MCW_RC;
|
|
_controlfp_s(&temp, nMask, kAllowedBits);
|
|
}
|
|
}
|