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
38namespace 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
72template <
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>>
78class 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
230template <typename T, typename Tag, typename Make, typename TLTag>
231detail::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