1 // -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:nil -*-
3 * This file is open source software, licensed to you under the terms
4 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
5 * distributed with this work for additional information regarding copyright
6 * ownership. You may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
20 * Copyright (C) 2014 Cloudius Systems, Ltd.
23 #ifndef CEPH_LW_SHARED_PTR_H_
24 #define CEPH_LW_SHARED_PTR_H_
27 #include <type_traits>
31 // This header defines a shared pointer facility, lw_shared_ptr<>,
32 // modeled after std::shared_ptr<>.
34 // Unlike std::shared_ptr<>, this implementation is thread
35 // safe, and two pointers sharing the same object must not be used in
38 // lw_shared_ptr<> is the more lightweight variant, with a lw_shared_ptr<>
39 // occupying just one machine word, and adding just one word to the shared
40 // object. However, it does not support polymorphism.
42 // It supports shared_from_this() via enable_shared_from_this<>
43 // and lw_enable_shared_from_this<>().
50 class enable_lw_shared_from_this
;
53 class enable_shared_from_this
;
55 template <typename T
, typename
... A
>
56 lw_shared_ptr
<T
> make_lw_shared(A
&&... a
);
59 lw_shared_ptr
<T
> make_lw_shared(T
&& a
);
62 lw_shared_ptr
<T
> make_lw_shared(T
& a
);
64 struct lw_shared_ptr_counter_base
{
71 template <class T
, class U
>
72 struct lw_shared_ptr_accessors
;
75 struct lw_shared_ptr_accessors_esft
;
78 struct lw_shared_ptr_accessors_no_esft
;
83 // We want to support two use cases for shared_ptr<T>:
85 // 1. T is any type (primitive or class type)
87 // 2. T is a class type that inherits from enable_shared_from_this<T>.
89 // In the first case, we must wrap T in an object containing the counter,
90 // since T may be a primitive type and cannot be a base class.
92 // In the second case, we want T to reach the counter through its
93 // enable_shared_from_this<> base class, so that we can implement
94 // shared_from_this().
96 // To implement those two conflicting requirements (T alongside its counter;
97 // T inherits from an object containing the counter) we use std::conditional<>
98 // and some accessor functions to select between two implementations.
101 // CRTP from this to enable shared_from_this:
102 template <typename T
>
103 class enable_lw_shared_from_this
: private lw_shared_ptr_counter_base
{
106 enable_lw_shared_from_this() noexcept
{}
107 enable_lw_shared_from_this(enable_lw_shared_from_this
&&) noexcept
{}
108 enable_lw_shared_from_this(const enable_lw_shared_from_this
&) noexcept
{}
109 enable_lw_shared_from_this
& operator=(const enable_lw_shared_from_this
&) noexcept
{ return *this; }
110 enable_lw_shared_from_this
& operator=(enable_lw_shared_from_this
&&) noexcept
{ return *this; }
112 lw_shared_ptr
<T
> shared_from_this();
113 lw_shared_ptr
<const T
> shared_from_this() const;
115 template <typename X
>
116 friend class lw_shared_ptr
;
117 template <typename X
>
118 friend class ::internal::lw_shared_ptr_accessors_esft
;
119 template <typename X
, class Y
>
120 friend class ::internal::lw_shared_ptr_accessors
;
123 template <typename T
>
124 struct shared_ptr_no_esft
: private lw_shared_ptr_counter_base
{
127 shared_ptr_no_esft() = default;
128 shared_ptr_no_esft(const T
& x
) : _value(x
) {}
129 shared_ptr_no_esft(T
&& x
) : _value(std::move(x
)) {}
130 template <typename
... A
>
131 shared_ptr_no_esft(A
&&... a
) : _value(std::forward
<A
>(a
)...) {}
133 template <typename X
>
134 friend class lw_shared_ptr
;
135 template <typename X
>
136 friend class ::internal::lw_shared_ptr_accessors_no_esft
;
137 template <typename X
, class Y
>
138 friend class ::internal::lw_shared_ptr_accessors
;
142 /// Extension point: the user may override this to change how \ref lw_shared_ptr objects are destroyed,
143 /// primarily so that incomplete classes can be used.
145 /// Customizing the deleter requires that \c T be derived from \c enable_lw_shared_from_this<T>.
146 /// The specialization must be visible for all uses of \c lw_shared_ptr<T>.
148 /// To customize, the template must have a `static void dispose(T*)` operator that disposes of
150 template <typename T
>
151 struct lw_shared_ptr_deleter
; // No generic implementation
155 template <typename T
>
156 struct lw_shared_ptr_accessors_esft
{
157 using concrete_type
= std::remove_const_t
<T
>;
158 static T
* to_value(lw_shared_ptr_counter_base
* counter
) {
159 return static_cast<T
*>(counter
);
161 static void dispose(lw_shared_ptr_counter_base
* counter
) {
162 delete static_cast<T
*>(counter
);
164 static void instantiate_to_value(lw_shared_ptr_counter_base
* p
) {
165 // since to_value() is defined above, we don't need to do anything special
166 // to force-instantiate it
170 template <typename T
>
171 struct lw_shared_ptr_accessors_no_esft
{
172 using concrete_type
= shared_ptr_no_esft
<T
>;
173 static T
* to_value(lw_shared_ptr_counter_base
* counter
) {
174 return &static_cast<concrete_type
*>(counter
)->_value
;
176 static void dispose(lw_shared_ptr_counter_base
* counter
) {
177 delete static_cast<concrete_type
*>(counter
);
179 static void instantiate_to_value(lw_shared_ptr_counter_base
* p
) {
180 // since to_value() is defined above, we don't need to do anything special
181 // to force-instantiate it
185 // Generic case: lw_shared_ptr_deleter<T> is not specialized, select
186 // implementation based on whether T inherits from enable_lw_shared_from_this<T>.
187 template <typename T
, typename U
= void>
188 struct lw_shared_ptr_accessors
: std::conditional_t
<
189 std::is_base_of
<enable_lw_shared_from_this
<T
>, T
>::value
,
190 lw_shared_ptr_accessors_esft
<T
>,
191 lw_shared_ptr_accessors_no_esft
<T
>> {
194 // Overload when lw_shared_ptr_deleter<T> specialized
195 template <typename T
>
196 struct lw_shared_ptr_accessors
<T
, std::void_t
<decltype(lw_shared_ptr_deleter
<T
>{})>> {
197 using concrete_type
= T
;
198 static T
* to_value(lw_shared_ptr_counter_base
* counter
);
199 static void dispose(lw_shared_ptr_counter_base
* counter
) {
200 lw_shared_ptr_deleter
<T
>::dispose(to_value(counter
));
202 static void instantiate_to_value(lw_shared_ptr_counter_base
* p
) {
203 // instantiate to_value(); must be defined by shared_ptr_incomplete.hh
210 template <typename T
>
211 class lw_shared_ptr
{
212 using accessors
= ::internal::lw_shared_ptr_accessors
<std::remove_const_t
<T
>>;
213 using concrete_type
= typename
accessors::concrete_type
;
214 mutable lw_shared_ptr_counter_base
* _p
= nullptr;
216 lw_shared_ptr(lw_shared_ptr_counter_base
* p
) noexcept
: _p(p
) {
221 template <typename
... A
>
222 static lw_shared_ptr
make(A
&&... a
) {
223 auto p
= new concrete_type(std::forward
<A
>(a
)...);
224 accessors::instantiate_to_value(p
);
225 return lw_shared_ptr(p
);
228 using element_type
= T
;
230 lw_shared_ptr() noexcept
= default;
231 lw_shared_ptr(std::nullptr_t
) noexcept
: lw_shared_ptr() {}
232 lw_shared_ptr(const lw_shared_ptr
& x
) noexcept
: _p(x
._p
) {
237 lw_shared_ptr(lw_shared_ptr
&& x
) noexcept
: _p(x
._p
) {
240 [[gnu::always_inline
]]
242 if (_p
&& !--_p
->_count
) {
243 accessors::dispose(_p
);
246 lw_shared_ptr
& operator=(const lw_shared_ptr
& x
) noexcept
{
248 this->~lw_shared_ptr();
249 new (this) lw_shared_ptr(x
);
253 lw_shared_ptr
& operator=(lw_shared_ptr
&& x
) noexcept
{
255 this->~lw_shared_ptr();
256 new (this) lw_shared_ptr(std::move(x
));
260 lw_shared_ptr
& operator=(std::nullptr_t
) noexcept
{
261 return *this = lw_shared_ptr();
263 lw_shared_ptr
& operator=(T
&& x
) noexcept
{
264 this->~lw_shared_ptr();
265 new (this) lw_shared_ptr(make_lw_shared
<T
>(std::move(x
)));
269 T
& operator*() const noexcept
{ return *accessors::to_value(_p
); }
270 T
* operator->() const noexcept
{ return accessors::to_value(_p
); }
271 T
* get() const noexcept
{
273 return accessors::to_value(_p
);
279 long int use_count() const noexcept
{
287 operator lw_shared_ptr
<const T
>() const noexcept
{
288 return lw_shared_ptr
<const T
>(_p
);
291 explicit operator bool() const noexcept
{
295 bool owned() const noexcept
{
296 return _p
->_count
== 1;
299 bool operator==(const lw_shared_ptr
<const T
>& x
) const {
303 bool operator!=(const lw_shared_ptr
<const T
>& x
) const {
304 return !operator==(x
);
307 bool operator==(const lw_shared_ptr
<std::remove_const_t
<T
>>& x
) const {
311 bool operator!=(const lw_shared_ptr
<std::remove_const_t
<T
>>& x
) const {
312 return !operator==(x
);
315 bool operator<(const lw_shared_ptr
<const T
>& x
) const {
319 bool operator<(const lw_shared_ptr
<std::remove_const_t
<T
>>& x
) const {
323 template <typename U
>
324 friend class lw_shared_ptr
;
326 template <typename X
, typename
... A
>
327 friend lw_shared_ptr
<X
> make_lw_shared(A
&&...);
329 template <typename U
>
330 friend lw_shared_ptr
<U
> make_lw_shared(U
&&);
332 template <typename U
>
333 friend lw_shared_ptr
<U
> make_lw_shared(U
&);
335 template <typename U
>
336 friend class enable_lw_shared_from_this
;
339 template <typename T
, typename
... A
>
341 lw_shared_ptr
<T
> make_lw_shared(A
&&... a
) {
342 return lw_shared_ptr
<T
>::make(std::forward
<A
>(a
)...);
345 template <typename T
>
347 lw_shared_ptr
<T
> make_lw_shared(T
&& a
) {
348 return lw_shared_ptr
<T
>::make(std::move(a
));
351 template <typename T
>
353 lw_shared_ptr
<T
> make_lw_shared(T
& a
) {
354 return lw_shared_ptr
<T
>::make(a
);
357 template <typename T
>
360 enable_lw_shared_from_this
<T
>::shared_from_this() {
361 return lw_shared_ptr
<T
>(this);
364 template <typename T
>
366 lw_shared_ptr
<const T
>
367 enable_lw_shared_from_this
<T
>::shared_from_this() const {
368 return lw_shared_ptr
<const T
>(const_cast<enable_lw_shared_from_this
*>(this));
371 template <typename T
>
373 std::ostream
& operator<<(std::ostream
& out
, const lw_shared_ptr
<T
>& p
) {
375 return out
<< "null";
382 template <typename T
>
383 struct hash
<lw_shared_ptr
<T
>> : private hash
<T
*> {
384 size_t operator()(const lw_shared_ptr
<T
>& p
) const {
385 return hash
<T
*>::operator()(p
.get());
391 #endif /* CEPH_LW_SHARED_PTR_H_ */