no_destructor.h 3.5 KB

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