C++20: Add TrivialInitAtomic

C++20 does away with trivial initializers for std::atomic<T>, which means our
global pagemaps always get zeroed, sometimes after other static ctors have run
(fun fun!).  Use the new std::atomic_ref<T> when available.  Abstract all this
behind an #ifdef-ful wrapper.
This commit is contained in:
Nathaniel Filardo
2021-04-20 19:06:39 +01:00
committed by Nathaniel Wesley Filardo
parent c132c86cf9
commit 80c6e95210
2 changed files with 89 additions and 10 deletions

View File

@@ -3,6 +3,8 @@
#include "bits.h"
#include "flaglock.h"
#include <type_traits>
namespace snmalloc
{
/*
@@ -156,4 +158,69 @@ namespace snmalloc
{
UNUSED(t);
}
/**
* Sometimes we need atomics with trivial initializer. Unfortunately, this
* became harder to accomplish in C++20. Fortunately, our rules for accessing
* these are at least as strong as those required by C++20's atomic_ref:
*
* * The objects outlive any references to them
*
* * We always access the objects through references (though we'd be allowed
* to access them without if we knew there weren't other references)
*
* * We don't access sub-objects at all, much less concurrently through
* other references.
*/
template<typename T>
class TrivialInitAtomic
{
static_assert(
std::is_trivially_default_constructible_v<T>,
"TrivialInitAtomic should not attempt to call nontrivial constructors");
#ifdef __cpp_lib_atomic_ref
using Val = T;
using Ref = std::atomic_ref<T>;
#else
using Val = std::atomic<T>;
using Ref = std::atomic<T>&;
#endif
Val v;
public:
/**
* Construct a reference to this value; use .load and .store to manipulate
* the value.
*/
SNMALLOC_FAST_PATH Ref ref()
{
#ifdef __cpp_lib_atomic_ref
return std::atomic_ref<T>(this->v);
#else
return this->v;
#endif
}
SNMALLOC_FAST_PATH T
load(std::memory_order mo = std::memory_order_seq_cst) noexcept
{
return this->ref().load(mo);
}
SNMALLOC_FAST_PATH void
store(T n, std::memory_order mo = std::memory_order_seq_cst) noexcept
{
return this->ref().store(n, mo);
}
SNMALLOC_FAST_PATH bool compare_exchange_strong(
T& exp, T des, std::memory_order mo = std::memory_order_seq_cst) noexcept
{
return this->ref().compare_exchange_strong(exp, des, mo);
}
};
static_assert(sizeof(TrivialInitAtomic<char>) == sizeof(char));
static_assert(alignof(TrivialInitAtomic<char>) == alignof(char));
} // namespace snmalloc