PhysicsLock.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #pragma once
  5. #include <Jolt/Core/Mutex.h>
  6. JPH_NAMESPACE_BEGIN
  7. #ifdef JPH_ENABLE_ASSERTS
  8. /// 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
  9. enum class EPhysicsLockTypes
  10. {
  11. BroadPhaseQuery = 1 << 0,
  12. PerBody = 1 << 1,
  13. BodiesList = 1 << 2,
  14. BroadPhaseUpdate = 1 << 3,
  15. ConstraintsList = 1 << 4,
  16. ActiveBodiesList = 1 << 5,
  17. };
  18. /// 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)
  19. class BodyManager;
  20. using PhysicsLockContext = const BodyManager *;
  21. #endif // !JPH_ENABLE_ASSERTS
  22. /// Helpers to safely lock the different mutexes that are part of the physics system while preventing deadlock
  23. /// Class that keeps track per thread which lock are taken and if the order of locking is correct
  24. class PhysicsLock
  25. {
  26. public:
  27. #ifdef JPH_ENABLE_ASSERTS
  28. /// Call before taking the lock
  29. static inline void sCheckLock(PhysicsLockContext inContext, EPhysicsLockTypes inType)
  30. {
  31. uint32 &mutexes = sGetLockedMutexes(inContext);
  32. JPH_ASSERT(uint32(inType) > mutexes, "A lock of same or higher priority was already taken, this can create a deadlock!");
  33. mutexes = mutexes | uint32(inType);
  34. }
  35. /// Call after releasing the lock
  36. static inline void sCheckUnlock(PhysicsLockContext inContext, EPhysicsLockTypes inType)
  37. {
  38. uint32 &mutexes = sGetLockedMutexes(inContext);
  39. JPH_ASSERT((mutexes & uint32(inType)) != 0, "Mutex was not locked!");
  40. mutexes = mutexes & ~uint32(inType);
  41. }
  42. #endif // !JPH_ENABLE_ASSERTS
  43. template <class LockType>
  44. static inline void sLock(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType))
  45. {
  46. JPH_IF_ENABLE_ASSERTS(sCheckLock(inContext, inType);)
  47. inMutex.lock();
  48. }
  49. template <class LockType>
  50. static inline void sUnlock(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType))
  51. {
  52. JPH_IF_ENABLE_ASSERTS(sCheckUnlock(inContext, inType);)
  53. inMutex.unlock();
  54. }
  55. template <class LockType>
  56. static inline void sLockShared(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType))
  57. {
  58. JPH_IF_ENABLE_ASSERTS(sCheckLock(inContext, inType);)
  59. inMutex.lock_shared();
  60. }
  61. template <class LockType>
  62. static inline void sUnlockShared(LockType &inMutex JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType))
  63. {
  64. JPH_IF_ENABLE_ASSERTS(sCheckUnlock(inContext, inType);)
  65. inMutex.unlock_shared();
  66. }
  67. #ifdef JPH_ENABLE_ASSERTS
  68. private:
  69. struct LockData
  70. {
  71. uint32 mLockedMutexes = 0;
  72. PhysicsLockContext mContext = nullptr;
  73. };
  74. static thread_local LockData sLocks[4];
  75. // Helper function to find the locked mutexes for a particular context
  76. static uint32 & sGetLockedMutexes(PhysicsLockContext inContext)
  77. {
  78. // If we find a matching context we can use it
  79. for (LockData &l : sLocks)
  80. if (l.mContext == inContext)
  81. return l.mLockedMutexes;
  82. // Otherwise we look for an entry that is not in use
  83. for (LockData &l : sLocks)
  84. if (l.mLockedMutexes == 0)
  85. {
  86. l.mContext = inContext;
  87. return l.mLockedMutexes;
  88. }
  89. JPH_ASSERT(false, "Too many physics systems locked at the same time!");
  90. return sLocks[0].mLockedMutexes;
  91. }
  92. #endif // !JPH_ENABLE_ASSERTS
  93. };
  94. /// Helper class that is similar to std::unique_lock
  95. template <class LockType>
  96. class UniqueLock : public NonCopyable
  97. {
  98. public:
  99. explicit UniqueLock(LockType &inLock JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) :
  100. mLock(inLock)
  101. #ifdef JPH_ENABLE_ASSERTS
  102. , mContext(inContext),
  103. mType(inType)
  104. #endif // JPH_ENABLE_ASSERTS
  105. {
  106. PhysicsLock::sLock(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType));
  107. }
  108. ~UniqueLock()
  109. {
  110. PhysicsLock::sUnlock(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType));
  111. }
  112. private:
  113. LockType & mLock;
  114. #ifdef JPH_ENABLE_ASSERTS
  115. PhysicsLockContext mContext;
  116. EPhysicsLockTypes mType;
  117. #endif // JPH_ENABLE_ASSERTS
  118. };
  119. /// Helper class that is similar to std::shared_lock
  120. template <class LockType>
  121. class SharedLock : public NonCopyable
  122. {
  123. public:
  124. explicit SharedLock(LockType &inLock JPH_IF_ENABLE_ASSERTS(, PhysicsLockContext inContext, EPhysicsLockTypes inType)) :
  125. mLock(inLock)
  126. #ifdef JPH_ENABLE_ASSERTS
  127. , mContext(inContext)
  128. , mType(inType)
  129. #endif // JPH_ENABLE_ASSERTS
  130. {
  131. PhysicsLock::sLockShared(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType));
  132. }
  133. ~SharedLock()
  134. {
  135. PhysicsLock::sUnlockShared(mLock JPH_IF_ENABLE_ASSERTS(, mContext, mType));
  136. }
  137. private:
  138. LockType & mLock;
  139. #ifdef JPH_ENABLE_ASSERTS
  140. PhysicsLockContext mContext;
  141. EPhysicsLockTypes mType;
  142. #endif // JPH_ENABLE_ASSERTS
  143. };
  144. JPH_NAMESPACE_END