Files
snmalloc/src/mem/globalalloc.h
Matthew Parkinson 5198821905 CR Feedback.
2021-05-18 14:58:15 +01:00

201 lines
4.6 KiB
C++

#pragma once
#include "../ds/helpers.h"
#include "alloc.h"
#include "pool.h"
namespace snmalloc
{
inline bool needs_initialisation(void*);
void* init_thread_allocator(function_ref<void*(void*)>);
template<class MemoryProvider, class Alloc>
class AllocPool : Pool<Alloc, MemoryProvider>
{
using Parent = Pool<Alloc, MemoryProvider>;
public:
static AllocPool* make(MemoryProvider& mp)
{
static_assert(
sizeof(AllocPool) == sizeof(Parent),
"You cannot add fields to this class.");
// This cast is safe due to the static assert.
return static_cast<AllocPool*>(Parent::make(mp));
}
static AllocPool* make() noexcept
{
return make(default_memory_provider());
}
Alloc* acquire()
{
return Parent::acquire(Parent::memory_provider);
}
void release(Alloc* a)
{
Parent::release(a);
}
public:
void aggregate_stats(Stats& stats)
{
auto* alloc = Parent::iterate();
while (alloc != nullptr)
{
stats.add(alloc->stats());
alloc = Parent::iterate(alloc);
}
}
#ifdef USE_SNMALLOC_STATS
void print_all_stats(std::ostream& o, uint64_t dumpid = 0)
{
auto alloc = Parent::iterate();
while (alloc != nullptr)
{
alloc->stats().template print<Alloc>(o, dumpid, alloc->id());
alloc = Parent::iterate(alloc);
}
}
#else
void print_all_stats(void*& o, uint64_t dumpid = 0)
{
UNUSED(o);
UNUSED(dumpid);
}
#endif
void cleanup_unused()
{
#ifndef SNMALLOC_PASS_THROUGH
// 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 = Parent::extract();
auto* alloc = first;
decltype(alloc) last;
if (alloc != nullptr)
{
while (alloc != nullptr)
{
alloc->handle_message_queue();
last = alloc;
alloc = Parent::extract(alloc);
}
restore(first, last);
}
#endif
}
/**
If you pass a pointer to a bool, then it returns whether all the
allocators are empty. If you don't pass a pointer to a bool, then will
raise an error all the allocators are not empty.
*/
void debug_check_empty(bool* result = nullptr)
{
#ifndef SNMALLOC_PASS_THROUGH
// This is a debugging function. It checks that all memory from all
// allocators has been freed.
auto* alloc = Parent::iterate();
bool done = false;
bool okay = true;
while (!done)
{
done = true;
alloc = Parent::iterate();
okay = true;
while (alloc != nullptr)
{
// Check that the allocator has freed all memory.
alloc->debug_is_empty(&okay);
// Post all remotes, including forwarded ones. If any allocator posts,
// repeat the loop.
if (alloc->remote_cache.capacity < REMOTE_CACHE)
{
alloc->stats().remote_post();
alloc->remote_cache.post(alloc, alloc->get_trunc_id());
done = false;
}
alloc = Parent::iterate(alloc);
}
}
if (result != nullptr)
{
*result = okay;
return;
}
if (!okay)
{
alloc = Parent::iterate();
while (alloc != nullptr)
{
alloc->debug_is_empty(nullptr);
alloc = Parent::iterate(alloc);
}
}
#else
UNUSED(result);
#endif
}
void debug_in_use(size_t count)
{
auto alloc = Parent::iterate();
while (alloc != nullptr)
{
if (alloc->debug_is_in_use())
{
if (count == 0)
{
error("ERROR: allocator in use.");
}
count--;
}
alloc = Parent::iterate(alloc);
if (count != 0)
{
error("Error: two few allocators in use.");
}
}
}
};
using Alloc = Allocator<
needs_initialisation,
init_thread_allocator,
GlobalVirtual,
SNMALLOC_DEFAULT_CHUNKMAP,
true>;
inline AllocPool<GlobalVirtual, Alloc>*& current_alloc_pool()
{
return Singleton<
AllocPool<GlobalVirtual, Alloc>*,
AllocPool<GlobalVirtual, Alloc>::make>::get();
}
template<class MemoryProvider, class Alloc>
inline AllocPool<MemoryProvider, Alloc>* make_alloc_pool(MemoryProvider& mp)
{
return AllocPool<MemoryProvider, Alloc>::make(mp);
}
} // namespace snmalloc