Revision control
++ // Copyright (c) 2001-2004 NovodeX AG. All rights reserved.
++ // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
++ // SPDX-FileCopyrightText: Copyright (c) 2008-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
++ // SPDX-License-Identifier: Apache-2.0
++
++ #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)
++ // PT: __try/__except is structured exception handling, which only the Microsoft
++ // compilers, including clang-cl, implement. MinGW just cannot name the thread.
++ #if PX_VC
++ __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)
++ }
++ #else
++ PX_UNUSED(info);
++ #endif
++ }
++ }
++
++ 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