Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/PhysicsLock.h
9912 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#pragma once56#include <Jolt/Core/Mutex.h>78JPH_NAMESPACE_BEGIN910#ifdef JPH_ENABLE_ASSERTS1112/// 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 deadlocks13enum class EPhysicsLockTypes14{15BroadPhaseQuery = 1 << 0,16PerBody = 1 << 1,17BodiesList = 1 << 2,18BroadPhaseUpdate = 1 << 3,19ConstraintsList = 1 << 4,20ActiveBodiesList = 1 << 5,21};2223/// 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)24class BodyManager;25using PhysicsLockContext = const BodyManager *;2627#endif // !JPH_ENABLE_ASSERTS2829/// Helpers to safely lock the different mutexes that are part of the physics system while preventing deadlock30/// Class that keeps track per thread which lock are taken and if the order of locking is correct31class JPH_EXPORT PhysicsLock32{33public:34#ifdef JPH_ENABLE_ASSERTS35/// Call before taking the lock36static inline void sCheckLock(PhysicsLockContext inContext, EPhysicsLockTypes inType)37{38uint32 &mutexes = sGetLockedMutexes(inContext);39JPH_ASSERT(uint32(inType) > mutexes, "A lock of same or higher priority was already taken, this can create a deadlock!");40mutexes = mutexes | uint32(inType);41}4243/// Call after releasing the lock44static inline void sCheckUnlock(PhysicsLockContext inContext, EPhysicsLockTypes inType)45{46uint32 &mutexes = sGetLockedMutexes(inContext);47JPH_ASSERT((mutexes & uint32(inType)) != 0, "Mutex was not locked!");48mutexes = mutexes & ~uint32(inType);49}50#endif // !JPH_ENABLE_ASSERTS5152template <class LockType>53static inline void sLock(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType))54{55JPH_IF_ENABLE_ASSERTS(sCheckLock(inContext, inType);)56inMutex.lock();57}5859template <class LockType>60static inline void sUnlock(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType))61{62JPH_IF_ENABLE_ASSERTS(sCheckUnlock(inContext, inType);)63inMutex.unlock();64}6566template <class LockType>67static inline void sLockShared(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType))68{69JPH_IF_ENABLE_ASSERTS(sCheckLock(inContext, inType);)70inMutex.lock_shared();71}7273template <class LockType>74static inline void sUnlockShared(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType))75{76JPH_IF_ENABLE_ASSERTS(sCheckUnlock(inContext, inType);)77inMutex.unlock_shared();78}7980#ifdef JPH_ENABLE_ASSERTS81private:82struct LockData83{84uint32 mLockedMutexes = 0;85PhysicsLockContext mContext = nullptr;86};8788// Helper function to find the locked mutexes for a particular context89static uint32 & sGetLockedMutexes(PhysicsLockContext inContext)90{91static thread_local LockData sLocks[4];9293// If we find a matching context we can use it94for (LockData &l : sLocks)95if (l.mContext == inContext)96return l.mLockedMutexes;9798// Otherwise we look for an entry that is not in use99for (LockData &l : sLocks)100if (l.mLockedMutexes == 0)101{102l.mContext = inContext;103return l.mLockedMutexes;104}105106JPH_ASSERT(false, "Too many physics systems locked at the same time!");107return sLocks[0].mLockedMutexes;108}109#endif // !JPH_ENABLE_ASSERTS110};111112/// Helper class that is similar to std::unique_lock113template <class LockType>114class UniqueLock : public NonCopyable115{116public:117explicit UniqueLock(LockType &inLock JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) :118mLock(inLock)119#ifdef JPH_ENABLE_ASSERTS120, mContext(inContext),121mType(inType)122#endif // JPH_ENABLE_ASSERTS123{124PhysicsLock::sLock(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType));125}126127~UniqueLock()128{129PhysicsLock::sUnlock(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType));130}131132private:133LockType & mLock;134#ifdef JPH_ENABLE_ASSERTS135PhysicsLockContext mContext;136EPhysicsLockTypes mType;137#endif // JPH_ENABLE_ASSERTS138};139140/// Helper class that is similar to std::shared_lock141template <class LockType>142class SharedLock : public NonCopyable143{144public:145explicit SharedLock(LockType &inLock JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) :146mLock(inLock)147#ifdef JPH_ENABLE_ASSERTS148, mContext(inContext)149, mType(inType)150#endif // JPH_ENABLE_ASSERTS151{152PhysicsLock::sLockShared(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType));153}154155~SharedLock()156{157PhysicsLock::sUnlockShared(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType));158}159160private:161LockType & mLock;162#ifdef JPH_ENABLE_ASSERTS163PhysicsLockContext mContext;164EPhysicsLockTypes mType;165#endif // JPH_ENABLE_ASSERTS166};167168JPH_NAMESPACE_END169170171