rwspinlock.h 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. #pragma once
  2. #include <library/cpp/deprecated/atomic/atomic.h>
  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. TAtomic State; // must be initialized by 'TRWSpinLock myLock = {0};' construction
  27. void Init() noexcept {
  28. State = 0;
  29. }
  30. void AcquireRead() noexcept {
  31. while (true) {
  32. TAtomic a = AtomicGet(State);
  33. if ((a & 1) == 0 && AtomicCas(&State, a + 2, a)) {
  34. break;
  35. }
  36. SpinLockPause();
  37. }
  38. }
  39. void ReleaseRead() noexcept {
  40. AtomicAdd(State, -2);
  41. }
  42. void AcquireWrite() noexcept {
  43. while (true) {
  44. TAtomic a = AtomicGet(State);
  45. if ((a & 1) == 0 && AtomicCas(&State, a + 1, a)) {
  46. break;
  47. }
  48. SpinLockPause();
  49. }
  50. while (!AtomicCas(&State, TAtomicBase(-1), 1)) {
  51. SpinLockPause();
  52. }
  53. }
  54. void ReleaseWrite() noexcept {
  55. AtomicSet(State, 0);
  56. }
  57. };
  58. struct TRWSpinLockReadOps {
  59. static inline void Acquire(TRWSpinLock* t) noexcept {
  60. t->AcquireRead();
  61. }
  62. static inline void Release(TRWSpinLock* t) noexcept {
  63. t->ReleaseRead();
  64. }
  65. };
  66. struct TRWSpinLockWriteOps {
  67. static inline void Acquire(TRWSpinLock* t) noexcept {
  68. t->AcquireWrite();
  69. }
  70. static inline void Release(TRWSpinLock* t) noexcept {
  71. t->ReleaseWrite();
  72. }
  73. };
  74. using TReadSpinLockGuard = TGuard<TRWSpinLock, TRWSpinLockReadOps>;
  75. using TWriteSpinLockGuard = TGuard<TRWSpinLock, TRWSpinLockWriteOps>;