Metaslab: SlabLink field rather than parent class

Introduce Metaslab::from_link(SlabLink*) to encapsulate the "container of"
dance.  Note that Metaslab was not a standard layout type prior to this change
(since both SlabLink and Metaslab defined non-static data members), and so the
reinterpret_cast<>s replaced here with ::from_link() were UB, but everyone lays
out classes as one expects so it was fine in practice.

Most of the uses of ::from_link() are already guarded by checks that the link
pointer is not nullptr, but in src/mem/corealloc.h:/debug_is_empty_impl we shift
to testing the link pointer explicitly before converting to the metaslab.

Despite that Metaslab is now standard layout, we still don't fall back to the
inter-convertibility of a standard layout class and its first[*] data member
since we're going to want to put a common initial sequence across Metaslab and
ChunkRecord and the SlabLink isn't likely to be in it.
This commit is contained in:
Nathaniel Wesley Filardo
2021-08-29 18:57:42 +01:00
committed by Nathaniel Wesley Filardo
parent c70c23ad74
commit dac1ba6a6c
2 changed files with 24 additions and 8 deletions

View File

@@ -304,7 +304,7 @@ namespace snmalloc
while (curr != nullptr)
{
auto nxt = curr->get_next();
auto meta = reinterpret_cast<Metaslab*>(curr);
auto meta = Metaslab::from_link(curr);
if (meta->needed() == 0)
{
prev->pop();
@@ -348,7 +348,7 @@ namespace snmalloc
// Wake slab up.
meta->set_not_sleeping(sizeclass);
alloc_classes[sizeclass].insert(meta);
alloc_classes[sizeclass].insert(&meta->link);
alloc_classes[sizeclass].length++;
#ifdef SNMALLOC_TRACING
@@ -593,7 +593,7 @@ namespace snmalloc
auto& sl = alloc_classes[sizeclass];
if (likely(!(sl.is_empty())))
{
auto meta = reinterpret_cast<Metaslab*>(sl.pop());
auto meta = Metaslab::from_link(sl.pop());
// Drop length of sl, and empty count if it was empty.
alloc_classes[sizeclass].length--;
if (meta->needed() == 0)
@@ -736,17 +736,18 @@ namespace snmalloc
auto test = [&result](auto& queue) {
if (!queue.is_empty())
{
auto curr = reinterpret_cast<Metaslab*>(queue.get_next());
auto curr = queue.get_next();
while (curr != nullptr)
{
if (curr->needed() != 0)
auto currmeta = Metaslab::from_link(curr);
if (currmeta->needed() != 0)
{
if (result != nullptr)
*result = false;
else
error("debug_is_empty: found non-empty allocator");
}
curr = reinterpret_cast<Metaslab*>(curr->get_next());
curr = currmeta->link.get_next();
}
}
};

View File

@@ -16,10 +16,19 @@ namespace snmalloc
// The Metaslab represent the status of a single slab.
// This can be either a short or a standard slab.
class alignas(CACHELINE_SIZE) Metaslab : public SlabLink
class alignas(CACHELINE_SIZE) Metaslab
{
public:
constexpr Metaslab() : SlabLink(true) {}
// TODO: Annotate with CHERI subobject unbound for pointer arithmetic
SlabLink link;
constexpr Metaslab() : link(true) {}
/**
* Metaslab::link points at another link field. To get the actual Metaslab,
* use this encapsulation of the container-of logic.
*/
static Metaslab* from_link(SlabLink* ptr);
/**
* Data-structure for building the free list for this slab.
@@ -160,6 +169,12 @@ namespace snmalloc
}
};
Metaslab* Metaslab::from_link(SlabLink* lptr)
{
return pointer_offset_signed<Metaslab>(
lptr, -static_cast<ptrdiff_t>(offsetof(Metaslab, link)));
}
struct RemoteAllocator;
/**