weak_ptr.h 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. #pragma once
  2. #include "ref_counted.h"
  3. #include <util/generic/hash.h>
  4. namespace NYT {
  5. ////////////////////////////////////////////////////////////////////////////////
  6. template <class T>
  7. class TWeakPtr
  8. {
  9. public:
  10. using TUnderlying = T;
  11. using element_type = T;
  12. //! Empty constructor.
  13. TWeakPtr() = default;
  14. TWeakPtr(std::nullptr_t)
  15. { }
  16. //! Constructor from an unqualified reference.
  17. /*!
  18. * Note that this constructor could be racy due to unsynchronized operations
  19. * on the object and on the counter.
  20. */
  21. explicit TWeakPtr(T* p) noexcept
  22. : T_(p)
  23. {
  24. #if defined(_tsan_enabled_)
  25. if (T_) {
  26. RefCounter_ = GetRefCounter(T_);
  27. }
  28. #endif
  29. AcquireRef();
  30. }
  31. //! Constructor from a strong reference.
  32. TWeakPtr(const TIntrusivePtr<T>& ptr) noexcept
  33. : TWeakPtr(ptr.Get())
  34. { }
  35. //! Constructor from a strong reference with an upcast.
  36. template <class U, class = typename std::enable_if_t<std::is_convertible_v<U*, T*>>>
  37. TWeakPtr(const TIntrusivePtr<U>& ptr) noexcept
  38. : TWeakPtr(ptr.Get())
  39. {
  40. static_assert(
  41. std::derived_from<T, TRefCountedBase>,
  42. "Cast allowed only for types derived from TRefCountedBase");
  43. }
  44. //! Copy constructor.
  45. TWeakPtr(const TWeakPtr& other) noexcept
  46. : TWeakPtr(other.T_)
  47. { }
  48. //! Copy constructor with an upcast.
  49. template <class U, class = typename std::enable_if_t<std::is_convertible_v<U*, T*>>>
  50. TWeakPtr(const TWeakPtr<U>& other) noexcept
  51. : TWeakPtr(other.Lock())
  52. {
  53. static_assert(
  54. std::derived_from<T, TRefCountedBase>,
  55. "Cast allowed only for types derived from TRefCountedBase");
  56. }
  57. //! Move constructor.
  58. TWeakPtr(TWeakPtr&& other) noexcept
  59. {
  60. other.Swap(*this);
  61. }
  62. //! Move constructor with an upcast.
  63. template <class U, class = typename std::enable_if_t<std::is_convertible_v<U*, T*>>>
  64. TWeakPtr(TWeakPtr<U>&& other) noexcept
  65. {
  66. static_assert(
  67. std::derived_from<T, TRefCountedBase>,
  68. "Cast allowed only for types derived from TRefCountedBase");
  69. TIntrusivePtr<U> strongOther = other.Lock();
  70. if (strongOther) {
  71. T_ = other.T_;
  72. other.T_ = nullptr;
  73. #if defined(_tsan_enabled_)
  74. RefCounter_ = other.RefCounter_;
  75. other.RefCounter_ = nullptr;
  76. #endif
  77. }
  78. }
  79. //! Destructor.
  80. ~TWeakPtr()
  81. {
  82. ReleaseRef();
  83. }
  84. //! Assignment operator from a strong reference.
  85. template <class U>
  86. TWeakPtr& operator=(const TIntrusivePtr<U>& ptr) noexcept
  87. {
  88. static_assert(
  89. std::is_convertible_v<U*, T*>,
  90. "U* must be convertible to T*");
  91. TWeakPtr(ptr).Swap(*this);
  92. return *this;
  93. }
  94. //! Copy assignment operator.
  95. TWeakPtr& operator=(const TWeakPtr& other) noexcept
  96. {
  97. TWeakPtr(other).Swap(*this);
  98. return *this;
  99. }
  100. //! Copy assignment operator with an upcast.
  101. template <class U>
  102. TWeakPtr& operator=(const TWeakPtr<U>& other) noexcept
  103. {
  104. static_assert(
  105. std::is_convertible_v<U*, T*>,
  106. "U* must be convertible to T*");
  107. TWeakPtr(other).Swap(*this);
  108. return *this;
  109. }
  110. //! Move assignment operator.
  111. TWeakPtr& operator=(TWeakPtr&& other) noexcept
  112. {
  113. other.Swap(*this);
  114. return *this;
  115. }
  116. //! Move assignment operator with an upcast.
  117. template <class U>
  118. TWeakPtr& operator=(TWeakPtr<U>&& other) noexcept
  119. {
  120. static_assert(
  121. std::is_convertible_v<U*, T*>,
  122. "U* must be convertible to T*");
  123. TWeakPtr(std::move(other)).Swap(*this);
  124. return *this;
  125. }
  126. //! Drop the pointer.
  127. void Reset() // noexcept
  128. {
  129. TWeakPtr().Swap(*this);
  130. }
  131. //! Replace the pointer with a specified one.
  132. void Reset(T* p) // noexcept
  133. {
  134. TWeakPtr(p).Swap(*this);
  135. }
  136. //! Replace the pointer with a specified one.
  137. template <class U>
  138. void Reset(const TIntrusivePtr<U>& ptr) // noexcept
  139. {
  140. static_assert(
  141. std::is_convertible_v<U*, T*>,
  142. "U* must be convertible to T*");
  143. TWeakPtr(ptr).Swap(*this);
  144. }
  145. //! Swap the pointer with the other one.
  146. void Swap(TWeakPtr& other) noexcept
  147. {
  148. DoSwap(T_, other.T_);
  149. #if defined(_tsan_enabled_)
  150. DoSwap(RefCounter_, other.RefCounter_);
  151. #endif
  152. }
  153. //! Acquire a strong reference to the pointee and return a strong pointer.
  154. TIntrusivePtr<T> Lock() const noexcept
  155. {
  156. return T_ && RefCounter()->TryRef()
  157. ? TIntrusivePtr<T>(T_, false)
  158. : TIntrusivePtr<T>();
  159. }
  160. bool IsExpired() const noexcept
  161. {
  162. return !T_ || (RefCounter()->GetRefCount() == 0);
  163. }
  164. const TRefCounter* TryGetRefCounter() const
  165. {
  166. return T_
  167. ? RefCounter()
  168. : nullptr;
  169. }
  170. private:
  171. void AcquireRef()
  172. {
  173. if (T_) {
  174. RefCounter()->WeakRef();
  175. }
  176. }
  177. void ReleaseRef()
  178. {
  179. if (T_) {
  180. // Support incomplete type.
  181. if (RefCounter()->WeakUnref()) {
  182. DeallocateRefCounted(T_);
  183. }
  184. }
  185. }
  186. template <class U>
  187. friend class TWeakPtr;
  188. template <class U>
  189. friend struct ::THash;
  190. T* T_ = nullptr;
  191. #if defined(_tsan_enabled_)
  192. const TRefCounter* RefCounter_ = nullptr;
  193. const TRefCounter* RefCounter() const
  194. {
  195. return RefCounter_;
  196. }
  197. #else
  198. const TRefCounter* RefCounter() const
  199. {
  200. return GetRefCounter(T_);
  201. }
  202. #endif
  203. };
  204. ////////////////////////////////////////////////////////////////////////////////
  205. //! Creates a weak pointer wrapper for a given raw pointer.
  206. //! Compared to |TWeakPtr<T>::ctor|, type inference enables omitting |T|.
  207. template <class T>
  208. TWeakPtr<T> MakeWeak(T* p)
  209. {
  210. return TWeakPtr<T>(p);
  211. }
  212. //! Creates a weak pointer wrapper for a given intrusive pointer.
  213. //! Compared to |TWeakPtr<T>::ctor|, type inference enables omitting |T|.
  214. template <class T>
  215. TWeakPtr<T> MakeWeak(const TIntrusivePtr<T>& p)
  216. {
  217. return TWeakPtr<T>(p);
  218. }
  219. //! A helper for acquiring weak pointer for pointee, resetting intrusive pointer and then
  220. //! returning the pointee reference count using the acquired weak pointer.
  221. //! This helper is designed for best effort in checking that the object is not leaked after
  222. //! destructing (what seems to be) the last pointer to it.
  223. //! NB: it is possible to rewrite this helper making it working event with intrinsic refcounted objects,
  224. //! but it requires much nastier integration with the intrusive pointer destruction routines.
  225. template <typename T>
  226. int ResetAndGetResidualRefCount(TIntrusivePtr<T>& pointer)
  227. {
  228. auto weakPointer = MakeWeak(pointer);
  229. pointer.Reset();
  230. pointer = weakPointer.Lock();
  231. if (pointer) {
  232. // This _may_ return 0 if we are again the only holder of the pointee.
  233. return pointer->GetRefCount() - 1;
  234. } else {
  235. return 0;
  236. }
  237. }
  238. ////////////////////////////////////////////////////////////////////////////////
  239. template <class T, class U>
  240. bool operator==(const TWeakPtr<T>& lhs, const TWeakPtr<U>& rhs)
  241. {
  242. static_assert(
  243. std::is_convertible_v<U*, T*>,
  244. "U* must be convertible to T*");
  245. return lhs.TryGetRefCounter() == rhs.TryGetRefCounter();
  246. }
  247. template <class T, class U>
  248. bool operator!=(const TWeakPtr<T>& lhs, const TWeakPtr<U>& rhs)
  249. {
  250. static_assert(
  251. std::is_convertible_v<U*, T*>,
  252. "U* must be convertible to T*");
  253. return lhs.TryGetRefCounter() != rhs.TryGetRefCounter();
  254. }
  255. template <class T>
  256. bool operator==(std::nullptr_t, const TWeakPtr<T>& rhs)
  257. {
  258. return nullptr == rhs.TryGetRefCounter();
  259. }
  260. template <class T>
  261. bool operator!=(std::nullptr_t, const TWeakPtr<T>& rhs)
  262. {
  263. return nullptr != rhs.TryGetRefCounter();
  264. }
  265. template <class T>
  266. bool operator==(const TWeakPtr<T>& lhs, std::nullptr_t)
  267. {
  268. return nullptr == lhs.TryGetRefCounter();
  269. }
  270. template <class T>
  271. bool operator!=(const TWeakPtr<T>& lhs, std::nullptr_t)
  272. {
  273. return nullptr != lhs.TryGetRefCounter();
  274. }
  275. ////////////////////////////////////////////////////////////////////////////////
  276. } // namespace NYT
  277. //! A hasher for TWeakPtr.
  278. template <class T>
  279. struct THash<NYT::TWeakPtr<T>>
  280. {
  281. size_t operator () (const NYT::TWeakPtr<T>& ptr) const
  282. {
  283. return THash<const NYT::TRefCountedBase*>()(ptr.T_);
  284. }
  285. };