Make DestructorCheck::Safety no-copy, no-move
[folly.git] / folly / io / async / DestructorCheck.h
1 /*
2  * Copyright 2017 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 #pragma once
17
18 namespace folly {
19
20 /**
21  * DestructorCheck is a helper class that helps to detect if a tracked object
22  * was deleted.
23  * This is useful for objects that request callbacks from other components.
24  *
25  * Classes needing this functionality should:
26  * - derive from DestructorCheck
27  *
28  * Callback context can be extended with an instance of DestructorCheck::Safety
29  * object initialized with a reference to the object dereferenced from the
30  * callback.  Once the callback is invoked, it can use this safety object to
31  * check if the object was not deallocated yet before dereferencing it.
32  *
33  * DestructorCheck does not perform any locking.  It is intended to be used
34  * only from a single thread.
35  *
36  * Example:
37  *
38  * class AsyncFoo : public DestructorCheck {
39  *  public:
40  *   ~AsyncFoo();
41  *   // awesome async code with circuitous deletion paths
42  *   void async1();
43  *   void async2();
44  * };
45  *
46  * righteousFunc(AsyncFoo& f) {
47  *   DestructorCheck::Safety safety(f);
48  *
49  *   f.async1(); // might have deleted f, oh noes
50  *   if (!safety.destroyed()) {
51  *     // phew, still there
52  *     f.async2();
53  *   }
54  * }
55  */
56
57 class DestructorCheck {
58  public:
59   virtual ~DestructorCheck() {
60     rootGuard_.setAllDestroyed();
61   }
62
63   class Safety;
64
65   class ForwardLink {
66    // These methods are mostly private because an outside caller could violate
67    // the integrity of the linked list.
68    private:
69     void setAllDestroyed() {
70       for (auto guard = next_; guard; guard = guard->next_) {
71         guard->setDestroyed();
72       }
73     }
74
75     // This is used to maintain the double-linked list. An intrusive list does
76     // not require any heap allocations, like a standard container would. This
77     // isolation of next_ in its own class means that the DestructorCheck can
78     // easily hold a next_ pointer without needing to hold a prev_ pointer.
79     // DestructorCheck never needs a prev_ pointer because it is the head node
80     // and this is a special list where the head never moves and never has a
81     // previous node.
82     Safety* next_{nullptr};
83
84     friend DestructorCheck::~DestructorCheck();
85     friend class Safety;
86   };
87
88   // See above example for usage
89   class Safety : public ForwardLink {
90    public:
91     explicit Safety(DestructorCheck& destructorCheck) {
92       // Insert this node at the head of the list.
93       prev_ = &destructorCheck.rootGuard_;
94       next_ = prev_->next_;
95       if (next_ != nullptr) {
96         next_->prev_ = this;
97       }
98       prev_->next_ = this;
99      }
100
101     ~Safety() {
102       if (!destroyed()) {
103         // Remove this node from the list.
104         prev_->next_ = next_;
105         if (next_ != nullptr) {
106           next_->prev_ = prev_;
107         }
108       }
109     }
110
111     Safety(const Safety&) = delete;
112     Safety(Safety&& goner) = delete;
113     Safety& operator=(const Safety&) = delete;
114     Safety& operator=(Safety&&) = delete;
115
116     bool destroyed() const {
117       return prev_ == nullptr;
118     }
119
120    private:
121     void setDestroyed() {
122       prev_ = nullptr;
123     }
124
125     // This field is used to maintain the double-linked list. If the root has
126     // been destroyed then the field is set to the nullptr sentinel value.
127     ForwardLink* prev_;
128
129     friend class ForwardLink;
130   };
131
132  private:
133   ForwardLink rootGuard_;
134 };
135
136 }