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