| // Copyright 2022 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| #pragma once |
| |
| #include <new> |
| #include <type_traits> |
| #include <utility> |
| |
| namespace pw { |
| |
| /// Helper type to create a function-local static variable of type `T` when `T` |
| /// has a non-trivial destructor. Storing a `T` in a `pw::NoDestructor<T>` will |
| /// prevent `~T()` from running, even when the variable goes out of scope. |
| /// |
| /// This class is useful when a variable has static storage duration but its |
| /// type has a non-trivial destructor. Destructor ordering is not defined and |
| /// can cause issues in multithreaded environments. Additionally, removing |
| /// destructor calls can save code size. |
| /// |
| /// Except in generic code, do not use `pw::NoDestructor<T>` with trivially |
| /// destructible types. Use the type directly instead. If the variable can be |
| /// `constexpr`, make it `constexpr`. |
| /// |
| /// `pw::NoDestructor<T>` provides a similar API to `std::optional`. Use `*` or |
| /// `->` to access the wrapped type. |
| /// |
| /// Example usage: |
| /// @code{.cpp} |
| /// |
| /// pw::sync::Mutex& GetMutex() { |
| /// // Use NoDestructor to avoid running the mutex destructor when exit-time |
| /// // destructors run. |
| /// static const pw::NoDestructor<pw::sync::Mutex> global_mutex; |
| /// return *global_mutex; |
| /// } |
| /// |
| /// @endcode |
| /// |
| /// In Clang, `pw::NoDestructor` can be replaced with the |
| /// <a href="https://clang.llvm.org/docs/AttributeReference.html#no-destroy"> |
| /// [[clang::no_destroy]]</a> attribute. `pw::NoDestructor<T>` is similar to |
| /// Chromium’s `base::NoDestructor<T>` in <a |
| /// href="https://chromium.googlesource.com/chromium/src/base/+/5ea6e31f927aa335bfceb799a2007c7f9007e680/no_destructor.h"> |
| /// src/base/no_destructor.h</a>. |
| /// |
| /// @warning Misuse of NoDestructor can cause memory leaks and other problems. |
| /// Only skip destructors when you know it is safe to do so. |
| template <typename T> |
| class NoDestructor { |
| public: |
| using value_type = T; |
| |
| // Initializes a T in place. |
| // |
| // This overload is disabled when it might collide with copy/move. |
| template <typename... Args, |
| typename std::enable_if<!std::is_same<void(std::decay_t<Args>&...), |
| void(NoDestructor&)>::value, |
| int>::type = 0> |
| explicit constexpr NoDestructor(Args&&... args) |
| : storage_(std::forward<Args>(args)...) {} |
| |
| // Move or copy from the contained type. This allows for construction from an |
| // initializer list, e.g. for std::vector. |
| explicit constexpr NoDestructor(const T& x) : storage_(x) {} |
| explicit constexpr NoDestructor(T&& x) : storage_(std::move(x)) {} |
| |
| NoDestructor(const NoDestructor&) = delete; |
| NoDestructor& operator=(const NoDestructor&) = delete; |
| |
| ~NoDestructor() = default; |
| |
| const T& operator*() const { return *storage_.get(); } |
| T& operator*() { return *storage_.get(); } |
| |
| const T* operator->() const { return storage_.get(); } |
| T* operator->() { return storage_.get(); } |
| |
| private: |
| class DirectStorage { |
| public: |
| template <typename... Args> |
| explicit constexpr DirectStorage(Args&&... args) |
| : value_(std::forward<Args>(args)...) {} |
| |
| const T* get() const { return &value_; } |
| T* get() { return &value_; } |
| |
| private: |
| T value_; |
| }; |
| |
| class PlacementStorage { |
| public: |
| template <typename... Args> |
| explicit PlacementStorage(Args&&... args) { |
| new (&memory_) T(std::forward<Args>(args)...); |
| } |
| |
| const T* get() const { |
| return std::launder(reinterpret_cast<const T*>(&memory_)); |
| } |
| T* get() { return std::launder(reinterpret_cast<T*>(&memory_)); } |
| |
| private: |
| alignas(T) char memory_[sizeof(T)]; |
| }; |
| |
| // If the type is already trivially destructible, use it directly. Trivially |
| // destructible types do not need NoDestructor, but NoDestructor supports them |
| // to work better with generic code. |
| std::conditional_t<std::is_trivially_destructible<T>::value, |
| DirectStorage, |
| PlacementStorage> |
| storage_; |
| }; |
| |
| } // namespace pw |