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