2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
19 * Copyright (C) 2014 Cloudius Systems, Ltd.
24 #include <seastar/core/shared_ptr_debug_helper.hh>
26 #include <type_traits>
29 #include <seastar/util/is_smart_ptr.hh>
30 #include <seastar/util/indirect.hh>
32 #include <boost/intrusive/parent_from_member.hpp>
34 #if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ >= 12)
35 // to silence the false alarm from GCC 12, see
36 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105204
37 #define SEASTAR_IGNORE_USE_AFTER_FREE
42 // This header defines two shared pointer facilities, lw_shared_ptr<> and
43 // shared_ptr<>, both modeled after std::shared_ptr<>.
45 // Unlike std::shared_ptr<>, neither of these implementations are thread
46 // safe, and two pointers sharing the same object must not be used in
49 // lw_shared_ptr<> is the more lightweight variant, with a lw_shared_ptr<>
50 // occupying just one machine word, and adding just one word to the shared
51 // object. However, it does not support polymorphism.
53 // shared_ptr<> is more expensive, with a pointer occupying two machine
54 // words, and with two words of overhead in the shared object. In return,
55 // it does support polymorphism.
57 // Both variants support shared_from_this() via enable_shared_from_this<>
58 // and lw_enable_shared_from_this<>().
61 #ifndef SEASTAR_DEBUG_SHARED_PTR
62 using shared_ptr_counter_type = long;
64 using shared_ptr_counter_type = debug_shared_ptr_counter_type;
74 class enable_lw_shared_from_this;
77 class enable_shared_from_this;
79 template <typename T, typename... A>
80 lw_shared_ptr<T> make_lw_shared(A&&... a);
83 lw_shared_ptr<T> make_lw_shared(T&& a);
86 lw_shared_ptr<T> make_lw_shared(T& a);
88 template <typename T, typename... A>
89 shared_ptr<T> make_shared(A&&... a);
92 shared_ptr<T> make_shared(T&& a);
94 template <typename T, typename U>
95 shared_ptr<T> static_pointer_cast(const shared_ptr<U>& p);
97 template <typename T, typename U>
98 shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& p);
100 template <typename T, typename U>
101 shared_ptr<T> const_pointer_cast(const shared_ptr<U>& p);
103 struct lw_shared_ptr_counter_base {
104 shared_ptr_counter_type _count = 0;
110 template <class T, class U>
111 struct lw_shared_ptr_accessors;
114 struct lw_shared_ptr_accessors_esft;
117 struct lw_shared_ptr_accessors_no_esft;
122 // We want to support two use cases for shared_ptr<T>:
124 // 1. T is any type (primitive or class type)
126 // 2. T is a class type that inherits from enable_shared_from_this<T>.
128 // In the first case, we must wrap T in an object containing the counter,
129 // since T may be a primitive type and cannot be a base class.
131 // In the second case, we want T to reach the counter through its
132 // enable_shared_from_this<> base class, so that we can implement
133 // shared_from_this().
135 // To implement those two conflicting requirements (T alongside its counter;
136 // T inherits from an object containing the counter) we use std::conditional<>
137 // and some accessor functions to select between two implementations.
140 // CRTP from this to enable shared_from_this:
141 template <typename T>
142 class enable_lw_shared_from_this : private lw_shared_ptr_counter_base {
145 enable_lw_shared_from_this() noexcept {}
146 enable_lw_shared_from_this(enable_lw_shared_from_this&&) noexcept {}
147 enable_lw_shared_from_this(const enable_lw_shared_from_this&) noexcept {}
148 enable_lw_shared_from_this& operator=(const enable_lw_shared_from_this&) noexcept { return *this; }
149 enable_lw_shared_from_this& operator=(enable_lw_shared_from_this&&) noexcept { return *this; }
151 lw_shared_ptr<T> shared_from_this() noexcept;
152 lw_shared_ptr<const T> shared_from_this() const noexcept;
153 long use_count() const noexcept { return _count; }
155 template <typename X>
156 friend class lw_shared_ptr;
157 template <typename X>
158 friend struct internal::lw_shared_ptr_accessors_esft;
159 template <typename X, class Y>
160 friend struct internal::lw_shared_ptr_accessors;
163 template <typename T>
164 struct lw_shared_ptr_no_esft : private lw_shared_ptr_counter_base {
167 lw_shared_ptr_no_esft() = default;
168 lw_shared_ptr_no_esft(const T& x) : _value(x) {}
169 lw_shared_ptr_no_esft(T&& x) : _value(std::move(x)) {}
170 template <typename... A>
171 lw_shared_ptr_no_esft(A&&... a) : _value(std::forward<A>(a)...) {}
173 template <typename X>
174 friend class lw_shared_ptr;
175 template <typename X>
176 friend struct internal::lw_shared_ptr_accessors_no_esft;
177 template <typename X, class Y>
178 friend struct internal::lw_shared_ptr_accessors;
182 /// Extension point: the user may override this to change how \ref lw_shared_ptr objects are destroyed,
183 /// primarily so that incomplete classes can be used.
185 /// Customizing the deleter requires that \c T be derived from \c enable_lw_shared_from_this<T>.
186 /// The specialization must be visible for all uses of \c lw_shared_ptr<T>.
188 /// To customize, the template must have a `static void dispose(T*)` operator that disposes of
190 template <typename T>
191 struct lw_shared_ptr_deleter; // No generic implementation
195 template <typename T>
196 struct lw_shared_ptr_accessors_esft {
197 using concrete_type = std::remove_const_t<T>;
198 static T* to_value(lw_shared_ptr_counter_base* counter) {
199 return static_cast<T*>(counter);
201 static void dispose(lw_shared_ptr_counter_base* counter) {
202 dispose(static_cast<T*>(counter));
204 static void dispose(T* value_ptr) {
207 static void instantiate_to_value(lw_shared_ptr_counter_base*) {
208 // since to_value() is defined above, we don't need to do anything special
209 // to force-instantiate it
213 template <typename T>
214 struct lw_shared_ptr_accessors_no_esft {
215 using concrete_type = lw_shared_ptr_no_esft<T>;
216 static T* to_value(lw_shared_ptr_counter_base* counter) {
217 return &static_cast<concrete_type*>(counter)->_value;
219 static void dispose(lw_shared_ptr_counter_base* counter) {
220 delete static_cast<concrete_type*>(counter);
222 static void dispose(T* value_ptr) {
223 delete boost::intrusive::get_parent_from_member(value_ptr, &concrete_type::_value);
225 static void instantiate_to_value(lw_shared_ptr_counter_base*) {
226 // since to_value() is defined above, we don't need to do anything special
227 // to force-instantiate it
231 // Generic case: lw_shared_ptr_deleter<T> is not specialized, select
232 // implementation based on whether T inherits from enable_lw_shared_from_this<T>.
233 template <typename T, typename U = void>
234 struct lw_shared_ptr_accessors : std::conditional_t<
235 std::is_base_of<enable_lw_shared_from_this<T>, T>::value,
236 lw_shared_ptr_accessors_esft<T>,
237 lw_shared_ptr_accessors_no_esft<T>> {
240 // void_t is C++17, use this temporarily
241 template <typename... T>
244 // Overload when lw_shared_ptr_deleter<T> specialized
245 template <typename T>
246 struct lw_shared_ptr_accessors<T, void_t<decltype(lw_shared_ptr_deleter<T>{})>> {
247 using concrete_type = T;
248 static T* to_value(lw_shared_ptr_counter_base* counter);
249 static void dispose(lw_shared_ptr_counter_base* counter) {
250 lw_shared_ptr_deleter<T>::dispose(to_value(counter));
252 static void instantiate_to_value(lw_shared_ptr_counter_base* p) {
253 // instantiate to_value(); must be defined by shared_ptr_incomplete.hh
260 template <typename T>
261 class lw_shared_ptr {
262 template <typename U>
263 using accessors = internal::lw_shared_ptr_accessors<std::remove_const_t<U>>;
265 mutable lw_shared_ptr_counter_base* _p = nullptr;
267 lw_shared_ptr(lw_shared_ptr_counter_base* p) noexcept : _p(p) {
272 template <typename... A>
273 static lw_shared_ptr make(A&&... a) {
274 auto p = new typename accessors<T>::concrete_type(std::forward<A>(a)...);
275 accessors<T>::instantiate_to_value(p);
276 return lw_shared_ptr(p);
279 using element_type = T;
281 // Destroys the object pointed to by p and disposes of its storage.
282 // The pointer to the object must have been obtained through release().
283 static void dispose(T* p) noexcept {
284 accessors<T>::dispose(const_cast<std::remove_const_t<T>*>(p));
287 // A functor which calls dispose().
290 void operator()(T* p) const noexcept {
295 lw_shared_ptr() noexcept = default;
296 lw_shared_ptr(std::nullptr_t) noexcept : lw_shared_ptr() {}
297 lw_shared_ptr(const lw_shared_ptr& x) noexcept : _p(x._p) {
299 #pragma GCC diagnostic push
300 #ifdef SEASTAR_IGNORE_USE_AFTER_FREE
301 #pragma GCC diagnostic ignored "-Wuse-after-free"
304 #pragma GCC diagnostic pop
307 lw_shared_ptr(lw_shared_ptr&& x) noexcept : _p(x._p) {
310 [[gnu::always_inline]]
312 #pragma GCC diagnostic push
313 #ifdef SEASTAR_IGNORE_USE_AFTER_FREE
314 #pragma GCC diagnostic ignored "-Wuse-after-free"
316 if (_p && !--_p->_count) {
317 accessors<T>::dispose(_p);
319 #pragma GCC diagnostic pop
321 lw_shared_ptr& operator=(const lw_shared_ptr& x) noexcept {
323 this->~lw_shared_ptr();
324 new (this) lw_shared_ptr(x);
328 lw_shared_ptr& operator=(lw_shared_ptr&& x) noexcept {
330 this->~lw_shared_ptr();
331 new (this) lw_shared_ptr(std::move(x));
335 lw_shared_ptr& operator=(std::nullptr_t) noexcept {
336 return *this = lw_shared_ptr();
338 lw_shared_ptr& operator=(T&& x) noexcept {
339 this->~lw_shared_ptr();
340 new (this) lw_shared_ptr(make_lw_shared<T>(std::move(x)));
344 T& operator*() const noexcept { return *accessors<T>::to_value(_p); }
345 T* operator->() const noexcept { return accessors<T>::to_value(_p); }
346 T* get() const noexcept {
348 return accessors<T>::to_value(_p);
354 // Releases ownership of the object without destroying it.
355 // If this was the last owner then returns an engaged unique_ptr
356 // which is now the sole owner of the object.
357 // Returns a disengaged pointer if there are still some owners.
359 // Note that in case the raw pointer is extracted from the unique_ptr
360 // using unique_ptr::release(), it must be still destroyed using
361 // lw_shared_ptr::disposer or lw_shared_ptr::dispose().
362 std::unique_ptr<T, disposer> release() noexcept {
363 auto p = std::exchange(_p, nullptr);
367 return std::unique_ptr<T, disposer>(accessors<T>::to_value(p));
371 long int use_count() const noexcept {
379 operator lw_shared_ptr<const T>() const noexcept {
380 return lw_shared_ptr<const T>(_p);
383 explicit operator bool() const noexcept {
387 bool owned() const noexcept {
388 return _p->_count == 1;
391 bool operator==(const lw_shared_ptr<const T>& x) const {
395 bool operator!=(const lw_shared_ptr<const T>& x) const {
396 return !operator==(x);
399 bool operator==(const lw_shared_ptr<std::remove_const_t<T>>& x) const {
403 bool operator!=(const lw_shared_ptr<std::remove_const_t<T>>& x) const {
404 return !operator==(x);
407 bool operator<(const lw_shared_ptr<const T>& x) const {
411 bool operator<(const lw_shared_ptr<std::remove_const_t<T>>& x) const {
415 template <typename U>
416 friend class lw_shared_ptr;
418 template <typename X, typename... A>
419 friend lw_shared_ptr<X> make_lw_shared(A&&...);
421 template <typename U>
422 friend lw_shared_ptr<U> make_lw_shared(U&&);
424 template <typename U>
425 friend lw_shared_ptr<U> make_lw_shared(U&);
427 template <typename U>
428 friend class enable_lw_shared_from_this;
431 template <typename T, typename... A>
433 lw_shared_ptr<T> make_lw_shared(A&&... a) {
434 return lw_shared_ptr<T>::make(std::forward<A>(a)...);
437 template <typename T>
439 lw_shared_ptr<T> make_lw_shared(T&& a) {
440 return lw_shared_ptr<T>::make(std::move(a));
443 template <typename T>
445 lw_shared_ptr<T> make_lw_shared(T& a) {
446 return lw_shared_ptr<T>::make(a);
449 template <typename T>
452 enable_lw_shared_from_this<T>::shared_from_this() noexcept {
453 return lw_shared_ptr<T>(this);
456 template <typename T>
458 lw_shared_ptr<const T>
459 enable_lw_shared_from_this<T>::shared_from_this() const noexcept {
460 return lw_shared_ptr<const T>(const_cast<enable_lw_shared_from_this*>(this));
463 template <typename T>
465 std::ostream& operator<<(std::ostream& out, const lw_shared_ptr<T>& p) {
467 return out << "null";
472 // Polymorphic shared pointer class
474 struct shared_ptr_count_base {
475 // destructor is responsible for fully-typed deletion
476 virtual ~shared_ptr_count_base() {}
477 shared_ptr_counter_type count = 0;
480 template <typename T>
481 struct shared_ptr_count_for : shared_ptr_count_base {
483 template <typename... A>
484 shared_ptr_count_for(A&&... a) : data(std::forward<A>(a)...) {}
487 template <typename T>
488 class enable_shared_from_this : private shared_ptr_count_base {
490 shared_ptr<T> shared_from_this() noexcept;
491 shared_ptr<const T> shared_from_this() const noexcept;
492 long use_count() const noexcept { return count; }
494 template <typename U>
495 friend class shared_ptr;
497 template <typename U, bool esft>
498 friend struct shared_ptr_make_helper;
501 template <typename T>
503 mutable shared_ptr_count_base* _b = nullptr;
504 mutable T* _p = nullptr;
506 explicit shared_ptr(shared_ptr_count_for<T>* b) noexcept : _b(b), _p(&b->data) {
509 shared_ptr(shared_ptr_count_base* b, T* p) noexcept : _b(b), _p(p) {
514 explicit shared_ptr(enable_shared_from_this<std::remove_const_t<T>>* p) noexcept : _b(p), _p(static_cast<T*>(p)) {
520 using element_type = T;
522 shared_ptr() noexcept = default;
523 shared_ptr(std::nullptr_t) noexcept : shared_ptr() {}
524 shared_ptr(const shared_ptr& x) noexcept
531 shared_ptr(shared_ptr&& x) noexcept
537 template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
538 shared_ptr(const shared_ptr<U>& x) noexcept
545 template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
546 shared_ptr(shared_ptr<U>&& x) noexcept
553 #pragma GCC diagnostic push
554 #ifdef SEASTAR_IGNORE_USE_AFTER_FREE
555 #pragma GCC diagnostic ignored "-Wuse-after-free"
557 if (_b && !--_b->count) {
560 #pragma GCC diagnostic pop
562 shared_ptr& operator=(const shared_ptr& x) noexcept {
565 new (this) shared_ptr(x);
569 shared_ptr& operator=(shared_ptr&& x) noexcept {
572 new (this) shared_ptr(std::move(x));
576 shared_ptr& operator=(std::nullptr_t) noexcept {
577 return *this = shared_ptr();
579 template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
580 shared_ptr& operator=(const shared_ptr<U>& x) noexcept {
583 new (this) shared_ptr(x);
587 template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
588 shared_ptr& operator=(shared_ptr<U>&& x) noexcept {
591 new (this) shared_ptr(std::move(x));
595 explicit operator bool() const noexcept {
598 T& operator*() const noexcept {
601 T* operator->() const noexcept {
604 T* get() const noexcept {
607 long use_count() const noexcept {
618 template <typename U, typename... A>
619 friend shared_ptr<U> make_shared(A&&... a);
621 template <typename U>
622 friend shared_ptr<U> make_shared(U&& a);
624 template <typename V, typename U>
625 friend shared_ptr<V> static_pointer_cast(const shared_ptr<U>& p);
627 template <typename V, typename U>
628 friend shared_ptr<V> dynamic_pointer_cast(const shared_ptr<U>& p);
630 template <typename V, typename U>
631 friend shared_ptr<V> const_pointer_cast(const shared_ptr<U>& p);
633 template <bool esft, typename... A>
634 static shared_ptr make(A&&... a);
636 template <typename U>
637 friend class enable_shared_from_this;
639 template <typename U, bool esft>
640 friend struct shared_ptr_make_helper;
642 template <typename U>
643 friend class shared_ptr;
646 template <typename U, bool esft>
647 struct shared_ptr_make_helper;
649 template <typename T>
650 struct shared_ptr_make_helper<T, false> {
651 template <typename... A>
652 static shared_ptr<T> make(A&&... a) {
653 return shared_ptr<T>(new shared_ptr_count_for<T>(std::forward<A>(a)...));
657 template <typename T>
658 struct shared_ptr_make_helper<T, true> {
659 template <typename... A>
660 static shared_ptr<T> make(A&&... a) {
661 auto p = new T(std::forward<A>(a)...);
662 return shared_ptr<T>(p, p);
666 template <typename T, typename... A>
669 make_shared(A&&... a) {
670 using helper = shared_ptr_make_helper<T, std::is_base_of<shared_ptr_count_base, T>::value>;
671 return helper::make(std::forward<A>(a)...);
674 template <typename T>
678 using helper = shared_ptr_make_helper<T, std::is_base_of<shared_ptr_count_base, T>::value>;
679 return helper::make(std::forward<T>(a));
682 template <typename T, typename U>
685 static_pointer_cast(const shared_ptr<U>& p) {
686 return shared_ptr<T>(p._b, static_cast<T*>(p._p));
689 template <typename T, typename U>
692 dynamic_pointer_cast(const shared_ptr<U>& p) {
693 auto q = dynamic_cast<T*>(p._p);
694 return shared_ptr<T>(q ? p._b : nullptr, q);
697 template <typename T, typename U>
700 const_pointer_cast(const shared_ptr<U>& p) {
701 return shared_ptr<T>(p._b, const_cast<T*>(p._p));
704 template <typename T>
707 enable_shared_from_this<T>::shared_from_this() noexcept {
708 auto unconst = reinterpret_cast<enable_shared_from_this<std::remove_const_t<T>>*>(this);
709 return shared_ptr<T>(unconst);
712 template <typename T>
715 enable_shared_from_this<T>::shared_from_this() const noexcept {
716 auto esft = const_cast<enable_shared_from_this*>(this);
717 auto unconst = reinterpret_cast<enable_shared_from_this<std::remove_const_t<T>>*>(esft);
718 return shared_ptr<const T>(unconst);
721 template <typename T, typename U>
724 operator==(const shared_ptr<T>& x, const shared_ptr<U>& y) {
725 return x.get() == y.get();
728 template <typename T>
731 operator==(const shared_ptr<T>& x, std::nullptr_t) {
732 return x.get() == nullptr;
735 template <typename T>
738 operator==(std::nullptr_t, const shared_ptr<T>& y) {
739 return nullptr == y.get();
742 template <typename T>
745 operator==(const lw_shared_ptr<T>& x, std::nullptr_t) {
746 return x.get() == nullptr;
749 template <typename T>
752 operator==(std::nullptr_t, const lw_shared_ptr<T>& y) {
753 return nullptr == y.get();
756 template <typename T, typename U>
759 operator!=(const shared_ptr<T>& x, const shared_ptr<U>& y) {
760 return x.get() != y.get();
763 template <typename T>
766 operator!=(const shared_ptr<T>& x, std::nullptr_t) {
767 return x.get() != nullptr;
770 template <typename T>
773 operator!=(std::nullptr_t, const shared_ptr<T>& y) {
774 return nullptr != y.get();
777 template <typename T>
780 operator!=(const lw_shared_ptr<T>& x, std::nullptr_t) {
781 return x.get() != nullptr;
784 template <typename T>
787 operator!=(std::nullptr_t, const lw_shared_ptr<T>& y) {
788 return nullptr != y.get();
791 template <typename T, typename U>
794 operator<(const shared_ptr<T>& x, const shared_ptr<U>& y) {
795 return x.get() < y.get();
798 template <typename T>
801 operator<(const shared_ptr<T>& x, std::nullptr_t) {
802 return x.get() < nullptr;
805 template <typename T>
808 operator<(std::nullptr_t, const shared_ptr<T>& y) {
809 return nullptr < y.get();
812 template <typename T, typename U>
815 operator<=(const shared_ptr<T>& x, const shared_ptr<U>& y) {
816 return x.get() <= y.get();
819 template <typename T>
822 operator<=(const shared_ptr<T>& x, std::nullptr_t) {
823 return x.get() <= nullptr;
826 template <typename T>
829 operator<=(std::nullptr_t, const shared_ptr<T>& y) {
830 return nullptr <= y.get();
833 template <typename T, typename U>
836 operator>(const shared_ptr<T>& x, const shared_ptr<U>& y) {
837 return x.get() > y.get();
840 template <typename T>
843 operator>(const shared_ptr<T>& x, std::nullptr_t) {
844 return x.get() > nullptr;
847 template <typename T>
850 operator>(std::nullptr_t, const shared_ptr<T>& y) {
851 return nullptr > y.get();
854 template <typename T, typename U>
857 operator>=(const shared_ptr<T>& x, const shared_ptr<U>& y) {
858 return x.get() >= y.get();
861 template <typename T>
864 operator>=(const shared_ptr<T>& x, std::nullptr_t) {
865 return x.get() >= nullptr;
868 template <typename T>
871 operator>=(std::nullptr_t, const shared_ptr<T>& y) {
872 return nullptr >= y.get();
875 template <typename T>
877 std::ostream& operator<<(std::ostream& out, const shared_ptr<T>& p) {
879 return out << "null";
885 using shared_ptr_equal_by_value = indirect_equal_to<shared_ptr<T>>;
888 using shared_ptr_value_hash = indirect_hash<shared_ptr<T>>;
894 template <typename T>
895 struct hash<seastar::lw_shared_ptr<T>> : private hash<T*> {
896 size_t operator()(const seastar::lw_shared_ptr<T>& p) const {
897 return hash<T*>::operator()(p.get());
901 template <typename T>
902 struct hash<seastar::shared_ptr<T>> : private hash<T*> {
903 size_t operator()(const seastar::shared_ptr<T>& p) const {
904 return hash<T*>::operator()(p.get());
913 const void* ptr(const seastar::lw_shared_ptr<T>& p) {
918 const void* ptr(const seastar::shared_ptr<T>& p) {
927 struct is_smart_ptr<shared_ptr<T>> : std::true_type {};
930 struct is_smart_ptr<lw_shared_ptr<T>> : std::true_type {};