486 lines
12 KiB
C++
486 lines
12 KiB
C++
// vi:set ft=cpp: -*- Mode: C++ -*-
|
|
/*
|
|
* (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
|
|
* Alexander Warg <warg@os.inf.tu-dresden.de>
|
|
* economic rights: Technische Universität Dresden (Germany)
|
|
*
|
|
* License: see LICENSE.spdx (in this directory or the directories above)
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <l4/cxx/std_alloc>
|
|
#include <l4/cxx/hlist>
|
|
#include <l4/sys/consts.h>
|
|
|
|
namespace cxx {
|
|
|
|
/**
|
|
* \ingroup cxx_api
|
|
* Basic slab allocator.
|
|
*
|
|
* \tparam Obj_size The size of the objects managed by the allocator (in bytes).
|
|
* \tparam Slab_size The size of a slab (in bytes).
|
|
* \tparam Max_free The maximum number of free slabs. When this limit is reached
|
|
* slabs are freed, provided that the backend allocator
|
|
* supports allocated memory to be freed.
|
|
* \tparam Alloc The backend allocator used to allocate slabs.
|
|
*/
|
|
template< int Obj_size, int Slab_size = L4_PAGESIZE,
|
|
int Max_free = 2, template<typename A> class Alloc = New_allocator >
|
|
class Base_slab
|
|
{
|
|
private:
|
|
struct Free_o
|
|
{
|
|
Free_o *next;
|
|
};
|
|
|
|
protected:
|
|
struct Slab_i;
|
|
|
|
private:
|
|
struct Slab_head : public H_list_item
|
|
{
|
|
/// Number of free objects in the slab.
|
|
unsigned num_free;
|
|
/// Pointer to the first free object in the slab.
|
|
Free_o *free;
|
|
/// Pointer to the slab cache (instance of the slab allocator).
|
|
Base_slab<Obj_size, Slab_size, Max_free, Alloc> *cache;
|
|
|
|
inline Slab_head() noexcept : num_free(0), free(0), cache(0)
|
|
{}
|
|
};
|
|
|
|
// In an empty or partially filled slab, each free object stores a pointer to
|
|
// the next free object. Thus, the size of an object needs to be at least the
|
|
// size of a pointer.
|
|
static_assert(Obj_size >= sizeof(void *),
|
|
"Object size must be at least the size of a pointer.");
|
|
static_assert(Obj_size <= Slab_size - sizeof(Slab_head),
|
|
"Object_size exceeds slab capability.");
|
|
|
|
public:
|
|
enum
|
|
{
|
|
/// Size of an object.
|
|
object_size = Obj_size,
|
|
/// Size of a slab.
|
|
slab_size = Slab_size,
|
|
/// Objects per slab.
|
|
objects_per_slab = (Slab_size - sizeof(Slab_head)) / object_size,
|
|
/// Maximum number of free slabs.
|
|
max_free_slabs = Max_free,
|
|
};
|
|
|
|
protected:
|
|
struct Slab_store
|
|
{
|
|
char _o[slab_size - sizeof(Slab_head)];
|
|
Free_o *object(unsigned obj) noexcept
|
|
{ return reinterpret_cast<Free_o*>(_o + object_size * obj); }
|
|
};
|
|
|
|
/// Type of a slab
|
|
struct Slab_i : public Slab_store, public Slab_head
|
|
{};
|
|
|
|
public:
|
|
/// Type of the backend allocator.
|
|
typedef Alloc<Slab_i> Slab_alloc;
|
|
|
|
typedef void Obj_type;
|
|
|
|
private:
|
|
/// Allocator used for slabs.
|
|
Slab_alloc _alloc;
|
|
/// Number of empty slabs.
|
|
unsigned _num_free;
|
|
/// Total number of slabs.
|
|
unsigned _num_slabs;
|
|
/// List of full slabs.
|
|
H_list<Slab_i> _full_slabs;
|
|
/// List of partial slabs.
|
|
H_list<Slab_i> _partial_slabs;
|
|
/// List of empty slabs.
|
|
H_list<Slab_i> _empty_slabs;
|
|
|
|
/// Add a new slab.
|
|
void add_slab(Slab_i *s) noexcept
|
|
{
|
|
s->num_free = objects_per_slab;
|
|
s->cache = this;
|
|
|
|
//L4::cerr << "Slab: " << this << "->add_slab(" << s << ", size="
|
|
// << slab_size << "):" << " f=" << s->object(0) << '\n';
|
|
|
|
// initialize free list
|
|
Free_o *f = s->free = s->object(0);
|
|
for (unsigned i = 1; i < objects_per_slab; ++i)
|
|
{
|
|
f->next = s->object(i);
|
|
f = f->next;
|
|
}
|
|
f->next = 0;
|
|
|
|
// insert slab into cache's list
|
|
_empty_slabs.push_front(s);
|
|
++_num_slabs;
|
|
++_num_free;
|
|
}
|
|
|
|
/// Grow the slab cache, by adding a new slab.
|
|
bool grow() noexcept
|
|
{
|
|
Slab_i *s = _alloc.alloc();
|
|
if (!s)
|
|
return false;
|
|
|
|
new (s, cxx::Nothrow()) Slab_i();
|
|
|
|
add_slab(s);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Shrink the slab cache by freeing empty slabs.
|
|
*
|
|
* The flow of memory from the slab cache back to the system is regulated via
|
|
* the backend allocator's flag `can_free`. If this flag is set to true, the
|
|
* slab cache retains at maximum Max_free empty slabs; empty slabs exceeding
|
|
* this limit are freed. If `can_free` is set to false, the shrink operation
|
|
* does nothing.
|
|
*/
|
|
void shrink() noexcept
|
|
{
|
|
if (!_alloc.can_free)
|
|
return;
|
|
|
|
while (!_empty_slabs.empty() && _num_free > max_free_slabs)
|
|
{
|
|
Slab_i *s = _empty_slabs.front();
|
|
_empty_slabs.remove(s);
|
|
--_num_free;
|
|
--_num_slabs;
|
|
_alloc.free(s);
|
|
}
|
|
}
|
|
|
|
public:
|
|
Base_slab(Slab_alloc const &alloc = Slab_alloc()) noexcept
|
|
: _alloc(alloc), _num_free(0), _num_slabs(0), _full_slabs(),
|
|
_partial_slabs(), _empty_slabs()
|
|
{}
|
|
|
|
~Base_slab() noexcept
|
|
{
|
|
while (!_empty_slabs.empty())
|
|
{
|
|
Slab_i *o = _empty_slabs.front();
|
|
_empty_slabs.remove(o);
|
|
_alloc.free(o);
|
|
}
|
|
while (!_partial_slabs.empty())
|
|
{
|
|
Slab_i *o = _partial_slabs.front();
|
|
_partial_slabs.remove(o);
|
|
_alloc.free(o);
|
|
}
|
|
while (!_full_slabs.empty())
|
|
{
|
|
Slab_i *o = _full_slabs.front();
|
|
_full_slabs.remove(o);
|
|
_alloc.free(o);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Allocate a new object.
|
|
*
|
|
* \return A pointer to the new object if the allocation succeeds, or 0 on
|
|
* failure to acquire memory from the backend allocator when the slab
|
|
* cache memory is already exhausted.
|
|
*
|
|
* \note The user is responsible for initializing the object.
|
|
*/
|
|
void *alloc() noexcept
|
|
{
|
|
H_list<Slab_i> *free = &_partial_slabs;
|
|
if (free->empty())
|
|
free = &_empty_slabs;
|
|
|
|
if (free->empty() && !grow())
|
|
return 0;
|
|
|
|
Slab_i *s = free->front();
|
|
Free_o *o = s->free;
|
|
s->free = o->next;
|
|
|
|
if (free == &_empty_slabs)
|
|
{
|
|
_empty_slabs.remove(s);
|
|
--_num_free;
|
|
}
|
|
|
|
--(s->num_free);
|
|
|
|
if (!s->free)
|
|
{
|
|
_partial_slabs.remove(s);
|
|
_full_slabs.push_front(s);
|
|
}
|
|
else if (free == &_empty_slabs)
|
|
_partial_slabs.push_front(s);
|
|
|
|
//L4::cerr << this << "->alloc(): " << o << ", of " << s << '\n';
|
|
|
|
return o;
|
|
}
|
|
|
|
/**
|
|
* Free the given object (`_o`).
|
|
*
|
|
* \pre The object must have been allocated with this allocator.
|
|
*/
|
|
void free(void *_o) noexcept
|
|
{
|
|
if (!_o)
|
|
return;
|
|
|
|
unsigned long addr = reinterpret_cast<unsigned long>(_o);
|
|
|
|
// find out the slab the object is in
|
|
addr = (addr / slab_size) * slab_size;
|
|
Slab_i *s = reinterpret_cast<Slab_i*>(addr);
|
|
|
|
if (s->cache != this)
|
|
return;
|
|
|
|
Free_o *o = reinterpret_cast<Free_o*>(_o);
|
|
|
|
o->next = s->free;
|
|
s->free = o;
|
|
|
|
bool was_full = false;
|
|
|
|
if (!s->num_free)
|
|
{
|
|
_full_slabs.remove(s);
|
|
was_full = true;
|
|
}
|
|
|
|
++(s->num_free);
|
|
|
|
if (s->num_free == objects_per_slab)
|
|
{
|
|
if (!was_full)
|
|
_partial_slabs.remove(s);
|
|
|
|
_empty_slabs.push_front(s);
|
|
++_num_free;
|
|
if (_num_free > max_free_slabs)
|
|
shrink();
|
|
|
|
was_full = false;
|
|
}
|
|
else if (was_full)
|
|
_partial_slabs.push_front(s);
|
|
|
|
//L4::cerr << this << "->free(" << _o << "): of " << s << '\n';
|
|
}
|
|
|
|
/**
|
|
* Get the total number of objects managed by the slab allocator.
|
|
*
|
|
* \return The number of objects managed by the allocator (including the
|
|
* free objects).
|
|
*/
|
|
unsigned total_objects() const noexcept
|
|
{ return _num_slabs * objects_per_slab; }
|
|
|
|
/**
|
|
* Get the number of objects which can be allocated before a new empty slab
|
|
* needs to be added to the slab allocator.
|
|
*
|
|
* \return The number of free objects in the slab allocator.
|
|
*/
|
|
unsigned free_objects() const noexcept
|
|
{
|
|
unsigned count = 0;
|
|
|
|
/* count partial slabs first */
|
|
for (typename H_list<Slab_i>::Const_iterator s = _partial_slabs.begin();
|
|
s != _partial_slabs.end(); ++s)
|
|
count += s->num_free;
|
|
|
|
/* add empty slabs */
|
|
count += _num_free * objects_per_slab;
|
|
|
|
return count;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* \ingroup cxx_api
|
|
* Slab allocator for object of type `Type`.
|
|
*
|
|
* \tparam Type The type of the objects to manage.
|
|
* \tparam Slab_size Size of a slab.
|
|
* \tparam Max_free The maximum number of free slabs.
|
|
* \tparam Alloc The allocator for the slabs.
|
|
*/
|
|
template<typename Type, int Slab_size = L4_PAGESIZE,
|
|
int Max_free = 2, template<typename A> class Alloc = New_allocator >
|
|
class Slab : public Base_slab<sizeof(Type), Slab_size, Max_free, Alloc>
|
|
{
|
|
private:
|
|
typedef Base_slab<sizeof(Type), Slab_size, Max_free, Alloc> Base_type;
|
|
public:
|
|
|
|
typedef Type Obj_type;
|
|
|
|
Slab(typename Base_type::Slab_alloc const &alloc
|
|
= typename Base_type::Slab_alloc()) noexcept
|
|
: Base_slab<sizeof(Type), Slab_size, Max_free, Alloc>(alloc) {}
|
|
|
|
|
|
/**
|
|
* Allocate an object of type `Type`.
|
|
*
|
|
* \return A pointer to the object just allocated, or 0 on failure.
|
|
*
|
|
* \note The user is responsible for initializing the object.
|
|
*/
|
|
Type *alloc() noexcept
|
|
{
|
|
return reinterpret_cast<Type *>(Base_type::alloc());
|
|
}
|
|
|
|
/**
|
|
* Free the object addressed by `o`.
|
|
*
|
|
* \param o The pointer to the object to free.
|
|
* \pre The object must have been allocated with this allocator.
|
|
*/
|
|
void free(Type *o) noexcept
|
|
{ Base_slab<sizeof(Type), Slab_size, Max_free, Alloc>::free(o); }
|
|
};
|
|
|
|
|
|
/**
|
|
* \ingroup cxx_api
|
|
* Merged slab allocator (allocators for objects of the same size are merged
|
|
* together).
|
|
*
|
|
* \tparam Obj_size The size of an object managed by the slab allocator.
|
|
* \tparam Slab_size The size of a slab.
|
|
* \tparam Max_free The maximum number of free slabs.
|
|
* \tparam Alloc The allocator for the slabs.
|
|
*
|
|
* This slab allocator class is useful for merging slab allocators with the
|
|
* same parameters (equal `Obj_size`, `Slab_size`, `Max_free`, and
|
|
* `Alloc` parameters) together and share the overhead for the slab caches
|
|
* among all equal-sized objects.
|
|
*/
|
|
template< int Obj_size, int Slab_size = L4_PAGESIZE,
|
|
int Max_free = 2, template<typename A> class Alloc = New_allocator >
|
|
class Base_slab_static
|
|
{
|
|
private:
|
|
typedef Base_slab<Obj_size, Slab_size, Max_free, Alloc> _A;
|
|
static _A _a;
|
|
public:
|
|
typedef void Obj_type;
|
|
enum
|
|
{
|
|
/// Size of an object.
|
|
object_size = Obj_size,
|
|
/// Size of a slab.
|
|
slab_size = Slab_size,
|
|
/// Number of objects per slab.
|
|
objects_per_slab = _A::objects_per_slab,
|
|
/// Maximum number of free slabs.
|
|
max_free_slabs = Max_free,
|
|
};
|
|
|
|
/**
|
|
* Allocate an object.
|
|
*
|
|
* \note The user is responsible for initializing the object.
|
|
*/
|
|
void *alloc() noexcept { return _a.alloc(); }
|
|
|
|
/**
|
|
* Free the given object (`p`).
|
|
*
|
|
* \param p The pointer to the object to free.
|
|
* \pre `p` must be a pointer to an object allocated by this allocator.
|
|
*/
|
|
void free(void *p) noexcept { _a.free(p); }
|
|
|
|
/**
|
|
* Get the total number of objects managed by the slab allocator.
|
|
*
|
|
* \return The number of objects managed by the allocator (including the
|
|
* free objects).
|
|
* \note The value is the merged value for all equal parameterized
|
|
* Base_slab_static instances.
|
|
*/
|
|
unsigned total_objects() const noexcept { return _a.total_objects(); }
|
|
|
|
/**
|
|
* Get the number of free objects in the slab allocator.
|
|
*
|
|
* \return The number of free objects in all free and partially used
|
|
* slabs managed by this allocator.
|
|
* \note The value is the merged value for all equal parameterized
|
|
* Base_slab_static instances.
|
|
*/
|
|
unsigned free_objects() const noexcept { return _a.free_objects(); }
|
|
};
|
|
|
|
|
|
template< int _O, int _S, int _M, template<typename A> class Alloc >
|
|
typename Base_slab_static<_O,_S,_M,Alloc>::_A
|
|
Base_slab_static<_O,_S,_M,Alloc>::_a;
|
|
|
|
/**
|
|
* \ingroup cxx_api
|
|
* Merged slab allocator (allocators for objects of the same size are merged
|
|
* together).
|
|
*
|
|
* \tparam Type The type of the objects to manage.
|
|
* \tparam Slab_size The size of a slab.
|
|
* \tparam Max_free The maximum number of free slabs.
|
|
* \tparam Alloc The allocator for the slabs.
|
|
*
|
|
* This slab allocator class is useful for merging slab allocators with the
|
|
* same parameters (equal `sizeof(Type)`, `Slab_size`, `Max_free`, and
|
|
* `Alloc` parameters) together and share the overhead for the slab caches
|
|
* among all equal-sized objects.
|
|
*/
|
|
template<typename Type, int Slab_size = L4_PAGESIZE,
|
|
int Max_free = 2, template<typename A> class Alloc = New_allocator >
|
|
class Slab_static
|
|
: public Base_slab_static<sizeof(Type), Slab_size, Max_free, Alloc>
|
|
{
|
|
public:
|
|
|
|
typedef Type Obj_type;
|
|
/**
|
|
* Allocate an object of type `Type`.
|
|
*
|
|
* \return A pointer to the just allocated object, or 0 on failure.
|
|
*
|
|
* \note The object is not zeroed out by the slab allocator.
|
|
*/
|
|
Type *alloc() noexcept
|
|
{
|
|
return reinterpret_cast<Type *>(
|
|
Base_slab_static<sizeof(Type), Slab_size, Max_free, Alloc>::alloc());
|
|
}
|
|
};
|
|
|
|
}
|