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>
36 // This header defines two shared pointer facilities, lw_shared_ptr<> and
37 // shared_ptr<>, both modeled after std::shared_ptr<>.
39 // Unlike std::shared_ptr<>, neither of these implementations are thread
40 // safe, and two pointers sharing the same object must not be used in
43 // lw_shared_ptr<> is the more lightweight variant, with a lw_shared_ptr<>
44 // occupying just one machine word, and adding just one word to the shared
45 // object. However, it does not support polymorphism.
47 // shared_ptr<> is more expensive, with a pointer occupying two machine
48 // words, and with two words of overhead in the shared object. In return,
49 // it does support polymorphism.
51 // Both variants support shared_from_this() via enable_shared_from_this<>
52 // and lw_enable_shared_from_this<>().
55 #ifndef SEASTAR_DEBUG_SHARED_PTR
56 using shared_ptr_counter_type = long;
58 using shared_ptr_counter_type = debug_shared_ptr_counter_type;
68 class enable_lw_shared_from_this;
71 class enable_shared_from_this;
73 template <typename T, typename... A>
74 lw_shared_ptr<T> make_lw_shared(A&&... a);
77 lw_shared_ptr<T> make_lw_shared(T&& a);
80 lw_shared_ptr<T> make_lw_shared(T& a);
82 template <typename T, typename... A>
83 shared_ptr<T> make_shared(A&&... a);
86 shared_ptr<T> make_shared(T&& a);
88 template <typename T, typename U>
89 shared_ptr<T> static_pointer_cast(const shared_ptr<U>& p);
91 template <typename T, typename U>
92 shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& p);
94 template <typename T, typename U>
95 shared_ptr<T> const_pointer_cast(const shared_ptr<U>& p);
97 struct lw_shared_ptr_counter_base {
98 shared_ptr_counter_type _count = 0;
104 template <class T, class U>
105 struct lw_shared_ptr_accessors;
108 struct lw_shared_ptr_accessors_esft;
111 struct lw_shared_ptr_accessors_no_esft;
116 // We want to support two use cases for shared_ptr<T>:
118 // 1. T is any type (primitive or class type)
120 // 2. T is a class type that inherits from enable_shared_from_this<T>.
122 // In the first case, we must wrap T in an object containing the counter,
123 // since T may be a primitive type and cannot be a base class.
125 // In the second case, we want T to reach the counter through its
126 // enable_shared_from_this<> base class, so that we can implement
127 // shared_from_this().
129 // To implement those two conflicting requirements (T alongside its counter;
130 // T inherits from an object containing the counter) we use std::conditional<>
131 // and some accessor functions to select between two implementations.
134 // CRTP from this to enable shared_from_this:
135 template <typename T>
136 class enable_lw_shared_from_this : private lw_shared_ptr_counter_base {
139 enable_lw_shared_from_this() noexcept {}
140 enable_lw_shared_from_this(enable_lw_shared_from_this&&) noexcept {}
141 enable_lw_shared_from_this(const enable_lw_shared_from_this&) noexcept {}
142 enable_lw_shared_from_this& operator=(const enable_lw_shared_from_this&) noexcept { return *this; }
143 enable_lw_shared_from_this& operator=(enable_lw_shared_from_this&&) noexcept { return *this; }
145 lw_shared_ptr<T> shared_from_this();
146 lw_shared_ptr<const T> shared_from_this() const;
148 template <typename X>
149 friend class lw_shared_ptr;
150 template <typename X>
151 friend struct internal::lw_shared_ptr_accessors_esft;
152 template <typename X, class Y>
153 friend struct internal::lw_shared_ptr_accessors;
156 template <typename T>
157 struct shared_ptr_no_esft : private lw_shared_ptr_counter_base {
160 shared_ptr_no_esft() = default;
161 shared_ptr_no_esft(const T& x) : _value(x) {}
162 shared_ptr_no_esft(T&& x) : _value(std::move(x)) {}
163 template <typename... A>
164 shared_ptr_no_esft(A&&... a) : _value(std::forward<A>(a)...) {}
166 template <typename X>
167 friend class lw_shared_ptr;
168 template <typename X>
169 friend struct internal::lw_shared_ptr_accessors_no_esft;
170 template <typename X, class Y>
171 friend struct internal::lw_shared_ptr_accessors;
175 /// Extension point: the user may override this to change how \ref lw_shared_ptr objects are destroyed,
176 /// primarily so that incomplete classes can be used.
178 /// Customizing the deleter requires that \c T be derived from \c enable_lw_shared_from_this<T>.
179 /// The specialization must be visible for all uses of \c lw_shared_ptr<T>.
181 /// To customize, the template must have a `static void dispose(T*)` operator that disposes of
183 template <typename T>
184 struct lw_shared_ptr_deleter; // No generic implementation
188 template <typename T>
189 struct lw_shared_ptr_accessors_esft {
190 using concrete_type = std::remove_const_t<T>;
191 static T* to_value(lw_shared_ptr_counter_base* counter) {
192 return static_cast<T*>(counter);
194 static void dispose(lw_shared_ptr_counter_base* counter) {
195 dispose(static_cast<T*>(counter));
197 static void dispose(T* value_ptr) {
200 static void instantiate_to_value(lw_shared_ptr_counter_base* p) {
201 // since to_value() is defined above, we don't need to do anything special
202 // to force-instantiate it
206 template <typename T>
207 struct lw_shared_ptr_accessors_no_esft {
208 using concrete_type = shared_ptr_no_esft<T>;
209 static T* to_value(lw_shared_ptr_counter_base* counter) {
210 return &static_cast<concrete_type*>(counter)->_value;
212 static void dispose(lw_shared_ptr_counter_base* counter) {
213 delete static_cast<concrete_type*>(counter);
215 static void dispose(T* value_ptr) {
216 delete boost::intrusive::get_parent_from_member(value_ptr, &concrete_type::_value);
218 static void instantiate_to_value(lw_shared_ptr_counter_base* p) {
219 // since to_value() is defined above, we don't need to do anything special
220 // to force-instantiate it
224 // Generic case: lw_shared_ptr_deleter<T> is not specialized, select
225 // implementation based on whether T inherits from enable_lw_shared_from_this<T>.
226 template <typename T, typename U = void>
227 struct lw_shared_ptr_accessors : std::conditional_t<
228 std::is_base_of<enable_lw_shared_from_this<T>, T>::value,
229 lw_shared_ptr_accessors_esft<T>,
230 lw_shared_ptr_accessors_no_esft<T>> {
233 // void_t is C++17, use this temporarily
234 template <typename... T>
237 // Overload when lw_shared_ptr_deleter<T> specialized
238 template <typename T>
239 struct lw_shared_ptr_accessors<T, void_t<decltype(lw_shared_ptr_deleter<T>{})>> {
240 using concrete_type = T;
241 static T* to_value(lw_shared_ptr_counter_base* counter);
242 static void dispose(lw_shared_ptr_counter_base* counter) {
243 lw_shared_ptr_deleter<T>::dispose(to_value(counter));
245 static void instantiate_to_value(lw_shared_ptr_counter_base* p) {
246 // instantiate to_value(); must be defined by shared_ptr_incomplete.hh
253 template <typename T>
254 class lw_shared_ptr {
255 using accessors = internal::lw_shared_ptr_accessors<std::remove_const_t<T>>;
256 using concrete_type = typename accessors::concrete_type;
257 mutable lw_shared_ptr_counter_base* _p = nullptr;
259 lw_shared_ptr(lw_shared_ptr_counter_base* p) noexcept : _p(p) {
264 template <typename... A>
265 static lw_shared_ptr make(A&&... a) {
266 auto p = new concrete_type(std::forward<A>(a)...);
267 accessors::instantiate_to_value(p);
268 return lw_shared_ptr(p);
271 using element_type = T;
273 // Destroys the object pointed to by p and disposes of its storage.
274 // The pointer to the object must have been obtained through release().
275 static void dispose(T* p) noexcept {
276 accessors::dispose(const_cast<std::remove_const_t<T>*>(p));
279 // A functor which calls dispose().
282 void operator()(T* p) const noexcept {
287 lw_shared_ptr() noexcept = default;
288 lw_shared_ptr(std::nullptr_t) noexcept : lw_shared_ptr() {}
289 lw_shared_ptr(const lw_shared_ptr& x) noexcept : _p(x._p) {
294 lw_shared_ptr(lw_shared_ptr&& x) noexcept : _p(x._p) {
297 [[gnu::always_inline]]
299 if (_p && !--_p->_count) {
300 accessors::dispose(_p);
303 lw_shared_ptr& operator=(const lw_shared_ptr& x) noexcept {
305 this->~lw_shared_ptr();
306 new (this) lw_shared_ptr(x);
310 lw_shared_ptr& operator=(lw_shared_ptr&& x) noexcept {
312 this->~lw_shared_ptr();
313 new (this) lw_shared_ptr(std::move(x));
317 lw_shared_ptr& operator=(std::nullptr_t) noexcept {
318 return *this = lw_shared_ptr();
320 lw_shared_ptr& operator=(T&& x) noexcept {
321 this->~lw_shared_ptr();
322 new (this) lw_shared_ptr(make_lw_shared<T>(std::move(x)));
326 T& operator*() const noexcept { return *accessors::to_value(_p); }
327 T* operator->() const noexcept { return accessors::to_value(_p); }
328 T* get() const noexcept {
330 return accessors::to_value(_p);
336 // Releases ownership of the object without destroying it.
337 // If this was the last owner then returns an engaged unique_ptr
338 // which is now the sole owner of the object.
339 // Returns a disengaged pointer if there are still some owners.
341 // Note that in case the raw pointer is extracted from the unique_ptr
342 // using unique_ptr::release(), it must be still destroyed using
343 // lw_shared_ptr::disposer or lw_shared_ptr::dispose().
344 std::unique_ptr<T, disposer> release() noexcept {
345 auto p = std::exchange(_p, nullptr);
349 return std::unique_ptr<T, disposer>(accessors::to_value(p));
353 long int use_count() const noexcept {
361 operator lw_shared_ptr<const T>() const noexcept {
362 return lw_shared_ptr<const T>(_p);
365 explicit operator bool() const noexcept {
369 bool owned() const noexcept {
370 return _p->_count == 1;
373 bool operator==(const lw_shared_ptr<const T>& x) const {
377 bool operator!=(const lw_shared_ptr<const T>& x) const {
378 return !operator==(x);
381 bool operator==(const lw_shared_ptr<std::remove_const_t<T>>& x) const {
385 bool operator!=(const lw_shared_ptr<std::remove_const_t<T>>& x) const {
386 return !operator==(x);
389 bool operator<(const lw_shared_ptr<const T>& x) const {
393 bool operator<(const lw_shared_ptr<std::remove_const_t<T>>& x) const {
397 template <typename U>
398 friend class lw_shared_ptr;
400 template <typename X, typename... A>
401 friend lw_shared_ptr<X> make_lw_shared(A&&...);
403 template <typename U>
404 friend lw_shared_ptr<U> make_lw_shared(U&&);
406 template <typename U>
407 friend lw_shared_ptr<U> make_lw_shared(U&);
409 template <typename U>
410 friend class enable_lw_shared_from_this;
413 template <typename T, typename... A>
415 lw_shared_ptr<T> make_lw_shared(A&&... a) {
416 return lw_shared_ptr<T>::make(std::forward<A>(a)...);
419 template <typename T>
421 lw_shared_ptr<T> make_lw_shared(T&& a) {
422 return lw_shared_ptr<T>::make(std::move(a));
425 template <typename T>
427 lw_shared_ptr<T> make_lw_shared(T& a) {
428 return lw_shared_ptr<T>::make(a);
431 template <typename T>
434 enable_lw_shared_from_this<T>::shared_from_this() {
435 return lw_shared_ptr<T>(this);
438 template <typename T>
440 lw_shared_ptr<const T>
441 enable_lw_shared_from_this<T>::shared_from_this() const {
442 return lw_shared_ptr<const T>(const_cast<enable_lw_shared_from_this*>(this));
445 template <typename T>
447 std::ostream& operator<<(std::ostream& out, const lw_shared_ptr<T>& p) {
449 return out << "null";
454 // Polymorphic shared pointer class
456 struct shared_ptr_count_base {
457 // destructor is responsible for fully-typed deletion
458 virtual ~shared_ptr_count_base() {}
459 shared_ptr_counter_type count = 0;
462 template <typename T>
463 struct shared_ptr_count_for : shared_ptr_count_base {
465 template <typename... A>
466 shared_ptr_count_for(A&&... a) : data(std::forward<A>(a)...) {}
469 template <typename T>
470 class enable_shared_from_this : private shared_ptr_count_base {
472 shared_ptr<T> shared_from_this();
473 shared_ptr<const T> shared_from_this() const;
475 template <typename U>
476 friend class shared_ptr;
478 template <typename U, bool esft>
479 friend struct shared_ptr_make_helper;
482 template <typename T>
484 mutable shared_ptr_count_base* _b = nullptr;
485 mutable T* _p = nullptr;
487 explicit shared_ptr(shared_ptr_count_for<T>* b) noexcept : _b(b), _p(&b->data) {
490 shared_ptr(shared_ptr_count_base* b, T* p) noexcept : _b(b), _p(p) {
495 explicit shared_ptr(enable_shared_from_this<std::remove_const_t<T>>* p) noexcept : _b(p), _p(static_cast<T*>(p)) {
501 using element_type = T;
503 shared_ptr() noexcept = default;
504 shared_ptr(std::nullptr_t) noexcept : shared_ptr() {}
505 shared_ptr(const shared_ptr& x) noexcept
512 shared_ptr(shared_ptr&& x) noexcept
518 template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
519 shared_ptr(const shared_ptr<U>& x) noexcept
526 template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
527 shared_ptr(shared_ptr<U>&& x) noexcept
534 if (_b && !--_b->count) {
538 shared_ptr& operator=(const shared_ptr& x) noexcept {
541 new (this) shared_ptr(x);
545 shared_ptr& operator=(shared_ptr&& x) noexcept {
548 new (this) shared_ptr(std::move(x));
552 shared_ptr& operator=(std::nullptr_t) noexcept {
553 return *this = shared_ptr();
555 template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
556 shared_ptr& operator=(const shared_ptr<U>& x) noexcept {
559 new (this) shared_ptr(x);
563 template <typename U, typename = std::enable_if_t<std::is_base_of<T, U>::value>>
564 shared_ptr& operator=(shared_ptr<U>&& x) noexcept {
567 new (this) shared_ptr(std::move(x));
571 explicit operator bool() const noexcept {
574 T& operator*() const noexcept {
577 T* operator->() const noexcept {
580 T* get() const noexcept {
583 long use_count() const noexcept {
594 template <typename U, typename... A>
595 friend shared_ptr<U> make_shared(A&&... a);
597 template <typename U>
598 friend shared_ptr<U> make_shared(U&& a);
600 template <typename V, typename U>
601 friend shared_ptr<V> static_pointer_cast(const shared_ptr<U>& p);
603 template <typename V, typename U>
604 friend shared_ptr<V> dynamic_pointer_cast(const shared_ptr<U>& p);
606 template <typename V, typename U>
607 friend shared_ptr<V> const_pointer_cast(const shared_ptr<U>& p);
609 template <bool esft, typename... A>
610 static shared_ptr make(A&&... a);
612 template <typename U>
613 friend class enable_shared_from_this;
615 template <typename U, bool esft>
616 friend struct shared_ptr_make_helper;
618 template <typename U>
619 friend class shared_ptr;
622 template <typename U, bool esft>
623 struct shared_ptr_make_helper;
625 template <typename T>
626 struct shared_ptr_make_helper<T, false> {
627 template <typename... A>
628 static shared_ptr<T> make(A&&... a) {
629 return shared_ptr<T>(new shared_ptr_count_for<T>(std::forward<A>(a)...));
633 template <typename T>
634 struct shared_ptr_make_helper<T, true> {
635 template <typename... A>
636 static shared_ptr<T> make(A&&... a) {
637 auto p = new T(std::forward<A>(a)...);
638 return shared_ptr<T>(p, p);
642 template <typename T, typename... A>
645 make_shared(A&&... a) {
646 using helper = shared_ptr_make_helper<T, std::is_base_of<shared_ptr_count_base, T>::value>;
647 return helper::make(std::forward<A>(a)...);
650 template <typename T>
654 using helper = shared_ptr_make_helper<T, std::is_base_of<shared_ptr_count_base, T>::value>;
655 return helper::make(std::forward<T>(a));
658 template <typename T, typename U>
661 static_pointer_cast(const shared_ptr<U>& p) {
662 return shared_ptr<T>(p._b, static_cast<T*>(p._p));
665 template <typename T, typename U>
668 dynamic_pointer_cast(const shared_ptr<U>& p) {
669 auto q = dynamic_cast<T*>(p._p);
670 return shared_ptr<T>(q ? p._b : nullptr, q);
673 template <typename T, typename U>
676 const_pointer_cast(const shared_ptr<U>& p) {
677 return shared_ptr<T>(p._b, const_cast<T*>(p._p));
680 template <typename T>
683 enable_shared_from_this<T>::shared_from_this() {
684 auto unconst = reinterpret_cast<enable_shared_from_this<std::remove_const_t<T>>*>(this);
685 return shared_ptr<T>(unconst);
688 template <typename T>
691 enable_shared_from_this<T>::shared_from_this() const {
692 auto esft = const_cast<enable_shared_from_this*>(this);
693 auto unconst = reinterpret_cast<enable_shared_from_this<std::remove_const_t<T>>*>(esft);
694 return shared_ptr<const T>(unconst);
697 template <typename T, typename U>
700 operator==(const shared_ptr<T>& x, const shared_ptr<U>& y) {
701 return x.get() == y.get();
704 template <typename T>
707 operator==(const shared_ptr<T>& x, std::nullptr_t) {
708 return x.get() == nullptr;
711 template <typename T>
714 operator==(std::nullptr_t, const shared_ptr<T>& y) {
715 return nullptr == y.get();
718 template <typename T, typename U>
721 operator!=(const shared_ptr<T>& x, const shared_ptr<U>& y) {
722 return x.get() != y.get();
725 template <typename T>
728 operator!=(const shared_ptr<T>& x, std::nullptr_t) {
729 return x.get() != nullptr;
732 template <typename T>
735 operator!=(std::nullptr_t, const shared_ptr<T>& y) {
736 return nullptr != y.get();
739 template <typename T, typename U>
742 operator<(const shared_ptr<T>& x, const shared_ptr<U>& y) {
743 return x.get() < y.get();
746 template <typename T>
749 operator<(const shared_ptr<T>& x, std::nullptr_t) {
750 return x.get() < nullptr;
753 template <typename T>
756 operator<(std::nullptr_t, const shared_ptr<T>& y) {
757 return nullptr < y.get();
760 template <typename T, typename U>
763 operator<=(const shared_ptr<T>& x, const shared_ptr<U>& y) {
764 return x.get() <= y.get();
767 template <typename T>
770 operator<=(const shared_ptr<T>& x, std::nullptr_t) {
771 return x.get() <= nullptr;
774 template <typename T>
777 operator<=(std::nullptr_t, const shared_ptr<T>& y) {
778 return nullptr <= y.get();
781 template <typename T, typename U>
784 operator>(const shared_ptr<T>& x, const shared_ptr<U>& y) {
785 return x.get() > y.get();
788 template <typename T>
791 operator>(const shared_ptr<T>& x, std::nullptr_t) {
792 return x.get() > nullptr;
795 template <typename T>
798 operator>(std::nullptr_t, const shared_ptr<T>& y) {
799 return nullptr > y.get();
802 template <typename T, typename U>
805 operator>=(const shared_ptr<T>& x, const shared_ptr<U>& y) {
806 return x.get() >= y.get();
809 template <typename T>
812 operator>=(const shared_ptr<T>& x, std::nullptr_t) {
813 return x.get() >= nullptr;
816 template <typename T>
819 operator>=(std::nullptr_t, const shared_ptr<T>& y) {
820 return nullptr >= y.get();
823 template <typename T>
825 std::ostream& operator<<(std::ostream& out, const shared_ptr<T>& p) {
827 return out << "null";
833 using shared_ptr_equal_by_value = indirect_equal_to<shared_ptr<T>>;
836 using shared_ptr_value_hash = indirect_hash<shared_ptr<T>>;
842 template <typename T>
843 struct hash<seastar::lw_shared_ptr<T>> : private hash<T*> {
844 size_t operator()(const seastar::lw_shared_ptr<T>& p) const {
845 return hash<T*>::operator()(p.get());
849 template <typename T>
850 struct hash<seastar::shared_ptr<T>> : private hash<T*> {
851 size_t operator()(const seastar::shared_ptr<T>& p) const {
852 return hash<T*>::operator()(p.get());
861 struct is_smart_ptr<shared_ptr<T>> : std::true_type {};
864 struct is_smart_ptr<lw_shared_ptr<T>> : std::true_type {};