|
|
|
@ -7,12 +7,18 @@ |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* IDENTIFICATION |
|
|
|
|
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.11 1998/09/01 04:33:34 momjian Exp $ |
|
|
|
|
* |
|
|
|
|
* NOTE |
|
|
|
|
* XXX This is a preliminary implementation which lacks fail-fast |
|
|
|
|
* XXX validity checking of arguments. |
|
|
|
|
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.12 1999/02/06 16:50:25 wieck Exp $ |
|
|
|
|
* |
|
|
|
|
* NOTE: |
|
|
|
|
* This is a new (Feb. 05, 1999) implementation of the allocation set |
|
|
|
|
* routines. AllocSet...() does not use OrderedSet...() any more. |
|
|
|
|
* Instead it manages allocations in a block pool by itself, combining |
|
|
|
|
* many small allocations in a few bigger blocks. AllocSetFree() does |
|
|
|
|
* never free() memory really. It just add's the free'd area to some |
|
|
|
|
* list for later reuse by AllocSetAlloc(). All memory blocks are free()'d |
|
|
|
|
* on AllocSetReset() at once, what happens when the memory context gets |
|
|
|
|
* destroyed. |
|
|
|
|
* Jan Wieck |
|
|
|
|
*------------------------------------------------------------------------- |
|
|
|
|
*/ |
|
|
|
|
#include <stdio.h> |
|
|
|
@ -25,56 +31,59 @@ |
|
|
|
|
#include <string.h> |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static void AllocPointerDump(AllocPointer pointer); |
|
|
|
|
static int AllocSetIterate(AllocSet set, |
|
|
|
|
void (*function) (AllocPointer pointer)); |
|
|
|
|
|
|
|
|
|
#undef AllocSetReset |
|
|
|
|
#undef malloc |
|
|
|
|
#undef free |
|
|
|
|
#undef realloc |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Internal type definitions |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* AllocElem -- |
|
|
|
|
* Allocation element. |
|
|
|
|
#define ALLOC_BLOCK_SIZE 8192 |
|
|
|
|
#define ALLOC_CHUNK_LIMIT 512 |
|
|
|
|
* |
|
|
|
|
* The above settings for block size and chunk limit gain better |
|
|
|
|
* performance. But the ones below force a bug that I didn't found |
|
|
|
|
* up to now letting the portals_p2 regression test fail. |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
typedef struct AllocElemData |
|
|
|
|
{ |
|
|
|
|
OrderedElemData elemData; /* elem in AllocSet */ |
|
|
|
|
Size size; |
|
|
|
|
} AllocElemData; |
|
|
|
|
#define ALLOC_BLOCK_SIZE 16384 |
|
|
|
|
#define ALLOC_CHUNK_LIMIT 256 |
|
|
|
|
|
|
|
|
|
typedef AllocElemData *AllocElem; |
|
|
|
|
#define ALLOC_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData)) |
|
|
|
|
#define ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData)) |
|
|
|
|
|
|
|
|
|
#define AllocPointerGetChunk(ptr) \ |
|
|
|
|
((AllocChunk)(((char *)(ptr)) - ALLOC_CHUNKHDRSZ)) |
|
|
|
|
#define AllocChunkGetPointer(chk) \ |
|
|
|
|
((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ)) |
|
|
|
|
#define AllocPointerGetAset(ptr) ((AllocSet)(AllocPointerGetChunk(ptr)->aset)) |
|
|
|
|
#define AllocPointerGetSize(ptr) (AllocPointerGetChunk(ptr)->size) |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Private method definitions |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* AllocPointerGetAllocElem -- |
|
|
|
|
* Returns allocation (internal) elem given (external) pointer. |
|
|
|
|
*/ |
|
|
|
|
#define AllocPointerGetAllocElem(pointer) (&((AllocElem)(pointer))[-1]) |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* AllocElemGetAllocPointer -- |
|
|
|
|
* Returns allocation (external) pointer given (internal) elem. |
|
|
|
|
/* ----------
|
|
|
|
|
* AllocSetFreeIndex - |
|
|
|
|
* |
|
|
|
|
* Depending on the size of an allocation compute which freechunk |
|
|
|
|
* list of the alloc set it belongs to. |
|
|
|
|
* ---------- |
|
|
|
|
*/ |
|
|
|
|
#define AllocElemGetAllocPointer(alloc) ((AllocPointer)&(alloc)[1]) |
|
|
|
|
static inline int |
|
|
|
|
AllocSetFreeIndex(Size size) |
|
|
|
|
{ |
|
|
|
|
int idx = 0; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* AllocElemIsValid -- |
|
|
|
|
* True iff alloc is valid. |
|
|
|
|
*/ |
|
|
|
|
#define AllocElemIsValid(alloc) PointerIsValid(alloc) |
|
|
|
|
size = (size - 1) >> 4; |
|
|
|
|
while (size != 0 && idx < 7) |
|
|
|
|
{ |
|
|
|
|
idx++; |
|
|
|
|
size >>= 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* non-export function prototypes */ |
|
|
|
|
static AllocPointer AllocSetGetFirst(AllocSet set); |
|
|
|
|
static AllocPointer AllocPointerGetNext(AllocPointer pointer); |
|
|
|
|
return idx; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Public routines |
|
|
|
@ -111,9 +120,10 @@ AllocSetInit(AllocSet set, AllocMode mode, Size limit) |
|
|
|
|
* limit is also ignored. This affects this whole file. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
OrderedSetInit(&set->setData, offsetof(AllocElemData, elemData)); |
|
|
|
|
memset(set, 0, sizeof(AllocSetData)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* AllocSetReset -- |
|
|
|
|
* Frees memory which is allocated in the given set. |
|
|
|
@ -124,28 +134,21 @@ AllocSetInit(AllocSet set, AllocMode mode, Size limit) |
|
|
|
|
void |
|
|
|
|
AllocSetReset(AllocSet set) |
|
|
|
|
{ |
|
|
|
|
AllocPointer pointer; |
|
|
|
|
AllocBlock block = set->blocks; |
|
|
|
|
AllocBlock next; |
|
|
|
|
|
|
|
|
|
AssertArg(AllocSetIsValid(set)); |
|
|
|
|
|
|
|
|
|
while (AllocPointerIsValid(pointer = AllocSetGetFirst(set))) |
|
|
|
|
AllocSetFree(set, pointer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef NOT_USED |
|
|
|
|
void |
|
|
|
|
AllocSetReset_debug(char *file, int line, AllocSet set) |
|
|
|
|
{ |
|
|
|
|
AllocPointer pointer; |
|
|
|
|
|
|
|
|
|
AssertArg(AllocSetIsValid(set)); |
|
|
|
|
while (block != NULL) |
|
|
|
|
{ |
|
|
|
|
next = block->next; |
|
|
|
|
free(block); |
|
|
|
|
block = next; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
while (AllocPointerIsValid(pointer = AllocSetGetFirst(set))) |
|
|
|
|
AllocSetFree(set, pointer); |
|
|
|
|
memset(set, 0, sizeof(AllocSetData)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* AllocSetContains -- |
|
|
|
|
* True iff allocation set contains given allocation element. |
|
|
|
@ -160,8 +163,7 @@ AllocSetContains(AllocSet set, AllocPointer pointer) |
|
|
|
|
AssertArg(AllocSetIsValid(set)); |
|
|
|
|
AssertArg(AllocPointerIsValid(pointer)); |
|
|
|
|
|
|
|
|
|
return (OrderedSetContains(&set->setData, |
|
|
|
|
&AllocPointerGetAllocElem(pointer)->elemData)); |
|
|
|
|
return (AllocPointerGetAset(pointer) == set); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@ -176,23 +178,107 @@ AllocSetContains(AllocSet set, AllocPointer pointer) |
|
|
|
|
AllocPointer |
|
|
|
|
AllocSetAlloc(AllocSet set, Size size) |
|
|
|
|
{ |
|
|
|
|
AllocElem alloc; |
|
|
|
|
AllocBlock block; |
|
|
|
|
AllocChunk chunk; |
|
|
|
|
AllocChunk freeref = NULL; |
|
|
|
|
int fidx; |
|
|
|
|
Size chunk_size; |
|
|
|
|
|
|
|
|
|
AssertArg(AllocSetIsValid(set)); |
|
|
|
|
|
|
|
|
|
/* allocate */ |
|
|
|
|
alloc = (AllocElem) malloc(sizeof(*alloc) + size); |
|
|
|
|
/*
|
|
|
|
|
* Lookup in the corresponding free list if there is a |
|
|
|
|
* free chunk we could reuse |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
fidx = AllocSetFreeIndex(size); |
|
|
|
|
for (chunk = set->freelist[fidx]; chunk; chunk = (AllocChunk)chunk->aset) |
|
|
|
|
{ |
|
|
|
|
if (chunk->size >= size) |
|
|
|
|
break; |
|
|
|
|
freeref = chunk; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If found, remove it from the free list, make it again |
|
|
|
|
* a member of the alloc set and return it's data address. |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
if (chunk != NULL) |
|
|
|
|
{ |
|
|
|
|
if (freeref == NULL) |
|
|
|
|
set->freelist[fidx] = (AllocChunk)chunk->aset; |
|
|
|
|
else |
|
|
|
|
freeref->aset = chunk->aset; |
|
|
|
|
|
|
|
|
|
if (!PointerIsValid(alloc)) |
|
|
|
|
elog(FATAL, "palloc failure: memory exhausted"); |
|
|
|
|
chunk->aset = (void *)set; |
|
|
|
|
return AllocChunkGetPointer(chunk); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* add to allocation list */ |
|
|
|
|
OrderedElemPushInto(&alloc->elemData, &set->setData); |
|
|
|
|
/*
|
|
|
|
|
* If requested size exceeds smallchunk limit, allocate a separate, |
|
|
|
|
* entire used block for this allocation |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
if (size > ALLOC_CHUNK_LIMIT) |
|
|
|
|
{ |
|
|
|
|
Size blksize; |
|
|
|
|
|
|
|
|
|
chunk_size = MAXALIGN(size); |
|
|
|
|
blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; |
|
|
|
|
block = (AllocBlock) malloc(blksize); |
|
|
|
|
if (block == NULL) |
|
|
|
|
elog(FATAL, "Memory exhausted in AllocSetAlloc()"); |
|
|
|
|
block->aset = set; |
|
|
|
|
block->freeptr = block->endptr = ((char *)block) + ALLOC_BLOCKHDRSZ; |
|
|
|
|
|
|
|
|
|
chunk = (AllocChunk) (((char *)block) + ALLOC_BLOCKHDRSZ); |
|
|
|
|
chunk->aset = set; |
|
|
|
|
chunk->size = chunk_size; |
|
|
|
|
|
|
|
|
|
if (set->blocks != NULL) |
|
|
|
|
{ |
|
|
|
|
block->next = set->blocks->next; |
|
|
|
|
set->blocks->next = block; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
block->next = NULL; |
|
|
|
|
set->blocks = block; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return AllocChunkGetPointer(chunk); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* set size */ |
|
|
|
|
alloc->size = size; |
|
|
|
|
chunk_size = 16 << fidx; |
|
|
|
|
|
|
|
|
|
return AllocElemGetAllocPointer(alloc); |
|
|
|
|
if ((block = set->blocks) != NULL) |
|
|
|
|
{ |
|
|
|
|
Size have_free = block->endptr - block->freeptr; |
|
|
|
|
|
|
|
|
|
if (have_free < (chunk_size + ALLOC_CHUNKHDRSZ)) |
|
|
|
|
block = NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (block == NULL) |
|
|
|
|
{ |
|
|
|
|
block = (AllocBlock) malloc(ALLOC_BLOCK_SIZE); |
|
|
|
|
if (block == NULL) |
|
|
|
|
elog(FATAL, "Memory exhausted in AllocSetAlloc()"); |
|
|
|
|
block->aset = set; |
|
|
|
|
block->next = set->blocks; |
|
|
|
|
block->freeptr = ((char *)block) + ALLOC_BLOCKHDRSZ; |
|
|
|
|
block->endptr = ((char *)block) + ALLOC_BLOCK_SIZE; |
|
|
|
|
|
|
|
|
|
set->blocks = block; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
chunk = (AllocChunk)(block->freeptr); |
|
|
|
|
chunk->aset = (void *)set; |
|
|
|
|
chunk->size = chunk_size; |
|
|
|
|
block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ); |
|
|
|
|
|
|
|
|
|
return AllocChunkGetPointer(chunk); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@ -207,19 +293,18 @@ AllocSetAlloc(AllocSet set, Size size) |
|
|
|
|
void |
|
|
|
|
AllocSetFree(AllocSet set, AllocPointer pointer) |
|
|
|
|
{ |
|
|
|
|
AllocElem alloc; |
|
|
|
|
int fidx; |
|
|
|
|
AllocChunk chunk; |
|
|
|
|
|
|
|
|
|
/* AssertArg(AllocSetIsValid(set)); */ |
|
|
|
|
/* AssertArg(AllocPointerIsValid(pointer)); */ |
|
|
|
|
AssertArg(AllocSetContains(set, pointer)); |
|
|
|
|
|
|
|
|
|
alloc = AllocPointerGetAllocElem(pointer); |
|
|
|
|
|
|
|
|
|
/* remove from allocation set */ |
|
|
|
|
OrderedElemPop(&alloc->elemData); |
|
|
|
|
chunk = AllocPointerGetChunk(pointer); |
|
|
|
|
fidx = AllocSetFreeIndex(chunk->size); |
|
|
|
|
|
|
|
|
|
/* free storage */ |
|
|
|
|
free(alloc); |
|
|
|
|
chunk->aset = (void *)set->freelist[fidx]; |
|
|
|
|
set->freelist[fidx] = chunk; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@ -238,25 +323,26 @@ AllocPointer |
|
|
|
|
AllocSetRealloc(AllocSet set, AllocPointer pointer, Size size) |
|
|
|
|
{ |
|
|
|
|
AllocPointer newPointer; |
|
|
|
|
AllocElem alloc; |
|
|
|
|
Size oldsize; |
|
|
|
|
|
|
|
|
|
/* AssertArg(AllocSetIsValid(set)); */ |
|
|
|
|
/* AssertArg(AllocPointerIsValid(pointer)); */ |
|
|
|
|
AssertArg(AllocSetContains(set, pointer)); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Calling realloc(3) directly is not be possible (unless we use our |
|
|
|
|
* own hacked version of malloc) since we must keep the allocations in |
|
|
|
|
* the allocation set. |
|
|
|
|
* Chunk sizes are aligned to power of 2 on AllocSetAlloc(). |
|
|
|
|
* Maybe the allocated area already is >= the new size. |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
alloc = AllocPointerGetAllocElem(pointer); |
|
|
|
|
if (AllocPointerGetSize(pointer) >= size) |
|
|
|
|
return pointer; |
|
|
|
|
|
|
|
|
|
/* allocate new pointer */ |
|
|
|
|
newPointer = AllocSetAlloc(set, size); |
|
|
|
|
|
|
|
|
|
/* fill new memory */ |
|
|
|
|
memmove(newPointer, pointer, (alloc->size < size) ? alloc->size : size); |
|
|
|
|
oldsize = AllocPointerGetSize(pointer); |
|
|
|
|
memmove(newPointer, pointer, (oldsize < size) ? oldsize : size); |
|
|
|
|
|
|
|
|
|
/* free old pointer */ |
|
|
|
|
AllocSetFree(set, pointer); |
|
|
|
@ -264,118 +350,6 @@ AllocSetRealloc(AllocSet set, AllocPointer pointer, Size size) |
|
|
|
|
return newPointer; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* AllocSetIterate -- |
|
|
|
|
* Returns size of set. Iterates through set elements calling function |
|
|
|
|
* (if valid) on each. |
|
|
|
|
* |
|
|
|
|
* Note: |
|
|
|
|
* This was written as an aid to debugging. It is intended for |
|
|
|
|
* debugging use only. |
|
|
|
|
* |
|
|
|
|
* Exceptions: |
|
|
|
|
* BadArg if set is invalid. |
|
|
|
|
*/ |
|
|
|
|
static int |
|
|
|
|
AllocSetIterate(AllocSet set, |
|
|
|
|
void (*function) (AllocPointer pointer)) |
|
|
|
|
{ |
|
|
|
|
int count = 0; |
|
|
|
|
AllocPointer pointer; |
|
|
|
|
|
|
|
|
|
AssertArg(AllocSetIsValid(set)); |
|
|
|
|
|
|
|
|
|
for (pointer = AllocSetGetFirst(set); |
|
|
|
|
AllocPointerIsValid(pointer); |
|
|
|
|
pointer = AllocPointerGetNext(pointer)) |
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
if (PointerIsValid(function)) |
|
|
|
|
(*function) (pointer); |
|
|
|
|
count += 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return count; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef NOT_USED |
|
|
|
|
int |
|
|
|
|
AllocSetCount(AllocSet set) |
|
|
|
|
{ |
|
|
|
|
int count = 0; |
|
|
|
|
AllocPointer pointer; |
|
|
|
|
|
|
|
|
|
AssertArg(AllocSetIsValid(set)); |
|
|
|
|
|
|
|
|
|
for (pointer = AllocSetGetFirst(set); |
|
|
|
|
AllocPointerIsValid(pointer); |
|
|
|
|
pointer = AllocPointerGetNext(pointer)) |
|
|
|
|
count++; |
|
|
|
|
return count; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Private routines |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* AllocSetGetFirst -- |
|
|
|
|
* Returns "first" allocation pointer in a set. |
|
|
|
|
* |
|
|
|
|
* Note: |
|
|
|
|
* Assumes set is valid. |
|
|
|
|
*/ |
|
|
|
|
static AllocPointer |
|
|
|
|
AllocSetGetFirst(AllocSet set) |
|
|
|
|
{ |
|
|
|
|
AllocElem alloc; |
|
|
|
|
|
|
|
|
|
alloc = (AllocElem) OrderedSetGetHead(&set->setData); |
|
|
|
|
|
|
|
|
|
if (!AllocElemIsValid(alloc)) |
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
|
|
return AllocElemGetAllocPointer(alloc); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* AllocPointerGetNext -- |
|
|
|
|
* Returns "successor" allocation pointer. |
|
|
|
|
* |
|
|
|
|
* Note: |
|
|
|
|
* Assumes pointer is valid. |
|
|
|
|
*/ |
|
|
|
|
static AllocPointer |
|
|
|
|
AllocPointerGetNext(AllocPointer pointer) |
|
|
|
|
{ |
|
|
|
|
AllocElem alloc; |
|
|
|
|
|
|
|
|
|
alloc = (AllocElem) |
|
|
|
|
OrderedElemGetSuccessor(&AllocPointerGetAllocElem(pointer)->elemData); |
|
|
|
|
|
|
|
|
|
if (!AllocElemIsValid(alloc)) |
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
|
|
return AllocElemGetAllocPointer(alloc); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Debugging routines |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* XXX AllocPointerDump -- |
|
|
|
|
* Displays allocated pointer. |
|
|
|
|
*/ |
|
|
|
|
static void |
|
|
|
|
AllocPointerDump(AllocPointer pointer) |
|
|
|
|
{ |
|
|
|
|
printf("\t%-10ld@ %0#lx\n", ((long *) pointer)[-1], (long) pointer); /* XXX */ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* AllocSetDump -- |
|
|
|
|
* Displays allocated set. |
|
|
|
@ -383,8 +357,5 @@ AllocPointerDump(AllocPointer pointer) |
|
|
|
|
void |
|
|
|
|
AllocSetDump(AllocSet set) |
|
|
|
|
{ |
|
|
|
|
int count; |
|
|
|
|
|
|
|
|
|
count = AllocSetIterate(set, AllocPointerDump); |
|
|
|
|
printf("\ttotal %d allocations\n", count); |
|
|
|
|
elog(DEBUG, "Currently unable to dump AllocSet"); |
|
|
|
|
} |
|
|
|
|