| /* | |
| * Copyright 2018 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #ifndef SkNoDestructor_DEFINED | |
| #define SkNoDestructor_DEFINED | |
| #include <cstddef> | |
| #include <new> | |
| #include <type_traits> // IWYU pragma: keep | |
| #include <utility> | |
| // Helper type to create a function-local static variable of type `T` when `T` | |
| // has a non-trivial destructor. Storing a `T` in a `SkNoDestructor<T>` will | |
| // prevent `~T()` from running, even when the variable goes out of scope. This | |
| // code is adapted from `base::NoDestructor<T>` in Chromium. | |
| // | |
| // Useful when a variable has static storage duration but its type has a | |
| // non-trivial destructor. Chromium (and transitively, Skia) bans global | |
| // constructors and destructors: using a function-local static variable prevents | |
| // the former, while using `SkNoDestructor<T>` prevents the latter. | |
| // | |
| // ## Caveats | |
| // | |
| // - Must not be used for locals or fields; by definition, this does not run | |
| // destructors, and this will likely lead to memory leaks and other | |
| // surprising and undesirable behaviour. | |
| // | |
| // - If `T` is not constexpr constructible, must be a function-local static | |
| // variable, since a global `NoDestructor<T>` will still generate a static | |
| // initializer. | |
| // | |
| // - If `T` is constinit constructible, may be used as a global, but mark the | |
| // global `constinit` (once C++20 is available) | |
| // | |
| // - If the data is rarely used, consider creating it on demand rather than | |
| // caching it for the lifetime of the program. Though `SkNoDestructor<T>` | |
| // does not heap allocate, the compiler still reserves space in bss for | |
| // storing `T`, which costs memory at runtime. | |
| // | |
| // - If `T` is trivially destructible, do not use `SkNoDestructor<T>`: | |
| // | |
| // const uint64_t GetUnstableSessionSeed() { | |
| // // No need to use `SkNoDestructor<T>` as `uint64_t` is trivially | |
| // // destructible and does not require a global destructor. | |
| // static const uint64_t kSessionSeed = GetRandUint64(); | |
| // return kSessionSeed; | |
| // } | |
| // | |
| // ## Example Usage | |
| // | |
| // const std::string& GetDefaultText() { | |
| // // Required since `static const std::string` requires a global destructor. | |
| // static const SkNoDestructor<std::string> s("Hello world!"); | |
| // return *s; | |
| // } | |
| // | |
| // More complex initialization using a lambda: | |
| // | |
| // const std::string& GetRandomNonce() { | |
| // // `nonce` is initialized with random data the first time this function is | |
| // // called, but its value is fixed thereafter. | |
| // static const SkNoDestructor<std::string> nonce([] { | |
| // std::string s(16); | |
| // GetRandString(s.data(), s.size()); | |
| // return s; | |
| // }()); | |
| // return *nonce; | |
| // } | |
| // | |
| // ## Thread safety | |
| // | |
| // Initialization of function-local static variables is thread-safe since C++11. | |
| // The standard guarantees that: | |
| // | |
| // - function-local static variables will be initialised the first time | |
| // execution passes through the declaration. | |
| // | |
| // - if another thread's execution concurrently passes through the declaration | |
| // in the middle of initialisation, that thread will wait for the in-progress | |
| // initialisation to complete. | |
| template <typename T> class SkNoDestructor { | |
| public: | |
| static_assert(!(std::is_trivially_constructible_v<T> && std::is_trivially_destructible_v<T>), | |
| "T is trivially constructible and destructible; please use a constinit object of " | |
| "type T directly instead"); | |
| static_assert(!std::is_trivially_destructible_v<T>, | |
| "T is trivially destructible; please use a function-local static of type T " | |
| "directly instead"); | |
| // Not constexpr; just write static constexpr T x = ...; if the value should be a constexpr. | |
| template <typename... Args> explicit SkNoDestructor(Args&&... args) { | |
| new (fStorage) T(std::forward<Args>(args)...); | |
| } | |
| // Allows copy and move construction of the contained type, to allow construction from an | |
| // initializer list, e.g. for std::vector. | |
| explicit SkNoDestructor(const T& x) { new (fStorage) T(x); } | |
| explicit SkNoDestructor(T&& x) { new (fStorage) T(std::move(x)); } | |
| SkNoDestructor(const SkNoDestructor&) = delete; | |
| SkNoDestructor& operator=(const SkNoDestructor&) = delete; | |
| ~SkNoDestructor() = default; | |
| const T& operator*() const { return *get(); } | |
| T& operator*() { return *get(); } | |
| const T* operator->() const { return get(); } | |
| T* operator->() { return get(); } | |
| const T* get() const { return reinterpret_cast<const T*>(fStorage); } | |
| T* get() { return reinterpret_cast<T*>(fStorage); } | |
| private: | |
| alignas(T) std::byte fStorage[sizeof(T)]; | |
| #if defined(__clang__) && defined(__has_feature) | |
| #if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer) | |
| // TODO(https://crbug.com/812277): This is a hack to work around the fact that LSan doesn't seem | |
| // to treat SkNoDestructor as a root for reachability analysis. This means that code like this: | |
| // static SkNoDestructor<std::vector<int>> v({1, 2, 3}); | |
| // is considered a leak. Using the standard leak sanitizer annotations to suppress leaks doesn't | |
| // work: std::vector is implicitly constructed before calling the SkNoDestructor constructor. | |
| // | |
| // Unfortunately, I haven't been able to demonstrate this issue in simpler reproductions: until | |
| // that's resolved, hold an explicit pointer to the placement-new'd object in leak sanitizer | |
| // mode to help LSan realize that objects allocated by the contained type are still reachable. | |
| T* fStoragePtr = reinterpret_cast<T*>(fStorage); | |
| #endif // leak_sanitizer/address_sanitizer | |
| #endif // __has_feature | |
| }; | |
| #endif // SkNoDestructor_DEFINED |