From c37595578a863d980d3371bb8700f759dfd616cd Mon Sep 17 00:00:00 2001 From: kpl Date: Mon, 27 Jan 2020 17:51:56 -0500 Subject: [PATCH] cwNbMem.h/cpp : Initial commit. --- cwNbMem.cpp | 407 ++++++++++++++++++++++++++++++++++++++++++++++++++++ cwNbMem.h | 17 +++ 2 files changed, 424 insertions(+) create mode 100644 cwNbMem.cpp create mode 100644 cwNbMem.h diff --git a/cwNbMem.cpp b/cwNbMem.cpp new file mode 100644 index 0000000..d736345 --- /dev/null +++ b/cwNbMem.cpp @@ -0,0 +1,407 @@ +#include "cwCommon.h" +#include "cwLog.h" +#include "cwCommonImpl.h" +#include "cwMem.h" +#include "cwNbMem.h" +#include "cwThread.h" + +/* +======================= +0000 void* mem - pointer to base of memory (also pointer to this block_t record) +0008 var_t* var - pointer to first var +0016 block_t* link - link to next block +0024 unsigned byteN - count of bytes following first +0028 unsigned - (reserved) +0032 unsigned byteN - data byte count (16) +0036 unsigned refN - reference count +0040 <----- first data word +0044 +0048 +0052 +0056 unsigned byteN - data byte count (16) +0060 unsigned refN - reference count +0064 <----- first data word +0068 +0072 +0076 + + + */ + +namespace cw +{ + namespace nbmem + { + typedef struct var_str + { + std::atomic refN; // + unsigned byteN; // byteN is the size of the data area of this variable (The total size is (sizeof(var_t) + byteN) + unsigned reserved; // + } var_t; + + typedef struct block_str + { + char* mem; // base of data memory for this block | block |var0|data0|var1|data1| |varx|datax| + var_t* var; // + struct block_str* link; // + unsigned byteN; // mem bytes = sizeof(block_t) + byteN + unsigned res0; // + } block_t; + + typedef struct nbmem_str + { + unsigned blockByteN; // + block_t* base; // + } nbmem_t; + + nbmem_t* _handleToPtr( handle_t h ) + { return handleToPtr(h); } + + rc_t _destroy( nbmem_t* p ) + { + rc_t rc = kOkRC; + block_t* b = p->base; + + while( b!=nullptr ) + { + block_t* b0 = b->link; + + mem::release(b); + b = b0; + } + + mem::release(p); + return rc; + } + + block_t* _alloc_block( unsigned byteN ) + { + unsigned blockByteN = byteN + sizeof(block_t); + char* mem = mem::allocZ(blockByteN); + block_t* b = (block_t*)mem; + + b->mem = mem; + b->byteN = byteN; + b->link = nullptr; + b->var = (var_t*)( mem + sizeof(block_t) ); + b->var->byteN = byteN - sizeof(var_t); + b->var->refN = 0; + + return b; + } + + // + void* _alloc_var( var_t* v, unsigned byteN, char* dataAreaEnd ) + { + // + while( (char*)v < dataAreaEnd ) + { + char* b = (char*)v; // base of this variable memory + char* d = b + sizeof(var_t); // ptr to variable data area + + // is this variable available? + if( v->refN == 0 && v->byteN > byteN ) + { + + v->refN = 1; // ATOMIC mark variable as unavailable - should set thread id NOT 1 + + // if there is more than sizeof(var_t) extra data space ... + if( v->byteN > byteN + sizeof(var_t) ) + { + // ... then split the space with another variable + unsigned byte0N = v->byteN; + + // setup the split var_t + var_t* v0 = (var_t*)(d + byteN); + v0->byteN = byte0N - (byteN + sizeof(var_t)); + v0->refN = 0; + + // update the leading var size - last + v->byteN = byteN; // ATOMIC release + } + + return d; + } + + // v is not available advance to the next var + v = (var_t*)(d + v->byteN); + } + + return nullptr; + } + } +} + + +cw::rc_t cw::nbmem::create( handle_t& h, unsigned blockByteN ) +{ + rc_t rc = kOkRC; + if((rc = destroy(h)) != kOkRC ) + return rc; + + nbmem_t* p = mem::allocZ< nbmem_t >(1); + + p->base = _alloc_block( blockByteN ); + p->blockByteN = blockByteN; + + h.set(p); + + return rc; +} + +cw::rc_t cw::nbmem::destroy( handle_t& h ) +{ + rc_t rc = kOkRC; + if( !h.isValid() ) + return rc; + + nbmem_t* p = _handleToPtr(h); + + if((rc = _destroy(p)) != kOkRC ) + return rc; + + h.clear(); + + return rc; +} + +void* cw::nbmem::alloc( handle_t h, unsigned byteN ) +{ + nbmem_t* p = _handleToPtr(h); + block_t* b = p->base; + void* result = nullptr; + + // try to locate an available variable in the existing blocks + for(; b!=nullptr; b=b->link) + if((result = _alloc_var( b->var, byteN, b->mem + b->byteN )) != nullptr ) + break; + + // if no available var space was found + if( b == nullptr ) + { + // the new block size must be at least as large as the requested variable size + unsigned blockByteN = std::max( p->blockByteN, byteN ); + + // allocate a new block + block_t* b = _alloc_block( blockByteN ); + + // try the var allocation again + result = _alloc_var( b->var, byteN, b->mem + b->byteN ); + + // link in the new block + b->link = p->base; + + p->base = b; // ATOMIC + } + + return result; +} + +void cw::nbmem::release( handle_t h, void* v ) +{ + nbmem_t* p = _handleToPtr(h); + block_t* b = p->base; + for(; b!=nullptr; b=b->link) + { + // if the memory to free is in this block + if( (b->mem <= (char*)v) && ((char*)v < b->mem + (sizeof(block_t) + b->byteN)) ) + { + // v points to data space - back up by the size of var_t to point to the block header + var_t* var = static_cast(v) - 1; + + var->refN -= 1; // ATOMIC + + // check for too many frees + if( var->refN < 0 ) + { + var->refN = 0; // ATOMIC + cwLogError(kInvalidOpRC,"An nbmem memory block was doube freed."); + } + + return; + } + } + + cwLogError(kInvalidArgRC,"Invalid memory pointer passed to nbmem::release()."); +} + +void cw::nbmem::report( handle_t h ) +{ + nbmem_t* p = _handleToPtr(h); + unsigned i = 0; + block_t* b = p->base; + for(; b!=nullptr; b=b->link) + { + printf("mem:%p byteN:%i var:%p link:%p\n",b->mem,b->byteN,b->var,b->link); + + var_t* v = b->var; + char* blockEnd = b->mem + sizeof(block_t) + b->byteN; + + while( (char*)v < blockEnd ) + { + void* data = (v+1); + + if( v->refN > 0 ) + { + unsigned* val = static_cast(data); + var_t* var = static_cast(data) - 1; + printf("%i %i bN:%i ref:%li 0x%p\n",i,*val, var->byteN, var->refN, val); + + i += 1; + } + + v = (var_t*)( static_cast(data) + v->byteN); + + } + } +} + +cw::rc_t cw::nbmem::test() +{ + unsigned preallocMemN = 64; + unsigned N = 64; + void* varA[ N ]; + handle_t h; + rc_t rc; + + // create a nbmem mgr object + if((rc = create( h, preallocMemN )) == kOkRC ) + { + // allocate N blocks of memory of length sizeof(unsigned) + for(unsigned i=0; i(arg); + + // select a random variable index + unsigned idx = lround((double)rand() * (r->varN-1) / RAND_MAX); + cwAssert( 0 <= idx && idx < r->varN ); + + // if the variable has not been allocated then allocate it ... + if( r->varA[idx] == nullptr ) + { + r->varA[idx] = alloc( r->ctx->nbmH, 10 ); + } + else // ... otherwise free it + { + release( r->ctx->nbmH, r->varA[idx] ); + r->varA[idx] = nullptr; + } + + return true; + } + } +} + +cw::rc_t cw::nbmem::test_multi_threaded() +{ + unsigned preallocMemN = 1024; + unsigned threadN = 10; + unsigned threadVarN = 20; + rc_t rc = kOkRC; + test_ctx_t ctx; + + ctx.threadN = threadN; + ctx.threadA = mem::allocZ(ctx.threadN); + + if((rc = create( ctx.nbmH, preallocMemN )) != kOkRC ) + goto errLabel; + + + // create ctx.threadN threads + for(unsigned i=0; i(threadVarN); + ctx.threadA[i].varN = threadVarN; + + if((rc = thread::create(ctx.threadA[i].threadH, _test_thread_func, ctx.threadA + i )) != kOkRC ) + break; + } + + if( rc == kOkRC ) + { + + // start each thread + for(unsigned i=0; i handle_t; + + rc_t create( handle_t& h, unsigned preallocMemN ); + rc_t destroy( handle_t& h ); + + void* alloc( handle_t h, unsigned byteN ); + void release( handle_t h, void* p ); + + void report( handle_t h ); + rc_t test(); + rc_t test_multi_threaded(); + } +}