ReentrantLock.h 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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() : m_ReentrantLockData(Baselib_ReentrantLock_Create())
  30. {
  31. }
  32. // Reclaim resources and memory held by lock.
  33. //
  34. // If threads are waiting on the lock, calling free may trigger an assert and may cause process abort.
  35. // Calling this function with a nullptr result in a no-op
  36. ~ReentrantLock()
  37. {
  38. Baselib_ReentrantLock_Free(&m_ReentrantLockData);
  39. }
  40. // Acquire lock.
  41. //
  42. // 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
  43. // to be made before the lock is released.
  44. // If lock is held by another thread, this function wait for lock to be released.
  45. //
  46. // This function is guaranteed to emit an acquire barrier.
  47. inline void Acquire()
  48. {
  49. return Baselib_ReentrantLock_Acquire(&m_ReentrantLockData);
  50. }
  51. // Try to acquire lock and return immediately.
  52. // 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
  53. // to be made before the lock is released.
  54. //
  55. // When lock is acquired this function is guaranteed to emit an acquire barrier.
  56. //
  57. // Return: true if lock was acquired.
  58. COMPILER_WARN_UNUSED_RESULT
  59. FORCE_INLINE bool TryAcquire()
  60. {
  61. return Baselib_ReentrantLock_TryAcquire(&m_ReentrantLockData);
  62. }
  63. // Try to acquire lock.
  64. // 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
  65. // to be made before the lock is released.
  66. // If lock is held by another thread, this function wait for timeoutInMilliseconds for lock to be released.
  67. //
  68. // When lock is acquired this function is guaranteed to emit an acquire barrier.
  69. //
  70. // TryAcquire with a zero timeout differs from TryAcquire() in that TryAcquire() is guaranteed to be a user space operation
  71. // while TryAcquire with zero timeout may enter the kernel and cause a context switch.
  72. //
  73. // Timeout passed to this function may be subject to system clock resolution.
  74. // 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.
  75. //
  76. // Return: true if lock was acquired.
  77. COMPILER_WARN_UNUSED_RESULT
  78. FORCE_INLINE bool TryTimedAcquire(const timeout_ms timeoutInMilliseconds)
  79. {
  80. return Baselib_ReentrantLock_TryTimedAcquire(&m_ReentrantLockData, timeoutInMilliseconds.count());
  81. }
  82. // Release lock.
  83. // If lock count is still higher than zero after the release operation then lock remain in a locked state.
  84. // If lock count reach zero the lock is unlocked and made available to other threads
  85. //
  86. // When the lock is released this function is guaranteed to emit a release barrier.
  87. //
  88. // Calling this function from a thread that doesn't own the lock triggers an assert in debug and causes undefined behavior in release builds.
  89. FORCE_INLINE void Release()
  90. {
  91. return Baselib_ReentrantLock_Release(&m_ReentrantLockData);
  92. }
  93. // Acquire lock and invoke user defined function.
  94. // If lock is held by another thread, this function wait for lock to be released.
  95. //
  96. // When a lock is acquired this function is guaranteed to emit an acquire barrier.
  97. //
  98. // Example usage:
  99. // lock.AcquireScoped([] {
  100. // enteredCriticalSection++;
  101. // });
  102. template<class FunctionType>
  103. FORCE_INLINE void AcquireScoped(const FunctionType& func)
  104. {
  105. ReleaseOnDestroy releaseScope(*this);
  106. Acquire();
  107. func();
  108. }
  109. // Try to acquire lock and invoke user defined function.
  110. // If lock is held by another thread, this function wait for timeoutInMilliseconds for lock to be released.
  111. // On failure to obtain lock the user defined function is not invoked.
  112. //
  113. // When lock is acquired this function is guaranteed to emit an acquire barrier.
  114. //
  115. // Example usage:
  116. // lock.TryAcquireScoped([] {
  117. // enteredCriticalSection++;
  118. // });
  119. //
  120. // Return: true if lock was acquired.
  121. template<class FunctionType>
  122. FORCE_INLINE bool TryAcquireScoped(const FunctionType& func)
  123. {
  124. if (TryAcquire())
  125. {
  126. ReleaseOnDestroy releaseScope(*this);
  127. func();
  128. return true;
  129. }
  130. return false;
  131. }
  132. // Try to acquire lock and invoke user defined function.
  133. // If lock is held by another thread, this function wait for timeoutInMilliseconds for lock to be released.
  134. // On failure to obtain lock the user defined function is not invoked.
  135. //
  136. // When lock is acquired this function is guaranteed to emit an acquire barrier.
  137. //
  138. // Timeout passed to this function may be subject to system clock resolution.
  139. // 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.
  140. //
  141. // Example usage:
  142. // bool lockAcquired = lock.TryTimedAcquireScoped(std::chrono::minutes(1), [] {
  143. // enteredCriticalSection++;
  144. // });
  145. // assert(lockAcquired);
  146. //
  147. // Return: true if lock was acquired.
  148. template<class FunctionType>
  149. FORCE_INLINE bool TryTimedAcquireScoped(const timeout_ms timeoutInMilliseconds, const FunctionType& func)
  150. {
  151. if (TryTimedAcquire(timeoutInMilliseconds))
  152. {
  153. ReleaseOnDestroy releaseScope(*this);
  154. func();
  155. return true;
  156. }
  157. return false;
  158. }
  159. private:
  160. class ReleaseOnDestroy
  161. {
  162. public:
  163. FORCE_INLINE ReleaseOnDestroy(ReentrantLock& lockReference) : m_LockReference(lockReference) {}
  164. FORCE_INLINE ~ReleaseOnDestroy() { m_LockReference.Release(); }
  165. private:
  166. ReentrantLock& m_LockReference;
  167. };
  168. Baselib_ReentrantLock m_ReentrantLockData;
  169. };
  170. }
  171. }