Files
snmalloc/src/snmalloc/mem/pool.h
David Chisnall f6e9796bbc Introduce header layering (#503)
See src/snmalloc/README.md for an explanation of the layers.

Some other cleanups on the way:

Fine-grained stats support is now gone.

It's been broken for two years, it depends on iostream (which then
causes linker failures with libstdc++) and it's collecting the wrong
stats for the new design.  After discussion with @mjp41, it's better to
remove it and introduce new stats support later, rather than keep broken
code in the main branch.

Tracing was controlled with a preprocessor macro, now there's also a
CMake option.
2022-04-06 09:59:33 +01:00

197 lines
5.2 KiB
C++

#pragma once
#include "../ds/ds.h"
#include "pooled.h"
#include <new>
namespace snmalloc
{
/**
* Pool of a particular type of object.
*
* This pool will never return objects to the OS. It maintains a list of all
* objects ever allocated that can be iterated (not concurrency safe). Pooled
* types can be acquired from the pool, and released back to the pool. This is
* concurrency safe.
*
* This is used to bootstrap the allocation of allocators.
*/
template<class T>
class PoolState
{
template<
typename TT,
SNMALLOC_CONCEPT(ConceptBackendGlobals) SharedStateHandle,
PoolState<TT>& get_state()>
friend class Pool;
private:
MPMCStack<T, PreZeroed> stack;
FlagWord lock{};
T* list{nullptr};
public:
constexpr PoolState() = default;
};
/**
* Helper class used to instantiate a global PoolState.
*
* SingletonPoolState::pool is the default provider for the PoolState within
* the Pool class.
*/
template<
typename T,
SNMALLOC_CONCEPT(ConceptBackendGlobals) SharedStateHandle>
class SingletonPoolState
{
/**
* SFINAE helper. Matched only if `T` implements `ensure_init`. Calls it
* if it exists.
*/
template<typename SharedStateHandle_>
SNMALLOC_FAST_PATH static auto call_ensure_init(SharedStateHandle_*, int)
-> decltype(SharedStateHandle_::ensure_init())
{
static_assert(
std::is_same<SharedStateHandle, SharedStateHandle_>::value,
"SFINAE parameter, should only be used with SharedStateHandle");
SharedStateHandle_::ensure_init();
}
/**
* SFINAE helper. Matched only if `T` does not implement `ensure_init`.
* Does nothing if called.
*/
template<typename SharedStateHandle_>
SNMALLOC_FAST_PATH static auto call_ensure_init(SharedStateHandle_*, long)
{
static_assert(
std::is_same<SharedStateHandle, SharedStateHandle_>::value,
"SFINAE parameter, should only be used with SharedStateHandle");
}
/**
* Call `SharedStateHandle::ensure_init()` if it is implemented, do nothing
* otherwise.
*/
SNMALLOC_FAST_PATH static void ensure_init()
{
call_ensure_init<SharedStateHandle>(nullptr, 0);
}
static void make_pool(PoolState<T>*) noexcept
{
ensure_init();
// Default initializer already called on PoolState, no need to use
// placement new.
}
public:
/**
* Returns a reference for the global PoolState for the given type.
* Also forces the initialization of the backend state, if needed.
*/
SNMALLOC_FAST_PATH static PoolState<T>& pool()
{
return Singleton<PoolState<T>, &make_pool>::get();
}
};
/**
* Wrapper class to access a pool of a particular type of object.
*
* The third template argument is a method to retrieve the actual PoolState.
*
* For the pool of allocators, refer to the AllocPool alias defined in
* corealloc.h.
*
* For a pool of another type, it is recommended to leave the
* third template argument with its default value. The SingletonPoolState
* class is used as a helper to provide a default PoolState management for
* this use case.
*/
template<
typename T,
SNMALLOC_CONCEPT(ConceptBackendGlobals) SharedStateHandle,
PoolState<T>& get_state() = SingletonPoolState<T, SharedStateHandle>::pool>
class Pool
{
public:
template<typename... Args>
static T* acquire(Args&&... args)
{
PoolState<T>& pool = get_state();
T* p = pool.stack.pop();
if (p != nullptr)
{
p->set_in_use();
return p;
}
auto raw =
SharedStateHandle::template alloc_meta_data<T>(nullptr, sizeof(T));
if (raw == nullptr)
{
SharedStateHandle::Pal::error(
"Failed to initialise thread local allocator.");
}
p = new (raw.unsafe_ptr()) T(std::forward<Args>(args)...);
FlagLock f(pool.lock);
p->list_next = pool.list;
pool.list = p;
p->set_in_use();
return p;
}
/**
* Return to the pool an object previously retrieved by `acquire`
*
* Do not return objects from `extract`.
*/
static void release(T* p)
{
// The object's destructor is not run. If the object is "reallocated", it
// is returned without the constructor being run, so the object is reused
// without re-initialisation.
p->reset_in_use();
get_state().stack.push(p);
}
static T* extract(T* p = nullptr)
{
// Returns a linked list of all objects in the stack, emptying the stack.
if (p == nullptr)
return get_state().stack.pop_all();
return p->next;
}
/**
* Return to the pool a list of object previously retrieved by `extract`
*
* Do not return objects from `acquire`.
*/
static void restore(T* first, T* last)
{
// Pushes a linked list of objects onto the stack. Use to put a linked
// list returned by extract back onto the stack.
get_state().stack.push(first, last);
}
static T* iterate(T* p = nullptr)
{
if (p == nullptr)
return get_state().list;
return p->list_next;
}
};
} // namespace snmalloc