Lock.h 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #pragma once
  2. #include "../C/Baselib_Lock.h"
  3. #include "Time.h"
  4. namespace baselib
  5. {
  6. BASELIB_CPP_INTERFACE
  7. {
  8. // In computer science, a lock or mutex (from mutual exclusion) is a synchronization mechanism for enforcing limits on access to a resource in an environment
  9. // where there are many threads of execution. A lock is designed to enforce a mutual exclusion concurrency control policy.
  10. //
  11. // "Lock (computer science)", Wikipedia: The Free Encyclopedia
  12. // https://en.wikipedia.org/w/index.php?title=Lock_(computer_science)&oldid=875674239
  13. class Lock
  14. {
  15. public:
  16. // non-copyable
  17. Lock(const Lock& other) = delete;
  18. Lock& operator=(const Lock& other) = delete;
  19. // non-movable (strictly speaking not needed but listed to signal intent)
  20. Lock(Lock&& other) = delete;
  21. Lock& operator=(Lock&& other) = delete;
  22. // Creates a lock synchronization primitive.
  23. // If there are not enough system resources to create a lock, process abort is triggered.
  24. Lock()
  25. {
  26. Baselib_Lock_CreateInplace(&m_LockData);
  27. }
  28. // Reclaim resources and memory held by lock.
  29. // If threads are waiting on the lock, calling free may trigger an assert and may cause process abort.
  30. ~Lock()
  31. {
  32. Baselib_Lock_FreeInplace(&m_LockData);
  33. }
  34. // Acquire lock.
  35. //
  36. // If lock is held, either by this or another thread, then the function wait for lock to be released.
  37. //
  38. // This function is guaranteed to emit an acquire barrier.
  39. inline void Acquire()
  40. {
  41. return Baselib_Lock_Acquire(&m_LockData);
  42. }
  43. // Try to acquire lock and return immediately.
  44. // If lock is held, either by this or another thread, then lock is not acquired and function return false.
  45. //
  46. // When a lock is acquired this function is guaranteed to emit an acquire barrier.
  47. //
  48. // Return: true if lock was acquired.
  49. COMPILER_WARN_UNUSED_RESULT
  50. FORCE_INLINE bool TryAcquire()
  51. {
  52. return Baselib_Lock_TryAcquire(&m_LockData);
  53. }
  54. // Try to acquire lock.
  55. // If lock is held, either by this or another thread, then the function wait for timeoutInMilliseconds for lock to be released.
  56. //
  57. // When a lock is acquired this function is guaranteed to emit an acquire barrier.
  58. //
  59. // TryAcquire with a zero timeout differs from TryAcquire() in that TryAcquire() is guaranteed to be a user space operation
  60. // while TryAcquire with zero timeout may enter the kernel and cause a context switch.
  61. //
  62. // Timeout passed to this function may be subject to system clock resolution.
  63. // 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.
  64. //
  65. // Return: true if lock was acquired.
  66. COMPILER_WARN_UNUSED_RESULT
  67. FORCE_INLINE bool TryTimedAcquire(const timeout_ms timeoutInMilliseconds)
  68. {
  69. return Baselib_Lock_TryTimedAcquire(&m_LockData, timeoutInMilliseconds.count());
  70. }
  71. // Release lock and make it available to other threads.
  72. //
  73. // This function can be called from any thread, not only the thread that acquired the lock.
  74. // If no lock was previously held calling this function result in a no-op.
  75. //
  76. // When the lock is released this function is guaranteed to emit a release barrier.
  77. FORCE_INLINE void Release()
  78. {
  79. return Baselib_Lock_Release(&m_LockData);
  80. }
  81. // Acquire lock and invoke user defined function.
  82. // If lock is held, either by this or another thread, then the function wait for lock to be released.
  83. //
  84. // When a lock is acquired this function is guaranteed to emit an acquire barrier.
  85. //
  86. // Example usage:
  87. // lock.AcquireScoped([] {
  88. // enteredCriticalSection++;
  89. // });
  90. template<class FunctionType>
  91. FORCE_INLINE void AcquireScoped(const FunctionType& func)
  92. {
  93. ReleaseOnDestroy releaseScope(*this);
  94. Acquire();
  95. func();
  96. }
  97. // Try to acquire lock and invoke user defined function.
  98. // If lock is held, either by this or another thread, then lock is not acquired and function return false.
  99. // On failure to obtain lock the user defined function is not invoked.
  100. //
  101. // When a lock is acquired this function is guaranteed to emit an acquire barrier.
  102. //
  103. // Example usage:
  104. // lock.TryAcquireScoped([] {
  105. // enteredCriticalSection++;
  106. // });
  107. //
  108. // Return: true if lock was acquired.
  109. template<class FunctionType>
  110. FORCE_INLINE bool TryAcquireScoped(const FunctionType& func)
  111. {
  112. if (TryAcquire())
  113. {
  114. ReleaseOnDestroy releaseScope(*this);
  115. func();
  116. return true;
  117. }
  118. return false;
  119. }
  120. // Try to acquire lock and invoke user defined function.
  121. // If lock is held, either by this or another thread, then the function wait for timeoutInMilliseconds for lock to be released.
  122. // On failure to obtain lock the user defined function is not invoked.
  123. //
  124. // When a lock is acquired this function is guaranteed to emit an acquire barrier.
  125. //
  126. // Timeout passed to this function may be subject to system clock resolution.
  127. // 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.
  128. //
  129. // Example usage:
  130. // bool lockAcquired = lock.TryTimedAcquireScoped(std::chrono::minutes(1), [] {
  131. // enteredCriticalSection++;
  132. // });
  133. // assert(lockAcquired);
  134. //
  135. // Return: true if lock was acquired.
  136. template<class FunctionType>
  137. FORCE_INLINE bool TryTimedAcquireScoped(const timeout_ms timeoutInMilliseconds, const FunctionType& func)
  138. {
  139. if (TryTimedAcquire(timeoutInMilliseconds))
  140. {
  141. ReleaseOnDestroy releaseScope(*this);
  142. func();
  143. return true;
  144. }
  145. return false;
  146. }
  147. private:
  148. class ReleaseOnDestroy
  149. {
  150. public:
  151. FORCE_INLINE ReleaseOnDestroy(Lock& lockReference) : m_LockReference(lockReference) {}
  152. FORCE_INLINE ~ReleaseOnDestroy() { m_LockReference.Release(); }
  153. private:
  154. Lock& m_LockReference;
  155. };
  156. Baselib_Lock m_LockData;
  157. };
  158. }
  159. }