feat(physics): wire physx sdk into build

This commit is contained in:
2026-04-15 12:22:15 +08:00
parent 5bf258df6d
commit 31f40e2cbb
2044 changed files with 752623 additions and 1 deletions

View File

@@ -0,0 +1,171 @@
// 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/PxAtomic.h"
#if ! PX_EMSCRIPTEN
#define PAUSE() asm("nop")
#else
#define PAUSE()
#endif
namespace physx
{
void* PxAtomicCompareExchangePointer(volatile void** dest, void* exch, void* comp)
{
return __sync_val_compare_and_swap(const_cast<void**>(dest), comp, exch);
}
PxI32 PxAtomicCompareExchange(volatile PxI32* dest, PxI32 exch, PxI32 comp)
{
return __sync_val_compare_and_swap(dest, comp, exch);
}
PxI64 PxAtomicCompareExchange(volatile PxI64* dest, PxI64 exch, PxI64 comp)
{
return __sync_val_compare_and_swap(dest, comp, exch);
}
PxI32 PxAtomicIncrement(volatile PxI32* val)
{
return __sync_add_and_fetch(val, 1);
}
PxI64 PxAtomicIncrement(volatile PxI64* val)
{
return __sync_add_and_fetch(val, 1);
}
PxI32 PxAtomicDecrement(volatile PxI32* val)
{
return __sync_sub_and_fetch(val, 1);
}
PxI64 PxAtomicDecrement(volatile PxI64* val)
{
return __sync_sub_and_fetch(val, 1);
}
PxI32 PxAtomicAdd(volatile PxI32* val, PxI32 delta)
{
return __sync_add_and_fetch(val, delta);
}
PxI64 PxAtomicAdd(volatile PxI64* val, PxI64 delta)
{
return __sync_add_and_fetch(val, delta);
}
PxI32 PxAtomicMax(volatile PxI32* val, PxI32 val2)
{
PxI32 oldVal, newVal;
do
{
PAUSE();
oldVal = *val;
if(val2 > oldVal)
newVal = val2;
else
newVal = oldVal;
} while(PxAtomicCompareExchange(val, newVal, oldVal) != oldVal);
return *val;
}
PxI64 PxAtomicMax(volatile PxI64* val, PxI64 val2)
{
PxI64 oldVal, newVal;
do
{
PAUSE();
oldVal = *val;
if(val2 > oldVal)
newVal = val2;
else
newVal = oldVal;
} while(PxAtomicCompareExchange(val, newVal, oldVal) != oldVal);
return *val;
}
PxI32 PxAtomicExchange(volatile PxI32* val, PxI32 val2)
{
PxI32 newVal, oldVal;
do
{
PAUSE();
oldVal = *val;
newVal = val2;
} while(PxAtomicCompareExchange(val, newVal, oldVal) != oldVal);
return oldVal;
}
PxI64 PxAtomicExchange(volatile PxI64* val, PxI64 val2)
{
PxI64 newVal, oldVal;
do
{
PAUSE();
oldVal = *val;
newVal = val2;
} while(PxAtomicCompareExchange(val, newVal, oldVal) != oldVal);
return oldVal;
}
PxI32 PxAtomicOr(volatile PxI32* val, PxI32 mask)
{
return __sync_or_and_fetch(val, mask);
}
PxI64 PxAtomicOr(volatile PxI64* val, PxI64 mask)
{
return __sync_or_and_fetch(val, mask);
}
PxI32 PxAtomicAnd(volatile PxI32* val, PxI32 mask)
{
return __sync_and_and_fetch(val, mask);
}
PxI64 PxAtomicAnd(volatile PxI64* val, PxI64 mask)
{
return __sync_and_and_fetch(val, mask);
}
} // namespace physx

View File

@@ -0,0 +1,107 @@
// 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/PxFPU.h"
#if !defined(__CYGWIN__)
#include <fenv.h>
PX_COMPILE_TIME_ASSERT(8 * sizeof(uint32_t) >= sizeof(fenv_t));
#endif
#if PX_OSX
// osx defines SIMD as standard for floating point operations.
#include <xmmintrin.h>
#endif
physx::PxFPUGuard::PxFPUGuard()
{
#if defined(__CYGWIN__)
#pragma message "FPUGuard::FPUGuard() is not implemented"
#elif PX_OSX
mControlWords[0] = _mm_getcsr();
// set default (disable exceptions: _MM_MASK_MASK) and FTZ (_MM_FLUSH_ZERO_ON), DAZ (_MM_DENORMALS_ZERO_ON: (1<<6))
_mm_setcsr(_MM_MASK_MASK | _MM_FLUSH_ZERO_ON | (1 << 6));
#elif defined(__EMSCRIPTEN__)
// not supported
#else
PX_COMPILE_TIME_ASSERT(sizeof(fenv_t) <= sizeof(mControlWords));
fegetenv(reinterpret_cast<fenv_t*>(mControlWords));
fesetenv(FE_DFL_ENV);
#if PX_LINUX
// need to explicitly disable exceptions because fesetenv does not modify
// the sse control word on 32bit linux (64bit is fine, but do it here just be sure)
fedisableexcept(FE_ALL_EXCEPT);
#endif
#endif
}
physx::PxFPUGuard::~PxFPUGuard()
{
#if defined(__CYGWIN__)
#pragma message "PxFPUGuard::~PxFPUGuard() is not implemented"
#elif PX_OSX
// restore control word and clear exception flags
// (setting exception state flags cause exceptions on the first following fp operation)
_mm_setcsr(mControlWords[0] & ~_MM_EXCEPT_MASK);
#elif defined(__EMSCRIPTEN__)
// not supported
#else
fesetenv(reinterpret_cast<fenv_t*>(mControlWords));
#endif
}
PX_FOUNDATION_API void physx::PxEnableFPExceptions()
{
#if PX_LINUX && !defined(__EMSCRIPTEN__)
feclearexcept(FE_ALL_EXCEPT);
feenableexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW);
#elif PX_OSX
// clear any pending exceptions
// (setting exception state flags cause exceptions on the first following fp operation)
uint32_t control = _mm_getcsr() & ~_MM_EXCEPT_MASK;
// enable all fp exceptions except inexact and underflow (common, benign)
// note: denorm has to be disabled as well because underflow can create denorms
_mm_setcsr((control & ~_MM_MASK_MASK) | _MM_MASK_INEXACT | _MM_MASK_UNDERFLOW | _MM_MASK_DENORM);
#endif
}
PX_FOUNDATION_API void physx::PxDisableFPExceptions()
{
#if PX_LINUX && !defined(__EMSCRIPTEN__)
fedisableexcept(FE_ALL_EXCEPT);
#elif PX_OSX
// clear any pending exceptions
// (setting exception state flags cause exceptions on the first following fp operation)
uint32_t control = _mm_getcsr() & ~_MM_EXCEPT_MASK;
_mm_setcsr(control | _MM_MASK_MASK);
#endif
}

View File

@@ -0,0 +1,199 @@
// 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/PxAssert.h"
#include "foundation/PxErrorCallback.h"
#include "foundation/PxUserAllocated.h"
#include "foundation/PxMutex.h"
#include "foundation/PxAtomic.h"
#include "foundation/PxThread.h"
#include <pthread.h>
namespace physx
{
#if PX_LINUX
#include <sched.h>
static int gMutexProtocol = PTHREAD_PRIO_INHERIT;
PX_FORCE_INLINE bool isLegalProtocol(const int mutexProtocol)
{
return
(
(PTHREAD_PRIO_NONE == mutexProtocol) ||
(PTHREAD_PRIO_INHERIT == mutexProtocol) ||
((PTHREAD_PRIO_PROTECT == mutexProtocol) && ((sched_getscheduler(0) == SCHED_FIFO) || (sched_getscheduler(0) == SCHED_RR)))
);
}
bool PxSetMutexProtocol(const int mutexProtocol)
{
if(isLegalProtocol(mutexProtocol))
{
gMutexProtocol = mutexProtocol;
return true;
}
return false;
}
int PxGetMutexProtocol()
{
return gMutexProtocol;
}
#endif //PX_LINUX
namespace
{
struct MutexUnixImpl
{
pthread_mutex_t lock;
PxThread::Id owner;
};
MutexUnixImpl* getMutex(PxMutexImpl* impl)
{
return reinterpret_cast<MutexUnixImpl*>(impl);
}
}
PxMutexImpl::PxMutexImpl()
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
#if PX_LINUX
pthread_mutexattr_setprotocol(&attr, gMutexProtocol);
pthread_mutexattr_setprioceiling(&attr, 0);
#endif
pthread_mutex_init(&getMutex(this)->lock, &attr);
pthread_mutexattr_destroy(&attr);
}
PxMutexImpl::~PxMutexImpl()
{
pthread_mutex_destroy(&getMutex(this)->lock);
}
void PxMutexImpl::lock()
{
int err = pthread_mutex_lock(&getMutex(this)->lock);
PX_ASSERT(!err);
PX_UNUSED(err);
#if PX_DEBUG
getMutex(this)->owner = PxThread::getId();
#endif
}
bool PxMutexImpl::trylock()
{
bool success = !pthread_mutex_trylock(&getMutex(this)->lock);
#if PX_DEBUG
if(success)
getMutex(this)->owner = PxThread::getId();
#endif
return success;
}
void PxMutexImpl::unlock()
{
#if PX_DEBUG
if(getMutex(this)->owner != PxThread::getId())
{
PxGetFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__,
"Mutex must be unlocked only by thread that has already acquired lock");
return;
}
#endif
int err = pthread_mutex_unlock(&getMutex(this)->lock);
PX_ASSERT(!err);
PX_UNUSED(err);
}
uint32_t PxMutexImpl::getSize()
{
return sizeof(MutexUnixImpl);
}
class ReadWriteLockImpl
{
public:
PxMutex mutex;
volatile int readerCounter;
};
PxReadWriteLock::PxReadWriteLock()
{
mImpl = reinterpret_cast<ReadWriteLockImpl*>(PX_ALLOC(sizeof(ReadWriteLockImpl), "ReadWriteLockImpl"));
PX_PLACEMENT_NEW(mImpl, ReadWriteLockImpl);
mImpl->readerCounter = 0;
}
PxReadWriteLock::~PxReadWriteLock()
{
mImpl->~ReadWriteLockImpl();
PX_FREE(mImpl);
}
void PxReadWriteLock::lockReader(bool takeLock)
{
if(takeLock)
mImpl->mutex.lock();
PxAtomicIncrement(&mImpl->readerCounter);
if(takeLock)
mImpl->mutex.unlock();
}
void PxReadWriteLock::lockWriter()
{
mImpl->mutex.lock();
// spin lock until no readers
while(mImpl->readerCounter);
}
void PxReadWriteLock::unlockReader()
{
PxAtomicDecrement(&mImpl->readerCounter);
}
void PxReadWriteLock::unlockWriter()
{
mImpl->mutex.unlock();
}
} // namespace physx

View File

@@ -0,0 +1,40 @@
// 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/PxString.h"
#include <stdio.h>
namespace physx
{
void PxPrintString(const char* str)
{
puts(str);
}
} // namespace physx

View File

@@ -0,0 +1,152 @@
// 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/PxAllocator.h"
#include "foundation/PxAtomic.h"
#include "foundation/PxSList.h"
#include "foundation/PxThread.h"
#include <pthread.h>
#if PX_EMSCRIPTEN
#define USE_MUTEX
#endif
namespace physx
{
namespace
{
#if defined(USE_MUTEX)
class ScopedMutexLock
{
pthread_mutex_t& mMutex;
public:
PX_INLINE ScopedMutexLock(pthread_mutex_t& mutex) : mMutex(mutex)
{
pthread_mutex_lock(&mMutex);
}
PX_INLINE ~ScopedMutexLock()
{
pthread_mutex_unlock(&mMutex);
}
};
typedef ScopedMutexLock ScopedLock;
#else
struct ScopedSpinLock
{
PX_FORCE_INLINE ScopedSpinLock(volatile int32_t& lock) : mLock(lock)
{
while(__sync_lock_test_and_set(&mLock, 1))
{
// spinning without atomics is usually
// causing less bus traffic. -> only one
// CPU is modifying the cache line.
while(lock)
PxSpinLockPause();
}
}
PX_FORCE_INLINE ~ScopedSpinLock()
{
__sync_lock_release(&mLock);
}
private:
volatile int32_t& mLock;
};
typedef ScopedSpinLock ScopedLock;
#endif
struct SListDetail
{
PxSListEntry* head;
#if defined(USE_MUTEX)
pthread_mutex_t lock;
#else
volatile int32_t lock;
#endif
};
template <typename T>
SListDetail* getDetail(T* impl)
{
return reinterpret_cast<SListDetail*>(impl);
}
}
PxSListImpl::PxSListImpl()
{
getDetail(this)->head = NULL;
#if defined(USE_MUTEX)
pthread_mutex_init(&getDetail(this)->lock, NULL);
#else
getDetail(this)->lock = 0; // 0 == unlocked
#endif
}
PxSListImpl::~PxSListImpl()
{
#if defined(USE_MUTEX)
pthread_mutex_destroy(&getDetail(this)->lock);
#endif
}
void PxSListImpl::push(PxSListEntry* entry)
{
ScopedLock lock(getDetail(this)->lock);
entry->mNext = getDetail(this)->head;
getDetail(this)->head = entry;
}
PxSListEntry* PxSListImpl::pop()
{
ScopedLock lock(getDetail(this)->lock);
PxSListEntry* result = getDetail(this)->head;
if(result != NULL)
getDetail(this)->head = result->mNext;
return result;
}
PxSListEntry* PxSListImpl::flush()
{
ScopedLock lock(getDetail(this)->lock);
PxSListEntry* result = getDetail(this)->head;
getDetail(this)->head = NULL;
return result;
}
uint32_t PxSListImpl::getSize()
{
return sizeof(SListDetail);
}
} // namespace physx

View File

@@ -0,0 +1,479 @@
// 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/PxIntrinsics.h"
#include "foundation/PxMathIntrinsics.h"
#include "foundation/PxSocket.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#define INVALID_SOCKET -1
#ifndef SOMAXCONN
#define SOMAXCONN 5
#endif
namespace physx
{
const uint32_t PxSocket::DEFAULT_BUFFER_SIZE = 32768;
class SocketImpl
{
public:
SocketImpl(bool isBlocking);
virtual ~SocketImpl();
bool connect(const char* host, uint16_t port, uint32_t timeout);
bool listen(uint16_t port);
bool accept(bool block);
void disconnect();
void setBlocking(bool blocking);
virtual uint32_t write(const uint8_t* data, uint32_t length);
virtual bool flush();
uint32_t read(uint8_t* data, uint32_t length);
PX_FORCE_INLINE bool isBlocking() const
{
return mIsBlocking;
}
PX_FORCE_INLINE bool isConnected() const
{
return mIsConnected;
}
PX_FORCE_INLINE const char* getHost() const
{
return mHost;
}
PX_FORCE_INLINE uint16_t getPort() const
{
return mPort;
}
protected:
bool nonBlockingTimeout() const;
int32_t mSocket;
int32_t mListenSocket;
const char* mHost;
uint16_t mPort;
bool mIsConnected;
bool mIsBlocking;
bool mListenMode;
};
void socketSetBlockingInternal(int32_t socket, bool blocking);
SocketImpl::SocketImpl(bool isBlocking)
: mSocket(INVALID_SOCKET)
, mListenSocket(INVALID_SOCKET)
, mHost(NULL)
, mPort(0)
, mIsConnected(false)
, mIsBlocking(isBlocking)
, mListenMode(false)
{
}
SocketImpl::~SocketImpl()
{
}
bool SocketImpl::connect(const char* host, uint16_t port, uint32_t timeout)
{
sockaddr_in socketAddress;
intrinsics::memSet(&socketAddress, 0, sizeof(sockaddr_in));
socketAddress.sin_family = AF_INET;
socketAddress.sin_port = htons(port);
// get host
hostent* hp = gethostbyname(host);
if(!hp)
{
in_addr a;
a.s_addr = inet_addr(host);
hp = gethostbyaddr(reinterpret_cast<const char*>(&a), sizeof(in_addr), AF_INET);
if(!hp)
return false;
}
intrinsics::memCopy(&socketAddress.sin_addr, hp->h_addr_list[0], hp->h_length);
// connect
mSocket = socket(AF_INET, SOCK_STREAM, 0);
if(mSocket == INVALID_SOCKET)
return false;
socketSetBlockingInternal(mSocket, false);
int connectRet = ::connect(mSocket, reinterpret_cast<sockaddr*>(&socketAddress), sizeof(socketAddress));
if(connectRet < 0)
{
if(errno != EINPROGRESS)
{
disconnect();
return false;
}
// Setup poll function call to monitor the connect call.
// By querying for POLLOUT we're checking if the socket is
// ready for writing.
pollfd pfd;
pfd.fd = mSocket;
pfd.events = POLLOUT;
const int pollResult = ::poll(&pfd, 1, timeout /*milliseconds*/);
const bool pollTimeout = (pollResult == 0);
const bool pollError = (pollResult < 0); // an error inside poll happened. Can check error with `errno` variable.
if(pollTimeout || pollError)
{
disconnect();
return false;
}
else
{
PX_ASSERT(pollResult == 1);
// check that event was precisely POLLOUT and not anything else (e.g., errors, hang-up)
bool test = (pfd.revents & POLLOUT) && !(pfd.revents & (~POLLOUT));
if(!test)
{
disconnect();
return false;
}
}
// check if we are really connected, above code seems to return
// true if host is a unix machine even if the connection was
// not accepted.
char buffer;
if(recv(mSocket, &buffer, 0, 0) < 0)
{
if(errno != EWOULDBLOCK)
{
disconnect();
return false;
}
}
}
socketSetBlockingInternal(mSocket, mIsBlocking);
#if PX_APPLE_FAMILY
int noSigPipe = 1;
setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(int));
#endif
mIsConnected = true;
mPort = port;
mHost = host;
return true;
}
bool SocketImpl::listen(uint16_t port)
{
mListenSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(mListenSocket == INVALID_SOCKET)
return false;
// enable address reuse: "Address already in use" error message
int yes = 1;
if(setsockopt(mListenSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
return false;
mListenMode = true;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
intrinsics::memSet(addr.sin_zero, '\0', sizeof addr.sin_zero);
return bind(mListenSocket, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) != -1 &&
::listen(mListenSocket, SOMAXCONN) != -1;
}
bool SocketImpl::accept(bool block)
{
if(mIsConnected || !mListenMode)
return false;
// set the listen socket to be non-blocking.
socketSetBlockingInternal(mListenSocket, block);
int32_t clientSocket = ::accept(mListenSocket, 0, 0);
if(clientSocket == INVALID_SOCKET)
return false;
mSocket = clientSocket;
mIsConnected = true;
socketSetBlockingInternal(mSocket, mIsBlocking); // force the mode to whatever the user set
return mIsConnected;
}
void SocketImpl::disconnect()
{
if(mListenSocket != INVALID_SOCKET)
{
close(mListenSocket);
mListenSocket = INVALID_SOCKET;
}
if(mSocket != INVALID_SOCKET)
{
if(mIsConnected)
{
socketSetBlockingInternal(mSocket, true);
shutdown(mSocket, SHUT_RDWR);
}
close(mSocket);
mSocket = INVALID_SOCKET;
}
mIsConnected = false;
mListenMode = false;
mPort = 0;
mHost = NULL;
}
bool SocketImpl::nonBlockingTimeout() const
{
return !mIsBlocking && errno == EWOULDBLOCK;
}
void socketSetBlockingInternal(int32_t socket, bool blocking)
{
int mode = fcntl(socket, F_GETFL, 0);
if(!blocking)
mode |= O_NONBLOCK;
else
mode &= ~O_NONBLOCK;
fcntl(socket, F_SETFL, mode);
}
// should be cross-platform from here down
void SocketImpl::setBlocking(bool blocking)
{
if(blocking != mIsBlocking)
{
mIsBlocking = blocking;
if(isConnected())
socketSetBlockingInternal(mSocket, blocking);
}
}
bool SocketImpl::flush()
{
return true;
}
uint32_t SocketImpl::write(const uint8_t* data, uint32_t length)
{
if(length == 0)
return 0;
int sent = send(mSocket, reinterpret_cast<const char*>(data), int32_t(length), 0);
if(sent <= 0 && !nonBlockingTimeout())
disconnect();
return uint32_t(sent > 0 ? sent : 0);
}
uint32_t SocketImpl::read(uint8_t* data, uint32_t length)
{
if(length == 0)
return 0;
int32_t received = recv(mSocket, reinterpret_cast<char*>(data), int32_t(length), 0);
if(received <= 0 && !nonBlockingTimeout())
disconnect();
return uint32_t(received > 0 ? received : 0);
}
class BufferedSocketImpl : public SocketImpl
{
public:
BufferedSocketImpl(bool isBlocking) : SocketImpl(isBlocking), mBufferPos(0)
{
}
virtual ~BufferedSocketImpl()
{
}
bool flush();
uint32_t write(const uint8_t* data, uint32_t length);
private:
uint32_t mBufferPos;
uint8_t mBuffer[PxSocket::DEFAULT_BUFFER_SIZE];
};
bool BufferedSocketImpl::flush()
{
uint32_t totalBytesWritten = 0;
while(totalBytesWritten < mBufferPos && mIsConnected)
totalBytesWritten += int32_t(SocketImpl::write(mBuffer + totalBytesWritten, mBufferPos - totalBytesWritten));
bool ret = (totalBytesWritten == mBufferPos);
mBufferPos = 0;
return ret;
}
uint32_t BufferedSocketImpl::write(const uint8_t* data, uint32_t length)
{
uint32_t bytesWritten = 0;
while(mBufferPos + length >= PxSocket::DEFAULT_BUFFER_SIZE)
{
uint32_t currentChunk = PxSocket::DEFAULT_BUFFER_SIZE - mBufferPos;
intrinsics::memCopy(mBuffer + mBufferPos, data + bytesWritten, currentChunk);
bytesWritten += uint32_t(currentChunk); // for the user, this is consumed even if we fail to shove it down a
// non-blocking socket
uint32_t sent = SocketImpl::write(mBuffer, PxSocket::DEFAULT_BUFFER_SIZE);
mBufferPos = PxSocket::DEFAULT_BUFFER_SIZE - sent;
if(sent < PxSocket::DEFAULT_BUFFER_SIZE) // non-blocking or error
{
if(sent) // we can reasonably hope this is rare
intrinsics::memMove(mBuffer, mBuffer + sent, mBufferPos);
return bytesWritten;
}
length -= currentChunk;
}
if(length > 0)
{
intrinsics::memCopy(mBuffer + mBufferPos, data + bytesWritten, length);
bytesWritten += length;
mBufferPos += length;
}
return bytesWritten;
}
PxSocket::PxSocket(bool inIsBuffering, bool isBlocking)
{
if(inIsBuffering)
{
void* mem = PX_ALLOC(sizeof(BufferedSocketImpl), "BufferedSocketImpl");
mImpl = PX_PLACEMENT_NEW(mem, BufferedSocketImpl)(isBlocking);
}
else
{
void* mem = PX_ALLOC(sizeof(SocketImpl), "SocketImpl");
mImpl = PX_PLACEMENT_NEW(mem, SocketImpl)(isBlocking);
}
}
PxSocket::~PxSocket()
{
mImpl->flush();
mImpl->disconnect();
mImpl->~SocketImpl();
PX_FREE(mImpl);
}
bool PxSocket::connect(const char* host, uint16_t port, uint32_t timeout)
{
return mImpl->connect(host, port, timeout);
}
bool PxSocket::listen(uint16_t port)
{
return mImpl->listen(port);
}
bool PxSocket::accept(bool block)
{
return mImpl->accept(block);
}
void PxSocket::disconnect()
{
mImpl->disconnect();
}
bool PxSocket::isConnected() const
{
return mImpl->isConnected();
}
const char* PxSocket::getHost() const
{
return mImpl->getHost();
}
uint16_t PxSocket::getPort() const
{
return mImpl->getPort();
}
bool PxSocket::flush()
{
if(!mImpl->isConnected())
return false;
return mImpl->flush();
}
uint32_t PxSocket::write(const uint8_t* data, uint32_t length)
{
if(!mImpl->isConnected())
return 0;
return mImpl->write(data, length);
}
uint32_t PxSocket::read(uint8_t* data, uint32_t length)
{
if(!mImpl->isConnected())
return 0;
return mImpl->read(data, length);
}
void PxSocket::setBlocking(bool blocking)
{
mImpl->setBlocking(blocking);
}
bool PxSocket::isBlocking() const
{
return mImpl->isBlocking();
}
} // namespace physx

View File

@@ -0,0 +1,159 @@
// 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/PxAssert.h"
#include "foundation/PxUserAllocated.h"
#include "foundation/PxSync.h"
#include <errno.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
namespace physx
{
namespace
{
class SyncImpl
{
public:
pthread_mutex_t mutex;
pthread_cond_t cond;
volatile int setCounter;
volatile bool is_set;
};
SyncImpl* getSync(PxSyncImpl* impl)
{
return reinterpret_cast<SyncImpl*>(impl);
}
}
uint32_t PxSyncImpl::getSize()
{
return sizeof(SyncImpl);
}
struct PxUnixScopeLock
{
PxUnixScopeLock(pthread_mutex_t& m) : mMutex(m)
{
pthread_mutex_lock(&mMutex);
}
~PxUnixScopeLock()
{
pthread_mutex_unlock(&mMutex);
}
private:
pthread_mutex_t& mMutex;
};
PxSyncImpl::PxSyncImpl()
{
int status = pthread_mutex_init(&getSync(this)->mutex, 0);
PX_ASSERT(!status);
status = pthread_cond_init(&getSync(this)->cond, 0);
PX_ASSERT(!status);
PX_UNUSED(status);
getSync(this)->is_set = false;
getSync(this)->setCounter = 0;
}
PxSyncImpl::~PxSyncImpl()
{
pthread_cond_destroy(&getSync(this)->cond);
pthread_mutex_destroy(&getSync(this)->mutex);
}
void PxSyncImpl::reset()
{
PxUnixScopeLock lock(getSync(this)->mutex);
getSync(this)->is_set = false;
}
void PxSyncImpl::set()
{
PxUnixScopeLock lock(getSync(this)->mutex);
if(!getSync(this)->is_set)
{
getSync(this)->is_set = true;
getSync(this)->setCounter++;
pthread_cond_broadcast(&getSync(this)->cond);
}
}
bool PxSyncImpl::wait(uint32_t ms)
{
PxUnixScopeLock lock(getSync(this)->mutex);
int lastSetCounter = getSync(this)->setCounter;
if(!getSync(this)->is_set)
{
if(ms == uint32_t(-1))
{
// have to loop here and check is_set since pthread_cond_wait can return successfully
// even if it was not signaled by pthread_cond_broadcast (OS efficiency design decision)
int status = 0;
while(!status && !getSync(this)->is_set && (lastSetCounter == getSync(this)->setCounter))
status = pthread_cond_wait(&getSync(this)->cond, &getSync(this)->mutex);
PX_ASSERT((!status && getSync(this)->is_set) || (lastSetCounter != getSync(this)->setCounter));
}
else
{
timespec ts;
timeval tp;
gettimeofday(&tp, NULL);
uint32_t sec = ms / 1000;
uint32_t usec = (ms - 1000 * sec) * 1000;
// sschirm: taking into account that us might accumulate to a second
// otherwise the pthread_cond_timedwait complains on osx.
usec = tp.tv_usec + usec;
uint32_t div_sec = usec / 1000000;
uint32_t rem_usec = usec - div_sec * 1000000;
ts.tv_sec = tp.tv_sec + sec + div_sec;
ts.tv_nsec = rem_usec * 1000;
// have to loop here and check is_set since pthread_cond_timedwait can return successfully
// even if it was not signaled by pthread_cond_broadcast (OS efficiency design decision)
int status = 0;
while(!status && !getSync(this)->is_set && (lastSetCounter == getSync(this)->setCounter))
status = pthread_cond_timedwait(&getSync(this)->cond, &getSync(this)->mutex, &ts);
PX_ASSERT((!status && getSync(this)->is_set) || (status == ETIMEDOUT) ||
(lastSetCounter != getSync(this)->setCounter));
}
}
return getSync(this)->is_set || (lastSetCounter != getSync(this)->setCounter);
}
} // namespace physx

View File

@@ -0,0 +1,466 @@
// 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/PxAssert.h"
#include "foundation/PxErrorCallback.h"
#include "foundation/PxAtomic.h"
#include "foundation/PxThread.h"
#include <math.h>
#if !PX_APPLE_FAMILY && !defined(__CYGWIN__) && !PX_EMSCRIPTEN
#include <bits/local_lim.h> // PTHREAD_STACK_MIN
#endif
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#if !PX_APPLE_FAMILY && !PX_EMSCRIPTEN
#include <asm/unistd.h>
#include <sys/resource.h>
#endif
#if PX_APPLE_FAMILY
#include <sys/types.h>
#include <sys/sysctl.h>
#include <TargetConditionals.h>
#include <pthread.h>
#endif
#define PxSpinLockPause() asm("nop")
namespace physx
{
namespace
{
typedef enum
{
ePxThreadNotStarted,
ePxThreadStarted,
ePxThreadStopped
} PxThreadState;
class ThreadImpl
{
public:
PxThreadImpl::ExecuteFn fn;
void* arg;
volatile int32_t quitNow;
volatile int32_t threadStarted;
volatile int32_t state;
pthread_t thread;
pid_t tid;
uint32_t affinityMask;
const char* name;
};
ThreadImpl* getThread(PxThreadImpl* impl)
{
return reinterpret_cast<ThreadImpl*>(impl);
}
static void setTid(ThreadImpl& threadImpl)
{
// query TID
// AM: TODO: neither of the below are implemented
#if PX_APPLE_FAMILY
threadImpl.tid = syscall(SYS_gettid);
#elif PX_EMSCRIPTEN
threadImpl.tid = pthread_self();
#else
threadImpl.tid = syscall(__NR_gettid);
#endif
// notify/unblock parent thread
PxAtomicCompareExchange(&(threadImpl.threadStarted), 1, 0);
}
void* PxThreadStart(void* arg)
{
ThreadImpl* impl = getThread(reinterpret_cast<PxThreadImpl*>(arg));
impl->state = ePxThreadStarted;
// run setTid in thread's context
setTid(*impl);
// then run either the passed in function or execute from the derived class (Runnable).
if(impl->fn)
(*impl->fn)(impl->arg);
else if(impl->arg)
(reinterpret_cast<PxRunnable*>(impl->arg))->execute();
return 0;
}
}
uint32_t PxThreadImpl::getSize()
{
return sizeof(ThreadImpl);
}
PxThreadImpl::Id PxThreadImpl::getId()
{
return Id(pthread_self());
}
PxThreadImpl::PxThreadImpl()
{
getThread(this)->thread = 0;
getThread(this)->tid = 0;
getThread(this)->state = ePxThreadNotStarted;
getThread(this)->quitNow = 0;
getThread(this)->threadStarted = 0;
getThread(this)->fn = NULL;
getThread(this)->arg = NULL;
getThread(this)->affinityMask = 0;
getThread(this)->name = "set my name before starting me";
}
PxThreadImpl::PxThreadImpl(PxThreadImpl::ExecuteFn fn, void* arg, const char* name)
{
getThread(this)->thread = 0;
getThread(this)->tid = 0;
getThread(this)->state = ePxThreadNotStarted;
getThread(this)->quitNow = 0;
getThread(this)->threadStarted = 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 == ePxThreadStarted)
kill();
}
void PxThreadImpl::start(uint32_t stackSize, PxRunnable* runnable)
{
if(getThread(this)->state != ePxThreadNotStarted)
return;
if(stackSize == 0)
stackSize = getDefaultStackSize();
#if defined(PTHREAD_STACK_MIN)
if(stackSize < PTHREAD_STACK_MIN)
{
PxGetFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__,
"PxThreadImpl::start(): stack size was set below PTHREAD_STACK_MIN");
stackSize = PTHREAD_STACK_MIN;
}
#endif
if(runnable && !getThread(this)->arg && !getThread(this)->fn)
getThread(this)->arg = runnable;
pthread_attr_t attr;
int status = pthread_attr_init(&attr);
PX_ASSERT(!status);
PX_UNUSED(status);
status = pthread_attr_setstacksize(&attr, stackSize);
PX_ASSERT(!status);
status = pthread_create(&getThread(this)->thread, &attr, PxThreadStart, this);
PX_ASSERT(!status);
// wait for thread to startup and write out TID
// otherwise TID dependent calls like setAffinity will fail.
while(PxAtomicCompareExchange(&(getThread(this)->threadStarted), 1, 1) == 0)
yield();
// here we are sure that getThread(this)->state >= ePxThreadStarted
status = pthread_attr_destroy(&attr);
PX_ASSERT(!status);
// apply stored affinity mask
if(getThread(this)->affinityMask)
setAffinityMask(getThread(this)->affinityMask);
if (getThread(this)->name)
setName(getThread(this)->name);
}
void PxThreadImpl::signalQuit()
{
PxAtomicIncrement(&(getThread(this)->quitNow));
}
bool PxThreadImpl::waitForQuit()
{
if(getThread(this)->state == ePxThreadNotStarted)
return false;
// works also with a stopped/exited thread if the handle is still valid
pthread_join(getThread(this)->thread, NULL);
getThread(this)->state = ePxThreadStopped;
return true;
}
bool PxThreadImpl::quitIsSignalled()
{
return PxAtomicCompareExchange(&(getThread(this)->quitNow), 0, 0) != 0;
}
#if defined(PX_GCC_FAMILY)
__attribute__((noreturn))
#endif
void PxThreadImpl::quit()
{
getThread(this)->state = ePxThreadStopped;
pthread_exit(0);
}
void PxThreadImpl::kill()
{
if(getThread(this)->state == ePxThreadStarted)
pthread_cancel(getThread(this)->thread);
getThread(this)->state = ePxThreadStopped;
}
void PxThreadImpl::sleep(uint32_t ms)
{
timespec sleepTime;
uint32_t remainder = ms % 1000;
sleepTime.tv_sec = ms - remainder;
sleepTime.tv_nsec = remainder * 1000000L;
while(nanosleep(&sleepTime, &sleepTime) == -1)
continue;
}
void PxThreadImpl::yield()
{
sched_yield();
}
void PxThreadImpl::yieldProcessor()
{
#if (PX_ARM || PX_A64)
__asm__ __volatile__("yield");
#else
__asm__ __volatile__("pause");
#endif
}
uint32_t PxThreadImpl::setAffinityMask(uint32_t mask)
{
// Same as windows impl if mask is zero
if(!mask)
return 0;
getThread(this)->affinityMask = mask;
uint64_t prevMask = 0;
if(getThread(this)->state == ePxThreadStarted)
{
#if PX_EMSCRIPTEN
// not supported
#elif !PX_APPLE_FAMILY // Apple doesn't support syscall with getaffinity and setaffinity
int32_t errGet = syscall(__NR_sched_getaffinity, getThread(this)->tid, sizeof(prevMask), &prevMask);
if(errGet < 0)
return 0;
int32_t errSet = syscall(__NR_sched_setaffinity, getThread(this)->tid, sizeof(mask), &mask);
if(errSet != 0)
return 0;
#endif
}
return uint32_t(prevMask);
}
void PxThreadImpl::setName(const char* name)
{
getThread(this)->name = name;
if (getThread(this)->state == ePxThreadStarted)
{
// not implemented because most unix APIs expect setName()
// to be called from the thread's context. Example see next comment:
// this works only with the current thread and can rename
// the main process if used in the wrong context:
// prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name) ,0,0,0);
PX_UNUSED(name);
}
}
#if !PX_APPLE_FAMILY
static PxThreadPriority::Enum convertPriorityFromLinux(uint32_t inPrio, int policy)
{
PX_COMPILE_TIME_ASSERT(PxThreadPriority::eLOW > PxThreadPriority::eHIGH);
PX_COMPILE_TIME_ASSERT(PxThreadPriority::eHIGH == 0);
int maxL = sched_get_priority_max(policy);
int minL = sched_get_priority_min(policy);
int rangeL = maxL - minL;
int rangeNv = PxThreadPriority::eLOW - PxThreadPriority::eHIGH;
// case for default scheduler policy
if(rangeL == 0)
return PxThreadPriority::eNORMAL;
float floatPrio = (float(maxL - inPrio) * float(rangeNv)) / float(rangeL);
return PxThreadPriority::Enum(int(roundf(floatPrio)));
}
static int convertPriorityToLinux(PxThreadPriority::Enum inPrio, int policy)
{
int maxL = sched_get_priority_max(policy);
int minL = sched_get_priority_min(policy);
int rangeL = maxL - minL;
int rangeNv = PxThreadPriority::eLOW - PxThreadPriority::eHIGH;
// case for default scheduler policy
if(rangeL == 0)
return 0;
float floatPrio = (float(PxThreadPriority::eLOW - inPrio) * float(rangeL)) / float(rangeNv);
return minL + int(roundf(floatPrio));
}
#endif
void PxThreadImpl::setPriority(PxThreadPriority::Enum val)
{
PX_UNUSED(val);
#if !PX_APPLE_FAMILY
int policy;
sched_param s_param;
pthread_getschedparam(getThread(this)->thread, &policy, &s_param);
s_param.sched_priority = convertPriorityToLinux(val, policy);
pthread_setschedparam(getThread(this)->thread, policy, &s_param);
#endif
}
PxThreadPriority::Enum PxThreadImpl::getPriority(Id pthread)
{
PX_UNUSED(pthread);
#if !PX_APPLE_FAMILY
int policy;
sched_param s_param;
int ret = pthread_getschedparam(pthread_t(pthread), &policy, &s_param);
if(ret == 0)
return convertPriorityFromLinux(s_param.sched_priority, policy);
else
return PxThreadPriority::eNORMAL;
#else
return PxThreadPriority::eNORMAL;
#endif
}
uint32_t PxThreadImpl::getNbPhysicalCores()
{
#if PX_APPLE_FAMILY
int count;
size_t size = sizeof(count);
return sysctlbyname("hw.physicalcpu", &count, &size, NULL, 0) ? 0 : count;
#else
// Linux exposes CPU topology using /sys/devices/system/cpu
// https://www.kernel.org/doc/Documentation/cputopology.txt
if(FILE* f = fopen("/sys/devices/system/cpu/possible", "r"))
{
int minIndex, maxIndex;
int n = fscanf(f, "%d-%d", &minIndex, &maxIndex);
fclose(f);
if(n == 2)
return (maxIndex - minIndex) + 1;
else if(n == 1)
return minIndex + 1;
}
// For non-Linux kernels this fallback is possibly the best we can do
// but will report logical (hyper-threaded) counts
int n = sysconf(_SC_NPROCESSORS_CONF);
if(n < 0)
return 0;
else
return n;
#endif
}
PxU32 PxTlsAlloc()
{
pthread_key_t key;
int status = pthread_key_create(&key, NULL);
PX_ASSERT(!status);
PX_UNUSED(status);
return PxU32(key);
}
void PxTlsFree(PxU32 index)
{
int status = pthread_key_delete(pthread_key_t(index));
PX_ASSERT(!status);
PX_UNUSED(status);
}
void* PxTlsGet(PxU32 index)
{
return reinterpret_cast<void*>(pthread_getspecific(pthread_key_t(index)));
}
size_t PxTlsGetValue(PxU32 index)
{
return reinterpret_cast<size_t>(pthread_getspecific(pthread_key_t(index)));
}
PxU32 PxTlsSet(PxU32 index, void* value)
{
int status = pthread_setspecific(pthread_key_t(index), value);
PX_ASSERT(!status);
return !status;
}
PxU32 PxTlsSetValue(PxU32 index, size_t value)
{
int status = pthread_setspecific(pthread_key_t(index), reinterpret_cast<void*>(value));
PX_ASSERT(!status);
return !status;
}
// DM: On Linux x86-32, without implementation-specific restrictions
// the default stack size for a new thread should be 2 megabytes (kernel.org).
// NOTE: take care of this value on other architectures!
PxU32 PxThreadImpl::getDefaultStackSize()
{
return 1 << 21;
}
} // namespace physx

View File

@@ -0,0 +1,115 @@
// 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/PxTime.h"
#include <time.h>
#include <sys/time.h>
#if PX_APPLE_FAMILY
#include <mach/mach_time.h>
#endif
// Use real-time high-precision timer.
#if !PX_APPLE_FAMILY
#define CLOCKID CLOCK_REALTIME
#endif
namespace physx
{
static const PxCounterFrequencyToTensOfNanos gCounterFreq = PxTime::getCounterFrequency();
const PxCounterFrequencyToTensOfNanos& PxTime::getBootCounterFrequency()
{
return gCounterFreq;
}
static PxTime::Second getTimeSeconds()
{
static struct timeval _tv;
gettimeofday(&_tv, NULL);
return double(_tv.tv_sec) + double(_tv.tv_usec) * 0.000001;
}
PxTime::PxTime()
{
mLastTime = getTimeSeconds();
}
PxTime::Second PxTime::getElapsedSeconds()
{
PxTime::Second curTime = getTimeSeconds();
PxTime::Second diff = curTime - mLastTime;
mLastTime = curTime;
return diff;
}
PxTime::Second PxTime::peekElapsedSeconds()
{
PxTime::Second curTime = getTimeSeconds();
PxTime::Second diff = curTime - mLastTime;
return diff;
}
PxTime::Second PxTime::getLastTime() const
{
return mLastTime;
}
#if PX_APPLE_FAMILY
PxCounterFrequencyToTensOfNanos PxTime::getCounterFrequency()
{
mach_timebase_info_data_t info;
mach_timebase_info(&info);
// mach_absolute_time * (info.numer/info.denom) is in units of nano seconds
return PxCounterFrequencyToTensOfNanos(info.numer, info.denom * 10);
}
uint64_t PxTime::getCurrentCounterValue()
{
return mach_absolute_time();
}
#else
PxCounterFrequencyToTensOfNanos PxTime::getCounterFrequency()
{
return PxCounterFrequencyToTensOfNanos(1, 10);
}
uint64_t PxTime::getCurrentCounterValue()
{
struct timespec mCurrTimeInt;
clock_gettime(CLOCKID, &mCurrTimeInt);
// Convert to nanos as this doesn't cause a large divide here
return (static_cast<uint64_t>(mCurrTimeInt.tv_sec) * 1000000000) + (static_cast<uint64_t>(mCurrTimeInt.tv_nsec));
}
#endif
} // namespace physx