f944d6062f92faf1014adc29e5d711799d0242b7
[folly.git] / folly / io / async / test / UndelayedDestruction.h
1 /*
2  * Copyright 2004-present 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 <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>
57   explicit UndelayedDestruction(Args&& ...args)
58     : TDD(std::forward<Args>(args)...) {
59   }
60
61   /**
62    * Public destructor.
63    *
64    * The caller is responsible for ensuring that the object is only destroyed
65    * where it is safe to do so.  (i.e., when the destructor guard count is 0).
66    *
67    * The exact conditions for meeting this may be dependent upon your class
68    * semantics.  Typically you are only guaranteed that it is safe to destroy
69    * the object directly from the event loop (e.g., directly from a
70    * EventBase::LoopCallback), or when the event loop is stopped.
71    */
72   ~UndelayedDestruction() override {
73     // Crash if the caller is destroying us with outstanding destructor guards.
74     if (this->getDestructorGuardCount() != 0) {
75       abort();
76     }
77     // Invoke destroy.  This is necessary since our base class may have
78     // implemented custom behavior in destroy().
79     this->destroy();
80   }
81
82   void onDelayedDestroy(bool delayed) override {
83     if (delayed && !this->TDD::getDestroyPending()) {
84       return;
85     }
86     // Do nothing.  This will always be invoked from the call to destroy
87     // inside our destructor.
88     assert(!delayed);
89     // prevent unused variable warnings when asserts are compiled out.
90     (void)delayed;
91   }
92
93  protected:
94   /**
95    * Override our parent's destroy() method to make it protected.
96    * Callers should use the normal destructor instead of destroy
97    */
98   void destroy() override {
99     this->TDD::destroy();
100   }
101
102  private:
103   // Forbidden copy constructor and assignment operator
104   UndelayedDestruction(UndelayedDestruction const &) = delete;
105   UndelayedDestruction& operator=(UndelayedDestruction const &) = delete;
106 };
107
108 }