Skip to content

Commit

Permalink
System allocator with rough test.
Browse files Browse the repository at this point in the history
  • Loading branch information
skaller committed Mar 18, 2024
1 parent 1cd8b61 commit 207be96
Showing 1 changed file with 107 additions and 13 deletions.
120 changes: 107 additions & 13 deletions src/packages/rt-alloc.fdoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@tangler block.hpp = share/lib/rtl/rt/block.hpp
@tangler ts_allocator.hpp = share/lib/rtl/rt/ts_allocator.hpp
@tangler ring_allocator.hpp = share/lib/rtl/rt/ring_allocator.hpp
@tangler system_allocator.hpp = share/lib/rtl/rt/system_allocator.hpp
@tangler test01.cxx = $PWD/test01.cxx
@h1 Real Time Allocators
In programming systems including C and C++, user space allocation is usually handled
Expand Down Expand Up @@ -267,6 +268,12 @@ struct allocator_t {
// destructor does nothing special but the parent will be destroyed
virtual ~allocator_t(){}

// no copy, move, copy assign or move assign
allocator_t(allocator_t const&)= delete;
allocator_t(allocator_t &&)= delete;
allocator_t& operator=(allocator_t const&)= delete;
allocator_t& operator=(allocator_t&&)= delete;

// must report the size in bytes of the object
virtual size_t size()const=0;

Expand Down Expand Up @@ -639,6 +646,7 @@ certainly <em>will</em> occur in test cases!
#define RING_ALLOCATOR

#include "allocator.hpp"

// client request entry: client needs n_blocks of size block_size
struct mem_req_t {
size_t block_size;
Expand Down Expand Up @@ -669,18 +677,14 @@ struct ring_buffer_t : public allocator_t {
// the tail points at a populated spot
void *allocate(size_t) override {
size_t old_tail = tail.load(::std::memory_order_relaxed);
retry:
size_t new_tail = (old_tail + 1) % mreq.n_blocks;
if(!tail.compare_exchange_weak(old_tail, new_tail)) goto retry;
while(!tail.compare_exchange_weak(old_tail, (old_tail + 1) % mreq.n_blocks));
return buffer[old_tail];
}

// the head points at a free slot
void deallocate(void *p, size_t) override {
size_t old_head = head.load(::std::memory_order_relaxed);
retry:
size_t new_head= (old_head + 1) % mreq.n_blocks;
if(!head.compare_exchange_weak(old_head, new_head)) goto retry;
while(!head.compare_exchange_weak(old_head, (old_head + 1) % mreq.n_blocks));
buffer[old_head] = p;
}

Expand All @@ -694,8 +698,82 @@ struct ring_buffer_t : public allocator_t {
static alloc_ref_t create(alloc_ref_t parent, mem_req_t req) {
return allocator_t::create (new ring_buffer_t (parent, req));
}
};
#endif
@

@h1 SYSTEM ALLOCATOR
This is the main allocator.

@tangle system_allocator.hpp
#ifndef SYSTEM_ALLOCATOR
#define SYSTEM_ALLOCATOR
#include <vector>
#include "ring_allocator.hpp"

// FIXME: WARNING: the system allocator MUST BE CONSTRUCTED AT STARTUP
// BECAUSE it uses std::vector, which does dynamic allocation
// using C++ standard allocator

// We really should use a C array but this version will suffice
// for testing

struct system_allocator_t : public allocator_t {
::std::vector<mem_req_t> reqs;
::std::vector<alloc_ref_t> allocs;

// the reqs MUST be sorted from low to high block size
system_allocator_t(alloc_ref_t parent, ::std::vector<mem_req_t> reqs_) :
reqs(reqs_)
{
for (auto req : reqs) allocs.push_back(ring_buffer_t::create(parent, req));
}

// find the index of the lowest value higher than the given one
// the request must be less than or equal to the largest (and last) block size
size_t find(size_t n) {
size_t j = 0;
while(n > reqs[j].block_size) ++j;
return j;
}

void *allocate (size_t n) override { return allocs[find(n)].allocate(n); }
void deallocate (void *p, size_t n) override { return allocs[find(n)].deallocate(p,n); }

size_t size() const override { return sizeof(*this); }

// factory function
static alloc_ref_t create(alloc_ref_t parent, ::std::vector<mem_req_t> reqs) {
return allocator_t::create (new system_allocator_t (parent, reqs));
}
};

// Helper function that takes a vector of requests and sorts them
// low to high, merging the block counts of requests for the same size
::std::vector<mem_req_t> fixup (::std::vector<mem_req_t> input) {
::std::vector<mem_req_t> output;
for (auto req : input) {
for( int idx = 0; idx <= output.size(); ++idx) {
// past last element
if(idx == output.size()) output.push_back(req);

// found equal so add to block count
else if(req.block_size == output[idx].block_size) {
output[idx].n_blocks += req.n_blocks;
break;
}

// overshot so inset new request
else if(req.block_size > output[idx].block_size) {
output.insert(output.begin() + idx, req);
break;
}
}
}
return output;
}


#endif
@

Expand All @@ -710,13 +788,29 @@ using namespace std;
#include "block.hpp"
#include "ts_allocator.hpp"
#include "ring_allocator.hpp"
#include "system_allocator.hpp"

int main () {
cout << "Hello World" << endl;
auto a1 = malloc_free_allocator_t::create();
auto a2 = dynamic_bump_allocator_t::create(a1, 1000);
unsigned char block_buffer[1000];
auto a3 = static_block_allocator_t::create(block_buffer, 100);
auto a4 = ts_allocator_t::create(a3, a2);
auto a5 = ring_buffer_t::create( a1, mem_req_t { 100,100 });
cout << "Hello World" << endl;
auto a1 = malloc_free_allocator_t::create();
auto a2 = dynamic_bump_allocator_t::create(a1, 1000);
unsigned char block_buffer[1000];
auto a3 = static_block_allocator_t::create(block_buffer, 100);
auto a4 = ts_allocator_t::create(a3, a2);
auto a5 = ring_buffer_t::create( a1, mem_req_t { 100,100 });
auto config = vector<mem_req_t>{
mem_req_t{16, 10},
mem_req_t{32, 10},
mem_req_t{64, 10},
mem_req_t{128, 10},
mem_req_t{256, 10}
};
auto a6 = system_allocator_t(a1, config);
for (int z : { 18, 43, 75 }) {
cout << "Request size " << z << endl;
for(int i = 0; i < 6; ++i) {
void *p = a6.allocate (z);
cout << "allocation " << i << " -> " << p << endl;
}
}
}

0 comments on commit 207be96

Please sign in to comment.