blob: 6d0d9530aae54b3f216eb1b51c6f2d9aa5dd99ac [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIB_LAZY_INIT_LAZY_INIT_H_
#define LIB_LAZY_INIT_LAZY_INIT_H_
#include <atomic>
#include <cstddef>
#include <new>
#include <type_traits>
#include <utility>
#include "internal/storage.h"
#include "options.h"
namespace lazy_init {
// Wrapper type for global variables that removes automatic constructor and
// destructor generation and provides explicit control over initialization.
// This avoids initialization order hazards between globals, as well as code
// that runs before and after global constructors are invoked.
//
// This is the base type that specifies the default parameters that control
// consistency checks and global destructor generation options.
//
// See lib/lazy_init/options.h for a description of CheckType and Destructor
// option values.
//
// Note, T must be construtible by LazyInit, however, its constructor need not
// be public if Access is declared as a friend. For example:
//
// class Foo {
// friend lazy_init::Access;
//
// Foo(int arg);
// };
//
template <typename T, CheckType = CheckType::Default, Destructor = Destructor::Default>
class LazyInit;
// Utility type to provide access to non-public constructors. Types with private
// or protected constructors that need lazy initialization must friend Access.
class Access {
template <typename, CheckType, Destructor>
friend class LazyInit;
// Constructs the given storage using the constructor matching T(Args...).
template <typename T, typename... Args>
static void Initialize(T* value, Args&&... args) {
new (value) T(std::forward<Args>(args)...);
}
};
// Specialization that does not provide consistency checks.
template <typename T>
class LazyInit<T, CheckType::None, Destructor::Disabled> {
public:
constexpr LazyInit() = default;
~LazyInit() = default;
LazyInit(const LazyInit&) = delete;
LazyInit& operator=(const LazyInit&) = delete;
LazyInit(LazyInit&&) = delete;
LazyInit& operator=(LazyInit&&) = delete;
// Explicitly constructs the wrapped global. It is up to the caller to
// ensure that initialization is not already performed.
// Returns a reference to the newly constructed global.
template <typename... Args>
T& Initialize(Args&&... args) {
static_assert(alignof(LazyInit) >= alignof(T));
Access::Initialize(&storage_.value, std::forward<Args>(args)...);
return *storage_;
}
// Returns a reference to the wrapped global. It is up to the caller to
// ensure that initialization is already performed and that the effects of
// initialization are visible.
T& Get() { return *storage_; }
const T& Get() const { return *storage_; }
// Accesses the wrapped global by pointer. It is up to the caller to ensure
// that initialization is already performed and that the effects of
// initialization are visible.
T* operator->() { return &Get(); }
const T* operator->() const { return &Get(); }
// Returns a pointer to the wrapped global. All specializations return a
// pointer without performing consistency checks. This should be used
// cautiously, preferably only in constant expressions that take the address
// of the wrapped global.
constexpr T* GetAddressUnchecked() { return storage_.GetStorageAddress(); }
constexpr const T* GetAddressUnchecked() const { return storage_.GetStorageAddress(); }
private:
template <typename, CheckType, Destructor>
friend class LazyInit;
// Explicitly destroys the wrapped global. Called by the specialization of
// LazyInit with the destructor enabled.
void Destruct() { storage_->T::~T(); }
// Lazy-initialized storage for a value of type T.
internal::LazyInitStorage<T> storage_;
};
// Specialization that provides basic consistency checks. It is up to the caller
// to ensure proper synchronization and barriers.
template <typename T>
class LazyInit<T, CheckType::Basic, Destructor::Disabled> {
public:
constexpr LazyInit() = default;
~LazyInit() = default;
LazyInit(const LazyInit&) = delete;
LazyInit& operator=(const LazyInit&) = delete;
LazyInit(LazyInit&&) = delete;
LazyInit& operator=(LazyInit&&) = delete;
// Explicitly constructs the wrapped global. Asserts that initialization is
// not already performed.
// Returns a reference to the newly constructed global.
template <typename... Args>
T& Initialize(Args&&... args) {
static_assert(alignof(LazyInit) >= alignof(T));
internal::Assert(!initialized_);
initialized_ = true;
Access::Initialize(&storage_.value, std::forward<Args>(args)...);
return *storage_;
}
// Returns a reference to the wrapped global. Asserts that initialization
// is already performed, however, it is up to the caller to ensure that the
// effects of initialization are visible.
T& Get() {
internal::Assert(initialized_);
return *storage_;
}
const T& Get() const {
internal::Assert(initialized_);
return *storage_;
}
// Accesses the wrapped global by pointer. Asserts that initialization is
// already performed, however, it is up the caller to ensure that the
// effects of initialization are visible.
T* operator->() { return &Get(); }
const T* operator->() const { return &Get(); }
// Returns a pointer to the wrapped global. All specializations return a
// pointer without performing consistency checks. This should be used
// cautiously, preferably only in constant expressions that take the address
// of the wrapped global.
constexpr T* GetAddressUnchecked() { return storage_.GetStorageAddress(); }
constexpr const T* GetAddressUnchecked() const { return storage_.GetStorageAddress(); }
private:
template <typename, CheckType, Destructor>
friend class LazyInit;
// Explicitly destroys the wrapped global. Called by the specialization of
// LazyInit with the destructor enabled.
void Destruct() {
internal::Assert(initialized_);
storage_->T::~T();
// Prevent the compiler from omitting this write during destruction.
static_cast<volatile bool&>(initialized_) = false;
}
// Lazy-initialized storage for a value of type T.
internal::LazyInitStorage<T> storage_;
// Guard variable to check for multiple initializations and access before
// initialization.
bool initialized_{};
};
// Specialization that provides atomic consistency checks. Checks are guaranteed
// to be consistent under races over initialization.
template <typename T>
class LazyInit<T, CheckType::Atomic, Destructor::Disabled> {
public:
constexpr LazyInit() = default;
~LazyInit() = default;
LazyInit(const LazyInit&) = delete;
LazyInit& operator=(const LazyInit&) = delete;
LazyInit(LazyInit&&) = delete;
LazyInit& operator=(LazyInit&&) = delete;
// Explicitly constructs the wrapped global. Asserts that initialization is
// not already performed.
// Returns a reference to the newly constructed global.
template <typename... Args>
T& Initialize(Args&&... args) {
static_assert(alignof(LazyInit) >= alignof(T));
TransitionState(State::Uninitialized, State::Constructing);
Access::Initialize(&storage_.value, std::forward<Args>(args)...);
state_.store(State::Initialized, std::memory_order_release);
return *storage_;
}
// Returns a reference to the wrapped global. Asserts that initialization
// is already performed. The effects of initialization are guaranteed to be
// visible if the assertion passes.
T& Get() {
internal::Assert(State::Initialized == state_.load(std::memory_order_relaxed));
return *storage_;
}
const T& Get() const {
internal::Assert(State::Initialized == state_.load(std::memory_order_relaxed));
return *storage_;
}
// Accesses the wrapped global by pointer. Asserts that initialization is
// already performed. The effects of initialization are guaranteed to be
// visible if the assertion passes.
T* operator->() { return &Get(); }
const T* operator->() const { return &Get(); }
// Returns a pointer to the wrapped global. All specializations return a
// pointer without performing consistency checks. This should be used
// cautiously, preferably only in constant expressions that take the address
// of the wrapped global.
constexpr T* GetAddressUnchecked() { return storage_.GetStorageAddress(); }
constexpr const T* GetAddressUnchecked() const { return storage_.GetStorageAddress(); }
private:
template <typename, CheckType, Destructor>
friend class LazyInit;
// Enum representing the states of initialization.
enum class State : int {
Uninitialized = 0,
Constructing,
Initialized,
Destructing,
Destroyed,
};
// Transitions the guard from the |expected| state to the |target| state.
// Asserts that the expected state matches the actual state.
void TransitionState(State expected, State target) {
State actual = state_.load(std::memory_order_relaxed);
internal::Assert(expected == actual);
while (!state_.compare_exchange_weak(actual, target, std::memory_order_acquire,
std::memory_order_relaxed)) {
internal::Assert(expected == actual);
}
}
// Explicitly destroys the wrapped global. Called by the specialization of
// LazyInit with the destructor enabled.
void Destruct() {
TransitionState(State::Initialized, State::Destructing);
storage_->T::~T();
state_.store(State::Destroyed, std::memory_order_release);
}
// Lazy-initialized storage for a value of type T.
internal::LazyInitStorage<T> storage_;
// Guard variable to check for multiple initializations and access before
// initialization.
std::atomic<State> state_{};
};
// Specialization that includes a global destructor. This type is based on the
// equivalent specialization without a global destructor and simply calls the
// base type Destruct() method.
template <typename T, CheckType Check>
class LazyInit<T, Check, Destructor::Enabled> : public LazyInit<T, Check, Destructor::Disabled> {
public:
using LazyInit<T, Check, Destructor::Disabled>::LazyInit;
~LazyInit() { this->Destruct(); }
};
} // namespace lazy_init
#endif // LIB_LAZY_INIT_LAZY_INIT_H_