1 | /* |
2 | * Copyright 2016-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 | |
17 | #pragma once |
18 | |
19 | #include <typeinfo> |
20 | |
21 | #include <boost/intrusive/list.hpp> |
22 | |
23 | #include <folly/ScopeGuard.h> |
24 | #include <folly/ThreadLocal.h> |
25 | #include <folly/detail/Iterators.h> |
26 | #include <folly/detail/Singleton.h> |
27 | #include <folly/functional/Invoke.h> |
28 | |
29 | namespace folly { |
30 | |
31 | namespace detail { |
32 | |
33 | class SingletonThreadLocalBase { |
34 | public: |
35 | class UniqueBase { |
36 | public: |
37 | using Ptr = std::type_info const*; |
38 | using Ref = std::type_info const&; |
39 | struct Value { |
40 | bool init; |
41 | Ptr make; |
42 | Ptr tltag; |
43 | }; |
44 | |
45 | template <typename T, typename Tag, typename Make, typename TLTag> |
46 | explicit UniqueBase(TypeTuple<T, Tag, Make, TLTag>) |
47 | : UniqueBase( |
48 | typeid(T), |
49 | typeid(Tag), |
50 | typeid(Make), |
51 | typeid(TLTag), |
52 | detail::createGlobal<Value, TypeTuple<T, Tag, UniqueBase>>()) {} |
53 | |
54 | UniqueBase(Ref type, Ref tag, Ref make, Ref tltag, Value& value); |
55 | }; |
56 | }; |
57 | |
58 | } // namespace detail |
59 | |
60 | /// SingletonThreadLocal |
61 | /// |
62 | /// Useful for a per-thread leaky-singleton model in libraries and applications. |
63 | /// |
64 | /// By "leaky" it is meant that the T instances held by the instantiation |
65 | /// SingletonThreadLocal<T> will survive until their owning thread exits. |
66 | /// Therefore, they can safely be used before main() begins and after main() |
67 | /// ends, and they can also safely be used in an application that spawns many |
68 | /// temporary threads throughout its life. |
69 | /// |
70 | /// Example: |
71 | /// |
72 | /// struct UsefulButHasExpensiveCtor { |
73 | /// UsefulButHasExpensiveCtor(); // this is expensive |
74 | /// Result operator()(Arg arg); |
75 | /// }; |
76 | /// |
77 | /// Result useful(Arg arg) { |
78 | /// using Useful = UsefulButHasExpensiveCtor; |
79 | /// auto& useful = folly::SingletonThreadLocal<Useful>::get(); |
80 | /// return useful(arg); |
81 | /// } |
82 | /// |
83 | /// As an example use-case, the random generators in <random> are expensive to |
84 | /// construct. And their constructors are deterministic, but many cases require |
85 | /// that they be randomly seeded. So folly::Random makes good canonical uses of |
86 | /// folly::SingletonThreadLocal so that a seed is computed from the secure |
87 | /// random device once per thread, and the random generator is constructed with |
88 | /// the seed once per thread. |
89 | /// |
90 | /// Keywords to help people find this class in search: |
91 | /// Thread Local Singleton ThreadLocalSingleton |
92 | template < |
93 | typename T, |
94 | typename Tag = detail::DefaultTag, |
95 | typename Make = detail::DefaultMake<T>, |
96 | typename TLTag = std:: |
97 | conditional_t<std::is_same<Tag, detail::DefaultTag>::value, void, Tag>> |
98 | class SingletonThreadLocal : private detail::SingletonThreadLocalBase { |
99 | private: |
100 | struct Unique final : UniqueBase { |
101 | Unique() : UniqueBase(detail::TypeTuple<T, Tag, Make, TLTag>{}) {} |
102 | }; |
103 | static Unique unique; |
104 | |
105 | struct Wrapper; |
106 | |
107 | using NodeBase = boost::intrusive::list_base_hook< |
108 | boost::intrusive::link_mode<boost::intrusive::auto_unlink>>; |
109 | |
110 | struct Node : NodeBase { |
111 | Wrapper*& cache; |
112 | bool& stale; |
113 | |
114 | Node(Wrapper*& cache_, bool& stale_) : cache(cache_), stale(stale_) { |
115 | auto& wrapper = getWrapper(); |
116 | wrapper.caches.push_front(*this); |
117 | cache = &wrapper; |
118 | } |
119 | ~Node() { |
120 | clear(); |
121 | } |
122 | |
123 | void clear() { |
124 | cache = nullptr; |
125 | stale = true; |
126 | } |
127 | }; |
128 | |
129 | using List = |
130 | boost::intrusive::list<Node, boost::intrusive::constant_time_size<false>>; |
131 | |
132 | struct Wrapper { |
133 | using Object = invoke_result_t<Make>; |
134 | static_assert(std::is_convertible<Object&, T&>::value, "inconvertible" ); |
135 | |
136 | // keep as first field, to save 1 instr in the fast path |
137 | Object object{Make{}()}; |
138 | List caches; |
139 | |
140 | /* implicit */ operator T&() { |
141 | return object; |
142 | } |
143 | |
144 | ~Wrapper() { |
145 | for (auto& node : caches) { |
146 | node.clear(); |
147 | } |
148 | caches.clear(); |
149 | } |
150 | }; |
151 | |
152 | using WrapperTL = ThreadLocal<Wrapper, TLTag>; |
153 | |
154 | SingletonThreadLocal() = delete; |
155 | |
156 | FOLLY_ALWAYS_INLINE static WrapperTL& getWrapperTL() { |
157 | return detail::createGlobal<WrapperTL, Tag>(); |
158 | } |
159 | |
160 | FOLLY_NOINLINE static Wrapper& getWrapper() { |
161 | return *getWrapperTL(); |
162 | } |
163 | |
164 | #ifdef FOLLY_TLS |
165 | FOLLY_NOINLINE static T& getSlow(Wrapper*& cache) { |
166 | (void)unique; // force the object not to be thrown out as unused |
167 | static thread_local Wrapper** check = &cache; |
168 | CHECK_EQ(check, &cache) << "inline function static thread_local merging" ; |
169 | static thread_local bool stale; |
170 | static thread_local Node node(cache, stale); |
171 | return !stale && node.cache ? *node.cache : getWrapper(); |
172 | } |
173 | #endif |
174 | |
175 | public: |
176 | FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& get() { |
177 | #ifdef FOLLY_TLS |
178 | static thread_local Wrapper* cache; |
179 | return FOLLY_LIKELY(!!cache) ? *cache : getSlow(cache); |
180 | #else |
181 | return getWrapper(); |
182 | #endif |
183 | } |
184 | |
185 | class Accessor { |
186 | private: |
187 | using Inner = typename WrapperTL::Accessor; |
188 | using IteratorBase = typename Inner::Iterator; |
189 | using IteratorTag = std::bidirectional_iterator_tag; |
190 | |
191 | Inner inner_; |
192 | |
193 | explicit Accessor(Inner inner) noexcept : inner_(std::move(inner)) {} |
194 | |
195 | public: |
196 | friend class SingletonThreadLocal<T, Tag, Make, TLTag>; |
197 | |
198 | class Iterator |
199 | : public detail:: |
200 | IteratorAdaptor<Iterator, IteratorBase, T, IteratorTag> { |
201 | private: |
202 | using Super = |
203 | detail::IteratorAdaptor<Iterator, IteratorBase, T, IteratorTag>; |
204 | using Super::Super; |
205 | |
206 | public: |
207 | friend class Accessor; |
208 | |
209 | T& dereference() const { |
210 | return const_cast<Iterator*>(this)->base()->object; |
211 | } |
212 | }; |
213 | |
214 | Accessor(const Accessor&) = delete; |
215 | Accessor& operator=(const Accessor&) = delete; |
216 | Accessor(Accessor&&) = default; |
217 | Accessor& operator=(Accessor&&) = default; |
218 | |
219 | Iterator begin() const { |
220 | return Iterator(inner_.begin()); |
221 | } |
222 | |
223 | Iterator end() const { |
224 | return Iterator(inner_.end()); |
225 | } |
226 | }; |
227 | |
228 | // Must use a unique Tag, takes a lock that is one per Tag |
229 | static Accessor accessAllThreads() { |
230 | return Accessor(getWrapperTL().accessAllThreads()); |
231 | } |
232 | }; |
233 | |
234 | template <typename T, typename Tag, typename Make, typename TLTag> |
235 | typename SingletonThreadLocal<T, Tag, Make, TLTag>::Unique |
236 | SingletonThreadLocal<T, Tag, Make, TLTag>::unique; |
237 | |
238 | } // namespace folly |
239 | |
240 | /// FOLLY_DECLARE_REUSED |
241 | /// |
242 | /// Useful for local variables of container types, where it is desired to avoid |
243 | /// the overhead associated with the local variable entering and leaving scope. |
244 | /// Rather, where it is desired that the memory be reused between invocations |
245 | /// of the same scope in the same thread rather than deallocated and reallocated |
246 | /// between invocations of the same scope in the same thread. Note that the |
247 | /// container will always be cleared between invocations; it is only the backing |
248 | /// memory allocation which is reused. |
249 | /// |
250 | /// Example: |
251 | /// |
252 | /// void traverse_perform(int root); |
253 | /// template <typename F> |
254 | /// void traverse_each_child_r(int root, F const&); |
255 | /// void traverse_depthwise(int root) { |
256 | /// // preserves some of the memory backing these per-thread data structures |
257 | /// FOLLY_DECLARE_REUSED(seen, std::unordered_set<int>); |
258 | /// FOLLY_DECLARE_REUSED(work, std::vector<int>); |
259 | /// // example algorithm that uses these per-thread data structures |
260 | /// work.push_back(root); |
261 | /// while (!work.empty()) { |
262 | /// root = work.back(); |
263 | /// work.pop_back(); |
264 | /// seen.insert(root); |
265 | /// traverse_perform(root); |
266 | /// traverse_each_child_r(root, [&](int item) { |
267 | /// if (!seen.count(item)) { |
268 | /// work.push_back(item); |
269 | /// } |
270 | /// }); |
271 | /// } |
272 | /// } |
273 | #define FOLLY_DECLARE_REUSED(name, ...) \ |
274 | struct __folly_reused_type_##name { \ |
275 | __VA_ARGS__ object; \ |
276 | }; \ |
277 | auto& name = \ |
278 | ::folly::SingletonThreadLocal<__folly_reused_type_##name>::get().object; \ |
279 | auto __folly_reused_g_##name = ::folly::makeGuard([&] { name.clear(); }) |
280 | |