2 * Copyright 2015 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 * - pass a callback to onDestroy_ 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_->onDestroy_(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 onDestroy_ in subclasses.
217 * onDestroy_() is invoked when the object is potentially being destroyed.
219 * @param delayed This parameter is true if destruction was delayed because
220 * of a DestructorGuard object, or false if onDestroy_() is
221 * being called directly from the destructor.
223 std::function<void(bool)> onDestroy_;
227 * guardCount_ is incremented by DestructorGuard, to indicate that one of
228 * the DelayedDestructionBase object's methods is currently running.
230 * If the destructor is called while guardCount_ is non-zero, destruction
231 * will be delayed until guardCount_ drops to 0. This allows
232 * DelayedDestructionBase objects to invoke callbacks without having to worry
233 * about being deleted before the callback returns.
235 uint32_t guardCount_;
238 inline bool operator ==(
239 const DelayedDestructionBase::DestructorGuard& left,
240 const DelayedDestructionBase::DestructorGuard& right) {
241 return left.get() == right.get();
243 inline bool operator !=(
244 const DelayedDestructionBase::DestructorGuard& left,
245 const DelayedDestructionBase::DestructorGuard& right) {
246 return left.get() != right.get();
248 inline bool operator ==(
249 const DelayedDestructionBase::DestructorGuard& left,
250 std::nullptr_t right) {
251 return left.get() == right;
253 inline bool operator ==(
255 const DelayedDestructionBase::DestructorGuard& right) {
256 return left == right.get();
258 inline bool operator !=(
259 const DelayedDestructionBase::DestructorGuard& left,
260 std::nullptr_t right) {
261 return left.get() != right;
263 inline bool operator !=(
265 const DelayedDestructionBase::DestructorGuard& right) {
266 return left != right.get();
269 template <typename LeftAliasType, typename RightAliasType>
270 inline bool operator ==(
271 const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
272 const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
273 return left.get() == right.get();
275 template <typename LeftAliasType, typename RightAliasType>
276 inline bool operator !=(
277 const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
278 const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
279 return left.get() != right.get();
281 template <typename LeftAliasType>
282 inline bool operator ==(
283 const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
284 std::nullptr_t right) {
285 return left.get() == right;
287 template <typename RightAliasType>
288 inline bool operator ==(
290 const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
291 return left == right.get();
293 template <typename LeftAliasType>
294 inline bool operator !=(
295 const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
296 std::nullptr_t right) {
297 return left.get() != right;
299 template <typename RightAliasType>
300 inline bool operator !=(
302 const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
303 return left != right.get();