// -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:nil -*- /* * This file is open source software, licensed to you under the terms * of the Apache License, Version 2.0 (the "License"). See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. You may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #ifndef CEPH_LW_SHARED_PTR_H_ #define CEPH_LW_SHARED_PTR_H_ #include #include #include #include // This header defines a shared pointer facility, lw_shared_ptr<>, // modeled after std::shared_ptr<>. // // Unlike std::shared_ptr<>, this implementation is thread // safe, and two pointers sharing the same object must not be used in // different threads. // // lw_shared_ptr<> is the more lightweight variant, with a lw_shared_ptr<> // occupying just one machine word, and adding just one word to the shared // object. However, it does not support polymorphism. // // It supports shared_from_this() via enable_shared_from_this<> // and lw_enable_shared_from_this<>(). // template class lw_shared_ptr; template class enable_lw_shared_from_this; template class enable_shared_from_this; template lw_shared_ptr make_lw_shared(A&&... a); template lw_shared_ptr make_lw_shared(T&& a); template lw_shared_ptr make_lw_shared(T& a); struct lw_shared_ptr_counter_base { long _count = 0; }; namespace internal { template struct lw_shared_ptr_accessors; template struct lw_shared_ptr_accessors_esft; template struct lw_shared_ptr_accessors_no_esft; } // We want to support two use cases for shared_ptr: // // 1. T is any type (primitive or class type) // // 2. T is a class type that inherits from enable_shared_from_this. // // In the first case, we must wrap T in an object containing the counter, // since T may be a primitive type and cannot be a base class. // // In the second case, we want T to reach the counter through its // enable_shared_from_this<> base class, so that we can implement // shared_from_this(). // // To implement those two conflicting requirements (T alongside its counter; // T inherits from an object containing the counter) we use std::conditional<> // and some accessor functions to select between two implementations. // CRTP from this to enable shared_from_this: template class enable_lw_shared_from_this : private lw_shared_ptr_counter_base { using ctor = T; protected: enable_lw_shared_from_this() noexcept {} enable_lw_shared_from_this(enable_lw_shared_from_this&&) noexcept {} enable_lw_shared_from_this(const enable_lw_shared_from_this&) noexcept {} enable_lw_shared_from_this& operator=(const enable_lw_shared_from_this&) noexcept { return *this; } enable_lw_shared_from_this& operator=(enable_lw_shared_from_this&&) noexcept { return *this; } public: lw_shared_ptr shared_from_this(); lw_shared_ptr shared_from_this() const; template friend class lw_shared_ptr; template friend class ::internal::lw_shared_ptr_accessors_esft; template friend class ::internal::lw_shared_ptr_accessors; }; template struct shared_ptr_no_esft : private lw_shared_ptr_counter_base { T _value; shared_ptr_no_esft() = default; shared_ptr_no_esft(const T& x) : _value(x) {} shared_ptr_no_esft(T&& x) : _value(std::move(x)) {} template shared_ptr_no_esft(A&&... a) : _value(std::forward(a)...) {} template friend class lw_shared_ptr; template friend class ::internal::lw_shared_ptr_accessors_no_esft; template friend class ::internal::lw_shared_ptr_accessors; }; /// Extension point: the user may override this to change how \ref lw_shared_ptr objects are destroyed, /// primarily so that incomplete classes can be used. /// /// Customizing the deleter requires that \c T be derived from \c enable_lw_shared_from_this. /// The specialization must be visible for all uses of \c lw_shared_ptr. /// /// To customize, the template must have a `static void dispose(T*)` operator that disposes of /// the object. template struct lw_shared_ptr_deleter; // No generic implementation namespace internal { template struct lw_shared_ptr_accessors_esft { using concrete_type = std::remove_const_t; static T* to_value(lw_shared_ptr_counter_base* counter) { return static_cast(counter); } static void dispose(lw_shared_ptr_counter_base* counter) { delete static_cast(counter); } static void instantiate_to_value(lw_shared_ptr_counter_base* p) { // since to_value() is defined above, we don't need to do anything special // to force-instantiate it } }; template struct lw_shared_ptr_accessors_no_esft { using concrete_type = shared_ptr_no_esft; static T* to_value(lw_shared_ptr_counter_base* counter) { return &static_cast(counter)->_value; } static void dispose(lw_shared_ptr_counter_base* counter) { delete static_cast(counter); } static void instantiate_to_value(lw_shared_ptr_counter_base* p) { // since to_value() is defined above, we don't need to do anything special // to force-instantiate it } }; // Generic case: lw_shared_ptr_deleter is not specialized, select // implementation based on whether T inherits from enable_lw_shared_from_this. template struct lw_shared_ptr_accessors : std::conditional_t< std::is_base_of, T>::value, lw_shared_ptr_accessors_esft, lw_shared_ptr_accessors_no_esft> { }; // Overload when lw_shared_ptr_deleter specialized template struct lw_shared_ptr_accessors{})>> { using concrete_type = T; static T* to_value(lw_shared_ptr_counter_base* counter); static void dispose(lw_shared_ptr_counter_base* counter) { lw_shared_ptr_deleter::dispose(to_value(counter)); } static void instantiate_to_value(lw_shared_ptr_counter_base* p) { // instantiate to_value(); must be defined by shared_ptr_incomplete.hh to_value(p); } }; } template class lw_shared_ptr { using accessors = ::internal::lw_shared_ptr_accessors>; using concrete_type = typename accessors::concrete_type; mutable lw_shared_ptr_counter_base* _p = nullptr; private: lw_shared_ptr(lw_shared_ptr_counter_base* p) noexcept : _p(p) { if (_p) { ++_p->_count; } } template static lw_shared_ptr make(A&&... a) { auto p = new concrete_type(std::forward(a)...); accessors::instantiate_to_value(p); return lw_shared_ptr(p); } public: using element_type = T; lw_shared_ptr() noexcept = default; lw_shared_ptr(std::nullptr_t) noexcept : lw_shared_ptr() {} lw_shared_ptr(const lw_shared_ptr& x) noexcept : _p(x._p) { if (_p) { ++_p->_count; } } lw_shared_ptr(lw_shared_ptr&& x) noexcept : _p(x._p) { x._p = nullptr; } [[gnu::always_inline]] ~lw_shared_ptr() { if (_p && !--_p->_count) { accessors::dispose(_p); } } lw_shared_ptr& operator=(const lw_shared_ptr& x) noexcept { if (_p != x._p) { this->~lw_shared_ptr(); new (this) lw_shared_ptr(x); } return *this; } lw_shared_ptr& operator=(lw_shared_ptr&& x) noexcept { if (_p != x._p) { this->~lw_shared_ptr(); new (this) lw_shared_ptr(std::move(x)); } return *this; } lw_shared_ptr& operator=(std::nullptr_t) noexcept { return *this = lw_shared_ptr(); } lw_shared_ptr& operator=(T&& x) noexcept { this->~lw_shared_ptr(); new (this) lw_shared_ptr(make_lw_shared(std::move(x))); return *this; } T& operator*() const noexcept { return *accessors::to_value(_p); } T* operator->() const noexcept { return accessors::to_value(_p); } T* get() const noexcept { if (_p) { return accessors::to_value(_p); } else { return nullptr; } } long int use_count() const noexcept { if (_p) { return _p->_count; } else { return 0; } } operator lw_shared_ptr() const noexcept { return lw_shared_ptr(_p); } explicit operator bool() const noexcept { return _p; } bool owned() const noexcept { return _p->_count == 1; } bool operator==(const lw_shared_ptr& x) const { return _p == x._p; } bool operator!=(const lw_shared_ptr& x) const { return !operator==(x); } bool operator==(const lw_shared_ptr>& x) const { return _p == x._p; } bool operator!=(const lw_shared_ptr>& x) const { return !operator==(x); } bool operator<(const lw_shared_ptr& x) const { return _p < x._p; } bool operator<(const lw_shared_ptr>& x) const { return _p < x._p; } template friend class lw_shared_ptr; template friend lw_shared_ptr make_lw_shared(A&&...); template friend lw_shared_ptr make_lw_shared(U&&); template friend lw_shared_ptr make_lw_shared(U&); template friend class enable_lw_shared_from_this; }; template inline lw_shared_ptr make_lw_shared(A&&... a) { return lw_shared_ptr::make(std::forward(a)...); } template inline lw_shared_ptr make_lw_shared(T&& a) { return lw_shared_ptr::make(std::move(a)); } template inline lw_shared_ptr make_lw_shared(T& a) { return lw_shared_ptr::make(a); } template inline lw_shared_ptr enable_lw_shared_from_this::shared_from_this() { return lw_shared_ptr(this); } template inline lw_shared_ptr enable_lw_shared_from_this::shared_from_this() const { return lw_shared_ptr(const_cast(this)); } template static inline std::ostream& operator<<(std::ostream& out, const lw_shared_ptr& p) { if (!p) { return out << "null"; } return out << *p; } namespace std { template struct hash> : private hash { size_t operator()(const lw_shared_ptr& p) const { return hash::operator()(p.get()); } }; } #endif /* CEPH_LW_SHARED_PTR_H_ */