Lock.h 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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() : m_LockData(Baselib_Lock_Create())
  25. {
  26. }
  27. // Reclaim resources and memory held by lock.
  28. // If threads are waiting on the lock, calling free may trigger an assert and may cause process abort.
  29. ~Lock()
  30. {
  31. Baselib_Lock_Free(&m_LockData);
  32. }
  33. // Acquire lock.
  34. //
  35. // If lock is held, either by this or another thread, then the function wait for lock to be released.
  36. //
  37. // This function is guaranteed to emit an acquire barrier.
  38. inline void Acquire()
  39. {
  40. return Baselib_Lock_Acquire(&m_LockData);
  41. }
  42. // Try to acquire lock and return immediately.
  43. // If lock is held, either by this or another thread, then lock is not acquired and function return false.
  44. //
  45. // When a lock is acquired this function is guaranteed to emit an acquire barrier.
  46. //
  47. // Return: true if lock was acquired.
  48. COMPILER_WARN_UNUSED_RESULT
  49. FORCE_INLINE bool TryAcquire()
  50. {
  51. return Baselib_Lock_TryAcquire(&m_LockData);
  52. }
  53. // Try to acquire lock.
  54. // If lock is held, either by this or another thread, then the function wait for timeoutInMilliseconds for lock to be released.
  55. //
  56. // When a lock is acquired this function is guaranteed to emit an acquire barrier.
  57. //
  58. // TryAcquire with a zero timeout differs from TryAcquire() in that TryAcquire() is guaranteed to be a user space operation
  59. // while TryAcquire with zero timeout may enter the kernel and cause a context switch.
  60. //
  61. // Timeout passed to this function may be subject to system clock resolution.
  62. // 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.
  63. //
  64. // Return: true if lock was acquired.
  65. COMPILER_WARN_UNUSED_RESULT
  66. FORCE_INLINE bool TryTimedAcquire(const timeout_ms timeoutInMilliseconds)
  67. {
  68. return Baselib_Lock_TryTimedAcquire(&m_LockData, timeoutInMilliseconds.count());
  69. }
  70. // Release lock and make it available to other threads.
  71. //
  72. // This function can be called from any thread, not only the thread that acquired the lock.
  73. // If no lock was previously held calling this function result in a no-op.
  74. //
  75. // When the lock is released this function is guaranteed to emit a release barrier.
  76. FORCE_INLINE void Release()
  77. {
  78. return Baselib_Lock_Release(&m_LockData);
  79. }
  80. // Acquire lock and invoke user defined function.
  81. // If lock is held, either by this or another thread, then the function wait for lock to be released.
  82. //
  83. // When a lock is acquired this function is guaranteed to emit an acquire barrier.
  84. //
  85. // Example usage:
  86. // lock.AcquireScoped([] {
  87. // enteredCriticalSection++;
  88. // });
  89. template<class FunctionType>
  90. FORCE_INLINE void AcquireScoped(const FunctionType& func)
  91. {
  92. ReleaseOnDestroy releaseScope(*this);
  93. Acquire();
  94. func();
  95. }
  96. // Try to acquire lock and invoke user defined function.
  97. // If lock is held, either by this or another thread, then lock is not acquired and function return false.
  98. // On failure to obtain lock the user defined function is not invoked.
  99. //
  100. // When a lock is acquired this function is guaranteed to emit an acquire barrier.
  101. //
  102. // Example usage:
  103. // lock.TryAcquireScoped([] {
  104. // enteredCriticalSection++;
  105. // });
  106. //
  107. // Return: true if lock was acquired.
  108. template<class FunctionType>
  109. FORCE_INLINE bool TryAcquireScoped(const FunctionType& func)
  110. {
  111. if (TryAcquire())
  112. {
  113. ReleaseOnDestroy releaseScope(*this);
  114. func();
  115. return true;
  116. }
  117. return false;
  118. }
  119. // Try to acquire lock and invoke user defined function.
  120. // If lock is held, either by this or another thread, then the function wait for timeoutInMilliseconds for lock to be released.
  121. // On failure to obtain lock the user defined function is not invoked.
  122. //
  123. // When a lock is acquired this function is guaranteed to emit an acquire barrier.
  124. //
  125. // Timeout passed to this function may be subject to system clock resolution.
  126. // 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.
  127. //
  128. // Example usage:
  129. // bool lockAcquired = lock.TryTimedAcquireScoped(std::chrono::minutes(1), [] {
  130. // enteredCriticalSection++;
  131. // });
  132. // assert(lockAcquired);
  133. //
  134. // Return: true if lock was acquired.
  135. template<class FunctionType>
  136. FORCE_INLINE bool TryTimedAcquireScoped(const timeout_ms timeoutInMilliseconds, const FunctionType& func)
  137. {
  138. if (TryTimedAcquire(timeoutInMilliseconds))
  139. {
  140. ReleaseOnDestroy releaseScope(*this);
  141. func();
  142. return true;
  143. }
  144. return false;
  145. }
  146. private:
  147. class ReleaseOnDestroy
  148. {
  149. public:
  150. FORCE_INLINE ReleaseOnDestroy(Lock& lockReference) : m_LockReference(lockReference) {}
  151. FORCE_INLINE ~ReleaseOnDestroy() { m_LockReference.Release(); }
  152. private:
  153. Lock& m_LockReference;
  154. };
  155. Baselib_Lock m_LockData;
  156. };
  157. }
  158. }