Files
XCEngine/engine/third_party/physx/source/foundation/windows/FdWindowsThread.cpp

422 lines
11 KiB
C++
Raw Normal View History

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
#include "foundation/windows/PxWindowsInclude.h"
#include "foundation/PxErrorCallback.h"
#include "foundation/PxAssert.h"
#include "foundation/PxThread.h"
#include "foundation/PxAlloca.h"
// an exception for setting the thread name in Microsoft debuggers
#define NS_MS_VC_EXCEPTION 0x406D1388
namespace physx
{
namespace
{
#if PX_VC
#pragma warning(disable : 4061) // enumerator 'identifier' in switch of enum 'enumeration' is not handled
#pragma warning(disable : 4191) //'operator/operation' : unsafe conversion from 'type of expression' to 'type required'
#endif
// struct for naming a thread in the debugger
#pragma pack(push, 8)
typedef struct tagTHREADNAME_INFO
{
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.
} THREADNAME_INFO;
#pragma pack(pop)
class ThreadImpl
{
public:
enum State
{
NotStarted,
Started,
Stopped
};
HANDLE thread;
LONG quitNow; // Should be 32bit aligned on SMP systems.
State state;
DWORD threadID;
PxThreadImpl::ExecuteFn fn;
void* arg;
uint32_t affinityMask;
const char* name;
};
static PX_FORCE_INLINE ThreadImpl* getThread(PxThreadImpl* impl)
{
return reinterpret_cast<ThreadImpl*>(impl);
}
static DWORD WINAPI PxThreadStart(LPVOID arg)
{
ThreadImpl* impl = getThread((PxThreadImpl*)arg);
// run either the passed in function or execute from the derived class (Runnable).
if(impl->fn)
(*impl->fn)(impl->arg);
else if(impl->arg)
((PxRunnable*)impl->arg)->execute();
return 0;
}
// cache physical thread count
static uint32_t gPhysicalCoreCount = 0;
}
uint32_t PxThreadImpl::getSize()
{
return sizeof(ThreadImpl);
}
PxThreadImpl::Id PxThreadImpl::getId()
{
return static_cast<Id>(GetCurrentThreadId());
}
// fwd GetLogicalProcessorInformation()
typedef BOOL(WINAPI* LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD);
uint32_t PxThreadImpl::getNbPhysicalCores()
{
if(!gPhysicalCoreCount)
{
// modified example code from: http://msdn.microsoft.com/en-us/library/ms683194
LPFN_GLPI glpi;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
DWORD returnLength = 0;
DWORD processorCoreCount = 0;
DWORD byteOffset = 0;
glpi = (LPFN_GLPI)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "GetLogicalProcessorInformation");
if(NULL == glpi)
{
// GetLogicalProcessorInformation not supported on OS < XP Service Pack 3
return 0;
}
DWORD rc = (DWORD)glpi(NULL, &returnLength);
PX_ASSERT(rc == FALSE);
PX_UNUSED(rc);
// first query reports required buffer space
if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)PxAlloca(returnLength);
}
else
{
PxGetFoundation().error(PxErrorCode::eINTERNAL_ERROR, PX_FL, "Error querying buffer size for number of physical processors");
return 0;
}
// retrieve data
rc = (DWORD)glpi(buffer, &returnLength);
if(rc != TRUE)
{
PxGetFoundation().error(PxErrorCode::eINTERNAL_ERROR, PX_FL, "Error querying number of physical processors");
return 0;
}
ptr = buffer;
while(byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength)
{
switch(ptr->Relationship)
{
case RelationProcessorCore:
processorCoreCount++;
break;
default:
break;
}
byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
ptr++;
}
gPhysicalCoreCount = processorCoreCount;
}
return gPhysicalCoreCount;
}
PxThreadImpl::PxThreadImpl()
{
getThread(this)->thread = NULL;
getThread(this)->state = ThreadImpl::NotStarted;
getThread(this)->quitNow = 0;
getThread(this)->fn = NULL;
getThread(this)->arg = NULL;
getThread(this)->affinityMask = 0;
getThread(this)->name = NULL;
}
PxThreadImpl::PxThreadImpl(ExecuteFn fn, void* arg, const char* name)
{
getThread(this)->thread = NULL;
getThread(this)->state = ThreadImpl::NotStarted;
getThread(this)->quitNow = 0;
getThread(this)->fn = fn;
getThread(this)->arg = arg;
getThread(this)->affinityMask = 0;
getThread(this)->name = name;
start(0, NULL);
}
PxThreadImpl::~PxThreadImpl()
{
if(getThread(this)->state == ThreadImpl::Started)
kill();
CloseHandle(getThread(this)->thread);
}
void PxThreadImpl::start(uint32_t stackSize, PxRunnable* runnable)
{
if(getThread(this)->state != ThreadImpl::NotStarted)
return;
getThread(this)->state = ThreadImpl::Started;
if(runnable && !getThread(this)->arg && !getThread(this)->fn)
getThread(this)->arg = runnable;
getThread(this)->thread =
CreateThread(NULL, stackSize, PxThreadStart, (LPVOID) this, CREATE_SUSPENDED, &getThread(this)->threadID);
if(!getThread(this)->thread)
{
PxGetFoundation().error(PxErrorCode::eINTERNAL_ERROR, PX_FL, "FdWindowsThread::start: Failed to create thread.");
getThread(this)->state = ThreadImpl::NotStarted;
return;
}
// set affinity, set name and resume
if(getThread(this)->affinityMask)
setAffinityMask(getThread(this)->affinityMask);
if (getThread(this)->name)
setName(getThread(this)->name);
DWORD rc = ResumeThread(getThread(this)->thread);
if(rc == DWORD(-1))
{
PxGetFoundation().error(PxErrorCode::eINTERNAL_ERROR, PX_FL, "FdWindowsThread::start: Failed to resume thread.");
getThread(this)->state = ThreadImpl::NotStarted;
return;
}
}
void PxThreadImpl::signalQuit()
{
InterlockedIncrement(&(getThread(this)->quitNow));
}
bool PxThreadImpl::waitForQuit()
{
if(getThread(this)->state == ThreadImpl::NotStarted)
return false;
WaitForSingleObject(getThread(this)->thread, INFINITE);
getThread(this)->state = ThreadImpl::Stopped;
return true;
}
bool PxThreadImpl::quitIsSignalled()
{
return InterlockedCompareExchange(&(getThread(this)->quitNow), 0, 0) != 0;
}
void PxThreadImpl::quit()
{
getThread(this)->state = ThreadImpl::Stopped;
ExitThread(0);
}
void PxThreadImpl::kill()
{
if(getThread(this)->state == ThreadImpl::Started)
TerminateThread(getThread(this)->thread, 0);
getThread(this)->state = ThreadImpl::Stopped;
}
void PxThreadImpl::sleep(uint32_t ms)
{
Sleep(ms);
}
void PxThreadImpl::yield()
{
SwitchToThread();
}
void PxThreadImpl::yieldProcessor()
{
YieldProcessor();
}
uint32_t PxThreadImpl::setAffinityMask(uint32_t mask)
{
if(mask)
{
// store affinity
getThread(this)->affinityMask = mask;
// if thread already started apply immediately
if(getThread(this)->state == ThreadImpl::Started)
{
uint32_t err = uint32_t(SetThreadAffinityMask(getThread(this)->thread, mask));
return err;
}
}
return 0;
}
void PxThreadImpl::setName(const char* name)
{
getThread(this)->name = name;
if (getThread(this)->state == ThreadImpl::Started)
{
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = name;
info.dwThreadID = getThread(this)->threadID;
info.dwFlags = 0;
// C++ Exceptions are disabled for this project, but SEH is not (and cannot be)
// http://stackoverflow.com/questions/943087/what-exactly-will-happen-if-i-disable-c-exceptions-in-a-project
__try
{
RaiseException(NS_MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
// this runs if not attached to a debugger (thus not really naming the thread)
}
}
}
void PxThreadImpl::setPriority(PxThreadPriority::Enum prio)
{
BOOL rc = false;
switch(prio)
{
case PxThreadPriority::eHIGH:
rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_HIGHEST);
break;
case PxThreadPriority::eABOVE_NORMAL:
rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_ABOVE_NORMAL);
break;
case PxThreadPriority::eNORMAL:
rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_NORMAL);
break;
case PxThreadPriority::eBELOW_NORMAL:
rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_BELOW_NORMAL);
break;
case PxThreadPriority::eLOW:
rc = SetThreadPriority(getThread(this)->thread, THREAD_PRIORITY_LOWEST);
break;
default:
break;
}
if(!rc)
{
PxGetFoundation().error(PxErrorCode::eINTERNAL_ERROR, PX_FL, "FdWindowsThread::setPriority: Failed to set thread priority.");
}
}
PxThreadPriority::Enum PxThreadImpl::getPriority(Id threadId)
{
PxThreadPriority::Enum retval = PxThreadPriority::eLOW;
int priority = GetThreadPriority((HANDLE)threadId);
PX_COMPILE_TIME_ASSERT(THREAD_PRIORITY_HIGHEST > THREAD_PRIORITY_ABOVE_NORMAL);
if(priority >= THREAD_PRIORITY_HIGHEST)
retval = PxThreadPriority::eHIGH;
else if(priority >= THREAD_PRIORITY_ABOVE_NORMAL)
retval = PxThreadPriority::eABOVE_NORMAL;
else if(priority >= THREAD_PRIORITY_NORMAL)
retval = PxThreadPriority::eNORMAL;
else if(priority >= THREAD_PRIORITY_BELOW_NORMAL)
retval = PxThreadPriority::eBELOW_NORMAL;
return retval;
}
PxU32 PxTlsAlloc()
{
DWORD rv = ::TlsAlloc();
PX_ASSERT(rv != TLS_OUT_OF_INDEXES);
return (PxU32)rv;
}
void PxTlsFree(PxU32 index)
{
::TlsFree(index);
}
void* PxTlsGet(PxU32 index)
{
return ::TlsGetValue(index);
}
size_t PxTlsGetValue(PxU32 index)
{
return size_t(::TlsGetValue(index));
}
PxU32 PxTlsSet(PxU32 index, void* value)
{
return PxU32(::TlsSetValue(index, value));
}
PxU32 PxTlsSetValue(PxU32 index, size_t value)
{
return PxU32(::TlsSetValue(index, reinterpret_cast<void*>(value)));
}
PxU32 PxThreadImpl::getDefaultStackSize()
{
return 1048576;
};
} // namespace physx