Randomise slab filling (#397)

# Free List builder track length

This commit makes the free list builder track the length of the lists in
the Random case.

# Refactor free list creation.

Minor refactoring to share code between the new free list and existing
path.

# Randomise slab filling

Knowing when a slab is going to become full makes it easier to by pass
the free list entries as protection for OOB writes.  This commit
randomises when a slab will become full.

This commit changes two things

* the free list builder can return some fraction of the deallocations
  on a slab.
* when there is a single free slab, we can with some probability
  allocate an additional slab.

These two combine to make it difficult to predict when a slab will be
free.

# Apply suggestions from code review

Co-authored-by: Nathaniel Wesley Filardo <nfilardo@microsoft.com>
This commit is contained in:
Matthew Parkinson
2021-10-07 15:51:18 +01:00
committed by GitHub
parent 8ac2adc4e5
commit bba66e4f7e
3 changed files with 119 additions and 51 deletions

View File

@@ -188,7 +188,7 @@ namespace snmalloc
static SNMALLOC_FAST_PATH void alloc_new_list(
CapPtr<void, CBChunk>& bumpptr,
FreeListIter& fast_free_list,
Metaslab* meta,
size_t rsize,
size_t slab_size,
LocalEntropy& entropy)
@@ -197,8 +197,7 @@ namespace snmalloc
auto& key = entropy.get_free_list_key();
FreeListBuilder<false> b;
SNMALLOC_ASSERT(b.empty());
auto& b = meta->free_queue;
#ifdef SNMALLOC_CHECK_CLIENT
// Structure to represent the temporary list elements
@@ -243,7 +242,7 @@ namespace snmalloc
auto curr_ptr = start_ptr;
do
{
b.add(FreeObject::make(curr_ptr.as_void()), key);
b.add(FreeObject::make(curr_ptr.as_void()), key, entropy);
curr_ptr = curr_ptr->next;
} while (curr_ptr != start_ptr);
#else
@@ -256,16 +255,14 @@ namespace snmalloc
#endif
// This code consumes everything up to slab_end.
bumpptr = slab_end;
SNMALLOC_ASSERT(!b.empty());
b.close(fast_free_list, key);
}
ChunkRecord* clear_slab(Metaslab* meta, sizeclass_t sizeclass)
{
auto& key = entropy.get_free_list_key();
FreeListIter fl;
meta->free_queue.close(fl, key);
auto more = meta->free_queue.close(fl, key);
UNUSED(more);
void* p = finish_alloc_no_zero(fl.take(key), sizeclass);
#ifdef SNMALLOC_CHECK_CLIENT
@@ -278,6 +275,21 @@ namespace snmalloc
count++;
}
// Check the list contains all the elements
SNMALLOC_ASSERT(
(count + more) == snmalloc::sizeclass_to_slab_object_count(sizeclass));
if (more > 0)
{
auto no_more = meta->free_queue.close(fl, key);
SNMALLOC_ASSERT(no_more == 0);
UNUSED(no_more);
while (!fl.empty())
{
fl.take(key);
count++;
}
}
SNMALLOC_ASSERT(
count == snmalloc::sizeclass_to_slab_object_count(sizeclass));
#endif
@@ -582,15 +594,32 @@ namespace snmalloc
// Look to see if we can grab a free list.
auto& sl = alloc_classes[sizeclass].queue;
if (likely(!(sl.is_empty())))
if (likely(alloc_classes[sizeclass].length > 0))
{
#ifdef SNMALLOC_CHECK_CLIENT
// Occassionally don't use the last list.
if (
unlikely(alloc_classes[sizeclass].length == 1) &&
(entropy.next_bit() == 0))
{
return small_alloc_slow<zero_mem>(sizeclass, fast_free_list, rsize);
}
#endif
auto meta = sl.pop();
// Drop length of sl, and empty count if it was empty.
alloc_classes[sizeclass].length--;
if (meta->needed() == 0)
alloc_classes[sizeclass].unused--;
auto p = Metaslab::alloc(meta, fast_free_list, entropy, sizeclass);
auto [p, still_active] =
Metaslab::alloc_free_list(meta, fast_free_list, entropy, sizeclass);
if (still_active)
{
alloc_classes[sizeclass].length++;
sl.insert(meta);
}
return finish_alloc<zero_mem, SharedStateHandle>(p, sizeclass);
}
@@ -641,16 +670,20 @@ namespace snmalloc
return nullptr;
}
// Build a free list for the slab
alloc_new_list(slab, fast_free_list, rsize, slab_size, entropy);
// Set meta slab to empty.
meta->initialise(sizeclass);
auto& key = entropy.get_free_list_key();
// Build a free list for the slab
alloc_new_list(slab, meta, rsize, slab_size, entropy);
// take an allocation from the free list
auto p = fast_free_list.take(key);
auto [p, still_active] =
Metaslab::alloc_free_list(meta, fast_free_list, entropy, sizeclass);
if (still_active)
{
alloc_classes[sizeclass].length++;
alloc_classes[sizeclass].queue.insert(meta);
}
return finish_alloc<zero_mem, SharedStateHandle>(p, sizeclass);
}