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_UTIL_REF_COUNTED_PTR_H
20 #define GRPC_SRC_CORE_UTIL_REF_COUNTED_PTR_H
21
22 #include <grpc/support/port_platform.h>
23
24 #include <cstddef>
25 #include <iosfwd>
26 #include <type_traits>
27 #include <utility>
28
29 #include "absl/hash/hash.h"
30 #include "src/core/util/debug_location.h"
31 #include "src/core/util/down_cast.h"
32
33 namespace grpc_core {
34
35 // A smart pointer class for objects that provide IncrementRefCount() and
36 // Unref() methods, such as those provided by the RefCounted base class.
37 template <typename T>
38 class RefCountedPtr {
39 public:
RefCountedPtr()40 RefCountedPtr() {}
41 // NOLINTNEXTLINE(google-explicit-constructor)
RefCountedPtr(std::nullptr_t)42 RefCountedPtr(std::nullptr_t) {}
43
44 // If value is non-null, we take ownership of a ref to it.
45 template <typename Y,
46 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
RefCountedPtr(Y * value)47 explicit RefCountedPtr(Y* value) : value_(value) {}
48
49 // Move ctors.
RefCountedPtr(RefCountedPtr && other)50 RefCountedPtr(RefCountedPtr&& other) noexcept {
51 value_ = other.value_;
52 other.value_ = nullptr;
53 }
54 template <typename Y,
55 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
56 // NOLINTNEXTLINE(google-explicit-constructor)
RefCountedPtr(RefCountedPtr<Y> && other)57 RefCountedPtr(RefCountedPtr<Y>&& other) noexcept {
58 value_ = static_cast<T*>(other.value_);
59 other.value_ = nullptr;
60 }
61
62 // Move assignment.
63 RefCountedPtr& operator=(RefCountedPtr&& other) noexcept {
64 reset(std::exchange(other.value_, nullptr));
65 return *this;
66 }
67 template <typename Y,
68 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
69 RefCountedPtr& operator=(RefCountedPtr<Y>&& other) noexcept {
70 reset(std::exchange(other.value_, nullptr));
71 return *this;
72 }
73
74 // Copy ctors.
RefCountedPtr(const RefCountedPtr & other)75 RefCountedPtr(const RefCountedPtr& other) {
76 if (other.value_ != nullptr) other.value_->IncrementRefCount();
77 value_ = other.value_;
78 }
79 template <typename Y,
80 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
81 // NOLINTNEXTLINE(google-explicit-constructor)
RefCountedPtr(const RefCountedPtr<Y> & other)82 RefCountedPtr(const RefCountedPtr<Y>& other) {
83 static_assert(std::has_virtual_destructor<T>::value,
84 "T does not have a virtual dtor");
85 if (other.value_ != nullptr) other.value_->IncrementRefCount();
86 value_ = static_cast<T*>(other.value_);
87 }
88
89 // Copy assignment.
90 // NOLINTNEXTLINE(bugprone-unhandled-self-assignment)
91 RefCountedPtr& operator=(const RefCountedPtr& other) {
92 // Note: Order of reffing and unreffing is important here in case value_
93 // and other.value_ are the same object.
94 if (other.value_ != nullptr) other.value_->IncrementRefCount();
95 reset(other.value_);
96 return *this;
97 }
98 template <typename Y,
99 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
100 RefCountedPtr& operator=(const RefCountedPtr<Y>& other) {
101 static_assert(std::has_virtual_destructor<T>::value,
102 "T does not have a virtual dtor");
103 // Note: Order of reffing and unreffing is important here in case value_
104 // and other.value_ are the same object.
105 if (other.value_ != nullptr) other.value_->IncrementRefCount();
106 reset(other.value_);
107 return *this;
108 }
109
~RefCountedPtr()110 ~RefCountedPtr() {
111 if (value_ != nullptr) value_->Unref();
112 }
113
114 // An explicit copy method that supports ref-count tracing.
Ref(const DebugLocation & location,const char * reason)115 RefCountedPtr<T> Ref(const DebugLocation& location, const char* reason) {
116 if (value_ != nullptr) value_->IncrementRefCount(location, reason);
117 return RefCountedPtr<T>(value_);
118 }
119
swap(RefCountedPtr & other)120 void swap(RefCountedPtr& other) { std::swap(value_, other.value_); }
121
122 // If value is non-null, we take ownership of a ref to it.
123 void reset(T* value = nullptr) {
124 T* old_value = std::exchange(value_, value);
125 if (old_value != nullptr) old_value->Unref();
126 }
127 void reset(const DebugLocation& location, const char* reason,
128 T* value = nullptr) {
129 T* old_value = std::exchange(value_, value);
130 if (old_value != nullptr) old_value->Unref(location, reason);
131 }
132 template <typename Y,
133 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
134 void reset(Y* value = nullptr) {
135 static_assert(std::has_virtual_destructor<T>::value,
136 "T does not have a virtual dtor");
137 reset(static_cast<T*>(value));
138 }
139 template <typename Y,
140 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
141 void reset(const DebugLocation& location, const char* reason,
142 Y* value = nullptr) {
143 static_assert(std::has_virtual_destructor<T>::value,
144 "T does not have a virtual dtor");
145 reset(location, reason, static_cast<T*>(value));
146 }
147
148 // This method is mostly useful for interoperating with C code.
149 // Eventually use within core should be banned, except at the surface API
150 // boundaries.
release()151 T* release() { return std::exchange(value_, nullptr); }
152
get()153 T* get() const { return value_; }
154
155 T& operator*() const { return *value_; }
156 T* operator->() const { return value_; }
157
158 template <typename Y,
159 std::enable_if_t<std::is_base_of<T, Y>::value, bool> = true>
TakeAsSubclass()160 RefCountedPtr<Y> TakeAsSubclass() {
161 return RefCountedPtr<Y>(DownCast<Y*>(release()));
162 }
163
164 template <typename Y,
165 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
166 bool operator==(const RefCountedPtr<Y>& other) const {
167 return value_ == other.value_;
168 }
169
170 template <typename Y,
171 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
172 bool operator==(const Y* other) const {
173 return value_ == other;
174 }
175
176 bool operator==(std::nullptr_t) const { return value_ == nullptr; }
177
178 template <typename Y,
179 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
180 bool operator!=(const RefCountedPtr<Y>& other) const {
181 return value_ != other.value_;
182 }
183
184 template <typename Y,
185 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
186 bool operator!=(const Y* other) const {
187 return value_ != other;
188 }
189
190 bool operator!=(std::nullptr_t) const { return value_ != nullptr; }
191
192 private:
193 template <typename Y>
194 friend class RefCountedPtr;
195
196 T* value_ = nullptr;
197 };
198
199 // A smart pointer class for objects that provide IncrementWeakRefCount() and
200 // WeakUnref() methods, such as those provided by the DualRefCounted base class.
201 template <typename T>
202 class WeakRefCountedPtr {
203 public:
WeakRefCountedPtr()204 WeakRefCountedPtr() {}
205 // NOLINTNEXTLINE(google-explicit-constructor)
WeakRefCountedPtr(std::nullptr_t)206 WeakRefCountedPtr(std::nullptr_t) {}
207
208 // If value is non-null, we take ownership of a ref to it.
209 template <typename Y,
210 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
WeakRefCountedPtr(Y * value)211 explicit WeakRefCountedPtr(Y* value) {
212 value_ = value;
213 }
214
215 // Move ctors.
WeakRefCountedPtr(WeakRefCountedPtr && other)216 WeakRefCountedPtr(WeakRefCountedPtr&& other) noexcept {
217 value_ = other.value_;
218 other.value_ = nullptr;
219 }
220 template <typename Y,
221 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
222 // NOLINTNEXTLINE(google-explicit-constructor)
WeakRefCountedPtr(WeakRefCountedPtr<Y> && other)223 WeakRefCountedPtr(WeakRefCountedPtr<Y>&& other) noexcept {
224 value_ = static_cast<T*>(other.value_);
225 other.value_ = nullptr;
226 }
227
228 // Move assignment.
229 WeakRefCountedPtr& operator=(WeakRefCountedPtr&& other) noexcept {
230 reset(std::exchange(other.value_, nullptr));
231 return *this;
232 }
233 template <typename Y,
234 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
235 WeakRefCountedPtr& operator=(WeakRefCountedPtr<Y>&& other) noexcept {
236 reset(std::exchange(other.value_, nullptr));
237 return *this;
238 }
239
240 // Copy ctors.
WeakRefCountedPtr(const WeakRefCountedPtr & other)241 WeakRefCountedPtr(const WeakRefCountedPtr& other) {
242 if (other.value_ != nullptr) other.value_->IncrementWeakRefCount();
243 value_ = other.value_;
244 }
245 template <typename Y,
246 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
247 // NOLINTNEXTLINE(google-explicit-constructor)
WeakRefCountedPtr(const WeakRefCountedPtr<Y> & other)248 WeakRefCountedPtr(const WeakRefCountedPtr<Y>& other) {
249 static_assert(std::has_virtual_destructor<T>::value,
250 "T does not have a virtual dtor");
251 if (other.value_ != nullptr) other.value_->IncrementWeakRefCount();
252 value_ = static_cast<T*>(other.value_);
253 }
254
255 // Copy assignment.
256 // NOLINTNEXTLINE(bugprone-unhandled-self-assignment)
257 WeakRefCountedPtr& operator=(const WeakRefCountedPtr& other) {
258 // Note: Order of reffing and unreffing is important here in case value_
259 // and other.value_ are the same object.
260 if (other.value_ != nullptr) other.value_->IncrementWeakRefCount();
261 reset(other.value_);
262 return *this;
263 }
264 template <typename Y,
265 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
266 WeakRefCountedPtr& operator=(const WeakRefCountedPtr<Y>& other) {
267 static_assert(std::has_virtual_destructor<T>::value,
268 "T does not have a virtual dtor");
269 // Note: Order of reffing and unreffing is important here in case value_
270 // and other.value_ are the same object.
271 if (other.value_ != nullptr) other.value_->IncrementWeakRefCount();
272 reset(other.value_);
273 return *this;
274 }
275
~WeakRefCountedPtr()276 ~WeakRefCountedPtr() {
277 if (value_ != nullptr) value_->WeakUnref();
278 }
279
280 // An explicit copy method that supports ref-count tracing.
WeakRef(const DebugLocation & location,const char * reason)281 WeakRefCountedPtr<T> WeakRef(const DebugLocation& location,
282 const char* reason) {
283 if (value_ != nullptr) value_->IncrementWeakRefCount(location, reason);
284 return WeakRefCountedPtr<T>(value_);
285 }
286
swap(WeakRefCountedPtr & other)287 void swap(WeakRefCountedPtr& other) { std::swap(value_, other.value_); }
288
289 // If value is non-null, we take ownership of a ref to it.
290 void reset(T* value = nullptr) {
291 T* old_value = std::exchange(value_, value);
292 if (old_value != nullptr) old_value->WeakUnref();
293 }
294 void reset(const DebugLocation& location, const char* reason,
295 T* value = nullptr) {
296 T* old_value = std::exchange(value_, value);
297 if (old_value != nullptr) old_value->WeakUnref(location, reason);
298 }
299 template <typename Y,
300 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
301 void reset(Y* value = nullptr) {
302 static_assert(std::has_virtual_destructor<T>::value,
303 "T does not have a virtual dtor");
304 reset(static_cast<T*>(value));
305 }
306 template <typename Y,
307 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
308 void reset(const DebugLocation& location, const char* reason,
309 Y* value = nullptr) {
310 static_assert(std::has_virtual_destructor<T>::value,
311 "T does not have a virtual dtor");
312 reset(location, reason, static_cast<T*>(value));
313 }
314
315 // TODO(roth): This method exists solely as a transition mechanism to allow
316 // us to pass a ref to idiomatic C code that does not use WeakRefCountedPtr<>.
317 // Once all of our code has been converted to idiomatic C++, this
318 // method should go away.
release()319 T* release() { return std::exchange(value_, nullptr); }
320
get()321 T* get() const { return value_; }
322
323 T& operator*() const { return *value_; }
324 T* operator->() const { return value_; }
325
326 template <typename Y,
327 std::enable_if_t<std::is_base_of<T, Y>::value, bool> = true>
TakeAsSubclass()328 WeakRefCountedPtr<Y> TakeAsSubclass() {
329 return WeakRefCountedPtr<Y>(static_cast<Y*>(release()));
330 }
331
332 template <typename Y,
333 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
334 bool operator==(const WeakRefCountedPtr<Y>& other) const {
335 return value_ == other.value_;
336 }
337
338 template <typename Y,
339 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
340 bool operator==(const Y* other) const {
341 return value_ == other;
342 }
343
344 bool operator==(std::nullptr_t) const { return value_ == nullptr; }
345
346 template <typename Y,
347 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
348 bool operator!=(const WeakRefCountedPtr<Y>& other) const {
349 return value_ != other.value_;
350 }
351
352 template <typename Y,
353 std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
354 bool operator!=(const Y* other) const {
355 return value_ != other;
356 }
357
358 bool operator!=(std::nullptr_t) const { return value_ != nullptr; }
359
360 private:
361 template <typename Y>
362 friend class WeakRefCountedPtr;
363
364 T* value_ = nullptr;
365 };
366
367 template <typename T, typename... Args>
MakeRefCounted(Args &&...args)368 inline RefCountedPtr<T> MakeRefCounted(Args&&... args) {
369 return RefCountedPtr<T>(new T(std::forward<Args>(args)...));
370 }
371
372 template <typename T>
373 bool operator<(const RefCountedPtr<T>& p1, const RefCountedPtr<T>& p2) {
374 return p1.get() < p2.get();
375 }
376
377 template <typename T>
378 bool operator<(const WeakRefCountedPtr<T>& p1, const WeakRefCountedPtr<T>& p2) {
379 return p1.get() < p2.get();
380 }
381
382 //
383 // absl::Hash integration
384 //
385
386 template <typename H, typename T>
AbslHashValue(H h,const RefCountedPtr<T> & p)387 H AbslHashValue(H h, const RefCountedPtr<T>& p) {
388 return H::combine(std::move(h), p.get());
389 }
390
391 template <typename H, typename T>
AbslHashValue(H h,const WeakRefCountedPtr<T> & p)392 H AbslHashValue(H h, const WeakRefCountedPtr<T>& p) {
393 return H::combine(std::move(h), p.get());
394 }
395
396 // Heterogenous lookup support.
397 template <typename T>
398 struct RefCountedPtrHash {
399 using is_transparent = void;
operatorRefCountedPtrHash400 size_t operator()(const RefCountedPtr<T>& p) const {
401 return absl::Hash<RefCountedPtr<T>>{}(p);
402 }
operatorRefCountedPtrHash403 size_t operator()(const WeakRefCountedPtr<T>& p) const {
404 return absl::Hash<WeakRefCountedPtr<T>>{}(p);
405 }
operatorRefCountedPtrHash406 size_t operator()(T* p) const { return absl::Hash<T*>{}(p); }
407 };
408 template <typename T>
409 struct RefCountedPtrEq {
410 using is_transparent = void;
operatorRefCountedPtrEq411 bool operator()(const RefCountedPtr<T>& p1,
412 const RefCountedPtr<T>& p2) const {
413 return p1 == p2;
414 }
operatorRefCountedPtrEq415 bool operator()(const WeakRefCountedPtr<T>& p1,
416 const WeakRefCountedPtr<T>& p2) const {
417 return p1 == p2;
418 }
operatorRefCountedPtrEq419 bool operator()(const RefCountedPtr<T>& p1,
420 const WeakRefCountedPtr<T>& p2) const {
421 return p1 == p2.get();
422 }
operatorRefCountedPtrEq423 bool operator()(const WeakRefCountedPtr<T>& p1,
424 const RefCountedPtr<T>& p2) const {
425 return p1 == p2.get();
426 }
operatorRefCountedPtrEq427 bool operator()(const RefCountedPtr<T>& p1, const T* p2) const {
428 return p1 == p2;
429 }
operatorRefCountedPtrEq430 bool operator()(const WeakRefCountedPtr<T>& p1, const T* p2) const {
431 return p1 == p2;
432 }
operatorRefCountedPtrEq433 bool operator()(const T* p1, const RefCountedPtr<T>& p2) const {
434 return p2 == p1;
435 }
operatorRefCountedPtrEq436 bool operator()(const T* p1, const WeakRefCountedPtr<T>& p2) const {
437 return p2 == p1;
438 }
439 };
440
441 } // namespace grpc_core
442
443 #endif // GRPC_SRC_CORE_UTIL_REF_COUNTED_PTR_H
444