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