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 <boost/intrusive/list.hpp> 20 21 #include <folly/ScopeGuard.h> 22 #include <folly/ThreadLocal.h> 23 #include <folly/detail/Singleton.h> 24 #include <folly/functional/Invoke.h> 25 26 namespace folly { 27 28 /// SingletonThreadLocal 29 /// 30 /// Useful for a per-thread leaky-singleton model in libraries and applications. 31 /// 32 /// By "leaky" it is meant that the T instances held by the instantiation 33 /// SingletonThreadLocal<T> will survive until their owning thread exits. 34 /// Therefore, they can safely be used before main() begins and after main() 35 /// ends, and they can also safely be used in an application that spawns many 36 /// temporary threads throughout its life. 37 /// 38 /// Example: 39 /// 40 /// struct UsefulButHasExpensiveCtor { 41 /// UsefulButHasExpensiveCtor(); // this is expensive 42 /// Result operator()(Arg arg); 43 /// }; 44 /// 45 /// Result useful(Arg arg) { 46 /// using Useful = UsefulButHasExpensiveCtor; 47 /// auto& useful = folly::SingletonThreadLocal<Useful>::get(); 48 /// return useful(arg); 49 /// } 50 /// 51 /// As an example use-case, the random generators in <random> are expensive to 52 /// construct. And their constructors are deterministic, but many cases require 53 /// that they be randomly seeded. So folly::Random makes good canonical uses of 54 /// folly::SingletonThreadLocal so that a seed is computed from the secure 55 /// random device once per thread, and the random generator is constructed with 56 /// the seed once per thread. 57 /// 58 /// Keywords to help people find this class in search: 59 /// Thread Local Singleton ThreadLocalSingleton 60 template < 61 typename T, 62 typename Tag = detail::DefaultTag, 63 typename Make = detail::DefaultMake<T>, 64 typename TLTag = _t<std::conditional< 65 std::is_same<Tag, detail::DefaultTag>::value, 66 void, 67 Tag>>> 68 class SingletonThreadLocal { 69 private: 70 struct Wrapper; 71 72 using NodeBase = boost::intrusive::list_base_hook< 73 boost::intrusive::link_mode<boost::intrusive::auto_unlink>>; 74 75 struct Node : NodeBase { 76 Wrapper*& cache; 77 bool& stale; 78 NodeNode79 Node(Wrapper*& cache_, bool& stale_) : cache(cache_), stale(stale_) { 80 auto& wrapper = getWrapper(); 81 wrapper.caches.push_front(*this); 82 cache = &wrapper; 83 } ~NodeNode84 ~Node() { 85 clear(); 86 } 87 clearNode88 void clear() { 89 cache = nullptr; 90 stale = true; 91 } 92 }; 93 94 using List = 95 boost::intrusive::list<Node, boost::intrusive::constant_time_size<false>>; 96 97 struct Wrapper { 98 template <typename S> 99 using MakeRet = is_invocable_r<S, Make>; 100 101 // keep as first field, to save 1 instr in the fast path 102 union { 103 alignas(alignof(T)) unsigned char storage[sizeof(T)]; 104 T object; 105 }; 106 List caches; 107 108 /* implicit */ operator T&() { 109 return object; 110 } 111 112 // normal make types 113 template <typename S = T, _t<std::enable_if<MakeRet<S>::value, int>> = 0> WrapperWrapper114 Wrapper() { 115 (void)new (storage) S(Make{}()); 116 } 117 // default and special make types for non-move-constructible T, until C++17 118 template <typename S = T, _t<std::enable_if<!MakeRet<S>::value, int>> = 0> WrapperWrapper119 Wrapper() { 120 (void)Make{}(storage); 121 } ~WrapperWrapper122 ~Wrapper() { 123 for (auto& node : caches) { 124 node.clear(); 125 } 126 caches.clear(); 127 object.~T(); 128 } 129 }; 130 131 using WrapperTL = ThreadLocal<Wrapper, TLTag>; 132 133 SingletonThreadLocal() = delete; 134 getWrapperTL()135 FOLLY_EXPORT FOLLY_NOINLINE static WrapperTL& getWrapperTL() { 136 static auto& entry = *detail::createGlobal<WrapperTL, Tag>(); 137 return entry; 138 } 139 getWrapper()140 FOLLY_NOINLINE static Wrapper& getWrapper() { 141 return *getWrapperTL(); 142 } 143 144 #ifdef FOLLY_TLS getSlow(Wrapper * & cache)145 FOLLY_NOINLINE static T& getSlow(Wrapper*& cache) { 146 static thread_local Wrapper** check = &cache; 147 CHECK_EQ(check, &cache) << "inline function static thread_local merging"; 148 static thread_local bool stale; 149 static thread_local Node node(cache, stale); 150 return !stale && node.cache ? *node.cache : getWrapper(); 151 } 152 #endif 153 154 public: get()155 FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& get() { 156 #ifdef FOLLY_TLS 157 static thread_local Wrapper* cache; 158 return FOLLY_LIKELY(!!cache) ? *cache : getSlow(cache); 159 #else 160 return getWrapper(); 161 #endif 162 } 163 164 // Must use a unique Tag, takes a lock that is one per Tag accessAllThreads()165 static typename WrapperTL::Accessor accessAllThreads() { 166 return getWrapperTL().accessAllThreads(); 167 } 168 }; 169 170 } // namespace folly 171 172 /// FOLLY_DECLARE_REUSED 173 /// 174 /// Useful for local variables of container types, where it is desired to avoid 175 /// the overhead associated with the local variable entering and leaving scope. 176 /// Rather, where it is desired that the memory be reused between invocations 177 /// of the same scope in the same thread rather than deallocated and reallocated 178 /// between invocations of the same scope in the same thread. Note that the 179 /// container will always be cleared between invocations; it is only the backing 180 /// memory allocation which is reused. 181 /// 182 /// Example: 183 /// 184 /// void traverse_perform(int root); 185 /// template <typename F> 186 /// void traverse_each_child_r(int root, F const&); 187 /// void traverse_depthwise(int root) { 188 /// // preserves some of the memory backing these per-thread data structures 189 /// FOLLY_DECLARE_REUSED(seen, std::unordered_set<int>); 190 /// FOLLY_DECLARE_REUSED(work, std::vector<int>); 191 /// // example algorithm that uses these per-thread data structures 192 /// work.push_back(root); 193 /// while (!work.empty()) { 194 /// root = work.back(); 195 /// work.pop_back(); 196 /// seen.insert(root); 197 /// traverse_perform(root); 198 /// traverse_each_child_r(root, [&](int item) { 199 /// if (!seen.count(item)) { 200 /// work.push_back(item); 201 /// } 202 /// }); 203 /// } 204 /// } 205 #define FOLLY_DECLARE_REUSED(name, ...) \ 206 struct __folly_reused_type_##name { \ 207 __VA_ARGS__ object; \ 208 }; \ 209 auto& name = \ 210 ::folly::SingletonThreadLocal<__folly_reused_type_##name>::get().object; \ 211 auto __folly_reused_g_##name = ::folly::makeGuard([&] { name.clear(); }) 212