暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ReentrantLock.h 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. #pragma once
  2. #include "../C/Baselib_ReentrantLock.h"
  3. #include "Internal/ScopedAcquireMixin.h"
  4. #include "Time.h"
  5. namespace baselib
  6. {
  7. BASELIB_CPP_INTERFACE
  8. {
  9. // In computer science, the reentrant mutex (recursive mutex, recursive lock) is particular type of mutual exclusion (mutex) device that may be locked multiple
  10. // times by the same process/thread, without causing a deadlock.
  11. // 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
  12. // 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
  13. // of times it has been locked, and requires equally many unlock operations to be performed before other threads may lock it.
  14. //
  15. // "Reentrant mutex", Wikipedia: The Free Encyclopedia
  16. // https://en.wikipedia.org/w/index.php?title=Reentrant_mutex&oldid=818566928
  17. //
  18. // For optimal performance, baselib::ReentrantLock should be stored at a cache aligned memory location.
  19. class ReentrantLock : public detail::ScopedAcquireMixin<ReentrantLock>
  20. {
  21. public:
  22. // non-copyable
  23. ReentrantLock(const ReentrantLock& other) = delete;
  24. ReentrantLock& operator=(const ReentrantLock& other) = delete;
  25. // non-movable (strictly speaking not needed but listed to signal intent)
  26. ReentrantLock(ReentrantLock&& other) = delete;
  27. ReentrantLock& operator=(ReentrantLock&& other) = delete;
  28. // Creates a reentrant lock synchronization primitive.
  29. // If there are not enough system resources to create a lock, process abort is triggered.
  30. ReentrantLock()
  31. {
  32. Baselib_ReentrantLock_CreateInplace(&m_ReentrantLockData);
  33. }
  34. // Reclaim resources and memory held by lock.
  35. //
  36. // If threads are waiting on the lock, calling free may trigger an assert and may cause process abort.
  37. // Calling this function with a nullptr result in a no-op
  38. ~ReentrantLock()
  39. {
  40. Baselib_ReentrantLock_FreeInplace(&m_ReentrantLockData);
  41. }
  42. // Acquire lock.
  43. //
  44. // 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
  45. // to be made before the lock is released.
  46. // If lock is held by another thread, this function wait for lock to be released.
  47. //
  48. // This function is guaranteed to emit an acquire barrier.
  49. //
  50. // \param maxSpinCount Max number of times to spin in user space before falling back to the kernel. The actual number
  51. // may differ depending on the underlying implementation but will never exceed the maxSpinCount
  52. // value.
  53. inline void Acquire(const uint32_t maxSpinCount = 0)
  54. {
  55. if (maxSpinCount && Baselib_ReentrantLock_TrySpinAcquire(&m_ReentrantLockData, maxSpinCount))
  56. return;
  57. return Baselib_ReentrantLock_Acquire(&m_ReentrantLockData);
  58. }
  59. // Try to acquire lock.
  60. //
  61. // 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
  62. // to be made before the lock is released.
  63. //
  64. // When lock is acquired this function is guaranteed to emit an acquire barrier.
  65. //
  66. // \param maxSpinCount Max number of times to spin in user space before falling back to the kernel. The actual number
  67. // may differ depending on the underlying implementation but will never exceed the maxSpinCount
  68. // value.
  69. // \returns true if lock was acquired.
  70. COMPILER_WARN_UNUSED_RESULT
  71. FORCE_INLINE bool TryAcquire(const uint32_t maxSpinCount = 0)
  72. {
  73. return Baselib_ReentrantLock_TrySpinAcquire(&m_ReentrantLockData, maxSpinCount);
  74. }
  75. // Try to acquire lock.
  76. // 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
  77. // to be made before the lock is released.
  78. // If lock is held by another thread, this function wait for timeoutInMilliseconds for lock to be released.
  79. //
  80. // When lock is acquired this function is guaranteed to emit an acquire barrier.
  81. //
  82. // TryAcquire with a zero timeout differs from TryAcquire() in that TryAcquire() is guaranteed to be a user space operation
  83. // while TryAcquire with zero timeout may enter the kernel and cause a context switch.
  84. //
  85. // Timeout passed to this function may be subject to system clock resolution.
  86. // 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.
  87. //
  88. // \param maxSpinCount Max number of times to spin in user space before falling back to the kernel. The actual number
  89. // may differ depending on the underlying implementation but will never exceed the maxSpinCount
  90. // value.
  91. // \returns true if lock was acquired.
  92. COMPILER_WARN_UNUSED_RESULT
  93. FORCE_INLINE bool TryTimedAcquire(const timeout_ms timeoutInMilliseconds, const uint32_t maxSpinCount = 0)
  94. {
  95. if (maxSpinCount && Baselib_ReentrantLock_TrySpinAcquire(&m_ReentrantLockData, maxSpinCount))
  96. return true;
  97. return Baselib_ReentrantLock_TryTimedAcquire(&m_ReentrantLockData, timeoutInMilliseconds.count());
  98. }
  99. // Release lock.
  100. // If lock count is still higher than zero after the release operation then lock remain in a locked state.
  101. // If lock count reach zero the lock is unlocked and made available to other threads
  102. //
  103. // When the lock is released this function is guaranteed to emit a release barrier.
  104. //
  105. // Calling this function from a thread that doesn't own the lock triggers an assert in debug and causes undefined behavior in release builds.
  106. FORCE_INLINE void Release()
  107. {
  108. return Baselib_ReentrantLock_Release(&m_ReentrantLockData);
  109. }
  110. private:
  111. Baselib_ReentrantLock m_ReentrantLockData;
  112. };
  113. }
  114. }