rw_spin_lock.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #pragma once
  2. #include "public.h"
  3. #include "spin_lock_base.h"
  4. #include "spin_lock_count.h"
  5. #include <library/cpp/yt/memory/public.h>
  6. #include <util/system/rwlock.h>
  7. #include <atomic>
  8. namespace NYT::NThreading {
  9. ////////////////////////////////////////////////////////////////////////////////
  10. //! Single-writer multiple-readers spin lock.
  11. /*!
  12. * Reader-side calls are pretty cheap.
  13. * The lock is unfair.
  14. */
  15. class TReaderWriterSpinLock
  16. : public TSpinLockBase
  17. {
  18. public:
  19. using TSpinLockBase::TSpinLockBase;
  20. //! Acquires the reader lock.
  21. /*!
  22. * Optimized for the case of read-intensive workloads.
  23. * Cheap (just one atomic increment and no spinning if no writers are present).
  24. * Don't use this call if forks are possible: forking at some
  25. * intermediate point inside #AcquireReader may corrupt the lock state and
  26. * leave lock forever stuck for the child process.
  27. */
  28. void AcquireReader() noexcept;
  29. //! Acquires the reader lock.
  30. /*!
  31. * A more expensive version of #AcquireReader (includes at least
  32. * one atomic load and CAS; also may spin even if just readers are present).
  33. * In contrast to #AcquireReader, this method can be used in the presence of forks.
  34. * Note that fork-friendliness alone does not provide fork-safety: additional
  35. * actions must be performed to release the lock after a fork.
  36. */
  37. void AcquireReaderForkFriendly() noexcept;
  38. //! Tries acquiring the reader lock; see #AcquireReader.
  39. //! Returns |true| on success.
  40. bool TryAcquireReader() noexcept;
  41. //! Tries acquiring the reader lock (and does this in a fork-friendly manner); see #AcquireReaderForkFriendly.
  42. //! returns |true| on success.
  43. bool TryAcquireReaderForkFriendly() noexcept;
  44. //! Releases the reader lock.
  45. /*!
  46. * Cheap (just one atomic decrement).
  47. */
  48. void ReleaseReader() noexcept;
  49. //! Acquires the writer lock.
  50. /*!
  51. * Rather cheap (just one CAS).
  52. */
  53. void AcquireWriter() noexcept;
  54. //! Tries acquiring the writer lock; see #AcquireWriter.
  55. //! Returns |true| on success.
  56. bool TryAcquireWriter() noexcept;
  57. //! Releases the writer lock.
  58. /*!
  59. * Cheap (just one atomic store).
  60. */
  61. void ReleaseWriter() noexcept;
  62. //! Returns true if the lock is taken (either by a reader or writer).
  63. /*!
  64. * This is inherently racy.
  65. * Only use for debugging and diagnostic purposes.
  66. */
  67. bool IsLocked() const noexcept;
  68. //! Returns true if the lock is taken by reader.
  69. /*!
  70. * This is inherently racy.
  71. * Only use for debugging and diagnostic purposes.
  72. */
  73. bool IsLockedByReader() const noexcept;
  74. //! Returns true if the lock is taken by writer.
  75. /*!
  76. * This is inherently racy.
  77. * Only use for debugging and diagnostic purposes.
  78. */
  79. bool IsLockedByWriter() const noexcept;
  80. private:
  81. using TValue = ui32;
  82. static constexpr TValue UnlockedValue = 0;
  83. static constexpr TValue WriterMask = 1;
  84. static constexpr TValue ReaderDelta = 2;
  85. std::atomic<TValue> Value_ = UnlockedValue;
  86. bool TryAndTryAcquireReader() noexcept;
  87. bool TryAndTryAcquireWriter() noexcept;
  88. void AcquireReaderSlow() noexcept;
  89. void AcquireReaderForkFriendlySlow() noexcept;
  90. void AcquireWriterSlow() noexcept;
  91. };
  92. REGISTER_TRACKED_SPIN_LOCK_CLASS(TReaderWriterSpinLock)
  93. ////////////////////////////////////////////////////////////////////////////////
  94. //! A variant of TReaderWriterSpinLock occupying the whole cache line.
  95. class alignas(CacheLineSize) TPaddedReaderWriterSpinLock
  96. : public TReaderWriterSpinLock
  97. { };
  98. REGISTER_TRACKED_SPIN_LOCK_CLASS(TPaddedReaderWriterSpinLock)
  99. ////////////////////////////////////////////////////////////////////////////////
  100. template <class T>
  101. struct TReaderSpinlockTraits
  102. {
  103. static void Acquire(T* spinlock);
  104. static void Release(T* spinlock);
  105. };
  106. template <class T>
  107. struct TForkFriendlyReaderSpinlockTraits
  108. {
  109. static void Acquire(T* spinlock);
  110. static void Release(T* spinlock);
  111. };
  112. template <class T>
  113. struct TWriterSpinlockTraits
  114. {
  115. static void Acquire(T* spinlock);
  116. static void Release(T* spinlock);
  117. };
  118. template <class T>
  119. using TReaderGuard = TGuard<T, TReaderSpinlockTraits<T>>;
  120. template <class T>
  121. using TWriterGuard = TGuard<T, TWriterSpinlockTraits<T>>;
  122. template <class T>
  123. auto ReaderGuard(const T& lock);
  124. template <class T>
  125. auto ReaderGuard(const T* lock);
  126. template <class T>
  127. auto ForkFriendlyReaderGuard(const T& lock);
  128. template <class T>
  129. auto ForkFriendlyReaderGuard(const T* lock);
  130. template <class T>
  131. auto WriterGuard(const T& lock);
  132. template <class T>
  133. auto WriterGuard(const T* lock);
  134. ////////////////////////////////////////////////////////////////////////////////
  135. } // namespace NYT::NThreading
  136. #define RW_SPIN_LOCK_INL_H_
  137. #include "rw_spin_lock-inl.h"
  138. #undef RW_SPIN_LOCK_INL_H_