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