753 lines
22 KiB
C++
753 lines
22 KiB
C++
/* -------------------------------------------------------------------------
|
|
*
|
|
* mcxt.c
|
|
* POSTGRES 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-2009, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
* Portions Copyright (c) 2010-2012 Postgres-XC Development Group
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/utils/mmgr/mcxt.c,v 1.65 2008/06/28 16:45:22 tgl Exp $
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "gtm/gtm_c.h"
|
|
#include "gtm/memutils.h"
|
|
#include "gtm/elog.h"
|
|
#include "gtm/assert.h"
|
|
#include "gtm/gtm.h"
|
|
|
|
/*****************************************************************************
|
|
* GLOBAL MEMORY *
|
|
*****************************************************************************/
|
|
|
|
/*
|
|
* Standard top-level contexts. For a description of the purpose of each
|
|
* of these contexts, refer to src/backend/utils/mmgr/README
|
|
*/
|
|
|
|
static void MemoryContextStatsInternal(MemoryContext context, int level);
|
|
static void MemoryContextDeleteInternal(MemoryContext context, bool parent_locked);
|
|
void* allocTopMemCxt(size_t s);
|
|
|
|
MemoryContext TopMostMemoryContext;
|
|
|
|
/*****************************************************************************
|
|
* EXPORTED ROUTINES *
|
|
*****************************************************************************/
|
|
|
|
/*
|
|
* MemoryContextInit
|
|
* Start up the memory-context subsystem.
|
|
*
|
|
* This must be called before creating contexts or allocating memory in
|
|
* contexts. TopMemoryContext and ErrorContext are initialized here;
|
|
* other contexts must be created afterwards.
|
|
*
|
|
* In normal multi-backend operation, this is called once during
|
|
* postmaster startup, and not at all by individual backend startup
|
|
* (since the backends inherit an already-initialized context subsystem
|
|
* by virtue of being forked off the postmaster).
|
|
*
|
|
* In a standalone backend this must be called during backend startup.
|
|
*/
|
|
void MemoryContextInit(void)
|
|
{
|
|
AssertState(TopMemoryContext == NULL);
|
|
|
|
/*
|
|
* Initialize TopMemoryContext as an AllocSetContext with slow growth rate
|
|
* --- we don't really expect much to be allocated in it.
|
|
*
|
|
* (There is special-case code in MemoryContextCreate() for this call.)
|
|
*
|
|
* This context is shared between different threads and must be made
|
|
* thread-safe
|
|
*/
|
|
TopMemoryContext = AllocSetContextCreate((MemoryContext)NULL, "TopMemoryContext", 0, 8 * 1024, 8 * 1024, true);
|
|
|
|
TopMostMemoryContext = TopMemoryContext;
|
|
|
|
/*
|
|
* Not having any other place to point CurrentMemoryContext, make it point
|
|
* to TopMemoryContext. Caller should change this soon!
|
|
*/
|
|
CurrentMemoryContext = TopMemoryContext;
|
|
|
|
/*
|
|
* Initialize ErrorContext as an AllocSetContext with slow growth rate ---
|
|
* we don't really expect much to be allocated in it. More to the point,
|
|
* require it to contain at least 8K at all times. This is the only case
|
|
* where retained memory in a context is *essential* --- we want to be
|
|
* sure ErrorContext still has some memory even if we've run out
|
|
* elsewhere!
|
|
*
|
|
* Similar to TopMostMemoryContext, this context may as well be shared
|
|
* between threads
|
|
*/
|
|
ErrorContext = AllocSetContextCreate(TopMemoryContext, "ErrorContext", 8 * 1024, 8 * 1024, 8 * 1024, true);
|
|
}
|
|
|
|
/*
|
|
* MemoryContextReset
|
|
* Release all space allocated within a context and its descendants,
|
|
* but don't delete the contexts themselves.
|
|
*
|
|
* The type-specific reset routine handles the context itself, but we
|
|
* have to do the recursion for the children.
|
|
*/
|
|
void MemoryContextReset(MemoryContext context)
|
|
{
|
|
AssertArg(MemoryContextIsValid(context));
|
|
|
|
if (MemoryContextIsShared(context)) {
|
|
MemoryContextLock(context);
|
|
}
|
|
|
|
/* save a function call in common case where there are no children */
|
|
if (context->firstchild != NULL) {
|
|
MemoryContextResetChildren(context);
|
|
}
|
|
|
|
if (MemoryContextIsShared(context)) {
|
|
MemoryContextUnlock(context);
|
|
}
|
|
|
|
(*context->methods->reset)(context);
|
|
}
|
|
|
|
/*
|
|
* MemoryContextResetChildren
|
|
* Release all space allocated within a context's descendants,
|
|
* but don't delete the contexts themselves. The named context
|
|
* itself is not touched.
|
|
*/
|
|
void MemoryContextResetChildren(MemoryContext context)
|
|
{
|
|
MemoryContext child;
|
|
|
|
AssertArg(MemoryContextIsValid(context));
|
|
|
|
/*
|
|
* For a shared context, lock the parent context before resetting the
|
|
* children contextes
|
|
*/
|
|
if (MemoryContextIsShared(context)) {
|
|
MemoryContextLock(context);
|
|
}
|
|
|
|
for (child = context->firstchild; child != NULL; child = child->nextchild) {
|
|
MemoryContextReset(child);
|
|
}
|
|
|
|
if (MemoryContextIsShared(context)) {
|
|
MemoryContextUnlock(context);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* MemoryContextDelete
|
|
* Delete a context and its descendants, and release all space
|
|
* allocated therein.
|
|
*
|
|
* The type-specific delete routine removes all subsidiary storage
|
|
* for the context, but we have to delete the context node itself,
|
|
* as well as recurse to get the children. We must also delink the
|
|
* node from its parent, if it has one.
|
|
*/
|
|
static void MemoryContextDeleteInternal(MemoryContext context, bool parent_locked)
|
|
{
|
|
AssertArg(MemoryContextIsValid(context));
|
|
/* We had better not be deleting TopMemoryContext ... */
|
|
Assert(context != TopMemoryContext);
|
|
/* And not CurrentMemoryContext, either */
|
|
Assert(context != CurrentMemoryContext);
|
|
|
|
MemoryContextDeleteChildren(context);
|
|
|
|
/*
|
|
* We delink the context from its parent before deleting it, so that if
|
|
* there's an error we won't have deleted/busted contexts still attached
|
|
* to the context tree. Better a leak than a crash.
|
|
*/
|
|
if (context->parent) {
|
|
MemoryContext parent = context->parent;
|
|
|
|
/*
|
|
* If the parent context is shared and is already locked by the caller,
|
|
* no need to relock again. In fact, that's not the right thing to do
|
|
* since it will lead to a self-deadlock
|
|
*/
|
|
if (MemoryContextIsShared(parent) && (!parent_locked)) {
|
|
MemoryContextLock(parent);
|
|
}
|
|
|
|
if (context == parent->firstchild) {
|
|
parent->firstchild = context->nextchild;
|
|
} else {
|
|
MemoryContext child;
|
|
|
|
for (child = parent->firstchild; child; child = child->nextchild) {
|
|
if (context == child->nextchild) {
|
|
child->nextchild = context->nextchild;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (MemoryContextIsShared(parent) && (!parent_locked)) {
|
|
MemoryContextUnlock(parent);
|
|
}
|
|
}
|
|
(*context->methods->delete)(context);
|
|
pfree(context);
|
|
}
|
|
|
|
void MemoryContextDelete(MemoryContext context)
|
|
{
|
|
MemoryContextDeleteInternal(context, false);
|
|
}
|
|
|
|
/*
|
|
* MemoryContextDeleteChildren
|
|
* Delete all the descendants of the named context and release all
|
|
* space allocated therein. The named context itself is not touched.
|
|
*/
|
|
void MemoryContextDeleteChildren(MemoryContext context)
|
|
{
|
|
AssertArg(MemoryContextIsValid(context));
|
|
|
|
if (MemoryContextIsShared(context)) {
|
|
MemoryContextLock(context);
|
|
}
|
|
|
|
/*
|
|
* MemoryContextDelete will delink the child from me, so just iterate as
|
|
* long as there is a child.
|
|
*
|
|
* Since the parent is already locked, pass that information to the child
|
|
* which would then not attempt to relock the parent
|
|
*/
|
|
while (context->firstchild != NULL)
|
|
MemoryContextDeleteInternal(context->firstchild, true);
|
|
|
|
if (MemoryContextIsShared(context)) {
|
|
MemoryContextUnlock(context);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* MemoryContextResetAndDeleteChildren
|
|
* Release all space allocated within a context and delete all
|
|
* its descendants.
|
|
*
|
|
* This is a common combination case where we want to preserve the
|
|
* specific context but get rid of absolutely everything under it.
|
|
*/
|
|
void MemoryContextResetAndDeleteChildren(MemoryContext context)
|
|
{
|
|
AssertArg(MemoryContextIsValid(context));
|
|
|
|
MemoryContextDeleteChildren(context);
|
|
(*context->methods->reset)(context);
|
|
}
|
|
|
|
/*
|
|
* GetMemoryChunkSpace
|
|
* Given a currently-allocated chunk, determine the total space
|
|
* it occupies (including all memory-allocation overhead).
|
|
*
|
|
* This is useful for measuring the total space occupied by a set of
|
|
* allocated chunks.
|
|
*/
|
|
Size GetMemoryChunkSpace(void* pointer)
|
|
{
|
|
StandardChunkHeader* header;
|
|
|
|
/*
|
|
* Try to detect bogus pointers handed to us, poorly though we can.
|
|
* Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
|
|
* allocated chunk.
|
|
*/
|
|
Assert(pointer != NULL);
|
|
Assert(pointer == (void*)MAXALIGN(pointer));
|
|
|
|
/*
|
|
* OK, it's probably safe to look at the chunk header.
|
|
*/
|
|
header = (StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE);
|
|
|
|
AssertArg(MemoryContextIsValid(header->context));
|
|
|
|
return (*header->context->methods->get_chunk_space)(header->context, pointer);
|
|
}
|
|
|
|
/*
|
|
* GetMemoryChunkContext
|
|
* Given a currently-allocated chunk, determine the context
|
|
* it belongs to.
|
|
*/
|
|
MemoryContext GetMemoryChunkContext(void* pointer)
|
|
{
|
|
StandardChunkHeader* header;
|
|
|
|
/*
|
|
* Try to detect bogus pointers handed to us, poorly though we can.
|
|
* Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
|
|
* allocated chunk.
|
|
*/
|
|
Assert(pointer != NULL);
|
|
Assert(pointer == (void*)MAXALIGN(pointer));
|
|
|
|
/*
|
|
* OK, it's probably safe to look at the chunk header.
|
|
*/
|
|
header = (StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE);
|
|
|
|
AssertArg(MemoryContextIsValid(header->context));
|
|
|
|
return header->context;
|
|
}
|
|
|
|
/*
|
|
* MemoryContextIsEmpty
|
|
* Is a memory context empty of any allocated space?
|
|
*/
|
|
bool MemoryContextIsEmpty(MemoryContext context)
|
|
{
|
|
AssertArg(MemoryContextIsValid(context));
|
|
|
|
/*
|
|
* For now, we consider a memory context nonempty if it has any children;
|
|
* perhaps this should be changed later.
|
|
*/
|
|
if (context->firstchild != NULL) {
|
|
return false;
|
|
}
|
|
|
|
/* Otherwise use the type-specific inquiry */
|
|
return (*context->methods->is_empty)(context);
|
|
}
|
|
|
|
/*
|
|
* MemoryContextStats
|
|
* Print statistics about the named context and all its descendants.
|
|
*
|
|
* This is just a debugging utility, so it's not fancy. The statistics
|
|
* are merely sent to stderr.
|
|
*/
|
|
void MemoryContextStats(MemoryContext context)
|
|
{
|
|
MemoryContextStatsInternal(context, 0);
|
|
}
|
|
|
|
static void MemoryContextStatsInternal(MemoryContext context, int level)
|
|
{
|
|
MemoryContext child;
|
|
|
|
AssertArg(MemoryContextIsValid(context));
|
|
|
|
(*context->methods->stats)(context, level);
|
|
|
|
for (child = context->firstchild; child != NULL; child = child->nextchild) {
|
|
MemoryContextStatsInternal(child, level + 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* MemoryContextCheck
|
|
* Check all chunks in the named context.
|
|
*
|
|
* This is just a debugging utility, so it's not fancy.
|
|
*/
|
|
#ifdef MEMORY_CONTEXT_CHECKING
|
|
void MemoryContextCheck(MemoryContext context)
|
|
{
|
|
MemoryContext child;
|
|
|
|
AssertArg(MemoryContextIsValid(context));
|
|
|
|
(*context->methods->check)(context);
|
|
|
|
for (child = context->firstchild; child != NULL; child = child->nextchild) {
|
|
MemoryContextCheck(child);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* MemoryContextContains
|
|
* Detect whether an allocated chunk of memory belongs to a given
|
|
* context or not.
|
|
*
|
|
* Caution: this test is reliable as long as 'pointer' does point to
|
|
* a chunk of memory allocated from *some* context. If 'pointer' points
|
|
* at memory obtained in some other way, there is a small chance of a
|
|
* false-positive result, since the bits right before it might look like
|
|
* a valid chunk header by chance.
|
|
*/
|
|
bool MemoryContextContains(MemoryContext context, void* pointer)
|
|
{
|
|
StandardChunkHeader* header;
|
|
|
|
/*
|
|
* Try to detect bogus pointers handed to us, poorly though we can.
|
|
* Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
|
|
* allocated chunk.
|
|
*/
|
|
if (pointer == NULL || pointer != (void*)MAXALIGN(pointer)) {
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* OK, it's probably safe to look at the chunk header.
|
|
*/
|
|
header = (StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE);
|
|
|
|
/*
|
|
* If the context link doesn't match then we certainly have a non-member
|
|
* chunk. Also check for a reasonable-looking size as extra guard against
|
|
* being fooled by bogus pointers.
|
|
*/
|
|
if (header->context == context && AllocSizeIsValid(header->size)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* --------------------
|
|
* 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
|
|
* TopMemoryContext (NOT from the parent context, since the node must
|
|
* survive resets of its parent context!). However, this routine is itself
|
|
* used to create TopMemoryContext! If we see that TopMemoryContext is NULL,
|
|
* we assume we are creating TopMemoryContext 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 MemoryContextCreate(Size size, MemoryContextMethods* methods, MemoryContext parent, const char* name)
|
|
{
|
|
MemoryContext node;
|
|
Size nameLen = strlen(name);
|
|
Size needed = size + nameLen + 1;
|
|
errno_t rc;
|
|
|
|
/* Get space for node and name */
|
|
if (TopMemoryContext != NULL) {
|
|
/* Normal case: allocate the node in TopMemoryContext */
|
|
node = (MemoryContext)MemoryContextAlloc(TopMemoryContext, needed);
|
|
} else {
|
|
/* Special case for startup: use good ol' malloc */
|
|
node = (MemoryContext)malloc(needed);
|
|
Assert(node != NULL);
|
|
}
|
|
|
|
/* Initialize the node as best we can */
|
|
rc = memset_s(node, size, 0, size);
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
node->methods = methods;
|
|
node->parent = NULL; /* for the moment */
|
|
node->firstchild = NULL;
|
|
node->nextchild = NULL;
|
|
node->name = ((char*)node) + size;
|
|
rc = strcpy_s(node->name, nameLen + 1, name);
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
/* Type-specific routine finishes any other essential initialization */
|
|
(*node->methods->init)(node);
|
|
|
|
/*
|
|
* Lock the parent context if the it is shared and must be made thread-safe
|
|
*/
|
|
if ((parent != NULL) && (MemoryContextIsShared(parent))) {
|
|
MemoryContextLock(parent);
|
|
}
|
|
|
|
/* OK to link node to parent (if any) */
|
|
if (parent) {
|
|
node->parent = parent;
|
|
node->nextchild = parent->firstchild;
|
|
parent->firstchild = node;
|
|
}
|
|
|
|
if ((parent != NULL) && (MemoryContextIsShared(parent))) {
|
|
MemoryContextUnlock(parent);
|
|
}
|
|
|
|
/* Return to type-specific creation routine to finish up */
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
* MemoryContextAlloc
|
|
* Allocate space within the specified context.
|
|
*
|
|
* This could be turned into a macro, but we'd have to import
|
|
* nodes/memnodes.h into postgres.h which seems a bad idea.
|
|
*/
|
|
void* MemoryContextAlloc(MemoryContext context, Size size)
|
|
{
|
|
AssertArg(MemoryContextIsValid(context));
|
|
|
|
if (!AllocSizeIsValid(size)) {
|
|
elog(ERROR, "invalid memory alloc request size %lu", (unsigned long)size);
|
|
}
|
|
|
|
return (*context->methods->alloc)(context, size);
|
|
}
|
|
|
|
/*
|
|
* MemoryContextAllocZero
|
|
* Like MemoryContextAlloc, but clears allocated memory
|
|
*
|
|
* We could just call MemoryContextAlloc then clear the memory, but this
|
|
* is a very common combination, so we provide the combined operation.
|
|
*/
|
|
void* MemoryContextAllocZero(MemoryContext context, Size size)
|
|
{
|
|
void* ret;
|
|
|
|
AssertArg(MemoryContextIsValid(context));
|
|
|
|
if (!AllocSizeIsValid(size)) {
|
|
elog(ERROR, "invalid memory alloc request size %lu", (unsigned long)size);
|
|
}
|
|
|
|
ret = (*context->methods->alloc)(context, size);
|
|
|
|
MemSetAligned(ret, 0, size);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* MemoryContextAllocZeroAligned
|
|
* MemoryContextAllocZero where length is suitable for MemSetLoop
|
|
*
|
|
* This might seem overly specialized, but it's not because newNode()
|
|
* is so often called with compile-time-constant sizes.
|
|
*/
|
|
void* MemoryContextAllocZeroAligned(MemoryContext context, Size size)
|
|
{
|
|
void* ret;
|
|
|
|
AssertArg(MemoryContextIsValid(context));
|
|
|
|
if (!AllocSizeIsValid(size)) {
|
|
elog(ERROR, "invalid memory alloc request size %lu", (unsigned long)size);
|
|
}
|
|
|
|
ret = (*context->methods->alloc)(context, size);
|
|
|
|
MemSetLoop(ret, 0, size);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* pfree
|
|
* Release an allocated chunk.
|
|
*/
|
|
void pfree(void* pointer)
|
|
{
|
|
StandardChunkHeader* header;
|
|
|
|
/*
|
|
* Try to detect bogus pointers handed to us, poorly though we can.
|
|
* Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
|
|
* allocated chunk.
|
|
*/
|
|
Assert(pointer != NULL);
|
|
Assert(pointer == (void*)MAXALIGN(pointer));
|
|
|
|
/*
|
|
* OK, it's probably safe to look at the chunk header.
|
|
*/
|
|
header = (StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE);
|
|
|
|
AssertArg(MemoryContextIsValid(header->context));
|
|
|
|
(*header->context->methods->free_p)(header->context, pointer);
|
|
}
|
|
|
|
/*
|
|
* repalloc
|
|
* Adjust the size of a previously allocated chunk.
|
|
*/
|
|
void* repalloc(void* pointer, Size size)
|
|
{
|
|
StandardChunkHeader* header;
|
|
|
|
/*
|
|
* Try to detect bogus pointers handed to us, poorly though we can.
|
|
* Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
|
|
* allocated chunk.
|
|
*/
|
|
Assert(pointer != NULL);
|
|
Assert(pointer == (void*)MAXALIGN(pointer));
|
|
|
|
/*
|
|
* OK, it's probably safe to look at the chunk header.
|
|
*/
|
|
header = (StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE);
|
|
|
|
AssertArg(MemoryContextIsValid(header->context));
|
|
|
|
if (!AllocSizeIsValid(size)) {
|
|
elog(ERROR, "invalid memory alloc request size %lu", (unsigned long)size);
|
|
}
|
|
|
|
return (*header->context->methods->realloc)(header->context, pointer, size);
|
|
}
|
|
|
|
/*
|
|
* MemoryContextSwitchTo
|
|
* Returns the current context; installs the given context.
|
|
*
|
|
* This is inlined when using GCC.
|
|
*
|
|
* TODO: investigate supporting inlining for some non-GCC compilers.
|
|
*/
|
|
MemoryContext MemoryContextSwitchTo(MemoryContext context)
|
|
{
|
|
MemoryContext old;
|
|
|
|
AssertArg(MemoryContextIsValid(context));
|
|
|
|
old = CurrentMemoryContext;
|
|
CurrentMemoryContext = context;
|
|
return old;
|
|
}
|
|
|
|
/*
|
|
* MemoryContextStrdup
|
|
* Like strdup(), but allocate from the specified context
|
|
*/
|
|
char* MemoryContextStrdup(MemoryContext context, const char* string)
|
|
{
|
|
char* nstr;
|
|
Size len = strlen(string) + 1;
|
|
errno_t rc;
|
|
|
|
nstr = (char*)MemoryContextAlloc(context, len);
|
|
|
|
rc = memcpy_s(nstr, len, string, len);
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
return nstr;
|
|
}
|
|
|
|
/*
|
|
* pnstrdup
|
|
* Like pstrdup(), but append null byte to a
|
|
* not-necessarily-null-terminated input string.
|
|
*/
|
|
char* pnstrdup(const char* in, Size len)
|
|
{
|
|
char* out = palloc(len + 1);
|
|
errno_t rc;
|
|
|
|
rc = memcpy_s(out, len, in, len);
|
|
securec_check(rc, "\0", "\0");
|
|
out[len] = '\0';
|
|
return out;
|
|
}
|
|
|
|
#if defined(WIN32) || defined(__CYGWIN__)
|
|
/*
|
|
* Memory support routines for libpgport on Win32
|
|
*
|
|
* Win32 can't load a library that PGDLLIMPORTs a variable
|
|
* if the link object files also PGDLLIMPORT the same variable.
|
|
* For this reason, libpgport can't reference CurrentMemoryContext
|
|
* in the palloc macro calls.
|
|
*
|
|
* To fix this, we create several functions here that allow us to
|
|
* manage memory without doing the inline in libpgport.
|
|
*/
|
|
void* pgport_palloc(Size sz)
|
|
{
|
|
return palloc(sz);
|
|
}
|
|
|
|
char* pgport_pstrdup(const char* str)
|
|
{
|
|
return pstrdup(str);
|
|
}
|
|
|
|
/* Doesn't reference a PGDLLIMPORT variable, but here for completeness. */
|
|
void pgport_pfree(void* pointer)
|
|
{
|
|
pfree(pointer);
|
|
}
|
|
|
|
#endif
|
|
|
|
#include "gen_alloc.h"
|
|
void* current_memcontext(void);
|
|
void* current_memcontext(void)
|
|
{
|
|
return ((void*)CurrentMemoryContext);
|
|
}
|
|
|
|
void* allocTopMemCxt(size_t s)
|
|
{
|
|
return (void*)MemoryContextAlloc(TopMostMemoryContext, (Size)s);
|
|
}
|
|
|
|
Gen_Alloc genAlloc_class = {(void*)MemoryContextAlloc,
|
|
(void*)MemoryContextAllocZero,
|
|
(void*)repalloc,
|
|
(void*)pfree,
|
|
(void*)current_memcontext,
|
|
(void*)allocTopMemCxt};
|