// SPDX-FileCopyrightText: 2021 Jorrit Rouwe // SPDX-License-Identifier: MIT #pragma once #include JPH_NAMESPACE_BEGIN /// 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, }; /// 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 PhysicsLock { public: #ifdef JPH_ENABLE_ASSERTS /// Call before taking the lock static inline void sCheckLock(EPhysicsLockTypes inType) { JPH_ASSERT((uint32)inType > sLockedMutexes, "A lock of same or higher priority was already taken, this can create a deadlock!"); sLockedMutexes = sLockedMutexes | (uint32)inType; } /// Call after releasing the lock static inline void sCheckUnlock(EPhysicsLockTypes inType) { JPH_ASSERT((sLockedMutexes & (uint32)inType) != 0, "Mutex was not locked!"); sLockedMutexes = sLockedMutexes & ~(uint32)inType; } #endif // !JPH_ENABLE_ASSERTS template static inline void sLock(LockType &inMutex, [[maybe_unused]] EPhysicsLockTypes inType) { JPH_IF_ENABLE_ASSERTS(sCheckLock(inType);) inMutex.lock(); } template static inline void sUnlock(LockType &inMutex, [[maybe_unused]] EPhysicsLockTypes inType) { JPH_IF_ENABLE_ASSERTS(sCheckUnlock(inType);) inMutex.unlock(); } template static inline void sLockShared(LockType &inMutex, [[maybe_unused]] EPhysicsLockTypes inType) { JPH_IF_ENABLE_ASSERTS(sCheckLock(inType);) inMutex.lock_shared(); } template static inline void sUnlockShared(LockType &inMutex, [[maybe_unused]] EPhysicsLockTypes inType) { JPH_IF_ENABLE_ASSERTS(sCheckUnlock(inType);) inMutex.unlock_shared(); } #ifdef JPH_ENABLE_ASSERTS private: static thread_local uint32 sLockedMutexes; #endif // !JPH_ENABLE_ASSERTS }; /// Helper class that is similar to std::unique_lock template class UniqueLock : public NonCopyable { public: UniqueLock(LockType &inLock, EPhysicsLockTypes inType) : mLock(inLock), mType(inType) { PhysicsLock::sLock(mLock, mType); } ~UniqueLock() { PhysicsLock::sUnlock(mLock, mType); } private: LockType & mLock; EPhysicsLockTypes mType; }; /// Helper class that is similar to std::shared_lock template class SharedLock : public NonCopyable { public: SharedLock(LockType &inLock, EPhysicsLockTypes inType) : mLock(inLock), mType(inType) { PhysicsLock::sLockShared(mLock, mType); } ~SharedLock() { PhysicsLock::sUnlockShared(mLock, mType); } private: LockType & mLock; EPhysicsLockTypes mType; }; JPH_NAMESPACE_END