blob: dc9ee0aa5d05cd73ef82730304710fa86f188646 [file] [log] [blame]
// 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