ref_counted-inl.h 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. #ifndef REF_COUNTED_INL_H_
  2. #error "Direct inclusion of this file is not allowed, include ref_counted.h"
  3. // For the sake of sane code completion.
  4. #include "ref_counted.h"
  5. #endif
  6. #include "tagged_ptr.h"
  7. #include <util/system/sanitizers.h>
  8. namespace NYT {
  9. ////////////////////////////////////////////////////////////////////////////////
  10. // TODO(babenko): move to hazard pointers
  11. void RetireHazardPointer(TPackedPtr packedPtr, void (*reclaimer)(TPackedPtr));
  12. ////////////////////////////////////////////////////////////////////////////////
  13. namespace NDetail {
  14. ////////////////////////////////////////////////////////////////////////////////
  15. template <class T, class = void>
  16. struct TFreeMemory
  17. {
  18. static void Do(void* ptr)
  19. {
  20. #ifdef _win_
  21. ::_aligned_free(ptr);
  22. #else
  23. ::free(ptr);
  24. #endif
  25. }
  26. };
  27. template <class T>
  28. struct TFreeMemory<T, std::void_t<typename T::TAllocator>>
  29. {
  30. static void Do(void* ptr)
  31. {
  32. using TAllocator = typename T::TAllocator;
  33. TAllocator::Free(ptr);
  34. }
  35. };
  36. ////////////////////////////////////////////////////////////////////////////////
  37. template <class T, class = void>
  38. struct TMemoryReleaser
  39. {
  40. static void Do(void* ptr, ui16 /*offset*/)
  41. {
  42. TFreeMemory<T>::Do(ptr);
  43. }
  44. };
  45. template <class T>
  46. struct TMemoryReleaser<T, std::enable_if_t<T::EnableHazard>>
  47. {
  48. static void Do(void* ptr, ui16 offset)
  49. {
  50. // Base pointer is used in HazardPtr as the identity of object.
  51. auto packedPtr = TTaggedPtr<char>{static_cast<char*>(ptr) + offset, offset}.Pack();
  52. RetireHazardPointer(packedPtr, [] (TPackedPtr packedPtr) {
  53. // Base ptr and the beginning of allocated memory region may differ.
  54. auto [ptr, offset] = TTaggedPtr<char>::Unpack(packedPtr);
  55. TFreeMemory<T>::Do(ptr - offset);
  56. });
  57. }
  58. };
  59. ////////////////////////////////////////////////////////////////////////////////
  60. template <class T>
  61. Y_FORCE_INLINE void DestroyRefCountedImpl(T* obj)
  62. {
  63. // No standard way to statically calculate the base offset even if T is final.
  64. // static_cast<TFinalDerived*>(virtualBasePtr) does not work.
  65. auto* basePtr = static_cast<TRefCountedBase*>(obj);
  66. auto offset = reinterpret_cast<uintptr_t>(basePtr) - reinterpret_cast<uintptr_t>(obj);
  67. auto* refCounter = GetRefCounter(obj);
  68. // No virtual call when T is final.
  69. obj->~T();
  70. // Fast path. Weak refs cannot appear if there are neither strong nor weak refs.
  71. if (refCounter->GetWeakRefCount() == 1) {
  72. NYT::NDetail::TMemoryReleaser<T>::Do(obj, offset);
  73. return;
  74. }
  75. YT_ASSERT(offset < (1ULL << PackedPtrTagBits));
  76. auto* vTablePtr = reinterpret_cast<TPackedPtr*>(basePtr);
  77. *vTablePtr = TTaggedPtr<void(void*, ui16)>(&NYT::NDetail::TMemoryReleaser<T>::Do, offset).Pack();
  78. if (refCounter->WeakUnref()) {
  79. NYT::NDetail::TMemoryReleaser<T>::Do(obj, offset);
  80. }
  81. }
  82. ////////////////////////////////////////////////////////////////////////////////
  83. // Specialization for final classes.
  84. template <class T, bool = std::derived_from<T, TRefCountedBase>>
  85. struct TRefCountedTraits
  86. {
  87. static_assert(
  88. std::is_final_v<T>,
  89. "Ref-counted objects must be derived from TRefCountedBase or to be final");
  90. static constexpr size_t RefCounterSpace = (sizeof(TRefCounter) + alignof(T) - 1) & ~(alignof(T) - 1);
  91. static constexpr size_t RefCounterOffset = RefCounterSpace - sizeof(TRefCounter);
  92. Y_FORCE_INLINE static const TRefCounter* GetRefCounter(const T* obj)
  93. {
  94. return reinterpret_cast<const TRefCounter*>(obj) - 1;
  95. }
  96. Y_FORCE_INLINE static void Destroy(const T* obj)
  97. {
  98. auto* refCounter = GetRefCounter(obj);
  99. // No virtual call when T is final.
  100. obj->~T();
  101. char* ptr = reinterpret_cast<char*>(const_cast<TRefCounter*>(refCounter));
  102. // Fast path. Weak refs cannot appear if there are neither strong nor weak refs.
  103. if (refCounter->GetWeakRefCount() == 1) {
  104. NYT::NDetail::TMemoryReleaser<T>::Do(ptr - RefCounterOffset, RefCounterSpace);
  105. return;
  106. }
  107. if (refCounter->WeakUnref()) {
  108. NYT::NDetail::TMemoryReleaser<T>::Do(ptr - RefCounterOffset, RefCounterSpace);
  109. }
  110. }
  111. Y_FORCE_INLINE static void Deallocate(const T* obj)
  112. {
  113. char* ptr = reinterpret_cast<char*>(const_cast<TRefCounter*>(GetRefCounter(obj)));
  114. NYT::NDetail::TMemoryReleaser<T>::Do(ptr - RefCounterOffset, RefCounterSpace);
  115. }
  116. };
  117. // Specialization for classes derived from TRefCountedBase.
  118. template <class T>
  119. struct TRefCountedTraits<T, true>
  120. {
  121. Y_FORCE_INLINE static const TRefCounter* GetRefCounter(const T* obj)
  122. {
  123. return obj;
  124. }
  125. Y_FORCE_INLINE static void Destroy(const TRefCountedBase* obj)
  126. {
  127. const_cast<TRefCountedBase*>(obj)->DestroyRefCounted();
  128. }
  129. Y_FORCE_INLINE static void Deallocate(const TRefCountedBase* obj)
  130. {
  131. auto* ptr = reinterpret_cast<TPackedPtr*>(const_cast<TRefCountedBase*>(obj));
  132. auto [ptrToDeleter, offset] = TTaggedPtr<void(void*, ui16)>::Unpack(*ptr);
  133. // The most derived type is erased here. So we cannot call TMemoryReleaser with derived type.
  134. ptrToDeleter(reinterpret_cast<char*>(ptr) - offset, offset);
  135. }
  136. };
  137. ////////////////////////////////////////////////////////////////////////////////
  138. } // namespace NDetail
  139. ////////////////////////////////////////////////////////////////////////////////
  140. Y_FORCE_INLINE int TRefCounter::GetRefCount() const noexcept
  141. {
  142. return StrongCount_.load(std::memory_order::acquire);
  143. }
  144. Y_FORCE_INLINE void TRefCounter::Ref(int n) const noexcept
  145. {
  146. YT_ASSERT(n >= 0);
  147. // It is safe to use relaxed here, since new reference is always created from another live reference.
  148. auto value = StrongCount_.fetch_add(n, std::memory_order::relaxed);
  149. YT_ASSERT(value > 0);
  150. YT_ASSERT(value <= std::numeric_limits<TRefCount>::max() - n);
  151. YT_ASSERT(WeakCount_.load(std::memory_order::relaxed) > 0);
  152. }
  153. Y_FORCE_INLINE void TRefCounter::DangerousRef(int n) const noexcept
  154. {
  155. YT_ASSERT(n >= 0);
  156. // Relaxed is fine as per lukyan@, the caller guarantees object liveness.
  157. auto value = StrongCount_.fetch_add(n, std::memory_order::relaxed);
  158. YT_ASSERT(value >= 0);
  159. YT_ASSERT(value <= std::numeric_limits<TRefCount>::max() - n);
  160. YT_ASSERT(WeakCount_.load(std::memory_order::relaxed) > 0);
  161. }
  162. Y_FORCE_INLINE bool TRefCounter::TryRef() const noexcept
  163. {
  164. auto value = StrongCount_.load(std::memory_order::relaxed);
  165. YT_ASSERT(value >= 0 && value < std::numeric_limits<TRefCount>::max());
  166. YT_ASSERT(WeakCount_.load(std::memory_order::relaxed) > 0);
  167. while (value != 0 && !StrongCount_.compare_exchange_weak(value, value + 1));
  168. return value != 0;
  169. }
  170. Y_FORCE_INLINE bool TRefCounter::Unref(int n) const
  171. {
  172. YT_ASSERT(n >= 0);
  173. // We must properly synchronize last access to object with it destruction.
  174. // Otherwise compiler might reorder access to object past this decrement.
  175. //
  176. // See http://www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html#boost_atomic.usage_examples.example_reference_counters
  177. //
  178. auto oldStrongCount = StrongCount_.fetch_sub(n, std::memory_order::release);
  179. YT_ASSERT(oldStrongCount >= n);
  180. if (oldStrongCount == n) {
  181. std::atomic_thread_fence(std::memory_order::acquire);
  182. NSan::Acquire(&StrongCount_);
  183. return true;
  184. } else {
  185. return false;
  186. }
  187. }
  188. Y_FORCE_INLINE int TRefCounter::GetWeakRefCount() const noexcept
  189. {
  190. return WeakCount_.load(std::memory_order::acquire);
  191. }
  192. Y_FORCE_INLINE void TRefCounter::WeakRef() const noexcept
  193. {
  194. auto oldWeakCount = WeakCount_.fetch_add(1, std::memory_order::relaxed);
  195. YT_ASSERT(oldWeakCount > 0);
  196. }
  197. Y_FORCE_INLINE bool TRefCounter::WeakUnref() const
  198. {
  199. auto oldWeakCount = WeakCount_.fetch_sub(1, std::memory_order::release);
  200. YT_ASSERT(oldWeakCount > 0);
  201. if (oldWeakCount == 1) {
  202. std::atomic_thread_fence(std::memory_order::acquire);
  203. NSan::Acquire(&WeakCount_);
  204. return true;
  205. } else {
  206. return false;
  207. }
  208. }
  209. ////////////////////////////////////////////////////////////////////////////////
  210. template <class T>
  211. Y_FORCE_INLINE const TRefCounter* GetRefCounter(const T* obj)
  212. {
  213. return NYT::NDetail::TRefCountedTraits<T>::GetRefCounter(obj);
  214. }
  215. template <class T>
  216. Y_FORCE_INLINE void DestroyRefCounted(const T* obj)
  217. {
  218. NYT::NDetail::TRefCountedTraits<T>::Destroy(obj);
  219. }
  220. template <class T>
  221. Y_FORCE_INLINE void DeallocateRefCounted(const T* obj)
  222. {
  223. NYT::NDetail::TRefCountedTraits<T>::Deallocate(obj);
  224. }
  225. ////////////////////////////////////////////////////////////////////////////////
  226. template <class T>
  227. Y_FORCE_INLINE void Ref(T* obj, int n)
  228. {
  229. GetRefCounter(obj)->Ref(n);
  230. }
  231. template <class T>
  232. Y_FORCE_INLINE void Unref(T* obj, int n)
  233. {
  234. if (GetRefCounter(obj)->Unref(n)) {
  235. DestroyRefCounted(obj);
  236. }
  237. }
  238. ////////////////////////////////////////////////////////////////////////////////
  239. Y_FORCE_INLINE void TRefCounted::Unref() const
  240. {
  241. ::NYT::Unref(this);
  242. }
  243. Y_FORCE_INLINE void TRefCounted::WeakUnref() const
  244. {
  245. if (TRefCounter::WeakUnref()) {
  246. DeallocateRefCounted(this);
  247. }
  248. }
  249. template <class T>
  250. void TRefCounted::DestroyRefCountedImpl(T* obj)
  251. {
  252. NYT::NDetail::DestroyRefCountedImpl<T>(obj);
  253. }
  254. ////////////////////////////////////////////////////////////////////////////////
  255. } // namespace NYT