Snmalloc2 API cleanups for sandbox use. (#359)

This is the set of changes required for snmalloc2 to be usable by the
process sandboxing code and incorporates some API changes that reduce
the amount of code required to embed snmalloc.  Highlights:

 - Merge the config and back-end classes.
 - Everything in config is now global (all methods are static)
 - The GlobalState class is gone (all global state is managed by global
   methods on the config class)
 - LocalState is now a member of the config class, all methods are
   instance methods.
 - Not every configuration needs to use the lazy initialisation hooks.
   They now need to be provided only if they are used.  If the
   configuration does not provide an `ensure_init` method, it is not
   called.  If it does not provide an `is_initialised` method then the
   global initialisation state is not checked.
 - There is now an `snmalloc::Options` class that default initialises
   itself to the default behaviour.  Every configuration must provide a
   `constexpr` instance of this class.  Each flag can be separately
   overridden and new flags can be added without breaking any existing
   API consumers.

The config classes are moved into the backend directory.
This commit is contained in:
David Chisnall
2021-08-05 15:08:12 +01:00
committed by GitHub
parent 2ef1eea3ba
commit e8374479f4
29 changed files with 676 additions and 380 deletions

View File

@@ -6,9 +6,13 @@
namespace snmalloc
{
template<class SharedStateHandle>
inline static void aggregate_stats(SharedStateHandle handle, Stats& stats)
inline static void aggregate_stats(Stats& stats)
{
auto* alloc = Pool<CoreAllocator<SharedStateHandle>>::iterate(handle);
static_assert(
SharedStateHandle::Options.CoreAllocIsPoolAllocated,
"Global statistics are available only for pool-allocated configurations");
auto* alloc = Pool<CoreAllocator<SharedStateHandle>>::template iterate<
SharedStateHandle>();
while (alloc != nullptr)
{
@@ -16,45 +20,51 @@ namespace snmalloc
if (a != nullptr)
stats.add(*a);
stats.add(alloc->stats());
alloc = Pool<CoreAllocator<SharedStateHandle>>::iterate(handle, alloc);
alloc = Pool<CoreAllocator<SharedStateHandle>>::template iterate<
SharedStateHandle>(alloc);
}
}
#ifdef USE_SNMALLOC_STATS
template<class SharedStateHandle>
inline static void print_all_stats(
SharedStateHandle handle, std::ostream& o, uint64_t dumpid = 0)
inline static void print_all_stats(std::ostream& o, uint64_t dumpid = 0)
{
auto alloc = Pool<CoreAllocator<SharedStateHandle>>::iterate(handle);
static_assert(
SharedStateHandle::Options.CoreAllocIsPoolAllocated,
"Global statistics are available only for pool-allocated configurations");
auto alloc = Pool<CoreAllocator<SharedStateHandle>>::template iterate<
SharedStateHandle>();
while (alloc != nullptr)
{
auto stats = alloc->stats();
if (stats != nullptr)
stats->template print<Alloc>(o, dumpid, alloc->id());
alloc = Pool<CoreAllocator<SharedStateHandle>>::iterate(handle, alloc);
stats->template print<decltype(alloc)>(o, dumpid, alloc->id());
alloc = Pool<CoreAllocator<SharedStateHandle>>::template iterate<
SharedStateHandle>(alloc);
}
}
#else
template<class SharedStateHandle>
inline static void
print_all_stats(SharedStateHandle handle, void*& o, uint64_t dumpid = 0)
inline static void print_all_stats(void*& o, uint64_t dumpid = 0)
{
UNUSED(o);
UNUSED(dumpid);
UNUSED(handle);
}
#endif
template<class SharedStateHandle>
inline static void cleanup_unused(SharedStateHandle handle)
inline static void cleanup_unused()
{
#ifndef SNMALLOC_PASS_THROUGH
static_assert(
SharedStateHandle::Options.CoreAllocIsPoolAllocated,
"Global cleanup is available only for pool-allocated configurations");
// Call this periodically to free and coalesce memory allocated by
// allocators that are not currently in use by any thread.
// One atomic operation to extract the stack, another to restore it.
// Handling the message queue for each stack is non-atomic.
auto* first = Pool<CoreAllocator<SharedStateHandle>>::extract(handle);
auto* first = Pool<CoreAllocator<SharedStateHandle>>::extract();
auto* alloc = first;
decltype(alloc) last;
@@ -64,10 +74,10 @@ namespace snmalloc
{
alloc->flush();
last = alloc;
alloc = Pool<CoreAllocator<SharedStateHandle>>::extract(handle, alloc);
alloc = Pool<CoreAllocator<SharedStateHandle>>::extract(alloc);
}
Pool<CoreAllocator<SharedStateHandle>>::restore(handle, first, last);
Pool<CoreAllocator<SharedStateHandle>>::restore(first, last);
}
#endif
}
@@ -78,13 +88,16 @@ namespace snmalloc
raise an error all the allocators are not empty.
*/
template<class SharedStateHandle>
inline static void
debug_check_empty(SharedStateHandle handle, bool* result = nullptr)
inline static void debug_check_empty(bool* result = nullptr)
{
#ifndef SNMALLOC_PASS_THROUGH
static_assert(
SharedStateHandle::Options.CoreAllocIsPoolAllocated,
"Global status is available only for pool-allocated configurations");
// This is a debugging function. It checks that all memory from all
// allocators has been freed.
auto* alloc = Pool<CoreAllocator<SharedStateHandle>>::iterate(handle);
auto* alloc = Pool<CoreAllocator<SharedStateHandle>>::template iterate<
SharedStateHandle>();
# ifdef SNMALLOC_TRACING
std::cout << "debug check empty: first " << alloc << std::endl;
@@ -98,7 +111,8 @@ namespace snmalloc
std::cout << "debug_check_empty: Check all allocators!" << std::endl;
# endif
done = true;
alloc = Pool<CoreAllocator<SharedStateHandle>>::iterate(handle);
alloc = Pool<CoreAllocator<SharedStateHandle>>::template iterate<
SharedStateHandle>();
okay = true;
while (alloc != nullptr)
@@ -120,7 +134,8 @@ namespace snmalloc
# ifdef SNMALLOC_TRACING
std::cout << "debug check empty: okay = " << okay << std::endl;
# endif
alloc = Pool<CoreAllocator<SharedStateHandle>>::iterate(handle, alloc);
alloc = Pool<CoreAllocator<SharedStateHandle>>::template iterate<
SharedStateHandle>(alloc);
}
}
@@ -133,11 +148,13 @@ namespace snmalloc
// Redo check so abort is on allocator with allocation left.
if (!okay)
{
alloc = Pool<CoreAllocator<SharedStateHandle>>::iterate(handle);
alloc = Pool<CoreAllocator<SharedStateHandle>>::template iterate<
SharedStateHandle>();
while (alloc != nullptr)
{
alloc->debug_is_empty(nullptr);
alloc = Pool<CoreAllocator<SharedStateHandle>>::iterate(handle, alloc);
alloc = Pool<CoreAllocator<SharedStateHandle>>::template iterate<
SharedStateHandle>(alloc);
}
}
#else
@@ -146,9 +163,13 @@ namespace snmalloc
}
template<class SharedStateHandle>
inline static void debug_in_use(SharedStateHandle handle, size_t count)
inline static void debug_in_use(size_t count)
{
auto alloc = Pool<CoreAllocator<SharedStateHandle>>::iterate(handle);
static_assert(
SharedStateHandle::Options.CoreAllocIsPoolAllocated,
"Global status is available only for pool-allocated configurations");
auto alloc = Pool<CoreAllocator<SharedStateHandle>>::template iterate<
SharedStateHandle>();
while (alloc != nullptr)
{
if (alloc->debug_is_in_use())
@@ -159,7 +180,8 @@ namespace snmalloc
}
count--;
}
alloc = Pool<CoreAllocator<SharedStateHandle>>::iterate(handle, alloc);
alloc = Pool<CoreAllocator<SharedStateHandle>>::template iterate<
SharedStateHandle>(alloc);
if (count != 0)
{