cwNbMem.h/cpp : Initial commit.
This commit is contained in:
parent
17318e24dc
commit
c37595578a
407
cwNbMem.cpp
Normal file
407
cwNbMem.cpp
Normal file
@ -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<unsigned long long> 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<handle_t,nbmem_t>(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<char>(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<var_t*>(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<unsigned *>(data);
|
||||
var_t* var = static_cast<var_t*>(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<char*>(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<N; ++i)
|
||||
{
|
||||
unsigned* ip;
|
||||
if((ip = (unsigned*)alloc(h,sizeof(unsigned))) != nullptr )
|
||||
{
|
||||
*ip = i; // set the data value
|
||||
varA[i] = ip; // keep a ptr to the data block for eventual release
|
||||
printf("%i alloc assign\n",i);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("alloc failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
// report the state of the nbmem mgr and data values
|
||||
report( h );
|
||||
|
||||
// free each of the memory blocks which were allocate above
|
||||
for(unsigned i=0; i<N; ++i)
|
||||
release(h,varA[i]);
|
||||
|
||||
|
||||
destroy( h );
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
namespace cw
|
||||
{
|
||||
namespace nbmem
|
||||
{
|
||||
struct text_ctx_str;
|
||||
|
||||
typedef struct nbm_thread_str
|
||||
{
|
||||
struct test_ctx_str* ctx;
|
||||
void** varA;
|
||||
unsigned varN;
|
||||
thread::handle_t threadH;
|
||||
} nbm_thread_t;
|
||||
|
||||
typedef struct test_ctx_str
|
||||
{
|
||||
nbm_thread_t* threadA;
|
||||
unsigned threadN;
|
||||
handle_t nbmH;
|
||||
} test_ctx_t;
|
||||
|
||||
//
|
||||
bool _test_thread_func( void* arg )
|
||||
{
|
||||
nbm_thread_t* r = static_cast<nbm_thread_t*>(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<nbm_thread_t>(ctx.threadN);
|
||||
|
||||
if((rc = create( ctx.nbmH, preallocMemN )) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
|
||||
// create ctx.threadN threads
|
||||
for(unsigned i=0; i<ctx.threadN; ++i)
|
||||
{
|
||||
ctx.threadA[i].ctx = &ctx;
|
||||
ctx.threadA[i].varA = mem::allocZ<void*>(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<ctx.threadN; ++i)
|
||||
thread::unpause(ctx.threadA[i].threadH);
|
||||
|
||||
char c;
|
||||
|
||||
while( c != 'q' )
|
||||
{
|
||||
|
||||
c = (char)fgetc(stdin);
|
||||
fflush(stdin);
|
||||
|
||||
switch( c )
|
||||
{
|
||||
case 'q':
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errLabel:
|
||||
|
||||
// destroy each thread
|
||||
for(unsigned i=0; i<ctx.threadN; ++i)
|
||||
if( ctx.threadA[i].threadH.isValid() )
|
||||
thread::destroy( ctx.threadA[i].threadH );
|
||||
|
||||
mem::release(ctx.threadA);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
17
cwNbMem.h
Normal file
17
cwNbMem.h
Normal file
@ -0,0 +1,17 @@
|
||||
namespace cw
|
||||
{
|
||||
namespace nbmem
|
||||
{
|
||||
typedef handle< struct nbmem_str > 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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user