blob: 6986a08a51bf292caf663de1012e9d5b5d88d350 [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:
//
// 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;
// }
//
// WARNING: Misuse of NoDestructor can cause memory leaks and other problems.
// Only skip destructors when you know it is safe to do so.
//
// In Clang, pw::NoDestructor can be replaced with the [[clang::no_destroy]]
// attribute.
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