1 //
2 //
3 // Copyright 2017 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #ifndef GRPC_SRC_CORE_LIB_GPRPP_REF_COUNTED_PTR_H
20 #define GRPC_SRC_CORE_LIB_GPRPP_REF_COUNTED_PTR_H
21
22 #include <grpc/support/port_platform.h>
23
24 #include <iosfwd>
25 #include <type_traits>
26 #include <utility>
27
28 #include "src/core/lib/gprpp/debug_location.h"
29
30 namespace grpc_core {
31
32 // A smart pointer class for objects that provide IncrementRefCount() and
33 // Unref() methods, such as those provided by the RefCounted base class.
34 template <typename T>
35 class RefCountedPtr {
36 public:
RefCountedPtr()37 RefCountedPtr() {}
38 // NOLINTNEXTLINE(google-explicit-constructor)
RefCountedPtr(std::nullptr_t)39 RefCountedPtr(std::nullptr_t) {}
40
41 // If value is non-null, we take ownership of a ref to it.
42 template <typename Y>
RefCountedPtr(Y * value)43 explicit RefCountedPtr(Y* value) : value_(value) {}
44
45 // Move ctors.
RefCountedPtr(RefCountedPtr && other)46 RefCountedPtr(RefCountedPtr&& other) noexcept {
47 value_ = other.value_;
48 other.value_ = nullptr;
49 }
50 template <typename Y>
51 // NOLINTNEXTLINE(google-explicit-constructor)
RefCountedPtr(RefCountedPtr<Y> && other)52 RefCountedPtr(RefCountedPtr<Y>&& other) noexcept {
53 value_ = static_cast<T*>(other.value_);
54 other.value_ = nullptr;
55 }
56
57 // Move assignment.
58 RefCountedPtr& operator=(RefCountedPtr&& other) noexcept {
59 reset(std::exchange(other.value_, nullptr));
60 return *this;
61 }
62 template <typename Y>
63 RefCountedPtr& operator=(RefCountedPtr<Y>&& other) noexcept {
64 reset(std::exchange(other.value_, nullptr));
65 return *this;
66 }
67
68 // Copy ctors.
RefCountedPtr(const RefCountedPtr & other)69 RefCountedPtr(const RefCountedPtr& other) {
70 if (other.value_ != nullptr) other.value_->IncrementRefCount();
71 value_ = other.value_;
72 }
73 template <typename Y>
74 // NOLINTNEXTLINE(google-explicit-constructor)
RefCountedPtr(const RefCountedPtr<Y> & other)75 RefCountedPtr(const RefCountedPtr<Y>& other) {
76 static_assert(std::has_virtual_destructor<T>::value,
77 "T does not have a virtual dtor");
78 if (other.value_ != nullptr) other.value_->IncrementRefCount();
79 value_ = static_cast<T*>(other.value_);
80 }
81
82 // Copy assignment.
83 // NOLINTNEXTLINE(bugprone-unhandled-self-assignment)
84 RefCountedPtr& operator=(const RefCountedPtr& other) {
85 // Note: Order of reffing and unreffing is important here in case value_
86 // and other.value_ are the same object.
87 if (other.value_ != nullptr) other.value_->IncrementRefCount();
88 reset(other.value_);
89 return *this;
90 }
91 template <typename Y>
92 RefCountedPtr& operator=(const RefCountedPtr<Y>& other) {
93 static_assert(std::has_virtual_destructor<T>::value,
94 "T does not have a virtual dtor");
95 // Note: Order of reffing and unreffing is important here in case value_
96 // and other.value_ are the same object.
97 if (other.value_ != nullptr) other.value_->IncrementRefCount();
98 reset(other.value_);
99 return *this;
100 }
101
~RefCountedPtr()102 ~RefCountedPtr() {
103 if (value_ != nullptr) value_->Unref();
104 }
105
swap(RefCountedPtr & other)106 void swap(RefCountedPtr& other) { std::swap(value_, other.value_); }
107
108 // If value is non-null, we take ownership of a ref to it.
109 void reset(T* value = nullptr) {
110 T* old_value = std::exchange(value_, value);
111 if (old_value != nullptr) old_value->Unref();
112 }
113 void reset(const DebugLocation& location, const char* reason,
114 T* value = nullptr) {
115 T* old_value = std::exchange(value_, value);
116 if (old_value != nullptr) old_value->Unref(location, reason);
117 }
118 template <typename Y>
119 void reset(Y* value = nullptr) {
120 static_assert(std::has_virtual_destructor<T>::value,
121 "T does not have a virtual dtor");
122 reset(static_cast<T*>(value));
123 }
124 template <typename Y>
125 void reset(const DebugLocation& location, const char* reason,
126 Y* value = nullptr) {
127 static_assert(std::has_virtual_destructor<T>::value,
128 "T does not have a virtual dtor");
129 reset(location, reason, static_cast<T*>(value));
130 }
131
132 // This method is mostly useful for interoperating with C code.
133 // Eventually use within core should be banned, except at the surface API
134 // boundaries.
release()135 T* release() { return std::exchange(value_, nullptr); }
136
get()137 T* get() const { return value_; }
138
139 T& operator*() const { return *value_; }
140 T* operator->() const { return value_; }
141
142 template <typename Y>
143 bool operator==(const RefCountedPtr<Y>& other) const {
144 return value_ == other.value_;
145 }
146
147 template <typename Y>
148 bool operator==(const Y* other) const {
149 return value_ == other;
150 }
151
152 bool operator==(std::nullptr_t) const { return value_ == nullptr; }
153
154 template <typename Y>
155 bool operator!=(const RefCountedPtr<Y>& other) const {
156 return value_ != other.value_;
157 }
158
159 template <typename Y>
160 bool operator!=(const Y* other) const {
161 return value_ != other;
162 }
163
164 bool operator!=(std::nullptr_t) const { return value_ != nullptr; }
165
166 private:
167 template <typename Y>
168 friend class RefCountedPtr;
169
170 T* value_ = nullptr;
171 };
172
173 // A smart pointer class for objects that provide IncrementWeakRefCount() and
174 // WeakUnref() methods, such as those provided by the DualRefCounted base class.
175 template <typename T>
176 class WeakRefCountedPtr {
177 public:
WeakRefCountedPtr()178 WeakRefCountedPtr() {}
179 // NOLINTNEXTLINE(google-explicit-constructor)
WeakRefCountedPtr(std::nullptr_t)180 WeakRefCountedPtr(std::nullptr_t) {}
181
182 // If value is non-null, we take ownership of a ref to it.
183 template <typename Y>
WeakRefCountedPtr(Y * value)184 explicit WeakRefCountedPtr(Y* value) {
185 value_ = value;
186 }
187
188 // Move ctors.
WeakRefCountedPtr(WeakRefCountedPtr && other)189 WeakRefCountedPtr(WeakRefCountedPtr&& other) noexcept {
190 value_ = other.value_;
191 other.value_ = nullptr;
192 }
193 template <typename Y>
194 // NOLINTNEXTLINE(google-explicit-constructor)
WeakRefCountedPtr(WeakRefCountedPtr<Y> && other)195 WeakRefCountedPtr(WeakRefCountedPtr<Y>&& other) noexcept {
196 value_ = static_cast<T*>(other.value_);
197 other.value_ = nullptr;
198 }
199
200 // Move assignment.
201 WeakRefCountedPtr& operator=(WeakRefCountedPtr&& other) noexcept {
202 reset(std::exchange(other.value_, nullptr));
203 return *this;
204 }
205 template <typename Y>
206 WeakRefCountedPtr& operator=(WeakRefCountedPtr<Y>&& other) noexcept {
207 reset(std::exchange(other.value_, nullptr));
208 return *this;
209 }
210
211 // Copy ctors.
WeakRefCountedPtr(const WeakRefCountedPtr & other)212 WeakRefCountedPtr(const WeakRefCountedPtr& other) {
213 if (other.value_ != nullptr) other.value_->IncrementWeakRefCount();
214 value_ = other.value_;
215 }
216 template <typename Y>
217 // NOLINTNEXTLINE(google-explicit-constructor)
WeakRefCountedPtr(const WeakRefCountedPtr<Y> & other)218 WeakRefCountedPtr(const WeakRefCountedPtr<Y>& other) {
219 static_assert(std::has_virtual_destructor<T>::value,
220 "T does not have a virtual dtor");
221 if (other.value_ != nullptr) other.value_->IncrementWeakRefCount();
222 value_ = static_cast<T*>(other.value_);
223 }
224
225 // Copy assignment.
226 // NOLINTNEXTLINE(bugprone-unhandled-self-assignment)
227 WeakRefCountedPtr& operator=(const WeakRefCountedPtr& other) {
228 // Note: Order of reffing and unreffing is important here in case value_
229 // and other.value_ are the same object.
230 if (other.value_ != nullptr) other.value_->IncrementWeakRefCount();
231 reset(other.value_);
232 return *this;
233 }
234 template <typename Y>
235 WeakRefCountedPtr& operator=(const WeakRefCountedPtr<Y>& other) {
236 static_assert(std::has_virtual_destructor<T>::value,
237 "T does not have a virtual dtor");
238 // Note: Order of reffing and unreffing is important here in case value_
239 // and other.value_ are the same object.
240 if (other.value_ != nullptr) other.value_->IncrementWeakRefCount();
241 reset(other.value_);
242 return *this;
243 }
244
~WeakRefCountedPtr()245 ~WeakRefCountedPtr() {
246 if (value_ != nullptr) value_->WeakUnref();
247 }
248
swap(WeakRefCountedPtr & other)249 void swap(WeakRefCountedPtr& other) { std::swap(value_, other.value_); }
250
251 // If value is non-null, we take ownership of a ref to it.
252 void reset(T* value = nullptr) {
253 T* old_value = std::exchange(value_, value);
254 if (old_value != nullptr) old_value->WeakUnref();
255 }
256 void reset(const DebugLocation& location, const char* reason,
257 T* value = nullptr) {
258 T* old_value = std::exchange(value_, value);
259 if (old_value != nullptr) old_value->WeakUnref(location, reason);
260 }
261 template <typename Y>
262 void reset(Y* value = nullptr) {
263 static_assert(std::has_virtual_destructor<T>::value,
264 "T does not have a virtual dtor");
265 reset(static_cast<T*>(value));
266 }
267 template <typename Y>
268 void reset(const DebugLocation& location, const char* reason,
269 Y* value = nullptr) {
270 static_assert(std::has_virtual_destructor<T>::value,
271 "T does not have a virtual dtor");
272 reset(location, reason, static_cast<T*>(value));
273 }
274
275 // TODO(roth): This method exists solely as a transition mechanism to allow
276 // us to pass a ref to idiomatic C code that does not use WeakRefCountedPtr<>.
277 // Once all of our code has been converted to idiomatic C++, this
278 // method should go away.
release()279 T* release() {
280 T* value = value_;
281 value_ = nullptr;
282 return value;
283 }
284
get()285 T* get() const { return value_; }
286
287 T& operator*() const { return *value_; }
288 T* operator->() const { return value_; }
289
290 template <typename Y>
291 bool operator==(const WeakRefCountedPtr<Y>& other) const {
292 return value_ == other.value_;
293 }
294
295 template <typename Y>
296 bool operator==(const Y* other) const {
297 return value_ == other;
298 }
299
300 bool operator==(std::nullptr_t) const { return value_ == nullptr; }
301
302 template <typename Y>
303 bool operator!=(const WeakRefCountedPtr<Y>& other) const {
304 return value_ != other.value_;
305 }
306
307 template <typename Y>
308 bool operator!=(const Y* other) const {
309 return value_ != other;
310 }
311
312 bool operator!=(std::nullptr_t) const { return value_ != nullptr; }
313
314 private:
315 template <typename Y>
316 friend class WeakRefCountedPtr;
317
318 T* value_ = nullptr;
319 };
320
321 template <typename T, typename... Args>
MakeRefCounted(Args &&...args)322 inline RefCountedPtr<T> MakeRefCounted(Args&&... args) {
323 return RefCountedPtr<T>(new T(std::forward<Args>(args)...));
324 }
325
326 template <typename T>
327 bool operator<(const RefCountedPtr<T>& p1, const RefCountedPtr<T>& p2) {
328 return p1.get() < p2.get();
329 }
330
331 template <typename T>
332 bool operator<(const WeakRefCountedPtr<T>& p1, const WeakRefCountedPtr<T>& p2) {
333 return p1.get() < p2.get();
334 }
335
336 } // namespace grpc_core
337
338 #endif // GRPC_SRC_CORE_LIB_GPRPP_REF_COUNTED_PTR_H
339