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