#pragma once #include "bits.h" #include "flaglock.h" namespace snmalloc { /* * In some use cases we need to run before any of the C++ runtime has been * initialised. This singleton class is designed to not depend on the * runtime. */ template class Singleton { inline static std::atomic_flag flag; inline static std::atomic initialised = false; inline static Object obj; public: /** * If argument is non-null, then it is assigned the value * true, if this is the first call to get. * At most one call will be first. */ inline SNMALLOC_SLOW_PATH static Object& get(bool* first = nullptr) { // If defined should be initially false; SNMALLOC_ASSERT(first == nullptr || *first == false); if (unlikely(!initialised.load(std::memory_order_acquire))) { FlagLock lock(flag); if (!initialised) { obj = init(); initialised.store(true, std::memory_order_release); if (first != nullptr) *first = true; } } return obj; } }; /** * Wrapper for wrapping values. * * Wraps on read. This allows code to trust the value is in range, even when * there is a memory corruption. */ template class Mod { static_assert( length == bits::next_pow2_const(length), "Must be a power of two."); private: T value = 0; public: operator T() { return static_cast(value & (length - 1)); } Mod& operator=(const T v) { value = v; return *this; } }; template class ModArray { static constexpr size_t rlength = bits::next_pow2_const(length); T array[rlength]; public: constexpr const T& operator[](const size_t i) const { return array[i & (rlength - 1)]; } constexpr T& operator[](const size_t i) { return array[i & (rlength - 1)]; } }; /** * Helper class to execute a specified function on destruction. */ template class OnDestruct { public: ~OnDestruct() { f(); } }; /** * Non-owning version of std::function. Wraps a reference to a callable object * (eg. a lambda) and allows calling it through dynamic dispatch, with no * allocation. This is useful in the allocator code paths, where we can't * safely use std::function. * * Inspired by the C++ proposal: * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0792r2.html */ template struct function_ref; template struct function_ref { // The enable_if is used to stop this constructor from shadowing the default // copy / move constructors. template< typename Fn, typename = std::enable_if_t, function_ref>>> function_ref(Fn&& fn) { data_ = static_cast(&fn); fn_ = execute; } R operator()(Args... args) const { return fn_(data_, args...); } private: void* data_; R (*fn_)(void*, Args...); template static R execute(void* p, Args... args) { return (*static_cast>(p))(args...); }; }; } // namespace snmalloc