2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include <type_traits>
24 #include <boost/noncopyable.hpp>
26 #include <glog/logging.h>
32 * DelayedDestructionBase is a helper class to ensure objects are not deleted
33 * while they still have functions executing in a higher stack frame.
35 * This is useful for objects that invoke callback functions, to ensure that a
36 * callback does not destroy the calling object.
38 * Classes needing this functionality should:
39 * - derive from DelayedDestructionBase directly
40 * - implement onDelayedDestroy which'll be called before the object is
41 * going to be destructed
42 * - create a DestructorGuard object on the stack in each public method that
43 * may invoke a callback
45 * DelayedDestructionBase does not perform any locking. It is intended to be
46 * used only from a single thread.
48 class DelayedDestructionBase : private boost::noncopyable {
50 virtual ~DelayedDestructionBase() = default;
53 * Classes should create a DestructorGuard object on the stack in any
54 * function that may invoke callback functions.
56 * The DestructorGuard prevents the guarded class from being destroyed while
57 * it exists. Without this, the callback function could delete the guarded
58 * object, causing problems when the callback function returns and the
59 * guarded object's method resumes execution.
61 class DestructorGuard {
64 explicit DestructorGuard(DelayedDestructionBase* dd = nullptr) :
68 assert(dd_->guardCount_ > 0); // check for wrapping
72 DestructorGuard(const DestructorGuard& dg) :
73 DestructorGuard(dg.dd_) {
76 DestructorGuard(DestructorGuard&& dg) noexcept :
81 DestructorGuard& operator =(DestructorGuard dg) noexcept {
82 std::swap(dd_, dg.dd_);
86 DestructorGuard& operator =(DelayedDestructionBase* dd) {
87 *this = DestructorGuard(dd);
93 assert(dd_->guardCount_ > 0);
95 if (dd_->guardCount_ == 0) {
96 dd_->onDelayedDestroy(true);
101 DelayedDestructionBase* get() const {
105 explicit operator bool() const {
106 return dd_ != nullptr;
110 DelayedDestructionBase* dd_;
114 * This smart pointer is a convenient way to manage a concrete
115 * DelayedDestructorBase child. It can replace the equivalent raw pointer and
116 * provide automatic memory management.
118 template <typename AliasType>
119 class IntrusivePtr : private DestructorGuard {
120 template <typename CopyAliasType>
121 friend class IntrusivePtr;
123 template <typename... Args>
124 static IntrusivePtr<AliasType> make(Args&&... args) {
125 return {new AliasType(std::forward<Args>(args)...)};
128 IntrusivePtr() = default;
129 IntrusivePtr(const IntrusivePtr&) = default;
130 IntrusivePtr(IntrusivePtr&&) noexcept = default;
132 template <typename CopyAliasType, typename =
133 typename std::enable_if<
134 std::is_convertible<CopyAliasType*, AliasType*>::value
136 IntrusivePtr(const IntrusivePtr<CopyAliasType>& copy) :
137 DestructorGuard(copy) {
140 template <typename CopyAliasType, typename =
141 typename std::enable_if<
142 std::is_convertible<CopyAliasType*, AliasType*>::value
144 IntrusivePtr(IntrusivePtr<CopyAliasType>&& copy) :
145 DestructorGuard(std::move(copy)) {
148 explicit IntrusivePtr(AliasType* dd) :
149 DestructorGuard(dd) {
152 // Copying from a unique_ptr is safe because if the upcast to
153 // DelayedDestructionBase works, then the instance is already using
154 // intrusive ref-counting.
155 template <typename CopyAliasType, typename Deleter, typename =
156 typename std::enable_if<
157 std::is_convertible<CopyAliasType*, AliasType*>::value
159 explicit IntrusivePtr(const std::unique_ptr<CopyAliasType, Deleter>& copy) :
160 DestructorGuard(copy.get()) {
163 IntrusivePtr& operator =(const IntrusivePtr&) = default;
164 IntrusivePtr& operator =(IntrusivePtr&&) noexcept = default;
166 template <typename CopyAliasType, typename =
167 typename std::enable_if<
168 std::is_convertible<CopyAliasType*, AliasType*>::value
170 IntrusivePtr& operator =(IntrusivePtr<CopyAliasType> copy) noexcept {
171 DestructorGuard::operator =(copy);
175 IntrusivePtr& operator =(AliasType* dd) {
176 DestructorGuard::operator =(dd);
180 void reset(AliasType* dd = nullptr) {
184 AliasType* get() const {
185 return static_cast<AliasType *>(DestructorGuard::get());
188 AliasType& operator *() const {
192 AliasType* operator ->() const {
196 explicit operator bool() const {
197 return DestructorGuard::operator bool();
202 DelayedDestructionBase()
206 * Get the number of DestructorGuards currently protecting this object.
208 * This is primarily intended for debugging purposes, such as asserting
209 * that an object has at least 1 guard.
211 uint32_t getDestructorGuardCount() const {
216 * Implement onDelayedDestroy in subclasses.
217 * onDelayedDestroy() is invoked when the object is potentially being
220 * @param delayed This parameter is true if destruction was delayed because
221 * of a DestructorGuard object, or false if onDelayedDestroy()
222 * is being called directly from the destructor.
224 virtual void onDelayedDestroy(bool delayed) = 0;
228 * guardCount_ is incremented by DestructorGuard, to indicate that one of
229 * the DelayedDestructionBase object's methods is currently running.
231 * If the destructor is called while guardCount_ is non-zero, destruction
232 * will be delayed until guardCount_ drops to 0. This allows
233 * DelayedDestructionBase objects to invoke callbacks without having to worry
234 * about being deleted before the callback returns.
236 uint32_t guardCount_;
239 inline bool operator ==(
240 const DelayedDestructionBase::DestructorGuard& left,
241 const DelayedDestructionBase::DestructorGuard& right) {
242 return left.get() == right.get();
244 inline bool operator !=(
245 const DelayedDestructionBase::DestructorGuard& left,
246 const DelayedDestructionBase::DestructorGuard& right) {
247 return left.get() != right.get();
249 inline bool operator ==(
250 const DelayedDestructionBase::DestructorGuard& left,
251 std::nullptr_t right) {
252 return left.get() == right;
254 inline bool operator ==(
256 const DelayedDestructionBase::DestructorGuard& right) {
257 return left == right.get();
259 inline bool operator !=(
260 const DelayedDestructionBase::DestructorGuard& left,
261 std::nullptr_t right) {
262 return left.get() != right;
264 inline bool operator !=(
266 const DelayedDestructionBase::DestructorGuard& right) {
267 return left != right.get();
270 template <typename LeftAliasType, typename RightAliasType>
271 inline bool operator ==(
272 const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
273 const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
274 return left.get() == right.get();
276 template <typename LeftAliasType, typename RightAliasType>
277 inline bool operator !=(
278 const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
279 const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
280 return left.get() != right.get();
282 template <typename LeftAliasType>
283 inline bool operator ==(
284 const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
285 std::nullptr_t right) {
286 return left.get() == right;
288 template <typename RightAliasType>
289 inline bool operator ==(
291 const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
292 return left == right.get();
294 template <typename LeftAliasType>
295 inline bool operator !=(
296 const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
297 std::nullptr_t right) {
298 return left.get() != right;
300 template <typename RightAliasType>
301 inline bool operator !=(
303 const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
304 return left != right.get();