1 /* 2 * Copyright (c) Facebook, Inc. and its affiliates. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <limits.h> 20 #include <atomic> 21 #include <memory> 22 23 #include <folly/lang/SafeAssert.h> 24 25 namespace folly { 26 namespace detail { 27 28 // This implementation is specific to libstdc++, now accepting 29 // diffs for other libraries. 30 31 // Specifically, this adds support for two things: 32 // 1) incrementing/decrementing the shared count by more than 1 at a time 33 // 2) Getting the thing the shared_ptr points to, which may be different from 34 // the aliased pointer. 35 36 class shared_ptr_internals { 37 public: 38 template <typename T, typename... Args> make_ptr(Args &&...args)39 static std::shared_ptr<T> make_ptr(Args&&... args) { 40 return std::make_shared<T>(std::forward<Args...>(args...)); 41 } 42 typedef std::__shared_count<std::_S_atomic> shared_count; 43 typedef std::_Sp_counted_base<std::_S_atomic> counted_base; 44 template <typename T> 45 using CountedPtr = std::shared_ptr<T>; 46 47 template <typename T> 48 static counted_base* get_counted_base(const std::shared_ptr<T>& bar); 49 50 static void inc_shared_count(counted_base* base, long count); 51 52 template <typename T> 53 static void release_shared(counted_base* base, long count); 54 55 template <typename T> 56 static T* get_shared_ptr(counted_base* base); 57 58 template <typename T> 59 static T* release_ptr(std::shared_ptr<T>& p); 60 61 template <typename T> 62 static std::shared_ptr<T> get_shared_ptr_from_counted_base( 63 counted_base* base, bool inc = true); 64 65 private: 66 /* Accessors for private members using explicit template instantiation */ 67 struct access_shared_ptr { 68 typedef shared_count std::__shared_ptr<const void, std::_S_atomic>::*type; 69 friend type fieldPtr(access_shared_ptr); 70 }; 71 72 struct access_base { 73 typedef counted_base* shared_count::*type; 74 friend type fieldPtr(access_base); 75 }; 76 77 struct access_use_count { 78 typedef _Atomic_word counted_base::*type; 79 friend type fieldPtr(access_use_count); 80 }; 81 82 struct access_weak_count { 83 typedef _Atomic_word counted_base::*type; 84 friend type fieldPtr(access_weak_count); 85 }; 86 87 struct access_counted_ptr_ptr { 88 typedef const void* std::_Sp_counted_ptr<const void*, std::_S_atomic>::* 89 type; 90 friend type fieldPtr(access_counted_ptr_ptr); 91 }; 92 93 struct access_shared_ptr_ptr { 94 typedef const void* std::__shared_ptr<const void, std::_S_atomic>::*type; 95 friend type fieldPtr(access_shared_ptr_ptr); 96 }; 97 98 struct access_refcount { 99 typedef shared_count std::__shared_ptr<const void, std::_S_atomic>::*type; 100 friend type fieldPtr(access_refcount); 101 }; 102 103 template <typename Tag, typename Tag::type M> 104 struct Rob { fieldPtrRob105 friend typename Tag::type fieldPtr(Tag) { return M; } 106 }; 107 }; 108 109 template struct shared_ptr_internals::Rob< 110 shared_ptr_internals::access_shared_ptr, 111 &std::__shared_ptr<const void, std::_S_atomic>::_M_refcount>; 112 template struct shared_ptr_internals::Rob< 113 shared_ptr_internals::access_base, 114 &shared_ptr_internals::shared_count::_M_pi>; 115 template struct shared_ptr_internals::Rob< 116 shared_ptr_internals::access_use_count, 117 &shared_ptr_internals::counted_base::_M_use_count>; 118 template struct shared_ptr_internals::Rob< 119 shared_ptr_internals::access_weak_count, 120 &shared_ptr_internals::counted_base::_M_weak_count>; 121 template struct shared_ptr_internals::Rob< 122 shared_ptr_internals::access_counted_ptr_ptr, 123 &std::_Sp_counted_ptr<const void*, std::_S_atomic>::_M_ptr>; 124 template struct shared_ptr_internals::Rob< 125 shared_ptr_internals::access_shared_ptr_ptr, 126 &std::__shared_ptr<const void, std::_S_atomic>::_M_ptr>; 127 template struct shared_ptr_internals::Rob< 128 shared_ptr_internals::access_refcount, 129 &std::__shared_ptr<const void, std::_S_atomic>::_M_refcount>; 130 131 template <typename T> 132 inline shared_ptr_internals::counted_base* 133 shared_ptr_internals::get_counted_base(const std::shared_ptr<T>& bar) { 134 // reinterpret_pointer_cast<const void> 135 // Not quite C++ legal, but explicit template instantiation access to 136 // private members requires full type name (i.e. shared_ptr<const void>, not 137 // shared_ptr<T>) 138 const std::shared_ptr<const void>& ptr( 139 reinterpret_cast<const std::shared_ptr<const void>&>(bar)); 140 return (ptr.*fieldPtr(access_shared_ptr{})).*fieldPtr(access_base{}); 141 } 142 143 inline void shared_ptr_internals::inc_shared_count( 144 counted_base* base, long count) { 145 // Check that we don't exceed the maximum number of atomic_shared_ptrs. 146 // Consider setting EXTERNAL_COUNT lower if this CHECK is hit. 147 FOLLY_SAFE_CHECK( 148 base->_M_get_use_count() + count < INT_MAX, "atomic_shared_ptr overflow"); 149 __gnu_cxx::__atomic_add_dispatch( 150 &(base->*fieldPtr(access_use_count{})), count); 151 } 152 153 template <typename T> 154 inline void shared_ptr_internals::release_shared( 155 counted_base* base, long count) { 156 // If count == 1, this is equivalent to base->_M_release() 157 if (__gnu_cxx::__exchange_and_add_dispatch( 158 &(base->*fieldPtr(access_use_count{})), -count) == count) { 159 base->_M_dispose(); 160 161 if (__gnu_cxx::__exchange_and_add_dispatch( 162 &(base->*fieldPtr(access_weak_count{})), -1) == 1) { 163 base->_M_destroy(); 164 } 165 } 166 } 167 168 template <typename T> 169 inline T* shared_ptr_internals::get_shared_ptr(counted_base* base) { 170 // See if this was a make_shared allocation 171 auto inplace = base->_M_get_deleter(typeid(std::_Sp_make_shared_tag)); 172 if (inplace) { 173 return (T*)inplace; 174 } 175 // Could also be a _Sp_counted_deleter, but the layout is the same 176 using derived_type = std::_Sp_counted_ptr<const void*, std::_S_atomic>; 177 auto ptr = reinterpret_cast<derived_type*>(base); 178 return (T*)(ptr->*fieldPtr(access_counted_ptr_ptr{})); 179 } 180 181 template <typename T> 182 inline T* shared_ptr_internals::release_ptr(std::shared_ptr<T>& p) { 183 auto res = p.get(); 184 std::shared_ptr<const void>& ptr( 185 reinterpret_cast<std::shared_ptr<const void>&>(p)); 186 ptr.*fieldPtr(access_shared_ptr_ptr{}) = nullptr; 187 (ptr.*fieldPtr(access_refcount{})).*fieldPtr(access_base{}) = nullptr; 188 return res; 189 } 190 191 template <typename T> 192 inline std::shared_ptr<T> 193 shared_ptr_internals::get_shared_ptr_from_counted_base( 194 counted_base* base, bool inc) { 195 if (!base) { 196 return nullptr; 197 } 198 std::shared_ptr<const void> newp; 199 if (inc) { 200 inc_shared_count(base, 1); 201 } 202 newp.*fieldPtr(access_shared_ptr_ptr{}) = 203 get_shared_ptr<const void>(base); // _M_ptr 204 (newp.*fieldPtr(access_refcount{})).*fieldPtr(access_base{}) = base; 205 // reinterpret_pointer_cast<T> 206 auto res = reinterpret_cast<std::shared_ptr<T>*>(&newp); 207 return std::move(*res); 208 } 209 210 } // namespace detail 211 } // namespace folly 212