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 BASE_NO_DESTRUCTOR_H_ 6 #define BASE_NO_DESTRUCTOR_H_ 7 8 #include <new> 9 #include <utility> 10 11 namespace base { 12 13 // A wrapper that makes it easy to create an object of type T with static 14 // storage duration that: 15 // - is only constructed on first access 16 // - never invokes the destructor 17 // in order to satisfy the styleguide ban on global constructors and 18 // destructors. 19 // 20 // Runtime constant example: 21 // const std::string& GetLineSeparator() { 22 // // Forwards to std::string(size_t, char, const Allocator&) constructor. 23 // static const base::NoDestructor<std::string> s(5, '-'); 24 // return *s; 25 // } 26 // 27 // More complex initialization with a lambda: 28 // const std::string& GetSessionNonce() { 29 // static const base::NoDestructor<std::string> nonce([] { 30 // std::string s(16); 31 // crypto::RandString(s.data(), s.size()); 32 // return s; 33 // }()); 34 // return *nonce; 35 // } 36 // 37 // NoDestructor<T> stores the object inline, so it also avoids a pointer 38 // indirection and a malloc. Also note that since C++11 static local variable 39 // initialization is thread-safe and so is this pattern. Code should prefer to 40 // use NoDestructor<T> over: 41 // - The CR_DEFINE_STATIC_LOCAL() helper macro. 42 // - A function scoped static T* or T& that is dynamically initialized. 43 // - A global base::LazyInstance<T>. 44 // 45 // Note that since the destructor is never run, this *will* leak memory if used 46 // as a stack or member variable. Furthermore, a NoDestructor<T> should never 47 // have global scope as that may require a static initializer. 48 template <typename T> 49 class NoDestructor { 50 public: 51 // Not constexpr; just write static constexpr T x = ...; if the value should 52 // be a constexpr. 53 template <typename... Args> NoDestructor(Args &&...args)54 explicit NoDestructor(Args&&... args) { 55 new (storage_) T(std::forward<Args>(args)...); 56 } 57 58 // Allows copy and move construction of the contained type, to allow 59 // construction from an initializer list, e.g. for std::vector. NoDestructor(const T & x)60 explicit NoDestructor(const T& x) { new (storage_) T(x); } NoDestructor(T && x)61 explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); } 62 63 NoDestructor(const NoDestructor&) = delete; 64 NoDestructor& operator=(const NoDestructor&) = delete; 65 66 ~NoDestructor() = default; 67 68 const T& operator*() const { return *get(); } 69 T& operator*() { return *get(); } 70 71 const T* operator->() const { return get(); } 72 T* operator->() { return get(); } 73 get()74 const T* get() const { return reinterpret_cast<const T*>(storage_); } get()75 T* get() { return reinterpret_cast<T*>(storage_); } 76 77 private: 78 alignas(T) char storage_[sizeof(T)]; 79 80 #if defined(LEAK_SANITIZER) 81 // TODO(https://crbug.com/812277): This is a hack to work around the fact 82 // that LSan doesn't seem to treat NoDestructor as a root for reachability 83 // analysis. This means that code like this: 84 // static base::NoDestructor<std::vector<int>> v({1, 2, 3}); 85 // is considered a leak. Using the standard leak sanitizer annotations to 86 // suppress leaks doesn't work: std::vector is implicitly constructed before 87 // calling the base::NoDestructor constructor. 88 // 89 // Unfortunately, I haven't been able to demonstrate this issue in simpler 90 // reproductions: until that's resolved, hold an explicit pointer to the 91 // placement-new'd object in leak sanitizer mode to help LSan realize that 92 // objects allocated by the contained type are still reachable. 93 T* storage_ptr_ = reinterpret_cast<T*>(storage_); 94 #endif // defined(LEAK_SANITIZER) 95 }; 96 97 } // namespace base 98 99 #endif // BASE_NO_DESTRUCTOR_H_ 100