From 207be9686fdee0108e29f3d35bf2829c0334bfcf Mon Sep 17 00:00:00 2001 From: skaller Date: Tue, 19 Mar 2024 06:59:08 +1100 Subject: [PATCH] System allocator with rough test. --- src/packages/rt-alloc.fdoc | 120 +++++++++++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 13 deletions(-) diff --git a/src/packages/rt-alloc.fdoc b/src/packages/rt-alloc.fdoc index 4206bbceb..956faafdf 100644 --- a/src/packages/rt-alloc.fdoc +++ b/src/packages/rt-alloc.fdoc @@ -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 @@ -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; @@ -639,6 +646,7 @@ certainly will 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; @@ -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; } @@ -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 +#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 reqs; + ::std::vector allocs; + + // the reqs MUST be sorted from low to high block size + system_allocator_t(alloc_ref_t parent, ::std::vector 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 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 fixup (::std::vector input) { + ::std::vector 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 @ @@ -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{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; + } + } }