新增GUC参数disable_memory_stats控制STANDARD MEMORY CONTEXT的分配,
开启该参数时逻辑简化,去除一些内存跟踪统计。
This commit is contained in:
@ -274,6 +274,7 @@ static const TagStr g_tagStrArr[] = {{T_Invalid, "Invalid"},
|
||||
{T_MergeAction, "MergeAction"},
|
||||
{T_MemoryContext, "MemoryContext"},
|
||||
{T_AllocSetContext, "AllocSetContext"},
|
||||
{T_OptAllocSetContext, "OptAllocSetContext"},
|
||||
{T_StackAllocSetContext, "StackAllocSetContext"},
|
||||
{T_SharedAllocSetContext, "SharedAllocSetContext"},
|
||||
{T_MemalignAllocSetContext, "MemalignAllocSetContext"},
|
||||
|
||||
@ -273,6 +273,19 @@ static void InitMemoryConfigureNamesBool()
|
||||
NULL,
|
||||
NULL},
|
||||
|
||||
// variable to disable memory stats
|
||||
{{"disable_memory_stats",
|
||||
PGC_POSTMASTER,
|
||||
NODE_ALL,
|
||||
RESOURCES_MEM,
|
||||
gettext_noop("disable memory stats for query execution."),
|
||||
NULL},
|
||||
&g_instance.attr.attr_memory.disable_memory_stats,
|
||||
false,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL},
|
||||
|
||||
#ifdef MEMORY_CONTEXT_CHECKING
|
||||
// variable to enable memory check
|
||||
{{"enable_memory_context_check_debug",
|
||||
|
||||
@ -19,6 +19,6 @@ ifneq "$(MAKECMDGOALS)" "clean"
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
OBJS = aset.o mcxt.o portalmem.o memprot.o asetstk.o asetalg.o memtrack.o AsanMemoryAllocator.o memgroup.o memtrace.o mem_snapshot.o
|
||||
OBJS = aset.o mcxt.o opt_aset.o opt_mcxt.o portalmem.o memprot.o asetstk.o asetalg.o memtrack.o AsanMemoryAllocator.o memgroup.o memtrace.o mem_snapshot.o
|
||||
|
||||
include $(top_srcdir)/src/gausskernel/common.mk
|
||||
|
||||
@ -379,9 +379,14 @@ MemoryContext AllocSetContextCreate(_in_ MemoryContext parent, _in_ const char*
|
||||
}
|
||||
switch (contextType) {
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
case STANDARD_CONTEXT:
|
||||
return GenericMemoryAllocator::AllocSetContextCreate(
|
||||
parent, name, minContextSize, initBlockSize, maxBlockSize, maxSize, false, isSession);
|
||||
case STANDARD_CONTEXT: {
|
||||
if (g_instance.attr.attr_memory.disable_memory_stats) {
|
||||
return opt_AllocSetContextCreate(parent, name, minContextSize, initBlockSize, maxBlockSize);
|
||||
} else {
|
||||
return GenericMemoryAllocator::AllocSetContextCreate(
|
||||
parent, name, minContextSize, initBlockSize, maxBlockSize, maxSize, false, isSession);
|
||||
}
|
||||
}
|
||||
case SHARED_CONTEXT:
|
||||
return GenericMemoryAllocator::AllocSetContextCreate(
|
||||
parent, name, minContextSize, initBlockSize, maxBlockSize, maxSize, true, false);
|
||||
|
||||
@ -216,6 +216,10 @@ void MemoryContextReset(MemoryContext context)
|
||||
{
|
||||
AssertArg(MemoryContextIsValid(context));
|
||||
|
||||
if (IsOptAllocSetContext(context)) {
|
||||
return opt_MemoryContextReset(context);
|
||||
}
|
||||
|
||||
PreventActionOnSealedContext(context);
|
||||
|
||||
if (MemoryContextIsShared(context))
|
||||
@ -300,6 +304,11 @@ void MemoryContextDeleteInternal(MemoryContext context, bool parent_locked,
|
||||
/* And not CurrentMemoryContext, either */
|
||||
Assert(context != CurrentMemoryContext);
|
||||
|
||||
if (IsOptAllocSetContext(context)) {
|
||||
opt_MemoryContextDeleteInternal(context, context_list);
|
||||
return;
|
||||
}
|
||||
|
||||
MemoryContextDeleteChildren(context, context_list);
|
||||
|
||||
#ifdef MEMORY_CONTEXT_CHECKING
|
||||
@ -991,6 +1000,11 @@ void MemoryContextCheckSessionMemory(MemoryContext context, Size size, const cha
|
||||
void* MemoryAllocFromContext(MemoryContext context, Size size, const char* file, int line)
|
||||
{
|
||||
void* ret = NULL;
|
||||
|
||||
if (IsOptAllocSetContext(context)) {
|
||||
return opt_MemoryAllocFromContext(context, size);
|
||||
}
|
||||
|
||||
if (!AllocSizeIsValid(size)) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
@ -1049,6 +1063,10 @@ void* MemoryContextAllocZeroDebug(MemoryContext context, Size size, const char*
|
||||
{
|
||||
void* ret = NULL;
|
||||
|
||||
if (IsOptAllocSetContext(context)) {
|
||||
return opt_MemoryContextAllocZeroDebug(context, size, file, line);
|
||||
}
|
||||
|
||||
AssertArg(MemoryContextIsValid(context));
|
||||
#ifdef MEMORY_CONTEXT_CHECKING
|
||||
PreventActionOnSealedContext(context);
|
||||
@ -1099,6 +1117,10 @@ void* MemoryContextAllocZeroAlignedDebug(MemoryContext context, Size size, const
|
||||
{
|
||||
void* ret = NULL;
|
||||
|
||||
if (IsOptAllocSetContext(context)) {
|
||||
return opt_MemoryContextAllocZeroAlignedDebug(context, size, file, line);
|
||||
}
|
||||
|
||||
AssertArg(MemoryContextIsValid(context));
|
||||
|
||||
PreventActionOnSealedContext(context);
|
||||
@ -1275,6 +1297,11 @@ void pfree(void* pointer)
|
||||
context = &(block->aset->header);
|
||||
#endif
|
||||
|
||||
if (IsOptAllocSetContext(context)) {
|
||||
opt_pfree(pointer);
|
||||
return;
|
||||
}
|
||||
|
||||
AssertArg(MemoryContextIsValid(context));
|
||||
#ifdef MEMORY_CONTEXT_CHECKING
|
||||
if (!IsTopMemCxt(context)) {
|
||||
@ -1292,6 +1319,20 @@ void* repalloc_noexcept_Debug(void* pointer, Size size, const char* file, int li
|
||||
MemoryContext context;
|
||||
void* ret = NULL;
|
||||
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
/*
|
||||
* OK, it's probably safe to look at the chunk header.
|
||||
*/
|
||||
context = ((StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE))->context;
|
||||
#else
|
||||
AsanBlock block = ((AsanBlock)(((char*)(pointer)) - ASAN_BLOCKHDRSZ));
|
||||
context = &(block->aset->header);
|
||||
#endif
|
||||
|
||||
if (IsOptAllocSetContext(context)) {
|
||||
return opt_repalloc_noexcept_Debug(pointer, size, file, line);
|
||||
}
|
||||
|
||||
if (!AllocSizeIsValid(size)) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
@ -1306,16 +1347,6 @@ void* repalloc_noexcept_Debug(void* pointer, Size size, const char* file, int li
|
||||
Assert(pointer != NULL);
|
||||
Assert(pointer == (void*)MAXALIGN(pointer));
|
||||
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
/*
|
||||
* OK, it's probably safe to look at the chunk header.
|
||||
*/
|
||||
context = ((StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE))->context;
|
||||
#else
|
||||
AsanBlock block = ((AsanBlock)(((char*)(pointer)) - ASAN_BLOCKHDRSZ));
|
||||
context = &(block->aset->header);
|
||||
#endif
|
||||
|
||||
AssertArg(MemoryContextIsValid(context));
|
||||
|
||||
/* isReset must be false already */
|
||||
@ -1346,6 +1377,20 @@ void* repallocDebug(void* pointer, Size size, const char* file, int line)
|
||||
MemoryContext context;
|
||||
void* ret = NULL;
|
||||
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
/*
|
||||
* OK, it's probably safe to look at the chunk header.
|
||||
*/
|
||||
context = ((StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE))->context;
|
||||
#else
|
||||
AsanBlock block = ((AsanBlock)(((char*)(pointer)) - ASAN_BLOCKHDRSZ));
|
||||
context = &(block->aset->header);
|
||||
#endif
|
||||
|
||||
if (IsOptAllocSetContext(context)) {
|
||||
return opt_repallocDebug(pointer, size, file, line);
|
||||
}
|
||||
|
||||
if (!AllocSizeIsValid(size)) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
@ -1359,15 +1404,7 @@ void* repallocDebug(void* pointer, Size size, const char* file, int line)
|
||||
*/
|
||||
Assert(pointer != NULL);
|
||||
Assert(pointer == (void*)MAXALIGN(pointer));
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
/*
|
||||
* OK, it's probably safe to look at the chunk header.
|
||||
*/
|
||||
context = ((StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE))->context;
|
||||
#else
|
||||
AsanBlock block = ((AsanBlock)(((char*)(pointer)) - ASAN_BLOCKHDRSZ));
|
||||
context = &(block->aset->header);
|
||||
#endif
|
||||
|
||||
AssertArg(MemoryContextIsValid(context));
|
||||
PreventActionOnSealedContext(context);
|
||||
/* isReset must be false already */
|
||||
@ -1455,6 +1492,10 @@ void* MemoryContextAllocHugeDebug(MemoryContext context, Size size, const char*
|
||||
{
|
||||
void* ret = NULL;
|
||||
|
||||
if (IsOptAllocSetContext(context)) {
|
||||
return opt_MemoryAllocFromContext(context, size);
|
||||
}
|
||||
|
||||
AssertArg(MemoryContextIsValid(context));
|
||||
|
||||
if (!AllocHugeSizeIsValid(size)) {
|
||||
@ -1502,6 +1543,20 @@ void* repallocHugeDebug(void* pointer, Size size, const char* file, int line)
|
||||
MemoryContext context;
|
||||
void* ret = NULL;
|
||||
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
/*
|
||||
* OK, it's probably safe to look at the chunk header.
|
||||
*/
|
||||
context = ((StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE))->context;
|
||||
#else
|
||||
AsanBlock block = ((AsanBlock)(((char*)(pointer)) - ASAN_BLOCKHDRSZ));
|
||||
context = &(block->aset->header);
|
||||
#endif
|
||||
|
||||
if (IsOptAllocSetContext(context)) {
|
||||
return opt_repallocDebug(pointer, size, file, line);
|
||||
}
|
||||
|
||||
if (!AllocHugeSizeIsValid(size)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid memory alloc request size %lu in %s:%d", size, file, line)));
|
||||
@ -1514,15 +1569,7 @@ void* repallocHugeDebug(void* pointer, Size size, const char* file, int line)
|
||||
*/
|
||||
Assert(pointer != NULL);
|
||||
Assert(pointer == (void*)MAXALIGN(pointer));
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
/*
|
||||
* OK, it's probably safe to look at the chunk header.
|
||||
*/
|
||||
context = ((StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE))->context;
|
||||
#else
|
||||
AsanBlock block = ((AsanBlock)(((char*)(pointer)) - ASAN_BLOCKHDRSZ));
|
||||
context = &(block->aset->header);
|
||||
#endif
|
||||
|
||||
AssertArg(MemoryContextIsValid(context));
|
||||
|
||||
/* isReset must be false already */
|
||||
|
||||
954
src/common/backend/utils/mmgr/opt_aset.cpp
Normal file
954
src/common/backend/utils/mmgr/opt_aset.cpp
Normal file
@ -0,0 +1,954 @@
|
||||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* std_aset.cpp
|
||||
* Allocation set definitions.
|
||||
*
|
||||
* AllocSet is our standard implementation of the abstract MemoryContext
|
||||
* type.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/backend/utils/mmgr/aset.c
|
||||
*
|
||||
* 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() normally
|
||||
* doesn't 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
|
||||
* at once on AllocSetReset(), which happens when the memory context gets
|
||||
* destroyed.
|
||||
* Jan Wieck
|
||||
*
|
||||
* Performance improvement from Tom Lane, 8/99: for extremely large request
|
||||
* sizes, we do want to be able to give the memory back to free() as soon
|
||||
* as it is pfree()'d. Otherwise we risk tying up a lot of memory in
|
||||
* freelist entries that might never be usable. This is specially needed
|
||||
* when the caller is repeatedly repalloc()'ing a block bigger and bigger;
|
||||
* the previous instances of the block were guaranteed to be wasted until
|
||||
* AllocSetReset() under the old way.
|
||||
*
|
||||
* Further improvement 12/00: as the code stood, request sizes in the
|
||||
* midrange between "small" and "large" were handled very inefficiently,
|
||||
* because any sufficiently large free chunk would be used to satisfy a
|
||||
* request, even if it was much larger than necessary. This led to more
|
||||
* and more wasted space in allocated chunks over time. To fix, get rid
|
||||
* of the midrange behavior: we now handle only "small" power-of-2-size
|
||||
* chunks as chunks. Anything "large" is passed off to malloc(). Change
|
||||
* the number of freelists to change the small/large boundary.
|
||||
*
|
||||
*
|
||||
* About CLOBBER_FREED_MEMORY:
|
||||
*
|
||||
* If this symbol is defined, all freed memory is overwritten with 0x7F's.
|
||||
* This is useful for catching places that reference already-freed memory.
|
||||
*
|
||||
* About MEMORY_CONTEXT_CHECKING:
|
||||
*
|
||||
* Since we usually round request sizes up to the next power of 2, there
|
||||
* is often some unused space immediately after a requested data area.
|
||||
* Thus, if someone makes the common error of writing past what they've
|
||||
* requested, the problem is likely to go unnoticed ... until the day when
|
||||
* there *isn't* any wasted space, perhaps because of different memory
|
||||
* alignment on a new platform, or some other effect. To catch this sort
|
||||
* of problem, the MEMORY_CONTEXT_CHECKING option stores 0x7E just beyond
|
||||
* the requested space whenever the request is less than the actual chunk
|
||||
* size, and verifies that the byte is undamaged when the chunk is freed.
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "postgres.h"
|
||||
#include "knl/knl_variable.h"
|
||||
|
||||
#include "utils/dynahash.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/mmpool.h"
|
||||
#include "utils/aset.h"
|
||||
#include "gssignal/gs_signal.h"
|
||||
#include "gs_register/gs_malloc.h"
|
||||
#include "pgstat.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/memprot.h"
|
||||
#include "utils/memtrack.h"
|
||||
#include "storage/procarray.h"
|
||||
|
||||
/* Define this to detail debug alloc information . HAVE_ALLOCINFO */
|
||||
|
||||
/* --------------------
|
||||
* Chunk freelist k holds chunks of size 1 << (k + ALLOC_MINBITS),
|
||||
* for k = 0 .. ALLOCSET_NUM_FREELISTS-1.
|
||||
*
|
||||
* Note that all chunks in the freelists have power-of-2 sizes. This
|
||||
* improves recyclability: we may waste some space, but the wasted space
|
||||
* should stay pretty constant as requests are made and released.
|
||||
*
|
||||
* A request too large for the last freelist is handled by allocating a
|
||||
* dedicated block from malloc(). The block still has a block header and
|
||||
* chunk header, but when the chunk is freed we'll return the whole block
|
||||
* to malloc(), not put it on our freelists.
|
||||
*
|
||||
* CAUTION: ALLOC_MINBITS must be large enough so that
|
||||
* 1<<ALLOC_MINBITS is at least MAXALIGN,
|
||||
* or we may fail to align the smallest chunks adequately.
|
||||
* 8-byte alignment is enough on all currently known machines.
|
||||
*
|
||||
* With the current parameters, request sizes up to 8K are treated as chunks,
|
||||
* larger requests go into dedicated blocks. Change ALLOCSET_NUM_FREELISTS
|
||||
* to adjust the boundary point. (But in contexts with small maxBlockSize,
|
||||
* we may set the allocChunkLimit to less than 8K, so as to avoid space
|
||||
* wastage.)
|
||||
* --------------------
|
||||
*/
|
||||
|
||||
#define ALLOC_MINBITS 3 /* smallest chunk size is 8 bytes */
|
||||
#define ALLOCSET_NUM_FREELISTS 11
|
||||
#define ALLOC_CHUNK_LIMIT (1 << (ALLOCSET_NUM_FREELISTS - 1 + ALLOC_MINBITS))
|
||||
/* Size of largest chunk that we use a fixed size for */
|
||||
#define ALLOC_CHUNK_FRACTION 4
|
||||
/* We allow chunks to be at most 1/4 of maxBlockSize (less overhead) */
|
||||
|
||||
/* --------------------
|
||||
* The first block allocated for an allocset has size initBlockSize.
|
||||
* Each time we have to allocate another block, we double the block size
|
||||
* (if possible, and without exceeding maxBlockSize), so as to reduce
|
||||
* the bookkeeping load on malloc().
|
||||
*
|
||||
* Blocks allocated to hold oversize chunks do not follow this rule, however;
|
||||
* they are just however big they need to be to hold that single chunk.
|
||||
* --------------------
|
||||
*/
|
||||
|
||||
#define ALLOC_MAGICHDRSZ 0
|
||||
|
||||
/*
|
||||
* AllocPointerIsValid
|
||||
* True iff pointer is valid allocation pointer.
|
||||
*/
|
||||
#define AllocPointerIsValid(pointer) PointerIsValid(pointer)
|
||||
|
||||
/*
|
||||
* AllocSetIsValid
|
||||
* True iff set is valid allocation set.
|
||||
*/
|
||||
#define AllocSetIsValid(set) PointerIsValid(set)
|
||||
|
||||
#define AllocPointerGetChunk(ptr) ((AllocChunk)(((char*)(ptr)) - ALLOC_CHUNKHDRSZ))
|
||||
#define AllocChunkGetPointer(chk) ((AllocPointer)(((char*)(chk)) + ALLOC_CHUNKHDRSZ))
|
||||
|
||||
/*
|
||||
* Table for opt_AllocSetFreeIndex
|
||||
*/
|
||||
#define LT16(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
|
||||
|
||||
static const unsigned char LogTable256[256] = {0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
4,
|
||||
4,
|
||||
4,
|
||||
4,
|
||||
4,
|
||||
4,
|
||||
4,
|
||||
4,
|
||||
LT16(5),
|
||||
LT16(6),
|
||||
LT16(6),
|
||||
LT16(7),
|
||||
LT16(7),
|
||||
LT16(7),
|
||||
LT16(7),
|
||||
LT16(8),
|
||||
LT16(8),
|
||||
LT16(8),
|
||||
LT16(8),
|
||||
LT16(8),
|
||||
LT16(8),
|
||||
LT16(8),
|
||||
LT16(8)};
|
||||
|
||||
#define MAX_FREE_CONTEXTS 100 /* arbitrary limit on freelist length */
|
||||
|
||||
typedef struct AllocSetFreeList
|
||||
{
|
||||
int num_free; /* current list length */
|
||||
AllocSetContext *first_free; /* list header */
|
||||
} AllocSetFreeList;
|
||||
|
||||
/* context_freelists[0] is for default params, [1] for small params */
|
||||
THR_LOCAL AllocSetFreeList context_freelists[2] =
|
||||
{
|
||||
{
|
||||
0, NULL
|
||||
},
|
||||
{
|
||||
0, NULL
|
||||
}
|
||||
};
|
||||
|
||||
/* ----------
|
||||
* opt_AllocSetFreeIndex -
|
||||
*
|
||||
* Depending on the size of an allocation compute which freechunk
|
||||
* list of the alloc set it belongs to. Caller must have verified
|
||||
* that size <= ALLOC_CHUNK_LIMIT.
|
||||
* ----------
|
||||
*/
|
||||
static inline int opt_AllocSetFreeIndex(Size size)
|
||||
{
|
||||
int idx;
|
||||
|
||||
if (size > (1 << ALLOC_MINBITS)) {
|
||||
idx = 31 - __builtin_clz((uint32) size - 1) - ALLOC_MINBITS + 1;
|
||||
Assert(idx < ALLOCSET_NUM_FREELISTS);
|
||||
} else
|
||||
idx = 0;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static void* opt_AllocSetAlloc(MemoryContext context, Size align, Size size, const char* file, int line);
|
||||
static void opt_AllocSetFree(MemoryContext context, void* pointer);
|
||||
static void* opt_AllocSetRealloc(MemoryContext context, void* pointer, Size align, Size size, const char* file, int line);
|
||||
static void opt_AllocSetInit(MemoryContext context);
|
||||
static void opt_AllocSetReset(MemoryContext context);
|
||||
static void opt_AllocSetDelete(MemoryContext context);
|
||||
static Size opt_AllocSetGetChunkSpace(MemoryContext context, void* pointer);
|
||||
static bool opt_AllocSetIsEmpty(MemoryContext context);
|
||||
static void opt_AllocSetStats(MemoryContext context, int level);
|
||||
|
||||
/*
|
||||
* Public routines
|
||||
*/
|
||||
static MemoryContextMethods AllocSetMethods = {
|
||||
opt_AllocSetAlloc,
|
||||
opt_AllocSetFree,
|
||||
opt_AllocSetRealloc,
|
||||
opt_AllocSetInit,
|
||||
opt_AllocSetReset,
|
||||
opt_AllocSetDelete,
|
||||
opt_AllocSetGetChunkSpace,
|
||||
opt_AllocSetIsEmpty,
|
||||
opt_AllocSetStats
|
||||
};
|
||||
|
||||
MemoryContextMethods *get_AllocSetMethods()
|
||||
{
|
||||
return &AllocSetMethods;
|
||||
}
|
||||
|
||||
/*
|
||||
* AllocSetContextCreate
|
||||
* Create a new AllocSet context.
|
||||
*
|
||||
* parent: parent context, or NULL if top-level context
|
||||
* name: name of context (for debugging --- string will be copied)
|
||||
* minContextSize: minimum context size
|
||||
* initBlockSize: initial allocation block size
|
||||
* maxBlockSize: maximum allocation block size
|
||||
*/
|
||||
MemoryContext opt_AllocSetContextCreate(MemoryContext parent, const char* name, Size minContextSize,
|
||||
Size initBlockSize, Size maxBlockSize)
|
||||
{
|
||||
AllocSet context;
|
||||
AllocBlock block;
|
||||
int freeListIndex;
|
||||
Size firstBlockSize;
|
||||
|
||||
if (minContextSize == ALLOCSET_DEFAULT_MINSIZE &&
|
||||
initBlockSize == ALLOCSET_DEFAULT_INITSIZE)
|
||||
freeListIndex = 0;
|
||||
else if (minContextSize == ALLOCSET_SMALL_MINSIZE &&
|
||||
initBlockSize == ALLOCSET_SMALL_INITSIZE)
|
||||
freeListIndex = 1;
|
||||
else
|
||||
freeListIndex = -1;
|
||||
|
||||
if (freeListIndex >= 0)
|
||||
{
|
||||
AllocSetFreeList *freelist = &context_freelists[freeListIndex];
|
||||
|
||||
if (freelist->first_free != NULL)
|
||||
{
|
||||
/* Remove entry from freelist */
|
||||
context = freelist->first_free;
|
||||
freelist->first_free = (AllocSet) context->header.nextchild;
|
||||
freelist->num_free--;
|
||||
|
||||
/* Update its maxBlockSize; everything else should be OK */
|
||||
context->maxBlockSize = maxBlockSize;
|
||||
|
||||
/* Reinitialize its header, installing correct name and parent */
|
||||
opt_MemoryContextCreate((MemoryContext) context,
|
||||
T_OptAllocSetContext,
|
||||
&AllocSetMethods,
|
||||
parent,
|
||||
name);
|
||||
|
||||
context->maxSpaceSize = SELF_GENRIC_MEMCTX_LIMITATION;
|
||||
return (MemoryContext) context;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine size of initial block */
|
||||
firstBlockSize = MAXALIGN(sizeof(AllocSetContext)) +
|
||||
ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
|
||||
if (minContextSize != 0)
|
||||
firstBlockSize = Max(firstBlockSize, minContextSize);
|
||||
else
|
||||
firstBlockSize = Max(firstBlockSize, initBlockSize);
|
||||
|
||||
context = (AllocSet) malloc(firstBlockSize);
|
||||
if (context == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OUT_OF_MEMORY),
|
||||
errmsg("out of memory"),
|
||||
errdetail("Failed while creating memory context \"%s\".",
|
||||
name)));
|
||||
|
||||
block = (AllocBlock) (((char *) context) + MAXALIGN(sizeof(AllocSetContext)));
|
||||
block->aset = context;
|
||||
block->freeptr = ((char*)block) + ALLOC_BLOCKHDRSZ;
|
||||
block->endptr = ((char*)context) + firstBlockSize;
|
||||
block->allocSize = firstBlockSize;
|
||||
|
||||
context->totalSpace += firstBlockSize;
|
||||
context->freeSpace += block->endptr - block->freeptr;
|
||||
|
||||
block->prev = NULL;
|
||||
block->next = NULL;
|
||||
|
||||
/* Remember block as part of block list */
|
||||
context->blocks = block;
|
||||
/* Mark block as not to be released at reset time */
|
||||
context->keeper = block;
|
||||
|
||||
MemSetAligned(context->freelist, 0, sizeof(context->freelist));
|
||||
|
||||
context->initBlockSize = initBlockSize;
|
||||
context->maxBlockSize = maxBlockSize;
|
||||
context->nextBlockSize = initBlockSize;
|
||||
context->freeListIndex = freeListIndex;
|
||||
|
||||
context->allocChunkLimit = ALLOC_CHUNK_LIMIT;
|
||||
while ((Size)(context->allocChunkLimit + ALLOC_CHUNKHDRSZ) >
|
||||
(Size)((maxBlockSize - ALLOC_BLOCKHDRSZ) / ALLOC_CHUNK_FRACTION)) {
|
||||
context->allocChunkLimit >>= 1;
|
||||
}
|
||||
|
||||
opt_MemoryContextCreate((MemoryContext) context,
|
||||
T_OptAllocSetContext,
|
||||
&AllocSetMethods,
|
||||
parent,
|
||||
name);
|
||||
|
||||
context->maxSpaceSize = SELF_GENRIC_MEMCTX_LIMITATION;
|
||||
|
||||
return (MemoryContext) context;
|
||||
}
|
||||
|
||||
/*
|
||||
* AllocSetInit
|
||||
* Context-type-specific initialization routine.
|
||||
*
|
||||
* This is called by MemoryContextCreate() after setting up the
|
||||
* generic MemoryContext fields and before linking the new context
|
||||
* into the context tree. We must do whatever is needed to make the
|
||||
* new context minimally valid for deletion. We must *not* risk
|
||||
* failure --- thus, for example, allocating more memory is not cool.
|
||||
* (AllocSetContextCreate can allocate memory when it gets control
|
||||
* back, however.)
|
||||
*/
|
||||
static void opt_AllocSetInit(MemoryContext context)
|
||||
{
|
||||
/*
|
||||
* Since MemoryContextCreate already zeroed the context node, we don't
|
||||
* have to do anything here: it's already OK.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* AllocSetReset
|
||||
* Frees all memory which is allocated in the given set.
|
||||
*
|
||||
* Actually, this routine has some discretion about what to do.
|
||||
* It should mark all allocated chunks freed, but it need not necessarily
|
||||
* give back all the resources the set owns. Our actual implementation is
|
||||
* that we hang onto any "keeper" block specified for the set. In this way,
|
||||
* we don't thrash malloc() when a context is repeatedly reset after small
|
||||
* allocations, which is typical behavior for per-tuple contexts.
|
||||
*/
|
||||
static void opt_AllocSetReset(MemoryContext context)
|
||||
{
|
||||
AllocSet set = (AllocSet)context;
|
||||
AllocBlock block;
|
||||
|
||||
AssertArg(AllocSetIsValid(set));
|
||||
|
||||
/* Clear chunk freelists */
|
||||
MemSetAligned(set->freelist, 0, sizeof(set->freelist));
|
||||
|
||||
block = set->blocks;
|
||||
|
||||
/* New blocks list is either empty or just the keeper block */
|
||||
set->blocks = set->keeper;
|
||||
while (block != NULL) {
|
||||
AllocBlock next = block->next;
|
||||
Size tempSize = block->allocSize;
|
||||
|
||||
if (block == set->keeper) {
|
||||
/* Reset the block, but don't return it to malloc */
|
||||
char* datastart = ((char*)block) + ALLOC_BLOCKHDRSZ;
|
||||
|
||||
block->freeptr = datastart;
|
||||
block->allocSize = tempSize;
|
||||
block->next = NULL;
|
||||
block->prev = NULL;
|
||||
} else {
|
||||
free(block);
|
||||
}
|
||||
block = next;
|
||||
}
|
||||
/* Reset block size allocation sequence, too */
|
||||
set->nextBlockSize = set->initBlockSize;
|
||||
|
||||
if (set->blocks != NULL) {
|
||||
/* calculate memory statisic after reset. */
|
||||
block = set->blocks;
|
||||
|
||||
set->freeSpace = block->endptr - block->freeptr;
|
||||
set->totalSpace = block->endptr - (char*)block;
|
||||
} else {
|
||||
set->freeSpace = 0;
|
||||
set->totalSpace = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* AllocSetDelete
|
||||
* Frees all memory which is allocated in the given set,
|
||||
* in preparation for deletion of the set.
|
||||
*
|
||||
* Unlike AllocSetReset, this *must* free all resources of the set.
|
||||
* But note we are not responsible for deleting the context node itself.
|
||||
*/
|
||||
static void opt_AllocSetDelete(MemoryContext context)
|
||||
{
|
||||
AllocSet set = (AllocSet)context;
|
||||
AssertArg(AllocSetIsValid(set));
|
||||
|
||||
AllocBlock block = set->blocks;
|
||||
|
||||
if (set->freeListIndex >= 0)
|
||||
{
|
||||
AllocSetFreeList *freelist = &context_freelists[set->freeListIndex];
|
||||
|
||||
if (!context->isReset) {
|
||||
context->methods->reset(context);
|
||||
context->isReset = true;
|
||||
}
|
||||
|
||||
if (freelist->num_free >= MAX_FREE_CONTEXTS)
|
||||
{
|
||||
while (freelist->first_free != NULL)
|
||||
{
|
||||
AllocSetContext *oldset = freelist->first_free;
|
||||
|
||||
freelist->first_free = (AllocSetContext *) oldset->header.nextchild;
|
||||
freelist->num_free--;
|
||||
|
||||
/* All that remains is to free the header/initial block */
|
||||
free(oldset);
|
||||
}
|
||||
Assert(freelist->num_free == 0);
|
||||
}
|
||||
|
||||
/* Now add the just-deleted context to the freelist. */
|
||||
set->header.nextchild = (MemoryContext) freelist->first_free;
|
||||
freelist->first_free = set;
|
||||
freelist->num_free++;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while (block != NULL) {
|
||||
AllocBlock next = block->next;
|
||||
|
||||
if (block != set->keeper)
|
||||
free(block);
|
||||
|
||||
block = next;
|
||||
}
|
||||
|
||||
free(set);
|
||||
}
|
||||
|
||||
/*
|
||||
* AllocSetAlloc
|
||||
* Returns pointer to allocated memory of given size; memory is added
|
||||
* to the set.
|
||||
*/
|
||||
static void* opt_AllocSetAlloc(MemoryContext context, Size align, Size size, const char* file, int line)
|
||||
{
|
||||
Assert(file != NULL);
|
||||
Assert(line != 0);
|
||||
AllocSet set = (AllocSet)context;
|
||||
AllocBlock block;
|
||||
AllocChunk chunk;
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
unsigned int fidx;
|
||||
#endif
|
||||
Size chunk_size;
|
||||
Size blksize;
|
||||
|
||||
AssertArg(AllocSetIsValid(set));
|
||||
AssertArg(align == 0);
|
||||
|
||||
size += ALLOC_MAGICHDRSZ;
|
||||
|
||||
/*
|
||||
* If requested size exceeds maximum for chunks, allocate an entire block
|
||||
* for this request.
|
||||
*/
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
if (size > set->allocChunkLimit) {
|
||||
#endif
|
||||
chunk_size = MAXALIGN(size);
|
||||
blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
|
||||
|
||||
block = (AllocBlock) malloc(blksize);
|
||||
if (block == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
block->aset = set;
|
||||
block->freeptr = block->endptr = ((char*)block) + blksize;
|
||||
block->allocSize = blksize;
|
||||
|
||||
/* enlarge total space only. */
|
||||
set->totalSpace += blksize;
|
||||
|
||||
chunk = (AllocChunk)(((char*)block) + ALLOC_BLOCKHDRSZ);
|
||||
chunk->aset = set;
|
||||
chunk->size = chunk_size;
|
||||
|
||||
/*
|
||||
* Stick the new block underneath the active allocation block, so that
|
||||
* we don't lose the use of the space remaining therein.
|
||||
*/
|
||||
if (set->blocks != NULL) {
|
||||
block->prev = set->blocks;
|
||||
block->next = set->blocks->next;
|
||||
if (block->next)
|
||||
block->next->prev = block;
|
||||
set->blocks->next = block;
|
||||
} else {
|
||||
block->prev = NULL;
|
||||
block->next = NULL;
|
||||
set->blocks = block;
|
||||
}
|
||||
|
||||
return AllocChunkGetPointer(chunk);
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
}
|
||||
|
||||
/*
|
||||
* Request is small enough to be treated as a chunk. Look in the
|
||||
* corresponding free list to see if there is a free chunk we could reuse.
|
||||
* If one is found, remove it from the free list, make it again a member
|
||||
* of the alloc set and return its data address.
|
||||
*/
|
||||
fidx = opt_AllocSetFreeIndex(size);
|
||||
chunk = set->freelist[fidx];
|
||||
if (chunk != NULL) {
|
||||
Assert(chunk->size >= size);
|
||||
Assert(chunk->aset != set);
|
||||
Assert((int)fidx == opt_AllocSetFreeIndex(chunk->size));
|
||||
|
||||
set->freelist[fidx] = (AllocChunk)chunk->aset;
|
||||
|
||||
chunk->aset = (void*)set;
|
||||
|
||||
set->freeSpace -= (chunk->size + ALLOC_CHUNKHDRSZ);
|
||||
|
||||
return AllocChunkGetPointer(chunk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Choose the actual chunk size to allocate.
|
||||
*/
|
||||
chunk_size = ((unsigned long)1 << ALLOC_MINBITS) << fidx;
|
||||
Assert(chunk_size >= size);
|
||||
|
||||
/*
|
||||
* If there is enough room in the active allocation block, we will put the
|
||||
* chunk into that block. Else must start a new one.
|
||||
*/
|
||||
if ((block = set->blocks) != NULL) {
|
||||
Size availspace = block->endptr - block->freeptr;
|
||||
|
||||
if (availspace < (chunk_size + ALLOC_CHUNKHDRSZ)) {
|
||||
/*
|
||||
* The existing active (top) block does not have enough room for
|
||||
* the requested allocation, but it might still have a useful
|
||||
* amount of space in it. Once we push it down in the block list,
|
||||
* we'll never try to allocate more space from it. So, before we
|
||||
* do that, carve up its free space into chunks that we can put on
|
||||
* the set's freelists.
|
||||
*
|
||||
* Because we can only get here when there's less than
|
||||
* ALLOC_CHUNK_LIMIT left in the block, this loop cannot iterate
|
||||
* more than ALLOCSET_NUM_FREELISTS-1 times.
|
||||
*/
|
||||
while (availspace >= ((1 << ALLOC_MINBITS) + ALLOC_CHUNKHDRSZ)) {
|
||||
Size availchunk = availspace - ALLOC_CHUNKHDRSZ;
|
||||
int a_fidx = opt_AllocSetFreeIndex(availchunk);
|
||||
|
||||
/*
|
||||
* In most cases, we'll get back the index of the next larger
|
||||
* freelist than the one we need to put this chunk on. The
|
||||
* exception is when availchunk is exactly a power of 2.
|
||||
*/
|
||||
if (availchunk != ((Size)1 << ((unsigned int)a_fidx + ALLOC_MINBITS))) {
|
||||
a_fidx--;
|
||||
Assert(a_fidx >= 0);
|
||||
availchunk = ((Size)1 << ((unsigned int)a_fidx + ALLOC_MINBITS));
|
||||
}
|
||||
|
||||
chunk = (AllocChunk)(block->freeptr);
|
||||
|
||||
block->freeptr += (availchunk + ALLOC_CHUNKHDRSZ);
|
||||
availspace -= (availchunk + ALLOC_CHUNKHDRSZ);
|
||||
|
||||
chunk->size = availchunk;
|
||||
chunk->aset = (void*)set->freelist[a_fidx];
|
||||
set->freelist[a_fidx] = chunk;
|
||||
}
|
||||
|
||||
/* Mark that we need to create a new block */
|
||||
block = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Time to create a new regular (multi-chunk) block?
|
||||
*/
|
||||
if (block == NULL) {
|
||||
Size required_size;
|
||||
|
||||
/*
|
||||
* The first such block has size initBlockSize, and we double the
|
||||
* space in each succeeding block, but not more than maxBlockSize.
|
||||
*/
|
||||
blksize = set->nextBlockSize;
|
||||
set->nextBlockSize <<= 1;
|
||||
if (set->nextBlockSize > set->maxBlockSize)
|
||||
set->nextBlockSize = set->maxBlockSize;
|
||||
|
||||
/*
|
||||
* If initBlockSize is less than ALLOC_CHUNK_LIMIT, we could need more
|
||||
* space... but try to keep it a power of 2.
|
||||
*/
|
||||
required_size = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
|
||||
while (blksize < required_size)
|
||||
blksize <<= 1;
|
||||
|
||||
/* Try to allocate it */
|
||||
block = (AllocBlock) malloc(blksize);
|
||||
if (block == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
block->aset = set;
|
||||
block->freeptr = ((char*)block) + ALLOC_BLOCKHDRSZ;
|
||||
block->endptr = ((char*)block) + blksize;
|
||||
block->allocSize = blksize;
|
||||
|
||||
set->totalSpace += blksize;
|
||||
set->freeSpace += blksize - ALLOC_BLOCKHDRSZ;
|
||||
|
||||
/*
|
||||
* If this is the first block of the set, make it the "keeper" block.
|
||||
* Formerly, a keeper block could only be created during context
|
||||
* creation, but allowing it to happen here lets us have fast reset
|
||||
* cycling even for contexts created with minContextSize = 0; that way
|
||||
* we don't have to force space to be allocated in contexts that might
|
||||
* never need any space. Don't mark an oversize block as a keeper,
|
||||
* however.
|
||||
*/
|
||||
if (set->keeper == NULL && blksize == set->initBlockSize)
|
||||
set->keeper = block;
|
||||
|
||||
block->prev = NULL;
|
||||
block->next = set->blocks;
|
||||
if (block->next)
|
||||
block->next->prev = block;
|
||||
set->blocks = block;
|
||||
}
|
||||
|
||||
/*
|
||||
* OK, do the allocation
|
||||
*/
|
||||
chunk = (AllocChunk)(block->freeptr);
|
||||
|
||||
block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
|
||||
|
||||
set->freeSpace -= (chunk_size + ALLOC_CHUNKHDRSZ);
|
||||
|
||||
Assert(block->freeptr <= block->endptr);
|
||||
|
||||
chunk->aset = (void*)set;
|
||||
chunk->size = chunk_size;
|
||||
|
||||
return AllocChunkGetPointer(chunk);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* AllocSetFree
|
||||
* Frees allocated memory; memory is removed from the set.
|
||||
*/
|
||||
static void opt_AllocSetFree(MemoryContext context, void* pointer)
|
||||
{
|
||||
AllocSet set = (AllocSet)context;
|
||||
AllocChunk chunk = AllocPointerGetChunk(pointer);
|
||||
Size tempSize = 0;
|
||||
|
||||
AssertArg(AllocSetIsValid(set));
|
||||
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
if (chunk->size > set->allocChunkLimit) {
|
||||
#endif
|
||||
/*
|
||||
* Big chunks are certain to have been allocated as single-chunk
|
||||
* blocks. Find the containing block and return it to malloc().
|
||||
*/
|
||||
AllocBlock block = (AllocBlock)(((char*)chunk) - ALLOC_BLOCKHDRSZ);
|
||||
|
||||
|
||||
/* OK, remove block from aset's list and free it */
|
||||
if (block->prev)
|
||||
block->prev->next = block->next;
|
||||
else
|
||||
set->blocks = block->next;
|
||||
|
||||
if (block->next)
|
||||
block->next->prev = block->prev;
|
||||
|
||||
tempSize = block->allocSize;
|
||||
|
||||
set->totalSpace -= block->allocSize;
|
||||
|
||||
/* clean the structure of block */
|
||||
block->aset = NULL;
|
||||
block->prev = NULL;
|
||||
block->next = NULL;
|
||||
block->freeptr = NULL;
|
||||
block->endptr = NULL;
|
||||
block->allocSize = 0;
|
||||
|
||||
free(block);
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
} else {
|
||||
/* Normal case, put the chunk into appropriate freelist */
|
||||
int fidx = opt_AllocSetFreeIndex(chunk->size);
|
||||
|
||||
chunk->aset = (void*)set->freelist[fidx];
|
||||
set->freeSpace += chunk->size + ALLOC_CHUNKHDRSZ;
|
||||
|
||||
set->freelist[fidx] = chunk;
|
||||
Assert(chunk->aset != set);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* AllocSetRealloc
|
||||
* Returns new pointer to allocated memory of given size; this memory
|
||||
* is added to the set. Memory associated with given pointer is copied
|
||||
* into the new memory, and the old memory is freed.
|
||||
*/
|
||||
static void* opt_AllocSetRealloc(MemoryContext context, void* pointer, Size align, Size size, const char* file, int line)
|
||||
{
|
||||
AllocSet set = (AllocSet)context;
|
||||
AllocChunk chunk = AllocPointerGetChunk(pointer);
|
||||
Size oldsize = chunk->size;
|
||||
|
||||
AssertArg(AllocSetIsValid(set));
|
||||
AssertArg(align == 0);
|
||||
|
||||
/*
|
||||
* Chunk sizes are aligned to power of 2 in AllocSetAlloc(). Maybe the
|
||||
* allocated area already is >= the new size. (In particular, we always
|
||||
* fall out here if the requested size is a decrease.)
|
||||
*/
|
||||
if (oldsize >= (size + ALLOC_MAGICHDRSZ)) {
|
||||
size += ALLOC_MAGICHDRSZ;
|
||||
return pointer;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
if (oldsize > set->allocChunkLimit)
|
||||
#endif
|
||||
{
|
||||
/*
|
||||
* The chunk must have been allocated as a single-chunk block. Find
|
||||
* the containing block and use realloc() to make it bigger with
|
||||
* minimum space wastage.
|
||||
*/
|
||||
AllocBlock block = (AllocBlock)(((char*)chunk) - ALLOC_BLOCKHDRSZ);
|
||||
AllocBlock oldBlock = NULL;
|
||||
Size chksize;
|
||||
Size blksize;
|
||||
|
||||
size += ALLOC_MAGICHDRSZ;
|
||||
|
||||
/*
|
||||
* Try to verify that we have a sane block pointer: it should
|
||||
* reference the correct aset, and freeptr and endptr should point
|
||||
* just past the chunk.
|
||||
*/
|
||||
|
||||
/* Do the realloc */
|
||||
chksize = MAXALIGN(size);
|
||||
blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
|
||||
oldBlock = block;
|
||||
oldsize = block->allocSize;
|
||||
|
||||
block = (AllocBlock) realloc(block, blksize);
|
||||
if (block == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
block->freeptr = block->endptr = ((char*)block) + blksize;
|
||||
block->allocSize = blksize;
|
||||
|
||||
set->totalSpace += blksize - oldsize;
|
||||
|
||||
/* Update pointers since block has likely been moved */
|
||||
chunk = (AllocChunk)(((char*)block) + ALLOC_BLOCKHDRSZ);
|
||||
if (block->prev)
|
||||
block->prev->next = block;
|
||||
else
|
||||
set->blocks = block;
|
||||
if (block->next)
|
||||
block->next->prev = block;
|
||||
chunk->size = chksize;
|
||||
|
||||
return AllocChunkGetPointer(chunk);
|
||||
}
|
||||
#ifndef ENABLE_MEMORY_CHECK
|
||||
else {
|
||||
/*
|
||||
* Small-chunk case. We just do this by brute force, ie, allocate a
|
||||
* new chunk and copy the data. Since we know the existing data isn't
|
||||
* huge, this won't involve any great memcpy expense, so it's not
|
||||
* worth being smarter. (At one time we tried to avoid memcpy when it
|
||||
* was possible to enlarge the chunk in-place, but that turns out to
|
||||
* misbehave unpleasantly for repeated cycles of
|
||||
* palloc/repalloc/pfree: the eventually freed chunks go into the
|
||||
* wrong freelist for the next initial palloc request, and so we leak
|
||||
* memory indefinitely. See pgsql-hackers archives for 2007-08-11.)
|
||||
*/
|
||||
AllocPointer newPointer;
|
||||
|
||||
/* allocate new chunk */
|
||||
newPointer =
|
||||
opt_AllocSetAlloc((MemoryContext)set, align, size, __FILE__, __LINE__);
|
||||
|
||||
/* leave immediately if request was not completed */
|
||||
if (newPointer == NULL)
|
||||
return NULL;
|
||||
|
||||
/* transfer existing data (certain to fit) */
|
||||
memcpy(newPointer, pointer, oldsize);
|
||||
|
||||
/* free old chunk */
|
||||
opt_AllocSetFree((MemoryContext)set, pointer);
|
||||
|
||||
return newPointer;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* AllocSetGetChunkSpace
|
||||
* Given a currently-allocated chunk, determine the total space
|
||||
* it occupies (including all memory-allocation overhead).
|
||||
*/
|
||||
static Size opt_AllocSetGetChunkSpace(MemoryContext context, void* pointer)
|
||||
{
|
||||
AllocChunk chunk = AllocPointerGetChunk(pointer);
|
||||
|
||||
return chunk->size + ALLOC_CHUNKHDRSZ;
|
||||
}
|
||||
|
||||
/*
|
||||
* AllocSetIsEmpty
|
||||
* Is an allocset empty of any allocated space?
|
||||
*/
|
||||
static bool opt_AllocSetIsEmpty(MemoryContext context)
|
||||
{
|
||||
/*
|
||||
* For now, we say "empty" only if the context is new or just reset. We
|
||||
* could examine the freelists to determine if all space has been freed,
|
||||
* but it's not really worth the trouble for present uses of this
|
||||
* functionality.
|
||||
*/
|
||||
if (context->isReset)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* AllocSetStats
|
||||
* Displays stats about memory consumption of an allocset.
|
||||
*/
|
||||
static void opt_AllocSetStats(MemoryContext context, int level)
|
||||
{
|
||||
AllocSet set = (AllocSet)context;
|
||||
long nblocks = 0;
|
||||
long nchunks = 0;
|
||||
long totalspace = 0;
|
||||
long freespace = 0;
|
||||
AllocBlock block;
|
||||
AllocChunk chunk;
|
||||
int fidx;
|
||||
int i;
|
||||
|
||||
for (block = set->blocks; block != NULL; block = block->next) {
|
||||
nblocks++;
|
||||
totalspace += block->endptr - ((char*)block);
|
||||
freespace += block->endptr - block->freeptr;
|
||||
}
|
||||
for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++) {
|
||||
for (chunk = set->freelist[fidx]; chunk != NULL; chunk = (AllocChunk)chunk->aset) {
|
||||
nchunks++;
|
||||
freespace += chunk->size + ALLOC_CHUNKHDRSZ;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < level; i++)
|
||||
fprintf(stderr, " ");
|
||||
|
||||
fprintf(stderr,
|
||||
"%s: %ld total in %ld blocks; %ld free (%ld chunks); %ld used\n",
|
||||
set->header.name,
|
||||
totalspace,
|
||||
nblocks,
|
||||
freespace,
|
||||
nchunks,
|
||||
totalspace - freespace);
|
||||
}
|
||||
|
||||
336
src/common/backend/utils/mmgr/opt_mcxt.cpp
Normal file
336
src/common/backend/utils/mmgr/opt_mcxt.cpp
Normal file
@ -0,0 +1,336 @@
|
||||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* std_mcxt.cpp
|
||||
* openGauss memory context management code.
|
||||
*
|
||||
* This module handles context management operations that are independent
|
||||
* of the particular kind of context being operated on. It calls
|
||||
* context-type-specific operations via the function pointers in a
|
||||
* context's MemoryContextMethods struct.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/backend/utils/mmgr/mcxt.c
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
#include "knl/knl_variable.h"
|
||||
#include "postmaster/postmaster.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/memtrace.h"
|
||||
#include "pgxc/pgxc.h"
|
||||
|
||||
#include "miscadmin.h"
|
||||
#include "storage/proc.h"
|
||||
#include "storage/cstore/cstore_mem_alloc.h"
|
||||
#include "threadpool/threadpool.h"
|
||||
#include "tcop/tcopprot.h"
|
||||
#include "workload/workload.h"
|
||||
#include "pgstat.h"
|
||||
|
||||
/*****************************************************************************
|
||||
* GLOBAL MEMORY *
|
||||
*****************************************************************************/
|
||||
|
||||
void* opt_MemoryAllocFromContext(MemoryContext context, Size size)
|
||||
{
|
||||
void* ret = NULL;
|
||||
|
||||
context->isReset = false;
|
||||
|
||||
ret = (*context->methods->alloc)(context, 0, size, __FILE__, __LINE__);
|
||||
if (ret == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OUT_OF_LOGICAL_MEMORY),
|
||||
errmsg("memory is temporarily unavailable"),
|
||||
errdetail("Failed on request of size %lu bytes under queryid %lu.",
|
||||
(unsigned long)size,
|
||||
u_sess->debug_query_id)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void opt_TagMemoryContextSessionId(MemoryContext node, MemoryContext parent)
|
||||
{
|
||||
uint64 sessId = 0;
|
||||
if (parent) {
|
||||
if (parent->session_id > 0) {
|
||||
if (u_sess && u_sess->session_id > 0) {
|
||||
Assert(parent->is_shared || parent->session_id == u_sess->session_id);
|
||||
}
|
||||
sessId = parent->session_id;
|
||||
} else {
|
||||
Assert(parent->is_shared || parent->thread_id == gs_thread_self());
|
||||
}
|
||||
} else {
|
||||
if (u_sess && u_sess->session_id)
|
||||
sessId = u_sess->session_id;
|
||||
}
|
||||
|
||||
node->session_id = sessId;
|
||||
}
|
||||
|
||||
/* --------------------
|
||||
* MemoryContextCreate
|
||||
* Context-type-independent part of context creation.
|
||||
*
|
||||
* This is only intended to be called by context-type-specific
|
||||
* context creation routines, not by the unwashed masses.
|
||||
*
|
||||
* The context creation procedure is a little bit tricky because
|
||||
* we want to be sure that we don't leave the context tree invalid
|
||||
* in case of failure (such as insufficient memory to allocate the
|
||||
* context node itself). The procedure goes like this:
|
||||
* 1. Context-type-specific routine first calls MemoryContextCreate(),
|
||||
* passing the appropriate tag/size/methods values (the methods
|
||||
* pointer will ordinarily point to statically allocated data).
|
||||
* The parent and name parameters usually come from the caller.
|
||||
* 2. MemoryContextCreate() attempts to allocate the context node,
|
||||
* plus space for the name. If this fails we can ereport() with no
|
||||
* damage done.
|
||||
* 3. We fill in all of the type-independent MemoryContext fields.
|
||||
* 4. We call the type-specific init routine (using the methods pointer).
|
||||
* The init routine is required to make the node minimally valid
|
||||
* with zero chance of failure --- it can't allocate more memory,
|
||||
* for example.
|
||||
* 5. Now we have a minimally valid node that can behave correctly
|
||||
* when told to reset or delete itself. We link the node to its
|
||||
* parent (if any), making the node part of the context tree.
|
||||
* 6. We return to the context-type-specific routine, which finishes
|
||||
* up type-specific initialization. This routine can now do things
|
||||
* that might fail (like allocate more memory), so long as it's
|
||||
* sure the node is left in a state that delete will handle.
|
||||
*
|
||||
* This protocol doesn't prevent us from leaking memory if step 6 fails
|
||||
* during creation of a top-level context, since there's no parent link
|
||||
* in that case. However, if you run out of memory while you're building
|
||||
* a top-level context, you might as well go home anyway...
|
||||
*
|
||||
* Normally, the context node and the name are allocated from
|
||||
* t_thrd.top_mem_cxt (NOT from the parent context, since the node must
|
||||
* survive resets of its parent context!). However, this routine is itself
|
||||
* used to create t_thrd.top_mem_cxt! If we see that t_thrd.top_mem_cxt is NULL,
|
||||
* we assume we are creating t_thrd.top_mem_cxt and use malloc() to allocate
|
||||
* the node.
|
||||
*
|
||||
* Note that the name field of a MemoryContext does not point to
|
||||
* separately-allocated storage, so it should not be freed at context
|
||||
* deletion.
|
||||
* --------------------
|
||||
*/
|
||||
MemoryContext opt_MemoryContextCreate(MemoryContext node, NodeTag tag,
|
||||
MemoryContextMethods *methods, MemoryContext parent, const char* name)
|
||||
{
|
||||
node->type = tag;
|
||||
node->parent = NULL; /* for the moment */
|
||||
node->firstchild = NULL;
|
||||
node->prevchild = NULL;
|
||||
node->nextchild = NULL;
|
||||
node->isReset = true;
|
||||
node->is_sealed = false;
|
||||
node->is_shared = false;
|
||||
node->methods = methods;
|
||||
node->name = (char *)name;
|
||||
node->cell.data.ptr_value = (void*)node;
|
||||
node->cell.next = NULL;
|
||||
node->thread_id = gs_thread_self();
|
||||
opt_TagMemoryContextSessionId(node, parent);
|
||||
|
||||
/* OK to link node to parent (if any) */
|
||||
/* Could use MemoryContextSetParent here, but doesn't seem worthwhile */
|
||||
if (parent) {
|
||||
node->parent = parent;
|
||||
node->nextchild = parent->firstchild;
|
||||
if (parent->firstchild != NULL)
|
||||
parent->firstchild->prevchild = node;
|
||||
parent->firstchild = node;
|
||||
|
||||
node->level = parent->level + 1;
|
||||
} else {
|
||||
node->nextchild = NULL;
|
||||
node->level = 0;
|
||||
}
|
||||
|
||||
/* Return to type-specific creation routine to finish up */
|
||||
return node;
|
||||
}
|
||||
|
||||
void* opt_MemoryContextAllocZeroAlignedDebug(MemoryContext context, Size size, const char* file, int line)
|
||||
{
|
||||
void* ret = NULL;
|
||||
|
||||
AssertArg(IsOptAllocSetContext(context));
|
||||
|
||||
context->isReset = false;
|
||||
|
||||
ret = (*context->methods->alloc)(context, 0, size, file, line);
|
||||
if (ret == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OUT_OF_LOGICAL_MEMORY),
|
||||
errmsg("memory is temporarily unavailable"),
|
||||
errdetail("Failed on request of size %lu bytes under queryid %lu in %s:%d.",
|
||||
(unsigned long)size,
|
||||
u_sess->debug_query_id,
|
||||
file,
|
||||
line)));
|
||||
|
||||
MemSetLoop(ret, 0, size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* opt_MemoryContextAllocZeroDebug(MemoryContext context, Size size, const char* file, int line)
|
||||
{
|
||||
void* ret = NULL;
|
||||
|
||||
AssertArg(IsOptAllocSetContext(context));
|
||||
|
||||
context->isReset = false;
|
||||
|
||||
ret = (*context->methods->alloc)(context, 0, size, file, line);
|
||||
if (ret == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OUT_OF_LOGICAL_MEMORY),
|
||||
errmsg("memory is temporarily unavailable"),
|
||||
errdetail("Failed on request of size %lu bytes under queryid %lu in %s:%d.",
|
||||
(unsigned long)size,
|
||||
u_sess->debug_query_id,
|
||||
file,
|
||||
line)));
|
||||
|
||||
MemSetAligned(ret, 0, size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void opt_MemoryContextSetParent(MemoryContext context, MemoryContext new_parent)
|
||||
{
|
||||
AssertArg(IsOptAllocSetContext(context));
|
||||
AssertArg(context != new_parent);
|
||||
|
||||
if (new_parent == context->parent)
|
||||
return;
|
||||
|
||||
/* Delink from existing parent, if any */
|
||||
if (context->parent) {
|
||||
MemoryContext parent = context->parent;
|
||||
|
||||
if (context->prevchild != NULL)
|
||||
context->prevchild->nextchild = context->nextchild;
|
||||
else {
|
||||
Assert(parent->firstchild == context);
|
||||
parent->firstchild = context->nextchild;
|
||||
}
|
||||
|
||||
if (context->nextchild != NULL)
|
||||
context->nextchild->prevchild = context->prevchild;
|
||||
}
|
||||
|
||||
/* And relink */
|
||||
if (new_parent) {
|
||||
AssertArg(IsOptAllocSetContext(new_parent));
|
||||
context->parent = new_parent;
|
||||
context->prevchild = NULL;
|
||||
context->nextchild = new_parent->firstchild;
|
||||
if (new_parent->firstchild != NULL)
|
||||
new_parent->firstchild->prevchild = context;
|
||||
new_parent->firstchild = context;
|
||||
} else {
|
||||
context->parent = NULL;
|
||||
context->prevchild = NULL;
|
||||
context->nextchild = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void opt_MemoryContextDeleteInternal(MemoryContext context, List *context_list)
|
||||
{
|
||||
AssertArg(IsOptAllocSetContext(context));
|
||||
|
||||
/* save a function call in common case where there are no children */
|
||||
if (context->firstchild != NULL)
|
||||
MemoryContextDeleteChildren(context, context_list);
|
||||
|
||||
opt_MemoryContextSetParent(context, NULL);
|
||||
|
||||
context->methods->delete_context(context);
|
||||
}
|
||||
|
||||
void opt_MemoryContextReset(MemoryContext context)
|
||||
{
|
||||
AssertArg(IsOptAllocSetContext(context));
|
||||
|
||||
/* save a function call in common case where there are no children */
|
||||
if (context->firstchild != NULL)
|
||||
MemoryContextResetChildren(context);
|
||||
|
||||
/* Nothing to do if no pallocs since startup or last reset */
|
||||
if (!context->isReset) {
|
||||
(*context->methods->reset)(context);
|
||||
context->isReset = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pfree
|
||||
* Release an allocated chunk.
|
||||
*/
|
||||
void
|
||||
opt_pfree(void *pointer)
|
||||
{
|
||||
MemoryContext context = GetMemoryChunkContext(pointer);
|
||||
|
||||
context->methods->free_p(context, pointer);
|
||||
}
|
||||
|
||||
void*
|
||||
opt_repallocDebug(void* pointer, Size size, const char *file, int line)
|
||||
{
|
||||
MemoryContext context;
|
||||
void* ret = NULL;
|
||||
|
||||
Assert(pointer != NULL);
|
||||
Assert(pointer == (void*)MAXALIGN(pointer));
|
||||
|
||||
context = GetMemoryChunkContext(pointer);
|
||||
|
||||
/* isReset must be false already */
|
||||
Assert(!context->isReset);
|
||||
|
||||
ret = (*context->methods->realloc)(context, pointer, 0, size, file, line);
|
||||
if (unlikely(ret == NULL))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OUT_OF_LOGICAL_MEMORY),
|
||||
errmsg("memory is temporarily unavailable"),
|
||||
errdetail("Failed on request of size %lu bytes under queryid %lu in %s:%d.",
|
||||
(unsigned long)size,
|
||||
u_sess->debug_query_id,
|
||||
file,
|
||||
line)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void*
|
||||
opt_repalloc_noexcept_Debug(void* pointer, Size size, const char *file, int line)
|
||||
{
|
||||
MemoryContext context;
|
||||
void* ret = NULL;
|
||||
|
||||
Assert(pointer != NULL);
|
||||
Assert(pointer == (void*)MAXALIGN(pointer));
|
||||
|
||||
context = GetMemoryChunkContext(pointer);
|
||||
|
||||
/* isReset must be false already */
|
||||
Assert(!context->isReset);
|
||||
|
||||
ret = (*context->methods->realloc)(context, pointer, 0, size, file, line);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -68,6 +68,7 @@ extern void* MemoryContextAllocZeroAligned(MemoryContext context, Size size);
|
||||
: MemoryContextAllocZero(CurrentMemoryContext, sz))
|
||||
|
||||
extern void pfree(void* pointer);
|
||||
extern void opt_pfree(void* pointer);
|
||||
|
||||
extern void* repalloc(void* pointer, Size size);
|
||||
|
||||
|
||||
@ -47,6 +47,7 @@ typedef struct knl_instance_attr_memory {
|
||||
int max_process_memory;
|
||||
bool enable_memory_context_check_debug;
|
||||
int global_syscache_threshold;
|
||||
bool disable_memory_stats;
|
||||
} knl_instance_attr_memory;
|
||||
|
||||
#endif /* SRC_INCLUDE_KNL_KNL_INSTANCE_ATTR_MEMORY_H_ */
|
||||
|
||||
@ -148,6 +148,7 @@ typedef struct AllocSetContext {
|
||||
|
||||
/* maximum memory allocation of MemoryContext.For more information,we could see @StackSetContext too. */
|
||||
Size maxSpaceSize;
|
||||
int freeListIndex;
|
||||
|
||||
MemoryTrack track; /* used to track the memory allocation information */
|
||||
} AllocSetContext;
|
||||
@ -278,6 +279,9 @@ extern MemoryProtectFuncDef GenericFunctions;
|
||||
extern MemoryProtectFuncDef SessionFunctions;
|
||||
extern MemoryProtectFuncDef SharedFunctions;
|
||||
|
||||
#define IsOptAllocSetContext(cxt) \
|
||||
((cxt) != NULL && (IsA((cxt), OptAllocSetContext)))
|
||||
|
||||
/*
|
||||
* MemoryContextIsValid
|
||||
* True iff memory context is valid.
|
||||
@ -288,7 +292,7 @@ extern MemoryProtectFuncDef SharedFunctions;
|
||||
((context) != NULL && \
|
||||
(IsA((context), AllocSetContext) || IsA((context), AsanSetContext) || IsA((context), StackAllocSetContext) || \
|
||||
IsA((context), SharedAllocSetContext) || IsA((context), MemalignAllocSetContext) || \
|
||||
IsA((context), MemalignSharedAllocSetContext)))
|
||||
IsA((context), MemalignSharedAllocSetContext) || IsA((context), OptAllocSetContext)))
|
||||
|
||||
#define AllocSetContextUsedSpace(aset) ((aset)->totalSpace - (aset)->freeSpace)
|
||||
|
||||
|
||||
@ -359,6 +359,7 @@ typedef enum NodeTag {
|
||||
*/
|
||||
T_MemoryContext = 600,
|
||||
T_AllocSetContext,
|
||||
T_OptAllocSetContext,
|
||||
T_AsanSetContext,
|
||||
T_StackAllocSetContext,
|
||||
T_SharedAllocSetContext,
|
||||
|
||||
@ -94,12 +94,15 @@ extern THR_LOCAL PGDLLIMPORT MemoryContext ErrorContext;
|
||||
*/
|
||||
extern void MemoryContextInit(void);
|
||||
extern void MemoryContextReset(MemoryContext context);
|
||||
extern void opt_MemoryContextReset(MemoryContext context);
|
||||
extern void MemoryContextDelete(MemoryContext context);
|
||||
extern void opt_MemoryContextDeleteInternal(MemoryContext context, List* context_list = NULL);
|
||||
extern void MemoryContextResetChildren(MemoryContext context);
|
||||
extern void MemoryContextDeleteChildren(MemoryContext context, List* context_list = NULL);
|
||||
extern void MemoryContextDestroyAtThreadExit(MemoryContext context);
|
||||
extern void MemoryContextResetAndDeleteChildren(MemoryContext context);
|
||||
extern void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent);
|
||||
extern void opt_MemoryContextSetParent(MemoryContext context, MemoryContext new_parent);
|
||||
extern Size GetMemoryChunkSpace(void* pointer);
|
||||
extern MemoryContext GetMemoryChunkContext(void* pointer);
|
||||
extern MemoryContext MemoryContextGetParent(MemoryContext context);
|
||||
@ -131,6 +134,8 @@ extern MemoryContext MemoryContextCreate(
|
||||
extern MemoryContext MemoryContextCreate(
|
||||
NodeTag tag, Size size, MemoryContextMethods* methods, MemoryContext parent, const char* name);
|
||||
|
||||
extern MemoryContext opt_MemoryContextCreate(MemoryContext node, NodeTag tag,
|
||||
MemoryContextMethods *methods, MemoryContext parent, const char* name);
|
||||
/*
|
||||
* Memory-context-type-specific functions
|
||||
*/
|
||||
@ -140,6 +145,8 @@ extern MemoryContext MemoryContextCreate(
|
||||
extern MemoryContext AllocSetContextCreate(MemoryContext parent, const char* name, Size minContextSize,
|
||||
Size initBlockSize, Size maxBlockSize, MemoryContextType type = STANDARD_CONTEXT,
|
||||
Size maxSize = DEFAULT_MEMORY_CONTEXT_MAX_SIZE, bool isSession = false);
|
||||
extern MemoryContext opt_AllocSetContextCreate(MemoryContext parent,
|
||||
const char* name, Size minContextSize, Size initBlockSize, Size maxBlockSize);
|
||||
|
||||
/*
|
||||
* Recommended default alloc parameters, suitable for "ordinary" contexts
|
||||
|
||||
@ -87,12 +87,15 @@ extern THR_LOCAL PGDLLIMPORT MemoryContext TopMemoryContext;
|
||||
* Fundamental memory-allocation operations (more are in utils/memutils.h)
|
||||
*/
|
||||
extern void* MemoryAllocFromContext(MemoryContext context, Size size, const char* file, int line);
|
||||
extern void* opt_MemoryAllocFromContext(MemoryContext context, Size size);
|
||||
extern void* MemoryContextAllocDebug(MemoryContext context, Size size, const char* file, int line);
|
||||
extern void* MemoryContextAllocHugeDebug(MemoryContext context, Size size, const char* file, int line);
|
||||
extern void* repallocHugeDebug(void* pointer, Size size, const char* file, int line);
|
||||
extern void* MemoryContextAllocZeroDebug(MemoryContext context, Size size, const char* file, int line);
|
||||
extern void* opt_MemoryContextAllocZeroDebug(MemoryContext context, Size size, const char* file, int line);
|
||||
extern void* MemoryContextAllocZeroAlignedDebug(MemoryContext context, Size size, const char* file, int line);
|
||||
extern void* MemoryContextAllocExtendedDebug(MemoryContext context, Size size, int flags, const char* file, int line);
|
||||
extern void* opt_MemoryContextAllocZeroAlignedDebug(MemoryContext context, Size size, const char* file, int line);
|
||||
extern char* MemoryContextStrdupDebug(MemoryContext context, const char* string, const char* file, int line);
|
||||
extern void* MemoryContextMemalignAllocDebug(MemoryContext context, Size align, Size size, const char* file, int line);
|
||||
extern void MemoryContextMemalignFree(MemoryContext context, void* pointer);
|
||||
@ -135,6 +138,7 @@ extern THR_LOCAL MemoryContext AlignMemoryContext;
|
||||
: MemoryContextAllocZero(CurrentMemoryContext, sz))
|
||||
|
||||
extern void pfree(void* pointer);
|
||||
extern void opt_pfree(void* pointer);
|
||||
|
||||
template <typename T>
|
||||
bool isConst(T& x)
|
||||
@ -161,7 +165,9 @@ bool isConst(T const& x)
|
||||
#define pfree_ext(__p) FREE_POINTER(__p)
|
||||
|
||||
extern void* repallocDebug(void* pointer, Size size, const char* file, int line);
|
||||
extern void* opt_repallocDebug(void* pointer, Size size, const char* file, int line);
|
||||
extern void* repalloc_noexcept_Debug(void* pointer, Size size, const char* file, int line);
|
||||
extern void* opt_repalloc_noexcept_Debug(void* pointer, Size size, const char* file, int line);
|
||||
|
||||
/*
|
||||
* MemoryContextSwitchTo can't be a macro in standard C compilers.
|
||||
|
||||
@ -5412,7 +5412,7 @@ static void CheckCleanCodeWarningInfo(const int baseNum, const int currentNum,
|
||||
return;
|
||||
}
|
||||
|
||||
#define BASE_GLOBAL_VARIABLE_NUM 220
|
||||
#define BASE_GLOBAL_VARIABLE_NUM 221
|
||||
|
||||
#define CMAKE_CMD_BUF_LEN 1000
|
||||
|
||||
|
||||
Reference in New Issue
Block a user