Improve the DelayedDestruction smart pointer
[folly.git] / folly / io / async / DelayedDestructionBase.h
1 /*
2  * Copyright 2015 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
17 #pragma once
18
19 #include <assert.h>
20 #include <cstddef>
21 #include <memory>
22 #include <type_traits>
23 #include <utility>
24 #include <boost/noncopyable.hpp>
25 #include <functional>
26 #include <glog/logging.h>
27 #include <inttypes.h>
28
29 namespace folly {
30
31 /**
32  * DelayedDestructionBase is a helper class to ensure objects are not deleted
33  * while they still have functions executing in a higher stack frame.
34  *
35  * This is useful for objects that invoke callback functions, to ensure that a
36  * callback does not destroy the calling object.
37  *
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
44  *
45  * DelayedDestructionBase does not perform any locking.  It is intended to be
46  * used only from a single thread.
47  */
48 class DelayedDestructionBase : private boost::noncopyable {
49  public:
50   virtual ~DelayedDestructionBase() = default;
51
52   /**
53    * Classes should create a DestructorGuard object on the stack in any
54    * function that may invoke callback functions.
55    *
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.
60    */
61   class DestructorGuard {
62    public:
63
64     explicit DestructorGuard(DelayedDestructionBase* dd = nullptr) :
65         dd_(dd) {
66       if (dd_ != nullptr) {
67         ++dd_->guardCount_;
68         assert(dd_->guardCount_ > 0); // check for wrapping
69       }
70     }
71
72     DestructorGuard(const DestructorGuard& dg) :
73         DestructorGuard(dg.dd_) {
74     }
75
76     DestructorGuard(DestructorGuard&& dg) noexcept :
77         dd_(dg.dd_) {
78       dg.dd_ = nullptr;
79     }
80
81     DestructorGuard& operator =(DestructorGuard dg) noexcept {
82       std::swap(dd_, dg.dd_);
83       return *this;
84     }
85
86     DestructorGuard& operator =(DelayedDestructionBase* dd) {
87       *this = DestructorGuard(dd);
88       return *this;
89     }
90
91     ~DestructorGuard() {
92       if (dd_ != nullptr) {
93         assert(dd_->guardCount_ > 0);
94         --dd_->guardCount_;
95         if (dd_->guardCount_ == 0) {
96           dd_->onDestroy_(true);
97         }
98       }
99     }
100
101     DelayedDestructionBase* get() const {
102       return dd_;
103     }
104
105     explicit operator bool() const {
106       return dd_ != nullptr;
107     }
108
109    private:
110     DelayedDestructionBase* dd_;
111   };
112
113   /**
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.
117    */
118   template <typename AliasType>
119   class IntrusivePtr : private DestructorGuard {
120     template <typename CopyAliasType>
121     friend class IntrusivePtr;
122    public:
123     template <typename... Args>
124     static IntrusivePtr<AliasType> make(Args&&... args) {
125       return {new AliasType(std::forward<Args>(args)...)};
126     }
127
128     IntrusivePtr() = default;
129     IntrusivePtr(const IntrusivePtr&) = default;
130     IntrusivePtr(IntrusivePtr&&) noexcept = default;
131
132     template <typename CopyAliasType, typename =
133       typename std::enable_if<
134         std::is_convertible<CopyAliasType*, AliasType*>::value
135       >::type>
136     IntrusivePtr(const IntrusivePtr<CopyAliasType>& copy) :
137         DestructorGuard(copy) {
138     }
139
140     template <typename CopyAliasType, typename =
141       typename std::enable_if<
142         std::is_convertible<CopyAliasType*, AliasType*>::value
143       >::type>
144     IntrusivePtr(IntrusivePtr<CopyAliasType>&& copy) :
145         DestructorGuard(std::move(copy)) {
146     }
147
148     explicit IntrusivePtr(AliasType* dd) :
149         DestructorGuard(dd) {
150     }
151
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
158       >::type>
159     explicit IntrusivePtr(const std::unique_ptr<CopyAliasType, Deleter>& copy) :
160         DestructorGuard(copy.get()) {
161     }
162
163     IntrusivePtr& operator =(const IntrusivePtr&) = default;
164     IntrusivePtr& operator =(IntrusivePtr&&) noexcept = default;
165
166     template <typename CopyAliasType, typename =
167       typename std::enable_if<
168         std::is_convertible<CopyAliasType*, AliasType*>::value
169       >::type>
170     IntrusivePtr& operator =(IntrusivePtr<CopyAliasType> copy) noexcept {
171       DestructorGuard::operator =(copy);
172       return *this;
173     }
174
175     IntrusivePtr& operator =(AliasType* dd) {
176       DestructorGuard::operator =(dd);
177       return *this;
178     }
179
180     void reset(AliasType* dd = nullptr) {
181       *this = dd;
182     }
183
184     AliasType* get() const {
185       return static_cast<AliasType *>(DestructorGuard::get());
186     }
187
188     AliasType& operator *() const {
189       return *get();
190     }
191
192     AliasType* operator ->() const {
193       return get();
194     }
195
196     explicit operator bool() const {
197       return DestructorGuard::operator bool();
198     }
199   };
200
201  protected:
202   DelayedDestructionBase()
203     : guardCount_(0) {}
204
205   /**
206    * Get the number of DestructorGuards currently protecting this object.
207    *
208    * This is primarily intended for debugging purposes, such as asserting
209    * that an object has at least 1 guard.
210    */
211   uint32_t getDestructorGuardCount() const {
212     return guardCount_;
213   }
214
215   /**
216    * Implement onDestroy_ in subclasses.
217    * onDestroy_() is invoked when the object is potentially being destroyed.
218    *
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.
222    */
223   std::function<void(bool)> onDestroy_;
224
225  private:
226   /**
227    * guardCount_ is incremented by DestructorGuard, to indicate that one of
228    * the DelayedDestructionBase object's methods is currently running.
229    *
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.
234    */
235   uint32_t guardCount_;
236 };
237
238 inline bool operator ==(
239     const DelayedDestructionBase::DestructorGuard& left,
240     const DelayedDestructionBase::DestructorGuard& right) {
241   return left.get() == right.get();
242 }
243 inline bool operator !=(
244     const DelayedDestructionBase::DestructorGuard& left,
245     const DelayedDestructionBase::DestructorGuard& right) {
246   return left.get() != right.get();
247 }
248 inline bool operator ==(
249     const DelayedDestructionBase::DestructorGuard& left,
250     std::nullptr_t right) {
251   return left.get() == right;
252 }
253 inline bool operator ==(
254     std::nullptr_t left,
255     const DelayedDestructionBase::DestructorGuard& right) {
256   return left == right.get();
257 }
258 inline bool operator !=(
259     const DelayedDestructionBase::DestructorGuard& left,
260     std::nullptr_t right) {
261   return left.get() != right;
262 }
263 inline bool operator !=(
264     std::nullptr_t left,
265     const DelayedDestructionBase::DestructorGuard& right) {
266   return left != right.get();
267 }
268
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();
274 }
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();
280 }
281 template <typename LeftAliasType>
282 inline bool operator ==(
283     const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
284     std::nullptr_t right) {
285   return left.get() == right;
286 }
287 template <typename RightAliasType>
288 inline bool operator ==(
289     std::nullptr_t left,
290     const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
291   return left == right.get();
292 }
293 template <typename LeftAliasType>
294 inline bool operator !=(
295     const DelayedDestructionBase::IntrusivePtr<LeftAliasType>& left,
296     std::nullptr_t right) {
297   return left.get() != right;
298 }
299 template <typename RightAliasType>
300 inline bool operator !=(
301     std::nullptr_t left,
302     const DelayedDestructionBase::IntrusivePtr<RightAliasType>& right) {
303   return left != right.get();
304 }
305 } // folly