Files
snmalloc/src/ds/helpers.h
Matthew Parkinson cf9c2eb9d9 Addressing OE linking issues when built with GCC (#167)
* Use C++17 inline statics

This leads to better codegen in GCC, and fixes some linking issues in OE.

* Detect GCC and OE combination and fall-back to lock based ABA.

* clangformat
2020-04-10 13:49:39 +01:00

147 lines
3.3 KiB
C++

#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 Object, Object init() noexcept>
class Singleton
{
inline static std::atomic_flag flag;
inline static std::atomic<bool> 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<size_t length, typename T>
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<T>(value & (length - 1));
}
Mod& operator=(const T v)
{
value = v;
return *this;
}
};
template<size_t length, typename T>
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<void f()>
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<typename Fn>
struct function_ref;
template<typename R, typename... Args>
struct function_ref<R(Args...)>
{
// The enable_if is used to stop this constructor from shadowing the default
// copy / move constructors.
template<
typename Fn,
typename =
std::enable_if_t<!std::is_same_v<std::decay_t<Fn>, function_ref>>>
function_ref(Fn&& fn)
{
data_ = static_cast<void*>(&fn);
fn_ = execute<Fn>;
}
R operator()(Args... args) const
{
return fn_(data_, args...);
}
private:
void* data_;
R (*fn_)(void*, Args...);
template<typename Fn>
static R execute(void* p, Args... args)
{
return (*static_cast<std::add_pointer_t<Fn>>(p))(args...);
};
};
} // namespace snmalloc