Add a check if max atomic_shared_ptrs is reached.
[folly.git] / folly / concurrency / detail / AtomicSharedPtr-detail.h
1 /*
2  * Copyright 2017-present Facebook, Inc.
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 #pragma once
17 #include <atomic>
18 #include <memory>
19
20 #include <folly/lang/SafeAssert.h>
21
22 namespace folly {
23 namespace detail {
24
25 // This implementation is specific to libstdc++, now accepting
26 // diffs for other libraries.
27
28 // Specifically, this adds support for two things:
29 // 1) incrementing/decrementing the shared count by more than 1 at a time
30 // 2) Getting the thing the shared_ptr points to, which may be different from
31 //    the aliased pointer.
32
33 class shared_ptr_internals {
34  public:
35   template <typename T, typename... Args>
36   static std::shared_ptr<T> make_ptr(Args&&... args) {
37     return std::make_shared<T>(std::forward<Args...>(args...));
38   }
39   typedef std::__shared_count<std::_S_atomic> shared_count;
40   typedef std::_Sp_counted_base<std::_S_atomic> counted_base;
41   template <typename T>
42   using CountedPtr = std::shared_ptr<T>;
43   template <typename T>
44   static counted_base* get_counted_base(const std::shared_ptr<T>& bar) {
45     // reinterpret_pointer_cast<const void>
46     // Not quite C++ legal, but explicit template instantiation access to
47     // private members requires full type name (i.e. shared_ptr<const void>, not
48     // shared_ptr<T>)
49     const std::shared_ptr<const void>& ptr(
50         reinterpret_cast<const std::shared_ptr<const void>&>(bar));
51     return (ptr.*fieldPtr(access_shared_ptr{})).*fieldPtr(access_base{});
52   }
53
54   static void inc_shared_count(counted_base* base, long count) {
55     // Check that we don't exceed the maximum number of atomic_shared_ptrs.
56     // Consider setting EXTERNAL_COUNT lower if this CHECK is hit.
57     FOLLY_SAFE_CHECK(
58         base->_M_get_use_count() + count < INT_MAX,
59         "atomic_shared_ptr overflow");
60     __gnu_cxx::__atomic_add_dispatch(
61         &(base->*fieldPtr(access_use_count{})), count);
62   }
63
64   template <typename T>
65   static void release_shared(counted_base* base, long count) {
66     // If count == 1, this is equivalent to base->_M_release()
67     if (__gnu_cxx::__exchange_and_add_dispatch(
68             &(base->*fieldPtr(access_use_count{})), -count) == count) {
69       base->_M_dispose();
70
71       if (__gnu_cxx::__exchange_and_add_dispatch(
72               &(base->*fieldPtr(access_weak_count{})), -1) == 1) {
73         base->_M_destroy();
74       }
75     }
76   }
77
78   template <typename T>
79   static T* get_shared_ptr(counted_base* base) {
80     // See if this was a make_shared allocation
81     auto inplace = base->_M_get_deleter(typeid(std::_Sp_make_shared_tag));
82     if (inplace) {
83       return (T*)inplace;
84     }
85     // Could also be a _Sp_counted_deleter, but the layout is the same
86     using derived_type = std::_Sp_counted_ptr<const void*, std::_S_atomic>;
87     auto ptr = reinterpret_cast<derived_type*>(base);
88     return (T*)(ptr->*fieldPtr(access_counted_ptr_ptr{}));
89   }
90
91   template <typename T>
92   static T* release_ptr(std::shared_ptr<T>& p) {
93     auto res = p.get();
94     std::shared_ptr<const void>& ptr(
95         reinterpret_cast<std::shared_ptr<const void>&>(p));
96     ptr.*fieldPtr(access_shared_ptr_ptr{}) = nullptr;
97     (ptr.*fieldPtr(access_refcount{})).*fieldPtr(access_base{}) = nullptr;
98     return res;
99   }
100
101   template <typename T>
102   static std::shared_ptr<T> get_shared_ptr_from_counted_base(
103       counted_base* base,
104       bool inc = true) {
105     if (!base) {
106       return nullptr;
107     }
108     std::shared_ptr<const void> newp;
109     if (inc) {
110       inc_shared_count(base, 1);
111     }
112     newp.*fieldPtr(access_shared_ptr_ptr{}) =
113         get_shared_ptr<const void>(base); // _M_ptr
114     (newp.*fieldPtr(access_refcount{})).*fieldPtr(access_base{}) = base;
115     // reinterpret_pointer_cast<T>
116     auto res = reinterpret_cast<std::shared_ptr<T>*>(&newp);
117     return std::move(*res);
118   }
119
120  private:
121   /* Accessors for private members using explicit template instantiation */
122   struct access_shared_ptr {
123     typedef shared_count std::__shared_ptr<const void, std::_S_atomic>::*type;
124     friend type fieldPtr(access_shared_ptr);
125   };
126
127   struct access_base {
128     typedef counted_base* shared_count::*type;
129     friend type fieldPtr(access_base);
130   };
131
132   struct access_use_count {
133     typedef _Atomic_word counted_base::*type;
134     friend type fieldPtr(access_use_count);
135   };
136
137   struct access_weak_count {
138     typedef _Atomic_word counted_base::*type;
139     friend type fieldPtr(access_weak_count);
140   };
141
142   struct access_counted_ptr_ptr {
143     typedef const void* std::_Sp_counted_ptr<const void*, std::_S_atomic>::*
144         type;
145     friend type fieldPtr(access_counted_ptr_ptr);
146   };
147
148   struct access_shared_ptr_ptr {
149     typedef const void* std::__shared_ptr<const void, std::_S_atomic>::*type;
150     friend type fieldPtr(access_shared_ptr_ptr);
151   };
152
153   struct access_refcount {
154     typedef shared_count std::__shared_ptr<const void, std::_S_atomic>::*type;
155     friend type fieldPtr(access_refcount);
156   };
157
158   template <typename Tag, typename Tag::type M>
159   struct Rob {
160     friend typename Tag::type fieldPtr(Tag) {
161       return M;
162     }
163   };
164 };
165
166 template struct shared_ptr_internals::Rob<
167     shared_ptr_internals::access_shared_ptr,
168     &std::__shared_ptr<const void, std::_S_atomic>::_M_refcount>;
169 template struct shared_ptr_internals::Rob<
170     shared_ptr_internals::access_base,
171     &shared_ptr_internals::shared_count::_M_pi>;
172 template struct shared_ptr_internals::Rob<
173     shared_ptr_internals::access_use_count,
174     &shared_ptr_internals::counted_base::_M_use_count>;
175 template struct shared_ptr_internals::Rob<
176     shared_ptr_internals::access_weak_count,
177     &shared_ptr_internals::counted_base::_M_weak_count>;
178 template struct shared_ptr_internals::Rob<
179     shared_ptr_internals::access_counted_ptr_ptr,
180     &std::_Sp_counted_ptr<const void*, std::_S_atomic>::_M_ptr>;
181 template struct shared_ptr_internals::Rob<
182     shared_ptr_internals::access_shared_ptr_ptr,
183     &std::__shared_ptr<const void, std::_S_atomic>::_M_ptr>;
184 template struct shared_ptr_internals::Rob<
185     shared_ptr_internals::access_refcount,
186     &std::__shared_ptr<const void, std::_S_atomic>::_M_refcount>;
187
188 } // namespace detail
189 } // namespace folly