mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 00:03:03 +01:00
Further class library development. Added memory pool locking and line number allocation information
This commit is contained in:
parent
745c17a574
commit
1a44760aed
@ -29,19 +29,19 @@
|
||||
|
||||
// Size in bytes, must be aligned according to ALLOC_ALIGNMENT
|
||||
#define MIN_EXTENT_SIZE 100000
|
||||
// Must be a power of 2
|
||||
#define ALLOC_ALIGNMENT 4
|
||||
|
||||
#define ALIGN(X) ((X+ALLOC_ALIGNMENT-1) & ~(ALLOC_ALIGNMENT-1))
|
||||
|
||||
#define FB_MAX(M,N) ((M)>(N)?(M):(N))
|
||||
|
||||
// TODO (in order of importance):
|
||||
// 1. Pool size limit
|
||||
// 2. debug alloc/free pattern
|
||||
// 3. line number debug info
|
||||
// 4. pool locking and allocation source
|
||||
// 5. red zones checking (not really needed because verify_pool is able to detect most corruption cases)
|
||||
// 1. local pool locking +
|
||||
// 2. line number debug info +
|
||||
// 3. debug alloc/free pattern
|
||||
// 4. print pool contents function
|
||||
//---- Not needed for current codebase
|
||||
// 5. Pool size limit
|
||||
// 6. allocation source
|
||||
// 7. shared pool locking
|
||||
// 8. red zones checking (not really needed because verify_pool is able to detect most corruption cases)
|
||||
|
||||
namespace Firebird {
|
||||
|
||||
@ -53,6 +53,19 @@ static void pool_out_of_memory()
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
static MemoryPool* processMemoryPool = MemoryPool::createPool(
|
||||
// Do global pool locking only for SS
|
||||
#ifdef SUPERSERVER
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
);
|
||||
|
||||
MemoryPool* MemoryPool::getProcessPool() {
|
||||
return processMemoryPool;
|
||||
}
|
||||
|
||||
void MemoryPool::updateSpare() {
|
||||
updatingSpare = true; // This is to prevent re-enterance
|
||||
do {
|
||||
@ -70,7 +83,7 @@ void MemoryPool::updateSpare() {
|
||||
while (pendingFree) {
|
||||
PendingFreeBlock *temp = pendingFree;
|
||||
pendingFree = temp->next;
|
||||
MemoryBlock *blk = (MemoryBlock*)((char*)temp-ALIGN(sizeof(MemoryBlock)));
|
||||
MemoryBlock *blk = (MemoryBlock*)((char*)temp-MEM_ALIGN(sizeof(MemoryBlock)));
|
||||
BlockInfo info = {
|
||||
blk,
|
||||
blk->length
|
||||
@ -97,14 +110,15 @@ void MemoryPool::external_free(void *blk) {
|
||||
}
|
||||
|
||||
void MemoryPool::verify_pool() {
|
||||
if (locking) lock.enter();
|
||||
assert (!pendingFree || needSpare); // needSpare flag should be set if we are in
|
||||
// a critically low memory condition
|
||||
// check each block in each segment for consistency with free blocks structure
|
||||
for (MemoryExtent *extent = extents; extent; extent=extent->next) {
|
||||
MemoryBlock *prev = NULL;
|
||||
for (MemoryBlock *blk = (MemoryBlock *)((char*)extent+ALIGN(sizeof(MemoryExtent)));
|
||||
for (MemoryBlock *blk = (MemoryBlock *)((char*)extent+MEM_ALIGN(sizeof(MemoryExtent)));
|
||||
!blk->last;
|
||||
blk = (MemoryBlock *)((char*)blk+ALIGN(sizeof(MemoryBlock))+blk->length))
|
||||
blk = (MemoryBlock *)((char*)blk+MEM_ALIGN(sizeof(MemoryBlock))+blk->length))
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
assert(blk->pool == this); // Pool is correct ?
|
||||
@ -112,7 +126,7 @@ void MemoryPool::verify_pool() {
|
||||
BlockInfo temp = {blk, blk->length};
|
||||
bool foundTree = freeBlocks.locate(temp), foundPending = false;
|
||||
for (PendingFreeBlock *tmp = pendingFree; tmp; tmp = tmp->next)
|
||||
if (tmp == (PendingFreeBlock *)((char*)blk+ALIGN(sizeof(MemoryBlock)))) {
|
||||
if (tmp == (PendingFreeBlock *)((char*)blk+MEM_ALIGN(sizeof(MemoryBlock)))) {
|
||||
assert(!foundPending); // Block may be in pending list only one time
|
||||
foundPending = true;
|
||||
}
|
||||
@ -127,17 +141,18 @@ void MemoryPool::verify_pool() {
|
||||
prev = blk;
|
||||
}
|
||||
}
|
||||
if (locking) lock.leave();
|
||||
}
|
||||
|
||||
MemoryPool* MemoryPool::createPool() {
|
||||
MemoryPool* MemoryPool::createPool(bool locking) {
|
||||
size_t alloc_size = FB_MAX(
|
||||
// This is the exact initial layout of memory pool in the first extent //
|
||||
ALIGN(sizeof(MemoryExtent)) +
|
||||
ALIGN(sizeof(MemoryBlock)) +
|
||||
ALIGN(sizeof(MemoryPool)) +
|
||||
ALIGN(sizeof(MemoryBlock)) +
|
||||
ALIGN(sizeof(FreeBlocksTree::ItemList)) +
|
||||
ALIGN(sizeof(MemoryBlock)) +
|
||||
MEM_ALIGN(sizeof(MemoryExtent)) +
|
||||
MEM_ALIGN(sizeof(MemoryBlock)) +
|
||||
MEM_ALIGN(sizeof(MemoryPool)) +
|
||||
MEM_ALIGN(sizeof(MemoryBlock)) +
|
||||
MEM_ALIGN(sizeof(FreeBlocksTree::ItemList)) +
|
||||
MEM_ALIGN(sizeof(MemoryBlock)) +
|
||||
ALLOC_ALIGNMENT,
|
||||
// ******************************************************************* //
|
||||
MIN_EXTENT_SIZE);
|
||||
@ -146,45 +161,46 @@ MemoryPool* MemoryPool::createPool() {
|
||||
if (!mem) pool_out_of_memory();
|
||||
((MemoryExtent *)mem)->next = NULL;
|
||||
MemoryPool* pool = new(mem +
|
||||
ALIGN(sizeof(MemoryExtent)) +
|
||||
ALIGN(sizeof(MemoryBlock)))
|
||||
MEM_ALIGN(sizeof(MemoryExtent)) +
|
||||
MEM_ALIGN(sizeof(MemoryBlock)))
|
||||
MemoryPool(mem, mem +
|
||||
ALIGN(sizeof(MemoryExtent)) +
|
||||
ALIGN(sizeof(MemoryBlock)) +
|
||||
ALIGN(sizeof(MemoryPool)) +
|
||||
ALIGN(sizeof(MemoryBlock)));
|
||||
MEM_ALIGN(sizeof(MemoryExtent)) +
|
||||
MEM_ALIGN(sizeof(MemoryBlock)) +
|
||||
MEM_ALIGN(sizeof(MemoryPool)) +
|
||||
MEM_ALIGN(sizeof(MemoryBlock)),
|
||||
locking);
|
||||
|
||||
MemoryBlock *poolBlk = (MemoryBlock*) (mem+ALIGN(sizeof(MemoryExtent)));
|
||||
MemoryBlock *poolBlk = (MemoryBlock*) (mem+MEM_ALIGN(sizeof(MemoryExtent)));
|
||||
poolBlk->pool = pool;
|
||||
poolBlk->used = true;
|
||||
poolBlk->last = false;
|
||||
poolBlk->type = TYPE_POOL;
|
||||
poolBlk->length = ALIGN(sizeof(MemoryPool));
|
||||
poolBlk->length = MEM_ALIGN(sizeof(MemoryPool));
|
||||
poolBlk->prev = NULL;
|
||||
|
||||
MemoryBlock *hdr = (MemoryBlock*) (mem +
|
||||
ALIGN(sizeof(MemoryExtent)) +
|
||||
ALIGN(sizeof(MemoryBlock)) +
|
||||
ALIGN(sizeof(MemoryPool)));
|
||||
MEM_ALIGN(sizeof(MemoryExtent)) +
|
||||
MEM_ALIGN(sizeof(MemoryBlock)) +
|
||||
MEM_ALIGN(sizeof(MemoryPool)));
|
||||
hdr->pool = pool;
|
||||
hdr->used = true;
|
||||
hdr->last = false;
|
||||
hdr->type = TYPE_LEAFPAGE;
|
||||
hdr->length = ALIGN(sizeof(FreeBlocksTree::ItemList));
|
||||
hdr->length = MEM_ALIGN(sizeof(FreeBlocksTree::ItemList));
|
||||
hdr->prev = poolBlk;
|
||||
MemoryBlock *blk = (MemoryBlock *)(mem +
|
||||
ALIGN(sizeof(MemoryExtent)) +
|
||||
ALIGN(sizeof(MemoryBlock)) +
|
||||
ALIGN(sizeof(MemoryPool)) +
|
||||
ALIGN(sizeof(MemoryBlock)) +
|
||||
ALIGN(sizeof(FreeBlocksTree::ItemList)));
|
||||
MEM_ALIGN(sizeof(MemoryExtent)) +
|
||||
MEM_ALIGN(sizeof(MemoryBlock)) +
|
||||
MEM_ALIGN(sizeof(MemoryPool)) +
|
||||
MEM_ALIGN(sizeof(MemoryBlock)) +
|
||||
MEM_ALIGN(sizeof(FreeBlocksTree::ItemList)));
|
||||
int blockLength = alloc_size -
|
||||
ALIGN(sizeof(MemoryExtent)) -
|
||||
ALIGN(sizeof(MemoryBlock)) -
|
||||
ALIGN(sizeof(MemoryPool)) -
|
||||
ALIGN(sizeof(MemoryBlock)) -
|
||||
ALIGN(sizeof(FreeBlocksTree::ItemList)) -
|
||||
ALIGN(sizeof(MemoryBlock));
|
||||
MEM_ALIGN(sizeof(MemoryExtent)) -
|
||||
MEM_ALIGN(sizeof(MemoryBlock)) -
|
||||
MEM_ALIGN(sizeof(MemoryPool)) -
|
||||
MEM_ALIGN(sizeof(MemoryBlock)) -
|
||||
MEM_ALIGN(sizeof(FreeBlocksTree::ItemList)) -
|
||||
MEM_ALIGN(sizeof(MemoryBlock));
|
||||
blk->pool = pool;
|
||||
blk->used = false;
|
||||
blk->last = true;
|
||||
@ -207,7 +223,11 @@ void MemoryPool::deletePool(MemoryPool* pool) {
|
||||
}
|
||||
}
|
||||
|
||||
void* MemoryPool::alloc(size_t size, SSHORT type) {
|
||||
void* MemoryPool::alloc(size_t size, SSHORT type
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
, char* file, int line
|
||||
#endif
|
||||
) {
|
||||
if (internalAlloc) {
|
||||
if (size == sizeof(FreeBlocksTree::ItemList))
|
||||
// This condition is to handle case when nodelist and itemlist have equal size
|
||||
@ -229,36 +249,45 @@ void* MemoryPool::alloc(size_t size, SSHORT type) {
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
if (locking) lock.enter();
|
||||
// Lookup a block greater or equal than size in freeBlocks tree
|
||||
size = ALIGN(size);
|
||||
size = MEM_ALIGN(size);
|
||||
BlockInfo temp = {NULL, size};
|
||||
void *result;
|
||||
if (freeBlocks.locate(locGreatEqual,temp)) {
|
||||
// Found large enough block
|
||||
BlockInfo* current = &freeBlocks.current();
|
||||
if (current->length-size < ALIGN(sizeof(MemoryBlock))+ALLOC_ALIGNMENT) {
|
||||
if (current->length-size < MEM_ALIGN(sizeof(MemoryBlock))+ALLOC_ALIGNMENT) {
|
||||
// Block is small enough to be returned AS IS
|
||||
current->block->used = true;
|
||||
current->block->type = type;
|
||||
result = (char *)current->block + ALIGN(sizeof(MemoryBlock));
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
current->block->file = file;
|
||||
current->block->line = line;
|
||||
#endif
|
||||
result = (char *)current->block + MEM_ALIGN(sizeof(MemoryBlock));
|
||||
internalAlloc = true;
|
||||
freeBlocks.fastRemove();
|
||||
internalAlloc = false;
|
||||
} else {
|
||||
// Cut a piece at the end of block in hope to avoid structural
|
||||
// modification of free blocks tree
|
||||
current->block->length -= ALIGN(sizeof(MemoryBlock))+size;
|
||||
current->block->length -= MEM_ALIGN(sizeof(MemoryBlock))+size;
|
||||
MemoryBlock *blk = (MemoryBlock *)((char*)current->block +
|
||||
ALIGN(sizeof(MemoryBlock)) + current->block->length);
|
||||
MEM_ALIGN(sizeof(MemoryBlock)) + current->block->length);
|
||||
blk->pool = this;
|
||||
blk->used = true;
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
blk->file = file;
|
||||
blk->line = line;
|
||||
#endif
|
||||
blk->last = current->block->last;
|
||||
current->block->last = false;
|
||||
blk->type = type;
|
||||
blk->length = size;
|
||||
blk->prev = current->block;
|
||||
if (!blk->last)
|
||||
((MemoryBlock *)((char*)blk + ALIGN(sizeof(MemoryBlock)) + blk->length))->prev = blk;
|
||||
((MemoryBlock *)((char*)blk + MEM_ALIGN(sizeof(MemoryBlock)) + blk->length))->prev = blk;
|
||||
// Update tree of free blocks
|
||||
if (!freeBlocks.getPrev() || freeBlocks.current().length < current->block->length)
|
||||
current->length = current->block->length;
|
||||
@ -277,81 +306,99 @@ void* MemoryPool::alloc(size_t size, SSHORT type) {
|
||||
addFreeBlock(block);
|
||||
internalAlloc = false;
|
||||
}
|
||||
result = (char*)blk+ALIGN(sizeof(MemoryBlock));
|
||||
result = (char*)blk+MEM_ALIGN(sizeof(MemoryBlock));
|
||||
}
|
||||
} else {
|
||||
// If we are in a critically low memory condition look up for a block in a list
|
||||
// of pending free blocks. We do not do "best fit" in this case
|
||||
PendingFreeBlock *itr = pendingFree, *prev = NULL;
|
||||
while (itr) {
|
||||
MemoryBlock *temp = (MemoryBlock *)((char*)itr-ALIGN(sizeof(MemoryBlock)));
|
||||
MemoryBlock *temp = (MemoryBlock *)((char*)itr-MEM_ALIGN(sizeof(MemoryBlock)));
|
||||
if (temp->length >= size) {
|
||||
if (temp->length-size < ALIGN(sizeof(MemoryBlock))+ALLOC_ALIGNMENT) {
|
||||
if (temp->length-size < MEM_ALIGN(sizeof(MemoryBlock))+ALLOC_ALIGNMENT) {
|
||||
// Block is small enough to be returned AS IS
|
||||
temp->used = true;
|
||||
temp->type = type;
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
temp->file = file;
|
||||
temp->line = line;
|
||||
#endif
|
||||
// Remove block from linked list
|
||||
if (prev)
|
||||
prev->next = itr->next;
|
||||
else
|
||||
pendingFree = itr->next;
|
||||
if (locking) lock.leave();
|
||||
return itr;
|
||||
} else {
|
||||
// Cut a piece at the end of block
|
||||
// We don't need to modify tree of free blocks or a list of
|
||||
// pending free blocks in this case
|
||||
temp->length -= ALIGN(sizeof(MemoryBlock))+size;
|
||||
temp->length -= MEM_ALIGN(sizeof(MemoryBlock))+size;
|
||||
MemoryBlock *blk = (MemoryBlock *)((char*)temp +
|
||||
ALIGN(sizeof(MemoryBlock)) + temp->length);
|
||||
MEM_ALIGN(sizeof(MemoryBlock)) + temp->length);
|
||||
blk->pool = this;
|
||||
blk->used = true;
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
blk->file = file;
|
||||
blk->line = line;
|
||||
#endif
|
||||
blk->last = temp->last;
|
||||
temp->last = false;
|
||||
blk->type = type;
|
||||
blk->length = size;
|
||||
blk->prev = temp;
|
||||
if (!blk->last)
|
||||
((MemoryBlock *)((char*)blk + ALIGN(sizeof(MemoryBlock)) + blk->length))->prev = blk;
|
||||
return (char *)blk + ALIGN(sizeof(MemoryBlock));
|
||||
((MemoryBlock *)((char*)blk + MEM_ALIGN(sizeof(MemoryBlock)) + blk->length))->prev = blk;
|
||||
if (locking) lock.leave();
|
||||
return (char *)blk + MEM_ALIGN(sizeof(MemoryBlock));
|
||||
}
|
||||
}
|
||||
prev = itr;
|
||||
itr = itr->next;
|
||||
}
|
||||
// No large enough block found. We need to extend the pool
|
||||
size_t alloc_size = FB_MAX(ALIGN(sizeof(MemoryExtent))+ALIGN(sizeof(MemoryBlock))+size, MIN_EXTENT_SIZE);
|
||||
size_t alloc_size = FB_MAX(MEM_ALIGN(sizeof(MemoryExtent))+MEM_ALIGN(sizeof(MemoryBlock))+size, MIN_EXTENT_SIZE);
|
||||
MemoryExtent* extent = (MemoryExtent *)external_alloc(alloc_size);
|
||||
if (!extent) pool_out_of_memory();
|
||||
if (!extent) {
|
||||
if (locking) lock.leave();
|
||||
pool_out_of_memory();
|
||||
}
|
||||
extent->next = extents;
|
||||
extents = extent;
|
||||
|
||||
MemoryBlock *blk = (MemoryBlock *)((char*)extent+ALIGN(sizeof(MemoryExtent)));
|
||||
MemoryBlock *blk = (MemoryBlock *)((char*)extent+MEM_ALIGN(sizeof(MemoryExtent)));
|
||||
blk->pool = this;
|
||||
blk->used = true;
|
||||
blk->type = type;
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
blk->file = file;
|
||||
blk->line = line;
|
||||
#endif
|
||||
blk->prev = NULL;
|
||||
if (alloc_size-size-ALIGN(sizeof(MemoryExtent))-ALIGN(sizeof(MemoryBlock)) < ALIGN(sizeof(MemoryBlock))+ALLOC_ALIGNMENT) {
|
||||
if (alloc_size-size-MEM_ALIGN(sizeof(MemoryExtent))-MEM_ALIGN(sizeof(MemoryBlock)) < MEM_ALIGN(sizeof(MemoryBlock))+ALLOC_ALIGNMENT) {
|
||||
// Block is small enough to be returned AS IS
|
||||
blk->last = true;
|
||||
blk->length = alloc_size - ALIGN(sizeof(MemoryExtent)) - ALIGN(sizeof(MemoryBlock));
|
||||
blk->length = alloc_size - MEM_ALIGN(sizeof(MemoryExtent)) - MEM_ALIGN(sizeof(MemoryBlock));
|
||||
} else {
|
||||
// Cut a piece at the beginning of the block
|
||||
blk->last = false;
|
||||
blk->length = size;
|
||||
// Put the rest to the tree of free blocks
|
||||
MemoryBlock *rest = (MemoryBlock *)((char *)blk + ALIGN(sizeof(MemoryBlock)) + size);
|
||||
MemoryBlock *rest = (MemoryBlock *)((char *)blk + MEM_ALIGN(sizeof(MemoryBlock)) + size);
|
||||
rest->pool = this;
|
||||
rest->used = false;
|
||||
rest->last = true;
|
||||
rest->length = alloc_size - ALIGN(sizeof(MemoryExtent)) -
|
||||
ALIGN(sizeof(MemoryBlock)) - size - ALIGN(sizeof(MemoryBlock));
|
||||
rest->length = alloc_size - MEM_ALIGN(sizeof(MemoryExtent)) -
|
||||
MEM_ALIGN(sizeof(MemoryBlock)) - size - MEM_ALIGN(sizeof(MemoryBlock));
|
||||
rest->prev = blk;
|
||||
internalAlloc = true;
|
||||
addFreeBlock(rest);
|
||||
internalAlloc = false;
|
||||
}
|
||||
result = (char*)blk+ALIGN(sizeof(MemoryBlock));
|
||||
result = (char*)blk+MEM_ALIGN(sizeof(MemoryBlock));
|
||||
}
|
||||
if (locking) lock.leave();
|
||||
// Grow spare blocks pool if necessary
|
||||
if (needSpare && !updatingSpare) updateSpare();
|
||||
return result;
|
||||
@ -364,7 +411,7 @@ void MemoryPool::addFreeBlock(MemoryBlock *blk) {
|
||||
} catch(...) {
|
||||
// Add item to the list of pending free blocks in case of critically-low memory condition
|
||||
PendingFreeBlock* temp = (PendingFreeBlock *)((char *)freeBlocks.getAddErrorValue().block+
|
||||
ALIGN(sizeof(MemoryBlock)));
|
||||
MEM_ALIGN(sizeof(MemoryBlock)));
|
||||
temp->next = pendingFree;
|
||||
pendingFree = temp;
|
||||
}
|
||||
@ -378,7 +425,7 @@ void MemoryPool::removeFreeBlock(MemoryBlock *blk) {
|
||||
// If we are in a critically-low memory condition our block could be in the
|
||||
// pending free blocks list. Remove it from there
|
||||
PendingFreeBlock *itr = pendingFree,
|
||||
*temp = (PendingFreeBlock *)((char *)blk+ALIGN(sizeof(MemoryBlock)));
|
||||
*temp = (PendingFreeBlock *)((char *)blk+MEM_ALIGN(sizeof(MemoryBlock)));
|
||||
if (itr == temp)
|
||||
pendingFree = itr->next;
|
||||
else
|
||||
@ -399,33 +446,34 @@ void MemoryPool::removeFreeBlock(MemoryBlock *blk) {
|
||||
void MemoryPool::free(void *block) {
|
||||
if (internalAlloc) {
|
||||
((PendingFreeBlock*)block)->next = pendingFree;
|
||||
((MemoryBlock*)((char*)block-ALIGN(sizeof(MemoryBlock))))->used = false;
|
||||
((MemoryBlock*)((char*)block-MEM_ALIGN(sizeof(MemoryBlock))))->used = false;
|
||||
pendingFree = (PendingFreeBlock*)block;
|
||||
needSpare = true;
|
||||
return;
|
||||
}
|
||||
internalAlloc = true;
|
||||
MemoryBlock *blk = (MemoryBlock *)((char*)block - ALIGN(sizeof(MemoryBlock))), *prev;
|
||||
if (locking) lock.enter();
|
||||
MemoryBlock *blk = (MemoryBlock *)((char*)block - MEM_ALIGN(sizeof(MemoryBlock))), *prev;
|
||||
// Try to merge block with preceding free block
|
||||
if ((prev = blk->prev) && !prev->used) {
|
||||
removeFreeBlock(prev);
|
||||
prev->length += blk->length + ALIGN(sizeof(MemoryBlock));
|
||||
prev->length += blk->length + MEM_ALIGN(sizeof(MemoryBlock));
|
||||
|
||||
MemoryBlock *next = NULL;
|
||||
if (blk->last) {
|
||||
prev->last = true;
|
||||
} else {
|
||||
next = (MemoryBlock *)((char *)blk+ALIGN(sizeof(MemoryBlock))+blk->length);
|
||||
next = (MemoryBlock *)((char *)blk+MEM_ALIGN(sizeof(MemoryBlock))+blk->length);
|
||||
if (next->used) {
|
||||
next->prev = prev;
|
||||
prev->last = false;
|
||||
} else {
|
||||
// Merge next block too
|
||||
removeFreeBlock(next);
|
||||
prev->length += next->length + ALIGN(sizeof(MemoryBlock));
|
||||
prev->length += next->length + MEM_ALIGN(sizeof(MemoryBlock));
|
||||
prev->last = next->last;
|
||||
if (!next->last)
|
||||
((MemoryBlock *)((char *)next+ALIGN(sizeof(MemoryBlock))+next->length))->prev = prev;
|
||||
((MemoryBlock *)((char *)next+MEM_ALIGN(sizeof(MemoryBlock))+next->length))->prev = prev;
|
||||
}
|
||||
}
|
||||
addFreeBlock(prev);
|
||||
@ -435,18 +483,68 @@ void MemoryPool::free(void *block) {
|
||||
blk->used = false;
|
||||
// Try to merge block with next free block
|
||||
if (!blk->last &&
|
||||
!(next = (MemoryBlock *)((char*)blk+ALIGN(sizeof(MemoryBlock))+blk->length))->used)
|
||||
!(next = (MemoryBlock *)((char*)blk+MEM_ALIGN(sizeof(MemoryBlock))+blk->length))->used)
|
||||
{
|
||||
removeFreeBlock(next);
|
||||
blk->length += next->length + ALIGN(sizeof(MemoryBlock));
|
||||
blk->length += next->length + MEM_ALIGN(sizeof(MemoryBlock));
|
||||
blk->last = next->last;
|
||||
if (!next->last)
|
||||
((MemoryBlock *)((char *)next+ALIGN(sizeof(MemoryBlock))+next->length))->prev = blk;
|
||||
((MemoryBlock *)((char *)next+MEM_ALIGN(sizeof(MemoryBlock))+next->length))->prev = blk;
|
||||
}
|
||||
addFreeBlock(blk);
|
||||
}
|
||||
if (locking) lock.leave();
|
||||
internalAlloc = false;
|
||||
if (needSpare && !updatingSpare) updateSpare();
|
||||
}
|
||||
|
||||
} /* namespace Firebird */
|
||||
|
||||
#ifndef TESTING_ONLY
|
||||
|
||||
extern "C" {
|
||||
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
void* API_ROUTINE gds__alloc_debug(SLONG size_request,
|
||||
TEXT* filename,
|
||||
ULONG lineno)
|
||||
{
|
||||
return Firebird::processMemoryPool->alloc(size_request, 0, filename, lineno);
|
||||
}
|
||||
#else
|
||||
void* API_ROUTINE gds__alloc(SLONG size_request)
|
||||
{
|
||||
return Firebird::processMemoryPool->alloc(size_request);
|
||||
}
|
||||
#endif
|
||||
|
||||
ULONG API_ROUTINE gds__free(void* blk) {
|
||||
Firebird::processMemoryPool->free(blk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void* operator new(size_t s) {
|
||||
#if defined(DEV_BUILD)
|
||||
printf("You MUST allocate all memory from a pool. Don't use the default global new().\n");
|
||||
#endif // DEV_BUILD
|
||||
return Firebird::processMemoryPool->alloc(s, 0
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
,__FILE__,__LINE__
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
void* operator new[](size_t s) {
|
||||
#if defined(DEV_BUILD)
|
||||
printf("You MUST allocate all memory from a pool. Don't use the default global new[]().\n");
|
||||
#endif // DEV_BUILD
|
||||
return Firebird::processMemoryPool->alloc(s, 0
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
,__FILE__,__LINE__
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -28,9 +28,16 @@
|
||||
|
||||
#include <malloc.h>
|
||||
#include "../../include/fb_types.h"
|
||||
#include "../../include/firebird.h"
|
||||
#include "../jrd/common.h"
|
||||
#include "tree.h"
|
||||
#include "locks.h"
|
||||
|
||||
#define MAX_TREE_DEPTH 4
|
||||
// Must be a power of 2
|
||||
#define ALLOC_ALIGNMENT 4
|
||||
|
||||
#define MEM_ALIGN(X) FB_ALIGN(X,ALLOC_ALIGNMENT)
|
||||
|
||||
namespace Firebird {
|
||||
|
||||
@ -41,10 +48,10 @@ struct MemoryBlock /* 16 bytes of block header is not too much I think */ {
|
||||
SSHORT type;
|
||||
size_t length; /* Includes only actual block size, header not included */
|
||||
struct MemoryBlock *prev;
|
||||
/*#ifdef DEBUG_GDS_ALLOC
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
char *file;
|
||||
int line;
|
||||
#endif*/
|
||||
#endif
|
||||
};
|
||||
|
||||
#define TYPE_POOL -1
|
||||
@ -72,6 +79,10 @@ struct PendingFreeBlock {
|
||||
};
|
||||
|
||||
// Memory pool based on B+ tree of free memory blocks
|
||||
|
||||
// We are going to have two target architectures:
|
||||
// 1. Multi-process server with customizable lock manager
|
||||
// 2. Multi-threaded server with single process (SUPERSERVER)
|
||||
class MemoryPool {
|
||||
private:
|
||||
typedef BePlusTree<BlockInfo, BlockInfo, MemoryPool,
|
||||
@ -85,15 +96,23 @@ private:
|
||||
bool needSpare;
|
||||
bool updatingSpare;
|
||||
PendingFreeBlock *pendingFree;
|
||||
#ifdef SUPERSERVER
|
||||
Spinlock lock;
|
||||
#else
|
||||
// FIXME: Shared memory lock needs to be here. Any attempt to use shared pool locking in CS will fail
|
||||
Spinlock lock;
|
||||
#endif
|
||||
bool locking;
|
||||
|
||||
// Do not allow to create and destroy pool directly from outside
|
||||
MemoryPool(void *first_extent, void *root_page) :
|
||||
MemoryPool(void *first_extent, void *root_page, bool _locking) :
|
||||
freeBlocks(this, root_page),
|
||||
extents((MemoryExtent *)first_extent),
|
||||
internalAlloc(false),
|
||||
needSpare(false),
|
||||
updatingSpare(false),
|
||||
pendingFree(NULL)
|
||||
pendingFree(NULL),
|
||||
locking(_locking)
|
||||
{
|
||||
}
|
||||
|
||||
@ -112,22 +131,87 @@ private:
|
||||
|
||||
void removeFreeBlock(MemoryBlock *blk);
|
||||
public:
|
||||
static MemoryPool* createPool();
|
||||
static MemoryPool* createPool(bool locking = false);
|
||||
|
||||
static MemoryPool* getProcessPool();
|
||||
|
||||
static void deletePool(MemoryPool* pool);
|
||||
|
||||
void* alloc(size_t size, SSHORT type = 0);
|
||||
void* alloc(size_t size, SSHORT type = 0
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
, char *file = NULL, int line = 0
|
||||
#endif
|
||||
);
|
||||
|
||||
void free(void *block);
|
||||
|
||||
void verify_pool();
|
||||
|
||||
/* void* calloc(size_t size) {
|
||||
void* result = malloc(size);
|
||||
static void globalFree(void *block) {
|
||||
((MemoryBlock*)((char*)block-MEM_ALIGN(sizeof(MemoryBlock))))->pool->free(block);
|
||||
}
|
||||
|
||||
void* calloc(size_t size, SSHORT type = 0
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
, char *file = NULL, int line = 0
|
||||
#endif
|
||||
) {
|
||||
void* result = alloc(size, type
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
, file, line
|
||||
#endif
|
||||
);
|
||||
memset(result,size,0);
|
||||
}*/
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace Firebird
|
||||
|
||||
#ifndef TESTING_ONLY
|
||||
|
||||
extern "C" {
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
void* API_ROUTINE gds__alloc_debug(SLONG size_request,
|
||||
TEXT* filename,
|
||||
ULONG lineno);
|
||||
#else
|
||||
void* API_ROUTINE gds__alloc(SLONG size_request);
|
||||
#endif
|
||||
ULONG API_ROUTINE gds__free(void* blk);
|
||||
};
|
||||
|
||||
// Global versions of operator new() for compatibility with crappy libraries
|
||||
void* operator new(size_t);
|
||||
void* operator new[](size_t);
|
||||
|
||||
#ifdef DEBUG_GDS_ALLOC
|
||||
inline void* operator new(size_t s, Firebird::MemoryPool* pool, char* file, int line) {
|
||||
pool->alloc(s, 0, file, line);
|
||||
}
|
||||
inline void* operator new[](size_t s, Firebird::MemoryPool* pool, char* file, int line) {
|
||||
pool->alloc(s, 0, pool, file, line);
|
||||
}
|
||||
#define FB_NEW(pool) new(pool,__FILE__,__LINE__)
|
||||
#define FB_NEW_RPT(pool,count) new(pool,count,__FILE__,__LINE__)
|
||||
#else
|
||||
inline void* operator new(size_t s, Firebird::MemoryPool* pool) {
|
||||
return pool->alloc(s);
|
||||
}
|
||||
inline void* operator new[](size_t s, Firebird::MemoryPool* pool) {
|
||||
return pool->alloc(s);
|
||||
}
|
||||
#define FB_NEW(pool) new(pool)
|
||||
#define FB_NEW_RPT(pool,count) new(pool,count)
|
||||
#endif
|
||||
|
||||
inline void operator delete(void* mem) {
|
||||
Firebird::MemoryPool::globalFree(mem);
|
||||
}
|
||||
inline void operator delete[](void* mem) {
|
||||
Firebird::MemoryPool::globalFree(mem);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -211,8 +211,46 @@ static void testAllocatorMemoryPool() {
|
||||
BePlusTree<AllocItem,AllocItem,MallocAllocator,DefaultKeyValue<AllocItem>,AllocItem> items(&allocator),
|
||||
bigItems(&allocator);
|
||||
// Allocate small items
|
||||
int n = 0;
|
||||
for (int i=0;i<ALLOC_ITEMS;i++) {
|
||||
int i, n = 0;
|
||||
for (i=0;i<ALLOC_ITEMS;i++) {
|
||||
n = n * 47163 - 57412;
|
||||
AllocItem temp = {n, pool->alloc((n % MAX_ITEM_SIZE + MAX_ITEM_SIZE)/2+1)};
|
||||
items.add(temp);
|
||||
}
|
||||
// Deallocate half of small items
|
||||
n = 0;
|
||||
if (items.getFirst()) do {
|
||||
pool->free(items.current().item);
|
||||
n++;
|
||||
} while (n < ALLOC_ITEMS/2 && items.getNext());
|
||||
// Allocate big items
|
||||
for (i=0;i<BIG_ITEMS;i++) {
|
||||
n = n * 47163 - 57412;
|
||||
AllocItem temp = {n, pool->alloc((n % BIG_SIZE + BIG_SIZE)/2+1)};
|
||||
bigItems.add(temp);
|
||||
}
|
||||
// Deallocate the rest of small items
|
||||
do {
|
||||
pool->free(items.current().item);
|
||||
} while (items.getNext());
|
||||
// Deallocate big items
|
||||
if (bigItems.getFirst()) do {
|
||||
pool->free(bigItems.current().item);
|
||||
} while (bigItems.getNext());
|
||||
Firebird::MemoryPool::deletePool(pool);
|
||||
report();
|
||||
}
|
||||
|
||||
static void testAllocatorMemoryPoolLocking() {
|
||||
printf("Test run for Firebird::MemoryPool with locking...\n");
|
||||
start();
|
||||
Firebird::MemoryPool* pool = Firebird::MemoryPool::createPool(true);
|
||||
MallocAllocator allocator;
|
||||
BePlusTree<AllocItem,AllocItem,MallocAllocator,DefaultKeyValue<AllocItem>,AllocItem> items(&allocator),
|
||||
bigItems(&allocator);
|
||||
// Allocate small items
|
||||
int i, n = 0;
|
||||
for (i=0;i<ALLOC_ITEMS;i++) {
|
||||
n = n * 47163 - 57412;
|
||||
AllocItem temp = {n, pool->alloc((n % MAX_ITEM_SIZE + MAX_ITEM_SIZE)/2+1)};
|
||||
items.add(temp);
|
||||
@ -248,8 +286,8 @@ static void testAllocatorMalloc() {
|
||||
BePlusTree<AllocItem,AllocItem,MallocAllocator,DefaultKeyValue<AllocItem>,AllocItem> items(&allocator),
|
||||
bigItems(&allocator);
|
||||
// Allocate small items
|
||||
int n = 0;
|
||||
for (int i=0;i<ALLOC_ITEMS;i++) {
|
||||
int i, n = 0;
|
||||
for (i=0;i<ALLOC_ITEMS;i++) {
|
||||
n = n * 47163 - 57412;
|
||||
AllocItem temp = {n, malloc((n % MAX_ITEM_SIZE + MAX_ITEM_SIZE)/2+1)};
|
||||
items.add(temp);
|
||||
@ -320,6 +358,7 @@ int main() {
|
||||
testTree();
|
||||
testAllocatorOverhead();
|
||||
testAllocatorMemoryPool();
|
||||
testAllocatorMemoryPoolLocking();
|
||||
testAllocatorMalloc();
|
||||
testAllocatorOldPool();
|
||||
}
|
||||
|
@ -317,7 +317,7 @@ void testAllocator() {
|
||||
MemoryPool::deletePool(pool);
|
||||
}
|
||||
|
||||
void main() {
|
||||
int main() {
|
||||
testVector();
|
||||
testSortedVector();
|
||||
testBePlusTree();
|
||||
|
69
src/common/classes/locks.h
Normal file
69
src/common/classes/locks.h
Normal file
@ -0,0 +1,69 @@
|
||||
#ifndef LOCKS_H
|
||||
#define LOCKS_H
|
||||
|
||||
#include "firebird.h"
|
||||
|
||||
#ifdef WIN_NT
|
||||
// It is relatively easy to avoid using this header. Maybe do the same stuff like
|
||||
// in thd.h ? This is Windows platform maintainers choice
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace Firebird {
|
||||
|
||||
#ifdef WIN_NT
|
||||
|
||||
/* Process-local spinlock. Used to manage memory heaps in threaded environment. */
|
||||
// Windows version of the class
|
||||
class Spinlock {
|
||||
private:
|
||||
CRITICAL_SECTION spinlock;
|
||||
public:
|
||||
Spinlock() {
|
||||
if (!InitializeCriticalSectionAndSpinCount(&spinlock, 4000))
|
||||
system_call_failed::raise();
|
||||
}
|
||||
~Spinlock() {
|
||||
DeleteCriticalSection(&spinlock);
|
||||
}
|
||||
void enter() {
|
||||
EnterCriticalSection(&spinlock);
|
||||
}
|
||||
void leave() {
|
||||
LeaveCriticalSection(&spinlock);
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
/* Process-local spinlock. Used to manage memory heaps in threaded environment. */
|
||||
// Pthreads version of the class
|
||||
class Spinlock {
|
||||
private:
|
||||
pthread_spinlock_t spinlock;
|
||||
public:
|
||||
Spinlock() {
|
||||
if (pthread_spin_init(&spinlock, false))
|
||||
system_call_failed::raise();
|
||||
}
|
||||
~Spinlock() {
|
||||
if (pthread_spin_destroy(&spinlock))
|
||||
system_call_failed::raise();
|
||||
}
|
||||
void enter() {
|
||||
if (pthread_spin_lock(&spinlock))
|
||||
system_call_failed::raise();
|
||||
}
|
||||
void leave() {
|
||||
if (pthread_spin_unlock(&spinlock))
|
||||
system_call_failed::raise();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,11 +1,11 @@
|
||||
# Test for library integrity
|
||||
# this should be compiled with optimization turned off and with NDEBUG undefined
|
||||
ulimit -s unlimited
|
||||
g++ -ggdb -Wall class_test.cpp alloc.cpp 2> aa
|
||||
g++ -ggdb -Wall -I../../include -pthread class_test.cpp alloc.cpp ../fb_exception.cpp 2> aa
|
||||
./a.out
|
||||
|
||||
# Chose the best algorithm parameters for the target architecture
|
||||
g++ -O3 -march=pentium3 -DNDEBUG -I../../include class_perf.cpp alloc.cpp \
|
||||
g++ -O3 -pthread -march=pentium3 -DNDEBUG -DTESTING_ONLY -I../../include class_perf.cpp alloc.cpp \
|
||||
../memory/memory_pool.cpp ../fb_exception.cpp ../memory/allocators.cpp 2> aa
|
||||
#g++ -ggdb -I../../include class_perf.cpp alloc.cpp \
|
||||
#../memory/memory_pool.cpp ../fb_exception.cpp ../memory/allocators.cpp 2> aa
|
||||
|
@ -45,6 +45,10 @@
|
||||
#define LEAF_PAGE_SIZE 100
|
||||
#define NODE_PAGE_SIZE 100
|
||||
|
||||
/*inline void* operator new (size_t size, void *place) {
|
||||
return place;
|
||||
}*/
|
||||
|
||||
namespace Firebird {
|
||||
|
||||
class MallocAllocator {
|
||||
@ -59,10 +63,6 @@ public:
|
||||
|
||||
enum LocType { locEqual, locLess, locGreat, locGreatEqual, locLessEqual };
|
||||
|
||||
/*inline void* operator new (size_t size, void *place) {
|
||||
return place;
|
||||
}*/
|
||||
|
||||
// Fast and simple B+ tree of simple types
|
||||
// Tree has state (current item) and no iterator classes.
|
||||
// This is by design to make it simplier and faster :)))
|
||||
|
Loading…
Reference in New Issue
Block a user