ReentrantLock.h 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. #pragma once
  2. #include "../C/Baselib_ReentrantLock.h"
  3. #include "Time.h"
  4. namespace baselib
  5. {
  6. BASELIB_CPP_INTERFACE
  7. {
  8. // In computer science, the reentrant mutex (recursive mutex, recursive lock) is particular type of mutual exclusion (mutex) device that may be locked multiple
  9. // times by the same process/thread, without causing a deadlock.
  10. // While any attempt to perform the "lock" operation on an ordinary mutex (lock) would either fail or block when the mutex is already locked, on a recursive
  11. // mutex this operation will succeed if and only if the locking thread is the one that already holds the lock. Typically, a recursive mutex tracks the number
  12. // of times it has been locked, and requires equally many unlock operations to be performed before other threads may lock it.
  13. //
  14. // "Reentrant mutex", Wikipedia: The Free Encyclopedia
  15. // https://en.wikipedia.org/w/index.php?title=Reentrant_mutex&oldid=818566928
  16. //
  17. // For optimal performance, baselib::ReentrantLock should be stored at a cache aligned memory location.
  18. class ReentrantLock
  19. {
  20. public:
  21. // non-copyable
  22. ReentrantLock(const ReentrantLock& other) = delete;
  23. ReentrantLock& operator=(const ReentrantLock& other) = delete;
  24. // non-movable (strictly speaking not needed but listed to signal intent)
  25. ReentrantLock(ReentrantLock&& other) = delete;
  26. ReentrantLock& operator=(ReentrantLock&& other) = delete;
  27. // Creates a reentrant lock synchronization primitive.
  28. // If there are not enough system resources to create a lock, process abort is triggered.
  29. ReentrantLock()
  30. {
  31. Baselib_ReentrantLock_CreateInplace(&m_ReentrantLockData);
  32. }
  33. // Reclaim resources and memory held by lock.
  34. //
  35. // If threads are waiting on the lock, calling free may trigger an assert and may cause process abort.
  36. // Calling this function with a nullptr result in a no-op
  37. ~ReentrantLock()
  38. {
  39. Baselib_ReentrantLock_FreeInplace(&m_ReentrantLockData);
  40. }
  41. // Acquire lock.
  42. //
  43. // If lock is already acquired by the current thread this function increase the lock count so that an equal number of calls to Baselib_ReentrantLock_Release needs
  44. // to be made before the lock is released.
  45. // If lock is held by another thread, this function wait for lock to be released.
  46. //
  47. // This function is guaranteed to emit an acquire barrier.
  48. inline void Acquire()
  49. {
  50. return Baselib_ReentrantLock_Acquire(&m_ReentrantLockData);
  51. }
  52. // Try to acquire lock and return immediately.
  53. // If lock is already acquired by the current thread this function increase the lock count so that an equal number of calls to Baselib_ReentrantLock_Release needs
  54. // to be made before the lock is released.
  55. //
  56. // When lock is acquired this function is guaranteed to emit an acquire barrier.
  57. //
  58. // Return: true if lock was acquired.
  59. COMPILER_WARN_UNUSED_RESULT
  60. FORCE_INLINE bool TryAcquire()
  61. {
  62. return Baselib_ReentrantLock_TryAcquire(&m_ReentrantLockData);
  63. }
  64. // Try to acquire lock.
  65. // If lock is already acquired by the current thread this function increase the lock count so that an equal number of calls to Baselib_ReentrantLock_Release needs
  66. // to be made before the lock is released.
  67. // If lock is held by another thread, this function wait for timeoutInMilliseconds for lock to be released.
  68. //
  69. // When lock is acquired this function is guaranteed to emit an acquire barrier.
  70. //
  71. // TryAcquire with a zero timeout differs from TryAcquire() in that TryAcquire() is guaranteed to be a user space operation
  72. // while TryAcquire with zero timeout may enter the kernel and cause a context switch.
  73. //
  74. // Timeout passed to this function may be subject to system clock resolution.
  75. // If the system clock has a resolution of e.g. 16ms that means this function may exit with a timeout error 16ms earlier than originally scheduled.
  76. //
  77. // Return: true if lock was acquired.
  78. COMPILER_WARN_UNUSED_RESULT
  79. FORCE_INLINE bool TryTimedAcquire(const timeout_ms timeoutInMilliseconds)
  80. {
  81. return Baselib_ReentrantLock_TryTimedAcquire(&m_ReentrantLockData, timeoutInMilliseconds.count());
  82. }
  83. // Release lock.
  84. // If lock count is still higher than zero after the release operation then lock remain in a locked state.
  85. // If lock count reach zero the lock is unlocked and made available to other threads
  86. //
  87. // When the lock is released this function is guaranteed to emit a release barrier.
  88. //
  89. // Calling this function from a thread that doesn't own the lock triggers an assert in debug and causes undefined behavior in release builds.
  90. FORCE_INLINE void Release()
  91. {
  92. return Baselib_ReentrantLock_Release(&m_ReentrantLockData);
  93. }
  94. // Acquire lock and invoke user defined function.
  95. // If lock is held by another thread, this function wait for lock to be released.
  96. //
  97. // When a lock is acquired this function is guaranteed to emit an acquire barrier.
  98. //
  99. // Example usage:
  100. // lock.AcquireScoped([] {
  101. // enteredCriticalSection++;
  102. // });
  103. template<class FunctionType>
  104. FORCE_INLINE void AcquireScoped(const FunctionType& func)
  105. {
  106. ReleaseOnDestroy releaseScope(*this);
  107. Acquire();
  108. func();
  109. }
  110. // Try to acquire lock and invoke user defined function.
  111. // If lock is held by another thread, this function wait for timeoutInMilliseconds for lock to be released.
  112. // On failure to obtain lock the user defined function is not invoked.
  113. //
  114. // When lock is acquired this function is guaranteed to emit an acquire barrier.
  115. //
  116. // Example usage:
  117. // lock.TryAcquireScoped([] {
  118. // enteredCriticalSection++;
  119. // });
  120. //
  121. // Return: true if lock was acquired.
  122. template<class FunctionType>
  123. FORCE_INLINE bool TryAcquireScoped(const FunctionType& func)
  124. {
  125. if (TryAcquire())
  126. {
  127. ReleaseOnDestroy releaseScope(*this);
  128. func();
  129. return true;
  130. }
  131. return false;
  132. }
  133. // Try to acquire lock and invoke user defined function.
  134. // If lock is held by another thread, this function wait for timeoutInMilliseconds for lock to be released.
  135. // On failure to obtain lock the user defined function is not invoked.
  136. //
  137. // When lock is acquired this function is guaranteed to emit an acquire barrier.
  138. //
  139. // Timeout passed to this function may be subject to system clock resolution.
  140. // If the system clock has a resolution of e.g. 16ms that means this function may exit with a timeout error 16ms earlier than originally scheduled.
  141. //
  142. // Example usage:
  143. // bool lockAcquired = lock.TryTimedAcquireScoped(std::chrono::minutes(1), [] {
  144. // enteredCriticalSection++;
  145. // });
  146. // assert(lockAcquired);
  147. //
  148. // Return: true if lock was acquired.
  149. template<class FunctionType>
  150. FORCE_INLINE bool TryTimedAcquireScoped(const timeout_ms timeoutInMilliseconds, const FunctionType& func)
  151. {
  152. if (TryTimedAcquire(timeoutInMilliseconds))
  153. {
  154. ReleaseOnDestroy releaseScope(*this);
  155. func();
  156. return true;
  157. }
  158. return false;
  159. }
  160. private:
  161. class ReleaseOnDestroy
  162. {
  163. public:
  164. FORCE_INLINE ReleaseOnDestroy(ReentrantLock& lockReference) : m_LockReference(lockReference) {}
  165. FORCE_INLINE ~ReleaseOnDestroy() { m_LockReference.Release(); }
  166. private:
  167. ReentrantLock& m_LockReference;
  168. };
  169. Baselib_ReentrantLock m_ReentrantLockData;
  170. };
  171. }
  172. }