1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
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 <cassert>
20 #include <cstdlib>
21 #include <type_traits>
22 #include <utility>
23 
24 namespace folly {
25 
26 /**
27  * A helper class to allow a DelayedDestruction object to be instantiated on
28  * the stack.
29  *
30  * This class derives from an existing DelayedDestruction type and makes the
31  * destructor public again.  This allows objects of this type to be declared on
32  * the stack or directly inside another class.  Normally DelayedDestruction
33  * objects must be dynamically allocated on the heap.
34  *
35  * However, the trade-off is that you lose some of the protections provided by
36  * DelayedDestruction::destroy().  DelayedDestruction::destroy() will
37  * automatically delay destruction of the object until it is safe to do so.
38  * If you use UndelayedDestruction, you become responsible for ensuring that
39  * you only destroy the object where it is safe to do so.  Attempting to
40  * destroy a UndelayedDestruction object while it has a non-zero destructor
41  * guard count will abort the program.
42  */
43 template <typename TDD>
44 class UndelayedDestruction : public TDD {
45  public:
46   // We could just use constructor inheritance, but not all compilers
47   // support that. So, just use a forwarding constructor.
48   //
49   // Ideally we would use std::enable_if<> and std::is_constructible<> to
50   // provide only constructor methods that are valid for our parent class.
51   // Unfortunately std::is_constructible<> doesn't work for types that aren't
52   // destructible.  In gcc-4.6 it results in a compiler error.  In the latest
53   // gcc code it looks like it has been fixed to return false.  (The language
54   // in the standard seems to indicate that returning false is the correct
55   // behavior for non-destructible types, which is unfortunate.)
56   template <typename... Args>
UndelayedDestruction(Args &&...args)57   explicit UndelayedDestruction(Args&&... args)
58       : TDD(std::forward<Args>(args)...) {}
59 
60   /**
61    * Public destructor.
62    *
63    * The caller is responsible for ensuring that the object is only destroyed
64    * where it is safe to do so.  (i.e., when the destructor guard count is 0).
65    *
66    * The exact conditions for meeting this may be dependent upon your class
67    * semantics.  Typically you are only guaranteed that it is safe to destroy
68    * the object directly from the event loop (e.g., directly from a
69    * EventBase::LoopCallback), or when the event loop is stopped.
70    */
~UndelayedDestruction()71   ~UndelayedDestruction() override {
72     // Crash if the caller is destroying us with outstanding destructor guards.
73     if (this->getDestructorGuardCount() != 0) {
74       abort();
75     }
76     // Invoke destroy.  This is necessary since our base class may have
77     // implemented custom behavior in destroy().
78     this->destroy();
79   }
80 
onDelayedDestroy(bool delayed)81   void onDelayedDestroy(bool delayed) override {
82     if (delayed && !this->TDD::getDestroyPending()) {
83       return;
84     }
85     // Do nothing.  This will always be invoked from the call to destroy
86     // inside our destructor.
87     assert(!delayed);
88     // prevent unused variable warnings when asserts are compiled out.
89     (void)delayed;
90   }
91 
92  protected:
93   /**
94    * Override our parent's destroy() method to make it protected.
95    * Callers should use the normal destructor instead of destroy
96    */
destroy()97   void destroy() override { this->TDD::destroy(); }
98 
99  private:
100   // Forbidden copy constructor and assignment operator
101   UndelayedDestruction(UndelayedDestruction const&) = delete;
102   UndelayedDestruction& operator=(UndelayedDestruction const&) = delete;
103 };
104 
105 } // namespace folly
106