DelayedDestructionBase.h 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /*
  2. * Copyright 2015-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #pragma once
  17. #include <assert.h>
  18. #include <cstddef>
  19. #include <cstdint>
  20. #include <functional>
  21. #include <memory>
  22. #include <type_traits>
  23. #include <utility>
  24. #include <boost/noncopyable.hpp>
  25. #include <glog/logging.h>
  26. namespace folly {
  27. /**
  28. * DelayedDestructionBase is a helper class to ensure objects are not deleted
  29. * while they still have functions executing in a higher stack frame.
  30. *
  31. * This is useful for objects that invoke callback functions, to ensure that a
  32. * callback does not destroy the calling object.
  33. *
  34. * Classes needing this functionality should:
  35. * - derive from DelayedDestructionBase directly
  36. * - implement onDelayedDestroy which'll be called before the object is
  37. * going to be destructed
  38. * - create a DestructorGuard object on the stack in each public method that
  39. * may invoke a callback
  40. *
  41. * DelayedDestructionBase does not perform any locking. It is intended to be
  42. * used only from a single thread.
  43. */
  44. class DelayedDestructionBase : private boost::noncopyable {
  45. public:
  46. virtual ~DelayedDestructionBase() = default;
  47. /**
  48. * Classes should create a DestructorGuard object on the stack in any
  49. * function that may invoke callback functions.
  50. *
  51. * The DestructorGuard prevents the guarded class from being destroyed while
  52. * it exists. Without this, the callback function could delete the guarded
  53. * object, causing problems when the callback function returns and the
  54. * guarded object's method resumes execution.
  55. */
  56. class DestructorGuard {
  57. public:
  58. explicit DestructorGuard(DelayedDestructionBase* dd) : dd_(dd) {
  59. if (dd_ != nullptr) {
  60. ++dd_->guardCount_;
  61. assert(dd_->guardCount_ > 0); // check for wrapping
  62. }
  63. }
  64. DestructorGuard(const DestructorGuard& dg) : DestructorGuard(dg.dd_) {}
  65. DestructorGuard(DestructorGuard&& dg) noexcept
  66. : dd_(std::exchange(dg.dd_, nullptr)) {}
  67. DestructorGuard& operator=(DestructorGuard dg) noexcept {
  68. std::swap(dd_, dg.dd_);
  69. return *this;
  70. }
  71. DestructorGuard& operator=(DelayedDestructionBase* dd) {
  72. *this = DestructorGuard(dd);
  73. return *this;
  74. }
  75. ~DestructorGuard() {
  76. if (dd_ != nullptr) {
  77. assert(dd_->guardCount_ > 0);
  78. --dd_->guardCount_;
  79. if (dd_->guardCount_ == 0) {
  80. dd_->onDelayedDestroy(true);
  81. }
  82. }
  83. }
  84. DelayedDestructionBase* get() const {
  85. return dd_;
  86. }
  87. explicit operator bool() const {
  88. return dd_ != nullptr;
  89. }
  90. private:
  91. DelayedDestructionBase* dd_;
  92. };
  93. /**
  94. * This smart pointer is a convenient way to manage a concrete
  95. * DelayedDestructorBase child. It can replace the equivalent raw pointer and
  96. * provide automatic memory management.
  97. */
  98. template <typename AliasType>
  99. class IntrusivePtr : private DestructorGuard {
  100. template <typename CopyAliasType>
  101. friend class IntrusivePtr;
  102. public:
  103. template <typename... Args>
  104. static IntrusivePtr<AliasType> make(Args&&... args) {
  105. return {new AliasType(std::forward<Args>(args)...)};
  106. }
  107. IntrusivePtr() = default;
  108. IntrusivePtr(const IntrusivePtr&) = default;
  109. IntrusivePtr(IntrusivePtr&&) noexcept = default;
  110. template <
  111. typename CopyAliasType,
  112. typename = typename std::enable_if<
  113. std::is_convertible<CopyAliasType*, AliasType*>::value>::type>
  114. IntrusivePtr(const IntrusivePtr<CopyAliasType>& copy)
  115. : DestructorGuard(copy) {}
  116. template <
  117. typename CopyAliasType,
  118. typename = typename std::enable_if<
  119. std::is_convertible<CopyAliasType*, AliasType*>::value>::type>
  120. IntrusivePtr(IntrusivePtr<CopyAliasType>&& copy)
  121. : DestructorGuard(std::move(copy)) {}
  122. explicit IntrusivePtr(AliasType* dd) : DestructorGuard(dd) {}
  123. // Copying from a unique_ptr is safe because if the upcast to
  124. // DelayedDestructionBase works, then the instance is already using
  125. // intrusive ref-counting.
  126. template <
  127. typename CopyAliasType,
  128. typename Deleter,
  129. typename = typename std::enable_if<
  130. std::is_convertible<CopyAliasType*, AliasType*>::value>::type>
  131. explicit IntrusivePtr(const std::unique_ptr<CopyAliasType, Deleter>& copy)
  132. : DestructorGuard(copy.get()) {}
  133. IntrusivePtr& operator=(const IntrusivePtr&) = default;
  134. IntrusivePtr& operator=(IntrusivePtr&&) noexcept = default;
  135. template <
  136. typename CopyAliasType,
  137. typename = typename std::enable_if<
  138. std::is_convertible<CopyAliasType*, AliasType*>::value>::type>
  139. IntrusivePtr& operator=(IntrusivePtr<CopyAliasType> copy) noexcept {
  140. DestructorGuard::operator=(copy);
  141. return *this;
  142. }
  143. IntrusivePtr& operator=(AliasType* dd) {
  144. DestructorGuard::operator=(dd);
  145. return *this;
  146. }
  147. void reset(AliasType* dd = nullptr) {
  148. *this = dd;
  149. }
  150. AliasType* get() const {
  151. return static_cast<AliasType*>(DestructorGuard::get());
  152. }
  153. AliasType& operator*() const {
  154. return *get();
  155. }
  156. AliasType* operator->() const {
  157. return get();
  158. }
  159. explicit operator bool() const {
  160. return DestructorGuard::operator bool();
  161. }
  162. };
  163. protected:
  164. DelayedDestructionBase() : guardCount_(0) {}
  165. /**
  166. * Get the number of DestructorGuards currently protecting this object.
  167. *
  168. * This is primarily intended for debugging purposes, such as asserting
  169. * that an object has at least 1 guard.
  170. */
  171. uint32_t getDestructorGuardCount() const {
  172. return guardCount_;
  173. }
  174. /**
  175. * Implement onDelayedDestroy in subclasses.
  176. * onDelayedDestroy() is invoked when the object is potentially being
  177. * destroyed.
  178. *
  179. * @param delayed This parameter is true if destruction was delayed because
  180. * of a DestructorGuard object, or false if onDelayedDestroy()
  181. * is being called directly from the destructor.
  182. */
  183. virtual void onDelayedDestroy(bool delayed) = 0;
  184. private:
  185. /**
  186. * guardCount_ is incremented by DestructorGuard, to indicate that one of
  187. * the DelayedDestructionBase object's methods is currently running.
  188. *
  189. * If the destructor is called while guardCount_ is non-zero, destruction
  190. * will be delayed until guardCount_ drops to 0. This allows
  191. * DelayedDestructionBase objects to invoke callbacks without having to worry
  192. * about being deleted before the callback returns.
  193. */
  194. uint32_t guardCount_;
  195. };
  196. inline bool operator==(
  197. const DelayedDestructionBase::DestructorGuard& left,
  198. const DelayedDestructionBase::DestructorGuard& right) {
  199. return left.get() == right.get();
  200. }
  201. inline bool operator!=(
  202. const DelayedDestructionBase::DestructorGuard& left,
  203. const DelayedDestructionBase::DestructorGuard& right) {
  204. return left.get() != right.get();
  205. }
  206. inline bool operator==(
  207. const DelayedDestructionBase::DestructorGuard& left,
  208. std::nullptr_t) {
  209. return left.get() == nullptr;
  210. }
  211. inline bool operator==(
  212. std::nullptr_t,
  213. const DelayedDestructionBase::DestructorGuard& right) {
  214. return nullptr == right.get();
  215. }
  216. inline bool operator!=(
  217. const DelayedDestructionBase::DestructorGuard& left,
  218. std::nullptr_t) {
  219. return left.get() != nullptr;
  220. }
  221. inline bool operator!=(
  222. std::nullptr_t,
  223. const DelayedDestructionBase::DestructorGuard& right) {
  224. return nullptr != right.get();
  225. }
  226. template <typename LeftAliasType, typename RightAliasType>
  227. inline bool operator==(
  228. const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
  229. const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
  230. return left.get() == right.get();
  231. }
  232. template <typename LeftAliasType, typename RightAliasType>
  233. inline bool operator!=(
  234. const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
  235. const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
  236. return left.get() != right.get();
  237. }
  238. template <typename LeftAliasType>
  239. inline bool operator==(
  240. const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
  241. std::nullptr_t) {
  242. return left.get() == nullptr;
  243. }
  244. template <typename RightAliasType>
  245. inline bool operator==(
  246. std::nullptr_t,
  247. const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
  248. return nullptr == right.get();
  249. }
  250. template <typename LeftAliasType>
  251. inline bool operator!=(
  252. const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
  253. std::nullptr_t) {
  254. return left.get() != nullptr;
  255. }
  256. template <typename RightAliasType>
  257. inline bool operator!=(
  258. std::nullptr_t,
  259. const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
  260. return nullptr != right.get();
  261. }
  262. } // namespace folly