@@ -32,6 +32,34 @@ namespace snmalloc
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper for wrapping values.
|
||||
*
|
||||
* Wraps on read. This allows code to trust the value is in range, even when
|
||||
* there is a memory corruption.
|
||||
**/
|
||||
template<size_t length, typename T>
|
||||
class Mod
|
||||
{
|
||||
static_assert(
|
||||
length == bits::next_pow2_const(length), "Must be a power of two.");
|
||||
|
||||
private:
|
||||
T value;
|
||||
|
||||
public:
|
||||
operator T()
|
||||
{
|
||||
return (T)(value & (length - 1));
|
||||
}
|
||||
|
||||
T& operator=(const T v)
|
||||
{
|
||||
value = v;
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t length, typename T>
|
||||
class ModArray
|
||||
{
|
||||
|
||||
@@ -359,12 +359,12 @@ namespace snmalloc
|
||||
{
|
||||
RemoteAllocator* target = super->get_allocator();
|
||||
Slab* slab = Slab::get(p);
|
||||
Metaslab* meta = super->get_meta(slab);
|
||||
Metaslab& meta = super->get_meta(slab);
|
||||
|
||||
// Reading a remote sizeclass won't fail, since the other allocator
|
||||
// can't reuse the slab, as we have not yet deallocated this
|
||||
// pointer.
|
||||
uint8_t sizeclass = meta->sizeclass;
|
||||
uint8_t sizeclass = meta.sizeclass;
|
||||
|
||||
if (super->get_allocator() == public_state())
|
||||
small_dealloc(super, p, sizeclass);
|
||||
@@ -411,9 +411,9 @@ namespace snmalloc
|
||||
if (size == PMSuperslab)
|
||||
{
|
||||
Slab* slab = Slab::get(p);
|
||||
Metaslab* meta = super->get_meta(slab);
|
||||
Metaslab& meta = super->get_meta(slab);
|
||||
|
||||
uint8_t sc = meta->sizeclass;
|
||||
uint8_t sc = meta.sizeclass;
|
||||
size_t slab_end = (size_t)slab + SLAB_SIZE - 1;
|
||||
|
||||
return external_pointer<location>(p, sc, slab_end);
|
||||
@@ -471,9 +471,9 @@ namespace snmalloc
|
||||
// Reading a remote sizeclass won't fail, since the other allocator
|
||||
// can't reuse the slab, as we have no yet deallocated this pointer.
|
||||
Slab* slab = Slab::get(p);
|
||||
Metaslab* meta = super->get_meta(slab);
|
||||
Metaslab& meta = super->get_meta(slab);
|
||||
|
||||
return sizeclass_to_size(meta->sizeclass);
|
||||
return sizeclass_to_size(meta.sizeclass);
|
||||
}
|
||||
else if (size == PMMediumslab)
|
||||
{
|
||||
@@ -755,7 +755,7 @@ namespace snmalloc
|
||||
if ((allow_reserve == NoReserve) && (super == nullptr))
|
||||
return super;
|
||||
|
||||
super->init(public_state(), large_allocator.memory_provider);
|
||||
super->init(public_state());
|
||||
pagemap().set_slab(super);
|
||||
super_available.insert(super);
|
||||
return super;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ds/dllist.h"
|
||||
#include "../ds/helpers.h"
|
||||
#include "sizeclass.h"
|
||||
|
||||
namespace snmalloc
|
||||
@@ -33,7 +34,7 @@ namespace snmalloc
|
||||
{
|
||||
private:
|
||||
// How many entries are used in this slab.
|
||||
uint16_t used;
|
||||
uint16_t used = 0;
|
||||
|
||||
public:
|
||||
// Bump free list of unused entries in this sizeclass.
|
||||
@@ -51,14 +52,15 @@ namespace snmalloc
|
||||
// The terminal value in the free list, and the terminal value in
|
||||
// the SlabLink previous field will alias. The SlabLink uses ~0 for
|
||||
// its terminal value to be a valid terminal bump ptr.
|
||||
uint16_t head;
|
||||
Mod<SLAB_SIZE, uint16_t> head;
|
||||
// When a slab has free space it will be on the has space list for
|
||||
// that size class. We use an empty block in this slab to be the
|
||||
// doubly linked node into that size class's free list.
|
||||
uint16_t link;
|
||||
Mod<SLAB_SIZE, uint16_t> link;
|
||||
|
||||
uint8_t sizeclass;
|
||||
uint8_t next;
|
||||
// Initially zero to encode the superslabs relative list of slabs.
|
||||
uint8_t next = 0;
|
||||
|
||||
void add_use()
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace snmalloc
|
||||
return (Slab*)((size_t)p & SLAB_MASK);
|
||||
}
|
||||
|
||||
Metaslab* get_meta()
|
||||
Metaslab& get_meta()
|
||||
{
|
||||
Superslab* super = Superslab::get(this);
|
||||
return super->get_meta(this);
|
||||
@@ -27,22 +27,22 @@ namespace snmalloc
|
||||
|
||||
SlabLink* get_link()
|
||||
{
|
||||
return get_meta()->get_link(this);
|
||||
return get_meta().get_link(this);
|
||||
}
|
||||
|
||||
template<ZeroMem zero_mem, typename MemoryProvider>
|
||||
void* alloc(SlabList* sc, size_t rsize, MemoryProvider& memory_provider)
|
||||
{
|
||||
// Read the head from the metadata stored in the superslab.
|
||||
Metaslab* meta = get_meta();
|
||||
uint16_t head = meta->head;
|
||||
Metaslab& meta = get_meta();
|
||||
uint16_t head = meta.head;
|
||||
|
||||
assert(rsize == sizeclass_to_size(meta->sizeclass));
|
||||
meta->debug_slab_invariant(is_short(), this);
|
||||
assert(sc->get_head() == (SlabLink*)((size_t)this + meta->link));
|
||||
assert(!meta->is_full());
|
||||
assert(rsize == sizeclass_to_size(meta.sizeclass));
|
||||
meta.debug_slab_invariant(is_short(), this);
|
||||
assert(sc->get_head() == (SlabLink*)((size_t)this + meta.link));
|
||||
assert(!meta.is_full());
|
||||
|
||||
meta->add_use();
|
||||
meta.add_use();
|
||||
|
||||
void* p;
|
||||
|
||||
@@ -52,24 +52,24 @@ namespace snmalloc
|
||||
|
||||
// Read the next slot from the memory that's about to be allocated.
|
||||
uint16_t next = *(uint16_t*)p;
|
||||
meta->head = next;
|
||||
meta.head = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This slab is being bump allocated.
|
||||
p = (void*)((size_t)this + head - 1);
|
||||
meta->head = (head + (uint16_t)rsize) & (SLAB_SIZE - 1);
|
||||
if (meta->head == 1)
|
||||
meta.head = (head + (uint16_t)rsize) & (SLAB_SIZE - 1);
|
||||
if (meta.head == 1)
|
||||
{
|
||||
meta->set_full();
|
||||
meta.set_full();
|
||||
}
|
||||
}
|
||||
|
||||
// If we're full, we're no longer the current slab for this sizeclass
|
||||
if (meta->is_full())
|
||||
if (meta.is_full())
|
||||
sc->pop();
|
||||
|
||||
meta->debug_slab_invariant(is_short(), this);
|
||||
meta.debug_slab_invariant(is_short(), this);
|
||||
|
||||
if (zero_mem == YesZero)
|
||||
{
|
||||
@@ -87,15 +87,15 @@ namespace snmalloc
|
||||
inline typename Superslab::Action dealloc(
|
||||
SlabList* sc, Superslab* super, void* p, MemoryProvider& memory_provider)
|
||||
{
|
||||
Metaslab* meta = super->get_meta(this);
|
||||
Metaslab& meta = super->get_meta(this);
|
||||
|
||||
bool was_full = meta->is_full();
|
||||
meta->debug_slab_invariant(is_short(), this);
|
||||
meta->sub_use();
|
||||
bool was_full = meta.is_full();
|
||||
meta.debug_slab_invariant(is_short(), this);
|
||||
meta.sub_use();
|
||||
|
||||
#ifndef SNMALLOC_SAFE_CLIENT
|
||||
if (!is_multiple_of_sizeclass(
|
||||
sizeclass_to_size(meta->sizeclass),
|
||||
sizeclass_to_size(meta.sizeclass),
|
||||
(uintptr_t)this + SLAB_SIZE - (uintptr_t)p))
|
||||
{
|
||||
error("Not deallocating start of an object");
|
||||
@@ -105,17 +105,17 @@ namespace snmalloc
|
||||
if (was_full)
|
||||
{
|
||||
// We are not on the sizeclass list.
|
||||
if (!meta->is_unused())
|
||||
if (!meta.is_unused())
|
||||
{
|
||||
// Update the head and the sizeclass link.
|
||||
uint16_t index = pointer_to_index(p);
|
||||
meta->head = index;
|
||||
assert(meta->valid_head(is_short()));
|
||||
meta->link = index;
|
||||
meta.head = index;
|
||||
assert(meta.valid_head(is_short()));
|
||||
meta.link = index;
|
||||
|
||||
// Push on the list of slabs for this sizeclass.
|
||||
sc->insert(meta->get_link(this));
|
||||
meta->debug_slab_invariant(is_short(), this);
|
||||
sc->insert(meta.get_link(this));
|
||||
meta.debug_slab_invariant(is_short(), this);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -126,10 +126,10 @@ namespace snmalloc
|
||||
return super->dealloc_slab(this, memory_provider);
|
||||
}
|
||||
}
|
||||
else if (meta->is_unused())
|
||||
else if (meta.is_unused())
|
||||
{
|
||||
// Remove from the sizeclass list and dealloc on the superslab.
|
||||
sc->remove(meta->get_link(this));
|
||||
sc->remove(meta.get_link(this));
|
||||
|
||||
if (is_short())
|
||||
return super->dealloc_short_slab(memory_provider);
|
||||
@@ -139,20 +139,20 @@ namespace snmalloc
|
||||
else
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
sc->debug_check_contains(meta->get_link(this));
|
||||
sc->debug_check_contains(meta.get_link(this));
|
||||
#endif
|
||||
|
||||
// Update the head and the next pointer in the free list.
|
||||
uint16_t head = meta->head;
|
||||
uint16_t head = meta.head;
|
||||
uint16_t current = pointer_to_index(p);
|
||||
|
||||
// Set the head to the memory being deallocated.
|
||||
meta->head = current;
|
||||
assert(meta->valid_head(is_short()));
|
||||
meta.head = current;
|
||||
assert(meta.valid_head(is_short()));
|
||||
|
||||
// Set the next pointer to the previous head.
|
||||
*(uint16_t*)p = head;
|
||||
meta->debug_slab_invariant(is_short(), this);
|
||||
meta.debug_slab_invariant(is_short(), this);
|
||||
}
|
||||
return Superslab::NoSlabReturn;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ds/helpers.h"
|
||||
#include "allocslab.h"
|
||||
#include "metaslab.h"
|
||||
|
||||
#include <cstring>
|
||||
@@ -30,14 +32,14 @@ namespace snmalloc
|
||||
// are the relative offset to the next entry minus 1. This means that
|
||||
// all zeros is a list that chains through all the blocks, so the zero
|
||||
// initialised memory requires no more work.
|
||||
uint8_t head;
|
||||
Mod<SLAB_COUNT, uint8_t> head;
|
||||
|
||||
// Represents twice the number of full size slabs used
|
||||
// plus 1 for the short slab. i.e. using 3 slabs and the
|
||||
// short slab would be 6 + 1 = 7
|
||||
uint16_t used;
|
||||
|
||||
Metaslab meta[SLAB_COUNT];
|
||||
ModArray<SLAB_COUNT, Metaslab> meta;
|
||||
|
||||
// Used size_t as results in better code in MSVC
|
||||
size_t slab_to_index(Slab* slab)
|
||||
@@ -74,8 +76,7 @@ namespace snmalloc
|
||||
return sizeclass <= h;
|
||||
}
|
||||
|
||||
template<typename MemoryProvider>
|
||||
void init(RemoteAllocator* alloc, MemoryProvider& memory_provider)
|
||||
void init(RemoteAllocator* alloc)
|
||||
{
|
||||
allocator = alloc;
|
||||
|
||||
@@ -91,10 +92,24 @@ namespace snmalloc
|
||||
{
|
||||
// If this wasn't previously Fresh, we need to zero some things.
|
||||
used = 0;
|
||||
memory_provider.zero(meta, SLAB_COUNT * sizeof(Metaslab));
|
||||
for (size_t i = 0; i < SLAB_COUNT; i++)
|
||||
{
|
||||
new (&(meta[i])) Metaslab();
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
auto curr = head;
|
||||
for (size_t i = 0; i < SLAB_COUNT - used - 1; i++)
|
||||
{
|
||||
curr = (curr + meta[curr].next + 1) & (SLAB_COUNT - 1);
|
||||
}
|
||||
assert(curr == 0);
|
||||
|
||||
meta[0].set_unused();
|
||||
for (size_t i = 0; i < SLAB_COUNT; i++)
|
||||
{
|
||||
assert(meta[i].is_unused());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,9 +154,9 @@ namespace snmalloc
|
||||
}
|
||||
}
|
||||
|
||||
Metaslab* get_meta(Slab* slab)
|
||||
Metaslab& get_meta(Slab* slab)
|
||||
{
|
||||
return &meta[slab_to_index(slab)];
|
||||
return meta[slab_to_index(slab)];
|
||||
}
|
||||
|
||||
template<typename MemoryProvider>
|
||||
@@ -167,15 +182,16 @@ namespace snmalloc
|
||||
template<typename MemoryProvider>
|
||||
Slab* alloc_slab(uint8_t sizeclass, MemoryProvider& memory_provider)
|
||||
{
|
||||
Slab* slab = (Slab*)((size_t)this + ((size_t)head << SLAB_BITS));
|
||||
uint8_t h = head;
|
||||
Slab* slab = (Slab*)((size_t)this + ((size_t)h << SLAB_BITS));
|
||||
|
||||
uint8_t n = meta[head].next;
|
||||
uint8_t n = meta[h].next;
|
||||
|
||||
meta[head].head = get_slab_offset(sizeclass, false);
|
||||
meta[head].sizeclass = sizeclass;
|
||||
meta[head].link = SLABLINK_INDEX;
|
||||
meta[h].head = get_slab_offset(sizeclass, false);
|
||||
meta[h].sizeclass = sizeclass;
|
||||
meta[h].link = SLABLINK_INDEX;
|
||||
|
||||
head = head + n + 1;
|
||||
head = h + n + 1;
|
||||
used += 2;
|
||||
|
||||
if (decommit_strategy == DecommitAll)
|
||||
|
||||
Reference in New Issue
Block a user