Files
jsix/src/tests/heap_manager.cpp
2019-02-28 00:37:00 -08:00

192 lines
4.4 KiB
C++

#include <chrono>
#include <random>
#include <vector>
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include "kutil/memory.h"
#include "kutil/heap_manager.h"
#include "catch.hpp"
using namespace kutil;
static std::vector<void *> memory;
static size_t total_alloc_size = 0;
static size_t total_alloc_calls = 0;
const size_t hs = 0x10; // header size
const size_t max_block = 1 << 16;
std::vector<size_t> sizes = {
16000, 8000, 4000, 4000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 150,
150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 48, 48, 48, 13 };
void * grow_callback(size_t length)
{
total_alloc_calls += 1;
total_alloc_size += length;
void *p = aligned_alloc(max_block, length * 2);
memory.push_back(p);
return p;
}
void free_memory()
{
for (void *p : memory) ::free(p);
memory.clear();
total_alloc_size = 0;
total_alloc_calls = 0;
}
TEST_CASE( "Buddy blocks tests", "[memory buddy]" )
{
using clock = std::chrono::system_clock;
unsigned seed = clock::now().time_since_epoch().count();
std::default_random_engine rng(seed);
heap_manager mm(grow_callback);
// The ctor should have allocated an initial block
CHECK( total_alloc_size == max_block );
CHECK( total_alloc_calls == 1 );
// Blocks should be:
// 16: 0-64K
std::vector<void *> allocs(6);
for (int i = 0; i < 6; ++i)
allocs[i] = mm.allocate(150); // size 8
// Should not have grown
CHECK( total_alloc_size == max_block );
CHECK( total_alloc_calls == 1 );
CHECK( memory[0] != nullptr );
// Blocks should be:
// 16: [0-64K]
// 15: [0-32K], 32K-64K
// 14: [0-16K], 16K-32K
// 13: [0-8K], 8K-16K
// 12: [0-4K], 4K-8K
// 11: [0-2K], 2K-4K
// 10: [0-1K, 1-2K]
// 9: [0, 512, 1024], 1536
// 8: [0, 256, 512, 768, 1024, 1280]
// We have free memory at 1526 and 2K, but we should get 4K
void *big = mm.allocate(4000); // size 12
REQUIRE( big == offset_pointer(memory[0], 4096 + hs) );
mm.free(big);
// free up 512
mm.free(allocs[3]);
mm.free(allocs[4]);
// Blocks should be:
// ...
// 9: [0, 512, 1024], 1536
// 8: [0, 256, 512], 768, 1024, [1280]
// A request for a 512-block should not cross the buddy divide
big = mm.allocate(500); // size 9
REQUIRE( big >= offset_pointer(memory[0], 1536 + hs) );
mm.free(big);
mm.free(allocs[0]);
mm.free(allocs[1]);
mm.free(allocs[2]);
mm.free(allocs[5]);
allocs.clear();
std::shuffle(sizes.begin(), sizes.end(), rng);
allocs.reserve(sizes.size());
for (size_t size : sizes)
allocs.push_back(mm.allocate(size));
std::shuffle(allocs.begin(), allocs.end(), rng);
for (void *p: allocs)
mm.free(p);
allocs.clear();
big = mm.allocate(64000);
// If everything was freed / joined correctly, that should not have allocated
CHECK( total_alloc_size == max_block );
CHECK( total_alloc_calls == 1 );
// And we should have gotten back the start of memory
CHECK( big == offset_pointer(memory[0], hs) );
free_memory();
}
bool check_in_memory(void *p)
{
for (void *mem : memory)
if (p >= mem && p <= offset_pointer(mem, max_block))
return true;
return false;
}
TEST_CASE( "Non-contiguous blocks tests", "[memory buddy]" )
{
using clock = std::chrono::system_clock;
unsigned seed = clock::now().time_since_epoch().count();
std::default_random_engine rng(seed);
heap_manager mm(grow_callback);
std::vector<void *> allocs;
const int blocks = 3;
for (int i = 0; i < blocks; ++i) {
void *p = mm.allocate(64000);
REQUIRE( memory[i] != nullptr );
REQUIRE( p == offset_pointer(memory[i], hs) );
allocs.push_back(p);
}
CHECK( total_alloc_size == max_block * blocks );
CHECK( total_alloc_calls == blocks );
for (void *p : allocs)
mm.free(p);
allocs.clear();
allocs.reserve(sizes.size() * blocks);
for (int i = 0; i < blocks; ++i) {
std::shuffle(sizes.begin(), sizes.end(), rng);
for (size_t size : sizes)
allocs.push_back(mm.allocate(size));
}
for (void *p : allocs)
CHECK( check_in_memory(p) );
std::shuffle(allocs.begin(), allocs.end(), rng);
for (void *p: allocs)
mm.free(p);
allocs.clear();
CHECK( total_alloc_size == max_block * blocks );
CHECK( total_alloc_calls == blocks );
for (int i = 0; i < blocks; ++i)
allocs.push_back(mm.allocate(64000));
// If everything was freed / joined correctly, that should not have allocated
CHECK( total_alloc_size == max_block * blocks );
CHECK( total_alloc_calls == blocks );
for (void *p : allocs)
CHECK( check_in_memory(p) );
free_memory();
}