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.
24 #include <type_traits>
27 #include <boost/noncopyable.hpp>
28 #include <glog/logging.h>
33 * DelayedDestructionBase is a helper class to ensure objects are not deleted
34 * while they still have functions executing in a higher stack frame.
36 * This is useful for objects that invoke callback functions, to ensure that a
37 * callback does not destroy the calling object.
39 * Classes needing this functionality should:
40 * - derive from DelayedDestructionBase directly
41 * - implement onDelayedDestroy which'll be called before the object is
42 * going to be destructed
43 * - create a DestructorGuard object on the stack in each public method that
44 * may invoke a callback
46 * DelayedDestructionBase does not perform any locking. It is intended to be
47 * used only from a single thread.
49 class DelayedDestructionBase : private boost::noncopyable {
51 virtual ~DelayedDestructionBase() = default;
54 * Classes should create a DestructorGuard object on the stack in any
55 * function that may invoke callback functions.
57 * The DestructorGuard prevents the guarded class from being destroyed while
58 * it exists. Without this, the callback function could delete the guarded
59 * object, causing problems when the callback function returns and the
60 * guarded object's method resumes execution.
62 class DestructorGuard {
65 explicit DestructorGuard(DelayedDestructionBase* dd = nullptr) :
69 assert(dd_->guardCount_ > 0); // check for wrapping
73 DestructorGuard(const DestructorGuard& dg) :
74 DestructorGuard(dg.dd_) {
77 DestructorGuard(DestructorGuard&& dg) noexcept :
82 DestructorGuard& operator =(DestructorGuard dg) noexcept {
83 std::swap(dd_, dg.dd_);
87 DestructorGuard& operator =(DelayedDestructionBase* dd) {
88 *this = DestructorGuard(dd);
94 assert(dd_->guardCount_ > 0);
96 if (dd_->guardCount_ == 0) {
97 dd_->onDelayedDestroy(true);
102 DelayedDestructionBase* get() const {
106 explicit operator bool() const {
107 return dd_ != nullptr;
111 DelayedDestructionBase* dd_;
115 * This smart pointer is a convenient way to manage a concrete
116 * DelayedDestructorBase child. It can replace the equivalent raw pointer and
117 * provide automatic memory management.
119 template <typename AliasType>
120 class IntrusivePtr : private DestructorGuard {
121 template <typename CopyAliasType>
122 friend class IntrusivePtr;
124 template <typename... Args>
125 static IntrusivePtr<AliasType> make(Args&&... args) {
126 return {new AliasType(std::forward<Args>(args)...)};
129 IntrusivePtr() = default;
130 IntrusivePtr(const IntrusivePtr&) = default;
131 IntrusivePtr(IntrusivePtr&&) noexcept = default;
133 template <typename CopyAliasType, typename =
134 typename std::enable_if<
135 std::is_convertible<CopyAliasType*, AliasType*>::value
137 IntrusivePtr(const IntrusivePtr<CopyAliasType>& copy) :
138 DestructorGuard(copy) {
141 template <typename CopyAliasType, typename =
142 typename std::enable_if<
143 std::is_convertible<CopyAliasType*, AliasType*>::value
145 IntrusivePtr(IntrusivePtr<CopyAliasType>&& copy) :
146 DestructorGuard(std::move(copy)) {
149 explicit IntrusivePtr(AliasType* dd) :
150 DestructorGuard(dd) {
153 // Copying from a unique_ptr is safe because if the upcast to
154 // DelayedDestructionBase works, then the instance is already using
155 // intrusive ref-counting.
156 template <typename CopyAliasType, typename Deleter, typename =
157 typename std::enable_if<
158 std::is_convertible<CopyAliasType*, AliasType*>::value
160 explicit IntrusivePtr(const std::unique_ptr<CopyAliasType, Deleter>& copy) :
161 DestructorGuard(copy.get()) {
164 IntrusivePtr& operator =(const IntrusivePtr&) = default;
165 IntrusivePtr& operator =(IntrusivePtr&&) noexcept = default;
167 template <typename CopyAliasType, typename =
168 typename std::enable_if<
169 std::is_convertible<CopyAliasType*, AliasType*>::value
171 IntrusivePtr& operator =(IntrusivePtr<CopyAliasType> copy) noexcept {
172 DestructorGuard::operator =(copy);
176 IntrusivePtr& operator =(AliasType* dd) {
177 DestructorGuard::operator =(dd);
181 void reset(AliasType* dd = nullptr) {
185 AliasType* get() const {
186 return static_cast<AliasType *>(DestructorGuard::get());
189 AliasType& operator *() const {
193 AliasType* operator ->() const {
197 explicit operator bool() const {
198 return DestructorGuard::operator bool();
203 DelayedDestructionBase()
207 * Get the number of DestructorGuards currently protecting this object.
209 * This is primarily intended for debugging purposes, such as asserting
210 * that an object has at least 1 guard.
212 uint32_t getDestructorGuardCount() const {
217 * Implement onDelayedDestroy in subclasses.
218 * onDelayedDestroy() is invoked when the object is potentially being
221 * @param delayed This parameter is true if destruction was delayed because
222 * of a DestructorGuard object, or false if onDelayedDestroy()
223 * is being called directly from the destructor.
225 virtual void onDelayedDestroy(bool delayed) = 0;
229 * guardCount_ is incremented by DestructorGuard, to indicate that one of
230 * the DelayedDestructionBase object's methods is currently running.
232 * If the destructor is called while guardCount_ is non-zero, destruction
233 * will be delayed until guardCount_ drops to 0. This allows
234 * DelayedDestructionBase objects to invoke callbacks without having to worry
235 * about being deleted before the callback returns.
237 uint32_t guardCount_;
240 inline bool operator ==(
241 const DelayedDestructionBase::DestructorGuard& left,
242 const DelayedDestructionBase::DestructorGuard& right) {
243 return left.get() == right.get();
245 inline bool operator !=(
246 const DelayedDestructionBase::DestructorGuard& left,
247 const DelayedDestructionBase::DestructorGuard& right) {
248 return left.get() != right.get();
250 inline bool operator ==(
251 const DelayedDestructionBase::DestructorGuard& left,
252 std::nullptr_t right) {
253 return left.get() == right;
255 inline bool operator ==(
257 const DelayedDestructionBase::DestructorGuard& right) {
258 return left == right.get();
260 inline bool operator !=(
261 const DelayedDestructionBase::DestructorGuard& left,
262 std::nullptr_t right) {
263 return left.get() != right;
265 inline bool operator !=(
267 const DelayedDestructionBase::DestructorGuard& right) {
268 return left != right.get();
271 template <typename LeftAliasType, typename RightAliasType>
272 inline bool operator ==(
273 const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
274 const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
275 return left.get() == right.get();
277 template <typename LeftAliasType, typename RightAliasType>
278 inline bool operator !=(
279 const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
280 const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
281 return left.get() != right.get();
283 template <typename LeftAliasType>
284 inline bool operator ==(
285 const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
286 std::nullptr_t right) {
287 return left.get() == right;
289 template <typename RightAliasType>
290 inline bool operator ==(
292 const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
293 return left == right.get();
295 template <typename LeftAliasType>
296 inline bool operator !=(
297 const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
298 std::nullptr_t right) {
299 return left.get() != right;
301 template <typename RightAliasType>
302 inline bool operator !=(
304 const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
305 return left != right.get();