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