rwspinlock.h 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. #pragma once
  2. #include <atomic>
  3. #include <util/system/spinlock.h>
  4. // State can be one of:
  5. // 0) [NOT_USED] State = 0:
  6. // * any reader can acquire lock (State += 2: -> READING)
  7. // * one writer can acquire lock (State = -1: -> WRITING)
  8. // 1) [READING] even number:
  9. // * State/2 = number of active readers
  10. // * no writers are waiting
  11. // * any reader can aqcuire lock (State += 2: -> READING)
  12. // * active readers can release lock (State -= 2)
  13. // * one writer can acquire lock (State += 1: -> WAITING)
  14. // 2) [WAITING] odd number > 0:
  15. // * (State-1)/2 = number of active readers
  16. // * no more readers/writers can acquire lock
  17. // * active readers can release lock (State -= 2: -> WAITING)
  18. // * exactly one writer is waiting for active readers to release lock
  19. // * if no more active readers left (State == 1) waiting writer acquires lock (State = -1: -> WRITING)
  20. // 3) [WRITING] State = -1
  21. // * exactly one active writer
  22. // * no active readers
  23. // * no more readers/writers can acquire lock
  24. // * writer can release lock (State = 0: -> READING)
  25. struct TRWSpinLock {
  26. std::atomic_signed_lock_free State;
  27. void Init() noexcept {
  28. State.store(0, std::memory_order_relaxed);
  29. }
  30. void AcquireRead() noexcept {
  31. while (true) {
  32. std::atomic_signed_lock_free::value_type a = State.load(std::memory_order_acquire);
  33. if ((a & 1) == 0 && State.compare_exchange_strong(a, a + 2, std::memory_order_acquire)) {
  34. break;
  35. }
  36. SpinLockPause();
  37. }
  38. }
  39. void ReleaseRead() noexcept {
  40. State.fetch_add(-2, std::memory_order_release);
  41. }
  42. void AcquireWrite() noexcept {
  43. while (true) {
  44. std::atomic_signed_lock_free::value_type a = State.load(std::memory_order_acquire);
  45. if ((a & 1) == 0 && State.compare_exchange_strong(a, a + 1, std::memory_order_acquire)) {
  46. break;
  47. }
  48. SpinLockPause();
  49. }
  50. while (true) {
  51. std::atomic_signed_lock_free::value_type a = 1;
  52. if (State.compare_exchange_strong(a, -1, std::memory_order_acquire)) {
  53. break;
  54. }
  55. SpinLockPause();
  56. }
  57. }
  58. void ReleaseWrite() noexcept {
  59. State.store(0, std::memory_order_release);
  60. }
  61. };
  62. struct TRWSpinLockReadOps {
  63. static inline void Acquire(TRWSpinLock* t) noexcept {
  64. t->AcquireRead();
  65. }
  66. static inline void Release(TRWSpinLock* t) noexcept {
  67. t->ReleaseRead();
  68. }
  69. };
  70. struct TRWSpinLockWriteOps {
  71. static inline void Acquire(TRWSpinLock* t) noexcept {
  72. t->AcquireWrite();
  73. }
  74. static inline void Release(TRWSpinLock* t) noexcept {
  75. t->ReleaseWrite();
  76. }
  77. };
  78. using TReadSpinLockGuard = TGuard<TRWSpinLock, TRWSpinLockReadOps>;
  79. using TWriteSpinLockGuard = TGuard<TRWSpinLock, TRWSpinLockWriteOps>;