godot-module-template/engine/thirdparty/jolt_physics/Jolt/Physics/PhysicsLock.h

170 lines
4.9 KiB
C++

// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Jolt/Core/Mutex.h>
JPH_NAMESPACE_BEGIN
#ifdef JPH_ENABLE_ASSERTS
/// This is the list of locks used by the physics engine, they need to be locked in a particular order (from top of the list to bottom of the list) in order to prevent deadlocks
enum class EPhysicsLockTypes
{
BroadPhaseQuery = 1 << 0,
PerBody = 1 << 1,
BodiesList = 1 << 2,
BroadPhaseUpdate = 1 << 3,
ConstraintsList = 1 << 4,
ActiveBodiesList = 1 << 5,
};
/// A token that indicates the context of a lock (we use 1 per physics system and we use the body manager pointer because it's convenient)
class BodyManager;
using PhysicsLockContext = const BodyManager *;
#endif // !JPH_ENABLE_ASSERTS
/// Helpers to safely lock the different mutexes that are part of the physics system while preventing deadlock
/// Class that keeps track per thread which lock are taken and if the order of locking is correct
class JPH_EXPORT PhysicsLock
{
public:
#ifdef JPH_ENABLE_ASSERTS
/// Call before taking the lock
static inline void sCheckLock(PhysicsLockContext inContext, EPhysicsLockTypes inType)
{
uint32 &mutexes = sGetLockedMutexes(inContext);
JPH_ASSERT(uint32(inType) > mutexes, "A lock of same or higher priority was already taken, this can create a deadlock!");
mutexes = mutexes | uint32(inType);
}
/// Call after releasing the lock
static inline void sCheckUnlock(PhysicsLockContext inContext, EPhysicsLockTypes inType)
{
uint32 &mutexes = sGetLockedMutexes(inContext);
JPH_ASSERT((mutexes & uint32(inType)) != 0, "Mutex was not locked!");
mutexes = mutexes & ~uint32(inType);
}
#endif // !JPH_ENABLE_ASSERTS
template <class LockType>
static inline void sLock(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType))
{
JPH_IF_ENABLE_ASSERTS(sCheckLock(inContext, inType);)
inMutex.lock();
}
template <class LockType>
static inline void sUnlock(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType))
{
JPH_IF_ENABLE_ASSERTS(sCheckUnlock(inContext, inType);)
inMutex.unlock();
}
template <class LockType>
static inline void sLockShared(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType))
{
JPH_IF_ENABLE_ASSERTS(sCheckLock(inContext, inType);)
inMutex.lock_shared();
}
template <class LockType>
static inline void sUnlockShared(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType))
{
JPH_IF_ENABLE_ASSERTS(sCheckUnlock(inContext, inType);)
inMutex.unlock_shared();
}
#ifdef JPH_ENABLE_ASSERTS
private:
struct LockData
{
uint32 mLockedMutexes = 0;
PhysicsLockContext mContext = nullptr;
};
// Helper function to find the locked mutexes for a particular context
static uint32 & sGetLockedMutexes(PhysicsLockContext inContext)
{
static thread_local LockData sLocks[4];
// If we find a matching context we can use it
for (LockData &l : sLocks)
if (l.mContext == inContext)
return l.mLockedMutexes;
// Otherwise we look for an entry that is not in use
for (LockData &l : sLocks)
if (l.mLockedMutexes == 0)
{
l.mContext = inContext;
return l.mLockedMutexes;
}
JPH_ASSERT(false, "Too many physics systems locked at the same time!");
return sLocks[0].mLockedMutexes;
}
#endif // !JPH_ENABLE_ASSERTS
};
/// Helper class that is similar to std::unique_lock
template <class LockType>
class UniqueLock : public NonCopyable
{
public:
explicit UniqueLock(LockType &inLock JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) :
mLock(inLock)
#ifdef JPH_ENABLE_ASSERTS
, mContext(inContext),
mType(inType)
#endif // JPH_ENABLE_ASSERTS
{
PhysicsLock::sLock(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType));
}
~UniqueLock()
{
PhysicsLock::sUnlock(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType));
}
private:
LockType & mLock;
#ifdef JPH_ENABLE_ASSERTS
PhysicsLockContext mContext;
EPhysicsLockTypes mType;
#endif // JPH_ENABLE_ASSERTS
};
/// Helper class that is similar to std::shared_lock
template <class LockType>
class SharedLock : public NonCopyable
{
public:
explicit SharedLock(LockType &inLock JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) :
mLock(inLock)
#ifdef JPH_ENABLE_ASSERTS
, mContext(inContext)
, mType(inType)
#endif // JPH_ENABLE_ASSERTS
{
PhysicsLock::sLockShared(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType));
}
~SharedLock()
{
PhysicsLock::sUnlockShared(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType));
}
private:
LockType & mLock;
#ifdef JPH_ENABLE_ASSERTS
PhysicsLockContext mContext;
EPhysicsLockTypes mType;
#endif // JPH_ENABLE_ASSERTS
};
JPH_NAMESPACE_END