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;
+ }
+ }
}