1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef THIRD_PARTY_BASE_NO_DESTRUCTOR_H_ 6 #define THIRD_PARTY_BASE_NO_DESTRUCTOR_H_ 7 8 #include <new> 9 #include <type_traits> 10 #include <utility> 11 12 namespace pdfium { 13 namespace base { 14 15 // Helper type to create a function-local static variable of type `T` when `T` 16 // has a non-trivial destructor. Storing a `T` in a `base::NoDestructor<T>` will 17 // prevent `~T()` from running, even when the variable goes out of scope. 18 // 19 // Useful when a variable has static storage duration but its type has a 20 // non-trivial destructor. Chromium bans global constructors and destructors: 21 // using a function-local static variable prevents the former, while using 22 // `base::NoDestructor<T>` prevents the latter. 23 // 24 // ## Caveats 25 // 26 // - Must only be used as a function-local static variable. Declaring a global 27 // variable of type `base::NoDestructor<T>` will still generate a global 28 // constructor; declaring a local or member variable will lead to memory leaks 29 // or other surprising and undesirable behaviour. 30 // 31 // - If the data is rarely used, consider creating it on demand rather than 32 // caching it for the lifetime of the program. Though `base::NoDestructor<T>` 33 // does not heap allocate, the compiler still reserves space in bss for 34 // storing `T`, which costs memory at runtime. 35 // 36 // - If `T` is trivially destructible, do not use `base::NoDestructor<T>`: 37 // 38 // const uint64_t GetUnstableSessionSeed() { 39 // // No need to use `base::NoDestructor<T>` as `uint64_t` is trivially 40 // // destructible and does not require a global destructor. 41 // static const uint64_t kSessionSeed = base::RandUint64(); 42 // return kSessionSeed; 43 // } 44 // 45 // ## Example Usage 46 // 47 // const std::string& GetDefaultText() { 48 // // Required since `static const std::string` requires a global destructor. 49 // static const base::NoDestructor<std::string> s("Hello world!"); 50 // return *s; 51 // } 52 // 53 // More complex initialization using a lambda: 54 // 55 // const std::string& GetRandomNonce() { 56 // // `nonce` is initialized with random data the first time this function is 57 // // called, but its value is fixed thereafter. 58 // static const base::NoDestructor<std::string> nonce([] { 59 // std::string s(16); 60 // crypto::RandString(s.data(), s.size()); 61 // return s; 62 // }()); 63 // return *nonce; 64 // } 65 // 66 // ## Thread safety 67 // 68 // Initialisation of function-local static variables is thread-safe since C++11. 69 // The standard guarantees that: 70 // 71 // - function-local static variables will be initialised the first time 72 // execution passes through the declaration. 73 // 74 // - if another thread's execution concurrently passes through the declaration 75 // in the middle of initialisation, that thread will wait for the in-progress 76 // initialisation to complete. 77 template <typename T> 78 class NoDestructor { 79 public: 80 static_assert( 81 !std::is_trivially_destructible_v<T>, 82 "T is trivially destructible; please use a function-local static " 83 "of type T directly instead"); 84 85 // Not constexpr; just write static constexpr T x = ...; if the value should 86 // be a constexpr. 87 template <typename... Args> NoDestructor(Args &&...args)88 explicit NoDestructor(Args&&... args) { 89 new (storage_) T(std::forward<Args>(args)...); 90 } 91 92 // Allows copy and move construction of the contained type, to allow 93 // construction from an initializer list, e.g. for std::vector. NoDestructor(const T & x)94 explicit NoDestructor(const T& x) { new (storage_) T(x); } NoDestructor(T && x)95 explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); } 96 97 NoDestructor(const NoDestructor&) = delete; 98 NoDestructor& operator=(const NoDestructor&) = delete; 99 100 ~NoDestructor() = default; 101 102 const T& operator*() const { return *get(); } 103 T& operator*() { return *get(); } 104 105 const T* operator->() const { return get(); } 106 T* operator->() { return get(); } 107 get()108 const T* get() const { return reinterpret_cast<const T*>(storage_); } get()109 T* get() { return reinterpret_cast<T*>(storage_); } 110 111 private: 112 alignas(T) char storage_[sizeof(T)]; 113 114 #if defined(LEAK_SANITIZER) 115 // TODO(https://crbug.com/812277): This is a hack to work around the fact 116 // that LSan doesn't seem to treat NoDestructor as a root for reachability 117 // analysis. This means that code like this: 118 // static base::NoDestructor<std::vector<int>> v({1, 2, 3}); 119 // is considered a leak. Using the standard leak sanitizer annotations to 120 // suppress leaks doesn't work: std::vector is implicitly constructed before 121 // calling the base::NoDestructor constructor. 122 // 123 // Unfortunately, I haven't been able to demonstrate this issue in simpler 124 // reproductions: until that's resolved, hold an explicit pointer to the 125 // placement-new'd object in leak sanitizer mode to help LSan realize that 126 // objects allocated by the contained type are still reachable. 127 T* storage_ptr_ = reinterpret_cast<T*>(storage_); 128 #endif // defined(LEAK_SANITIZER) 129 }; 130 131 } // namespace base 132 } // namespace pdfium 133 134 #endif // THIRD_PARTY_BASE_NO_DESTRUCTOR_H_ 135