1 /** @file
2
3 This is an intrusive shared pointer class designed for internal use in various containers inside
4 Traffic Server. It is not dependent on TS and could be used elsewhere. The design tries to
5 follow that of @c std::shared_ptr as it performs a similar function. The key difference is
6 this shared pointer requires the reference count to be in the target class. This is done by
7 inheriting a class containing the counter. This has the benefits
8
9 - improved locality for instances of the class and the reference count
10 - the ability to reliably construct shared pointers from raw pointers.
11 - lower overhead (a single reference counter).
12
13 The requirement of modifying the target class limits the generality of this class but it is
14 still quite useful in specific cases (particularly containers and their internal node classes).
15
16 @section license License
17
18 Licensed to the Apache Software Foundation (ASF) under one or more contributor license
19 agreements. See the NOTICE file distributed with this work for additional information regarding
20 copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0
21 (the "License"); you may not use this file except in compliance with the License. You may
22 obtain a copy of the License at
23
24 http://www.apache.org/licenses/LICENSE-2.0
25
26 Unless required by applicable law or agreed to in writing, software distributed under the
27 License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
28 express or implied. See the License for the specific language governing permissions and
29 limitations under the License.
30 */
31
32 #pragma once
33
34 #include <sys/types.h>
35 #include <type_traits>
36 #include <cassert>
37 #include <functional>
38 #include <atomic>
39
40 namespace ts
41 {
42 template <typename T> class IntrusivePtr; // forward declare
43
44 /* ----------------------------------------------------------------------- */
45 /** Reference counter mixin.
46
47 To add support for @c IntrusivePtr to class @a T, it must publicly inherit
48 @c IntrusivePtrCounter. Doing so
49
50 - provides a reference count member
51 - forces the reference count to initialize to zero
52 - prevents reference count copying on copy construction or assignment
53 - provides access to the reference counter from the shared pointer
54 - provides the `use_count` method to check the reference count.
55
56 @note You can use this with insulated (by name only) classes. The important thing is to make
57 sure that any such class that uses @c IntrusivePtr has all of its constructors and destructors
58 declared in the header and defined in the implementation translation unit. If the compiler
59 generates any of those, it will not compile due to missing functions or methods
60
61 */
62 class IntrusivePtrCounter
63 {
64 template <typename T> friend class IntrusivePtr;
65
66 using self_type = IntrusivePtrCounter;
67
68 public:
69 /** Copy constructor.
70
71 @internal We have to define this to explicitly _not_ copy the reference count. Otherwise any
72 client that uses a default copy constructor will _copy the ref count into the new object_.
73 That way lies madness.
74 */
75 IntrusivePtrCounter(self_type const &that);
76 /// No move constructor - that won't work for an object that is the target of a shared pointer.
77 IntrusivePtrCounter(self_type &&that) = delete;
78
79 /** Assignment operator.
80
81 @internal We need this for the same reason as the copy constructor. The reference count must
82 not be copied.
83 */
84 self_type &operator=(self_type const &that);
85 /// No move assignment - that won't work for an object that is the target of a shared pointer.
86 self_type &operator=(self_type &&that) = delete;
87
88 protected:
89 /// The reference counter type is signed because detecting underflow is more important than having
90 /// more than 2G references.
91 using intrusive_ptr_reference_counter_type = long int;
92 /// The reference counter.
93 intrusive_ptr_reference_counter_type m_intrusive_pointer_reference_count{0};
94 /// Default constructor (0 init counter).
95 /// @internal Only subclasses can access this.
96 IntrusivePtrCounter();
97 };
98
99 /** Atomic reference counting mixin.
100 */
101 class IntrusivePtrAtomicCounter
102 {
103 template <typename T> friend class IntrusivePtr;
104
105 using self_type = IntrusivePtrAtomicCounter; ///< Self reference type.
106
107 public:
108 /// Copy constructor - do not copy reference count.
109 IntrusivePtrAtomicCounter(self_type const &that);
110 /// No move constructor - that won't work for an object that is the target of a shared pointer.
111 IntrusivePtrAtomicCounter(self_type &&that) = delete;
112
113 /// Self assignment - do not copy reference count.
114 self_type &operator=(self_type const &that);
115 /// No move assignment - that won't work for an object that is the target of a shared pointer.
116 self_type &operator=(self_type &&that) = delete;
117
118 protected:
119 /// The reference counter type is signed because detecting underflow is more important than having
120 /// more than 2G references.
121 using intrusive_ptr_reference_counter_type = std::atomic<long int>;
122 /// The reference counter.
123 intrusive_ptr_reference_counter_type m_intrusive_pointer_reference_count{0};
124 /// Default constructor (0 init counter).
125 /// @internal Only subclasses can access this.
126 IntrusivePtrAtomicCounter();
127 };
128
129 /* ----------------------------------------------------------------------- */
130 /** Intrusive shared pointer.
131
132 This is a reference counted smart pointer. A single object is jointly ownded by a set of
133 pointers. When the last of the pointers is destructed the target object is also destructed.
134
135 To use @c IntrusivePtr on type @a T, @a T must inherit from a reference counter class,
136 such as @c IntrusivePtrCounter or @c IntrusivePtrAtomicCounter. If this inheritance is @c public
137 then no other support is required. If the inheritance is not @c public then @a T must declare
138 @c IntrusivePtr<T> as a @c friend class.
139
140 If it is not possible to have @a T inherit from a reference counter class, use @c std::shared_ptr.
141
142 The smart pointer actions can be changed through class specific policy by specializing the @c
143 IntrusivePtrPolicy template class.
144 */
145 template <typename T> class IntrusivePtr
146 {
147 private: /* don't pollute client with these typedefs */
148 using self_type = IntrusivePtr; ///< Self reference type.
149 template <typename U> friend class IntrusivePtr; // Make friends with siblings for cross type casts.
150 public:
151 /// The externally used type for the reference count.
152 using counter_type = long int;
153
154 /// Default constructor (@c nullptr initialized).
155 IntrusivePtr();
156 /// Construct from instance.
157 /// The instance becomes referenced and owned by the pointer.
158 explicit IntrusivePtr(T *obj);
159 /// Destructor. If last referencing pointer, the contained instance is destroyed.
160 ~IntrusivePtr();
161
162 /// Copy constructor. A new reference to the object is created.
163 IntrusivePtr(self_type const &that);
164 /// Move constructor. The reference in @a that is moved to @a this.
165 IntrusivePtr(self_type &&that);
166 /// Self assignment. A new reference to the object is created.
167 self_type &operator=(self_type const &that);
168 /// Move assignment. The reference in @a that is moved to @a this.
169 self_type &operator=(self_type &&that);
170
171 /** Assign from instance.
172
173 The instance becomes referenced and owned by the pointer. The reference to the current object
174 is dropped.
175
176 @internal Direct assignment of a raw pointer was supported but I think that is too dangerous.
177 */
178 void reset(T *obj = nullptr);
179
180 /** Clear reference without cleanup.
181
182 This unsets this smart pointer and decrements the reference count, but does @b not perform any
183 finalization on the target object. This can easily lead to memory leaks and in some sense
184 vitiates the point of this class, but it is occasionally the right thing to do. Use with
185 caution.
186
187 @internal Unfortunately this is current used and can't be removed at this time. Hopefully some
188 refactoring elsewhere will make it obsolete.
189
190 @return @c true if there are no references upon return,
191 @c false if the reference count is not zero.
192 */
193 T *release();
194
195 /// Test for not @c nullptr
196 explicit operator bool() const;
197
198 /// Member dereference.
199 T *operator->() const;
200 /// Dereference.
201 T &operator*() const;
202 /// Access raw pointer.
203 T *get() const;
204
205 /** User conversion to raw pointer.
206
207 @internal allow implicit conversion to the underlying pointer which is one of the advantages
208 of intrusive pointers because the reference count doesn't get lost.
209
210 */
211 operator T *() const;
212
213 /** Cross type construction.
214 This succeeds if an @a U* can be implicitly converted to a @a T*.
215 */
216 template <typename U> IntrusivePtr(IntrusivePtr<U> const &that);
217
218 template <typename U> IntrusivePtr(IntrusivePtr<U> &&that);
219
220 /** Cross type assignment.
221 This succeeds if an @a U* can be implicitily converted to a @a T*.
222 */
223 template <typename U> self_type &operator=(IntrusivePtr<U> const &that);
224
225 template <typename U> self_type &operator=(IntrusivePtr<U> &&that);
226
227 /// Reference count.
228 /// @return Number of references.
229 counter_type use_count() const;
230
231 private:
232 T *m_obj{nullptr}; ///< Pointer to object.
233
234 /// Reference @a obj.
235 void set(T *obj);
236 /// Drop the current reference.
237 void unset();
238 };
239
240 /** Pointer dynamic cast.
241
242 This allows a smart pointer to be cast from one type to another. It must be used when the types
243 do not implicitly convert (generally a downcast).
244
245 @tparam T Type of the resulting intrusive pointer. Must be explicit.
246 @tparam U Type of the intrusive pointer @a src - can be deduced.
247 @param src The original intrusive pointer.
248 @return An intrusive pointer to @a T pointing at the same object as @a src.
249
250 @code
251 class A { ... };
252 class B : public A { ... };
253 IntrusivePtr<A> really_b(new B);
254 InstruivePtr<B> the_b;
255 the_b = dynamic_ptr_cast<B>(really_b);
256 @endcode
257 */
258 template <typename T, typename U>
259 IntrusivePtr<T>
dynamic_ptr_cast(IntrusivePtr<U> const & src)260 dynamic_ptr_cast(IntrusivePtr<U> const &src)
261 {
262 return IntrusivePtr<T>(dynamic_cast<T *>(src.get()));
263 }
264
265 /** Pointer cast.
266
267 This allows a smart pointer to be cast from one type to another.
268 It must be used when the types do not implicitly convert (generally
269 a downcast). This uses @c static_cast and so performs only compile
270 time checks.
271
272 @tparam T Type of the resulting intrusive pointer. Must be explicit.
273 @tparam U Type of the intrusive pointer @a src - can be deduced.
274 @param src The original intrusive pointer.
275 @return An intrusive pointer to @a T pointing at the same object as @a src.
276
277 @code
278 class A { ... };
279 class B : public A { ... };
280 IntrusivePtr<A> really_b(new B);
281 IntrusivePtr<B> the_b;
282 the_b = ptr_cast<B>(really_b);
283 @endcode
284 */
285 template <typename T, typename U>
286 IntrusivePtr<T>
ptr_cast(IntrusivePtr<U> const & src)287 ptr_cast(IntrusivePtr<U> const &src)
288 {
289 return IntrusivePtr<T>(static_cast<T *>(src.get()));
290 }
291 /* ----------------------------------------------------------------------- */
292 /* ----------------------------------------------------------------------- */
293 /** Default policy class for intrusive pointers.
294
295 This allows per type policy, although not per target instance. Clients can override policy by
296 specializing @c IntrusivePtrPolicy and inheriting from this class to provide default actions.
297
298 @code
299 template <> IntrusivePtrPolicy<SomeType>
300 : IntrusivePtrDefaultPolicy<SomeType> {
301 ... Redefinition of methods and nested types ...
302 };
303 @endcode
304
305 The inherited class will provide the default definitions so you can
306 override only what is different. Although this can be omitted if you
307 override everything, it is more robust for maintenance to inherit
308 anyway.
309 */
310
311 template <typename T> class IntrusivePtrDefaultPolicy
312 {
313 public:
314 /// Called when the pointer is dereferenced.
315 /// Default is empty (no action).
316 static void dereferenceCheck(T *);
317
318 /** Perform clean up on a target object that is no longer referenced.
319
320 @param t Instance to finalize.
321
322 Default is calling @c delete. Any specialization that overrides this @b must clean up the
323 object. The primary use of this is to perform a clean up other than @c delete.
324
325 @note When this is called, the target object reference count is zero. If it is necessary to
326 pass a smart pointer to the target object, it will be necessary to call @c
327 IntrusivePtr::release to drop the reference without another finalization. Further care must be
328 taken that none of the called logic keeps a copy of the smart pointer. Use with caution.
329 */
330 static void finalize(T *t);
331
332 /// Strict weak order for STL containers.
333 class Order : public std::binary_function<IntrusivePtr<T>, IntrusivePtr<T>, bool>
334 {
335 public:
336 /// Default constructor.
Order()337 Order() {}
338 /// Compare by raw pointer.
339 bool operator()(IntrusivePtr<T> const &lhs, IntrusivePtr<T> const &rhs) const;
340 };
341 };
342
343 // If no specialization is provided then use the default;
344 template <typename T> struct IntrusivePtrPolicy : public IntrusivePtrDefaultPolicy<T> {
345 };
346 /* ----------------------------------------------------------------------- */
347 /* ----------------------------------------------------------------------- */
348 /* Inline Methods */
IntrusivePtrCounter()349 inline IntrusivePtrCounter::IntrusivePtrCounter() {}
350
IntrusivePtrCounter(self_type const &)351 inline IntrusivePtrCounter::IntrusivePtrCounter(self_type const &) {}
352
353 inline IntrusivePtrCounter &
354 IntrusivePtrCounter::operator=(self_type const &)
355 {
356 return *this;
357 }
358
IntrusivePtrAtomicCounter()359 inline IntrusivePtrAtomicCounter::IntrusivePtrAtomicCounter() {}
360
IntrusivePtrAtomicCounter(self_type const &)361 inline IntrusivePtrAtomicCounter::IntrusivePtrAtomicCounter(self_type const &) {}
362
363 inline IntrusivePtrAtomicCounter &
364 IntrusivePtrAtomicCounter::operator=(self_type const &)
365 {
366 return *this;
367 }
368
369 /* ----------------------------------------------------------------------- */
370 /* ----------------------------------------------------------------------- */
371
372 template <typename T>
373 void
dereferenceCheck(T *)374 IntrusivePtrDefaultPolicy<T>::dereferenceCheck(T *)
375 {
376 }
377
378 template <typename T>
379 void
finalize(T * obj)380 IntrusivePtrDefaultPolicy<T>::finalize(T *obj)
381 {
382 delete obj;
383 }
384
385 template <typename T>
386 bool
operator()387 IntrusivePtrDefaultPolicy<T>::Order::operator()(IntrusivePtr<T> const &lhs, IntrusivePtr<T> const &rhs) const
388 {
389 return lhs.get() < rhs.get();
390 }
391 /* ----------------------------------------------------------------------- */
392 /* ----------------------------------------------------------------------- */
IntrusivePtr()393 template <typename T> IntrusivePtr<T>::IntrusivePtr() {}
394
IntrusivePtr(T * obj)395 template <typename T> IntrusivePtr<T>::IntrusivePtr(T *obj)
396 {
397 this->set(obj);
398 }
399
~IntrusivePtr()400 template <typename T> IntrusivePtr<T>::~IntrusivePtr()
401 {
402 this->unset();
403 }
404
IntrusivePtr(self_type const & that)405 template <typename T> IntrusivePtr<T>::IntrusivePtr(self_type const &that)
406 {
407 this->set(that.m_obj);
408 }
409
IntrusivePtr(IntrusivePtr<U> const & that)410 template <typename T> template <typename U> IntrusivePtr<T>::IntrusivePtr(IntrusivePtr<U> const &that)
411 {
412 static_assert(std::is_convertible<U *, T *>::value, "The argument type is not implicitly convertible to the return type.");
413 this->set(that.m_obj);
414 }
415
IntrusivePtr(self_type && that)416 template <typename T> IntrusivePtr<T>::IntrusivePtr(self_type &&that)
417 {
418 std::swap<T *>(m_obj, that.m_obj);
419 }
420
IntrusivePtr(IntrusivePtr<U> && that)421 template <typename T> template <typename U> IntrusivePtr<T>::IntrusivePtr(IntrusivePtr<U> &&that)
422 {
423 static_assert(std::is_convertible<U *, T *>::value, "The argument type is not implicitly convertible to the return type.");
424 std::swap<T *>(m_obj, that.get());
425 }
426
427 template <typename T>
428 IntrusivePtr<T> &
429 IntrusivePtr<T>::operator=(self_type const &that)
430 {
431 this->reset(that.get());
432 return *this;
433 }
434
435 template <typename T>
436 template <typename U>
437 IntrusivePtr<T> &
438 IntrusivePtr<T>::operator=(IntrusivePtr<U> const &that)
439 {
440 static_assert(std::is_convertible<U *, T *>::value, "The argument type is not implicitly convertible to the return type.");
441 this->reset(that.get());
442 return *this;
443 }
444
445 template <typename T>
446 IntrusivePtr<T> &
447 IntrusivePtr<T>::operator=(self_type &&that)
448 {
449 if (m_obj != that.m_obj) {
450 this->unset();
451 m_obj = that.m_obj;
452 that.m_obj = nullptr;
453 } else {
454 that.unset();
455 }
456 return *this;
457 }
458
459 template <typename T>
460 template <typename U>
461 IntrusivePtr<T> &
462 IntrusivePtr<T>::operator=(IntrusivePtr<U> &&that)
463 {
464 static_assert(std::is_convertible<U *, T *>::value, "The argument type is not implicitly convertible to the return type.");
465 if (m_obj != that.m_obj) {
466 this->unset();
467 m_obj = that.m_obj;
468 that.m_obj = nullptr;
469 } else {
470 that.unset();
471 }
472 return *this;
473 }
474
475 template <typename T>
476 T *
477 IntrusivePtr<T>::operator->() const
478 {
479 IntrusivePtrPolicy<T>::dereferenceCheck(m_obj);
480 return m_obj;
481 }
482
483 template <typename T>
484 T &
485 IntrusivePtr<T>::operator*() const
486 {
487 IntrusivePtrPolicy<T>::dereferenceCheck(m_obj);
488 return *m_obj;
489 }
490
491 template <typename T>
492 T *
get()493 IntrusivePtr<T>::get() const
494 {
495 IntrusivePtrPolicy<T>::dereferenceCheck(m_obj);
496 return m_obj;
497 }
498
499 /* The Set/Unset methods are the basic implementation of our
500 * reference counting. The Reset method is the standard way
501 * of invoking the pair, although splitting them allows some
502 * additional efficiency in certain situations.
503 */
504
505 /* @c set and @c unset are two half operations that don't do checks.
506 It is the caller's responsibility to do that.
507 */
508
509 template <typename T>
510 void
unset()511 IntrusivePtr<T>::unset()
512 {
513 if (nullptr != m_obj) {
514 auto &cp = m_obj->m_intrusive_pointer_reference_count;
515
516 if (0 == --cp) {
517 IntrusivePtrPolicy<T>::finalize(m_obj);
518 }
519 m_obj = nullptr;
520 }
521 }
522
523 template <typename T>
524 void
set(T * obj)525 IntrusivePtr<T>::set(T *obj)
526 {
527 m_obj = obj; /* update to new object */
528 if (nullptr != m_obj) /* if a real object, bump the ref count */
529 ++(m_obj->m_intrusive_pointer_reference_count);
530 }
531
532 template <typename T>
533 void
reset(T * obj)534 IntrusivePtr<T>::reset(T *obj)
535 {
536 if (obj != m_obj) {
537 this->unset();
538 this->set(obj);
539 }
540 }
541
542 template <typename T>
543 T *
release()544 IntrusivePtr<T>::release()
545 {
546 T *zret = m_obj;
547 if (m_obj) {
548 auto &cp = m_obj->m_intrusive_pointer_reference_count;
549 // If the client is using this method, they're doing something funky
550 // so be extra careful with the reference count.
551 if (cp > 0)
552 --cp;
553 m_obj = nullptr;
554 }
555 return zret;
556 }
557
558 template <typename T> IntrusivePtr<T>::operator bool() const
559 {
560 return m_obj != nullptr;
561 }
562
563 /* Pointer comparison */
564 template <typename T>
565 bool
566 operator==(IntrusivePtr<T> const &lhs, IntrusivePtr<T> const &rhs)
567 {
568 return lhs.get() == rhs.get();
569 }
570
571 template <typename T>
572 bool
573 operator!=(IntrusivePtr<T> const &lhs, IntrusivePtr<T> const &rhs)
574 {
575 return lhs.get() != rhs.get();
576 }
577
578 template <typename T>
579 bool
580 operator<(IntrusivePtr<T> const &lhs, IntrusivePtr<T> const &rhs)
581 {
582 return lhs.get() < rhs.get();
583 }
584
585 template <typename T> IntrusivePtr<T>::operator T *() const
586 {
587 return m_obj;
588 }
589
590 template <typename T>
591 typename IntrusivePtr<T>::counter_type
use_count()592 IntrusivePtr<T>::use_count() const
593 {
594 return m_obj ? counter_type{m_obj->m_intrusive_pointer_reference_count} : counter_type{0};
595 }
596 /* ----------------------------------------------------------------------- */
597 /* ----------------------------------------------------------------------- */
598 } // namespace ts
599