/* * repalloc_huge * Adjust the size of a previously allocated chunk, permitting a large * value. The previous allocation need not have been "huge". */ void * repalloc_huge(void *pointer, Size size) { MemoryContext context; void *ret; if (!AllocHugeSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); /* * 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. */ context = ((StandardChunkHeader *) ((char *) pointer - STANDARDCHUNKHEADERSIZE))->context; AssertArg(MemoryContextIsValid(context)); AssertNotInCriticalSection(context); /* isReset must be false already */ Assert(!context->isReset); ret = (*context->methods->realloc) (context, pointer, size); VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size); return ret; }
/* * 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. */ void MemoryContextDelete(MemoryContext context) { AssertArg(MemoryContextIsValid(context)); /* We had better not be deleting TopMemoryContext ... */ Assert(context != TopMemoryContext); /* And not CurrentMemoryContext, either */ Assert(context != CurrentMemoryContext); MemoryContextDeleteChildren(context); /* * It's not entirely clear whether 'tis better to do this before or after * delinking the context; but an error in a callback will likely result in * leaking the whole context (if it's not a root context) if we do it * after, so let's do it before. */ MemoryContextCallResetCallbacks(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. */ MemoryContextSetParent(context, NULL); (*context->methods->delete_context) (context); VALGRIND_DESTROY_MEMPOOL(context); pfree(context); }
/* * MemoryContextAllowInCriticalSection * Allow/disallow allocations in this memory context within a critical * section. * * Normally, memory allocations are not allowed within a critical section, * because a failure would lead to PANIC. There are a few exceptions to * that, like allocations related to debugging code that is not supposed to * be enabled in production. This function can be used to exempt specific * memory contexts from the assertion in palloc(). */ void MemoryContextAllowInCriticalSection(MemoryContext context, bool allow) { AssertArg(MemoryContextIsValid(context)); context->allowInCritSection = allow; }
/* * MemoryContextGetParent * Get the parent context (if any) of the specified context */ MemoryContext MemoryContextGetParent(MemoryContext context) { AssertArg(MemoryContextIsValid(context)); return context->parent; }
/* * MemoryContextStats * Prints the usage details of a context. * * Parameters: * context: the context of interest. */ void MemoryContextStats(MemoryContext context) { char* name; char namebuf[MAX_CONTEXT_NAME_SIZE]; AssertArg(MemoryContextIsValid(context)); name = MemoryContextName(context, NULL, namebuf, sizeof(namebuf)); write_stderr("pid %d: Memory statistics for %s/\n", MyProcPid, name); write_stderr("context: occurrences_count, currently_allocated, currently_available, total_allocated, total_freed, name\n"); uint64 nBlocks = 0; uint64 nChunks = 0; uint64 currentAvailable = 0; uint64 allAllocated = 0; uint64 allFreed = 0; uint64 maxHeld = 0; int namebufsize = sizeof(namebuf); /* Get the root context's stat and pass it to the MemoryContextStats_recur for printing */ (*context->methods.stats)(context, &nBlocks, &nChunks, ¤tAvailable, &allAllocated, &allFreed, &maxHeld); name = MemoryContextName(context, context, namebuf, namebufsize); MemoryContextStats_recur(context, context, name, namebuf, namebufsize, nBlocks, nChunks, currentAvailable, allAllocated, allFreed, maxHeld); }
/* * 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 * MemoryContextAllocZeroAlignedImpl(MemoryContext context, Size size, const char* sfile, const char *sfunc, int sline) { void *ret; #ifdef PGTRACE_ENABLED StandardChunkHeader *header; #endif AssertArg(MemoryContextIsValid(context)); #ifdef CDB_PALLOC_CALLER_ID context->callerFile = sfile; context->callerLine = sline; #endif if (!AllocSizeIsValid(size)) MemoryContextError(ERRCODE_INTERNAL_ERROR, context, CDB_MCXT_WHERE(context), "invalid memory alloc request size %lu", (unsigned long)size); ret = (*context->methods.alloc) (context, size); MemSetLoop(ret, 0, size); #ifdef PGTRACE_ENABLED header = (StandardChunkHeader *) ((char *) ret - STANDARDCHUNKHEADERSIZE); PG_TRACE5(memctxt__alloc, size, header->size, 0, 0, (long) context->name); #endif 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)); AssertNotInCriticalSection(context); if (!AllocSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); context->isReset = false; ret = (*context->methods->alloc) (context, size); if (ret == NULL) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu.", size))); } VALGRIND_MEMPOOL_ALLOC(context, ret, size); MemSetLoop(ret, 0, size); return ret; }
/* * MemoryContextAllocExtended * Allocate space within the specified context using the given flags. */ void * MemoryContextAllocExtended(MemoryContext context, Size size, int flags) { void *ret; AssertArg(MemoryContextIsValid(context)); AssertNotInCriticalSection(context); if (((flags & MCXT_ALLOC_HUGE) != 0 && !AllocHugeSizeIsValid(size)) || ((flags & MCXT_ALLOC_HUGE) == 0 && !AllocSizeIsValid(size))) elog(ERROR, "invalid memory alloc request size %zu", size); context->isReset = false; ret = (*context->methods->alloc) (context, size); if (ret == NULL) { if ((flags & MCXT_ALLOC_NO_OOM) == 0) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu.", size))); } return NULL; } VALGRIND_MEMPOOL_ALLOC(context, ret, size); if ((flags & MCXT_ALLOC_ZERO) != 0) MemSetAligned(ret, 0, size); return ret; }
/* * 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); /* isReset must be false already */ Assert(!header->context->isReset); return (*header->context->methods->realloc) (header->context, pointer, size); }
void * palloc0(Size size) { /* duplicates MemoryContextAllocZero to avoid increased overhead */ void *ret; AssertArg(MemoryContextIsValid(CurrentMemoryContext)); AssertNotInCriticalSection(CurrentMemoryContext); if (!AllocSizeIsValid(size)) elog(ERROR, "invalid memory alloc request size %zu", size); CurrentMemoryContext->isReset = false; ret = (*CurrentMemoryContext->methods->alloc) (CurrentMemoryContext, size); if (ret == NULL) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %zu.", size))); } VALGRIND_MEMPOOL_ALLOC(CurrentMemoryContext, ret, size); MemSetAligned(ret, 0, size); return ret; }
/* * 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); MemoryContextReset(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); }
/* * MemoryContextSetParent * Change a context to belong to a new parent (or no parent). * * We provide this as an API function because it is sometimes useful to * change a context's lifespan after creation. For example, a context * might be created underneath a transient context, filled with data, * and then reparented underneath CacheMemoryContext to make it long-lived. * In this way no special effort is needed to get rid of the context in case * a failure occurs before its contents are completely set up. * * Callers often assume that this function cannot fail, so don't put any * elog(ERROR) calls in it. * * A possible caller error is to reparent a context under itself, creating * a loop in the context graph. We assert here that context != new_parent, * but checking for multi-level loops seems more trouble than it's worth. */ void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent) { AssertArg(MemoryContextIsValid(context)); AssertArg(context != new_parent); /* Fast path if it's got correct parent already */ if (new_parent == context->parent) return; /* Delink from existing parent, if any */ if (context->parent) { MemoryContext parent = context->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; } } } } /* And relink */ if (new_parent) { AssertArg(MemoryContextIsValid(new_parent)); context->parent = new_parent; context->nextchild = new_parent->firstchild; new_parent->firstchild = context; } else { context->parent = NULL; context->nextchild = NULL; } }
/* * MemoryContextSetParent * Change a context to belong to a new parent (or no parent). * * We provide this as an API function because it is sometimes useful to * change a context's lifespan after creation. For example, a context * might be created underneath a transient context, filled with data, * and then reparented underneath CacheMemoryContext to make it long-lived. * In this way no special effort is needed to get rid of the context in case * a failure occurs before its contents are completely set up. * * Callers often assume that this function cannot fail, so don't put any * elog(ERROR) calls in it. * * A possible caller error is to reparent a context under itself, creating * a loop in the context graph. We assert here that context != new_parent, * but checking for multi-level loops seems more trouble than it's worth. */ void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent) { AssertArg(MemoryContextIsValid(context)); AssertArg(context != new_parent); /* Fast path if it's got correct parent already */ 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(MemoryContextIsValid(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; } }
/* * MemoryContextGetCurrentSpace * Return the number of bytes currently occupied by the memory context. * * This is the amount of space obtained from the lower-level source of the * memory (e.g. malloc) and not yet released back to that source. Includes * overhead and free space held and managed within this context by the * context-type-specific memory manager. */ Size MemoryContextGetCurrentSpace(MemoryContext context) { AssertArg(MemoryContextIsValid(context)); Assert(context->allBytesAlloc >= context->allBytesFreed); Assert(context->allBytesAlloc - context->allBytesFreed < SIZE_MAX); return (Size)(context->allBytesAlloc - context->allBytesFreed); } /* MemoryContextGetCurrentSpace */
/* * repalloc * Adjust the size of a previously allocated chunk. */ void * MemoryContextReallocImpl(void *pointer, Size size, const char *sfile, const char *sfunc, int sline) { StandardChunkHeader *header; void *ret; #ifdef PGTRACE_ENABLED long old_reqsize; long old_size; #endif /* * 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->sharedHeader->context)); #ifdef PGTRACE_ENABLED #ifdef MEMORY_CONTEXT_CHECKING old_reqsize = header->requested_size; #else old_reqsize = 0; #endif old_size = header->size; #endif #ifdef CDB_PALLOC_CALLER_ID header->sharedHeader->context->callerFile = sfile; header->sharedHeader->context->callerLine = sline; #endif if (!AllocSizeIsValid(size)) MemoryContextError(ERRCODE_INTERNAL_ERROR, header->sharedHeader->context, CDB_MCXT_WHERE(header->sharedHeader->context), "invalid memory alloc request size %lu", (unsigned long)size); ret = (*header->sharedHeader->context->methods.realloc) (header->sharedHeader->context, pointer, size); #ifdef PGTRACE_ENABLED header = (StandardChunkHeader *) ((char *) ret - STANDARDCHUNKHEADERSIZE); PG_TRACE5(memctxt__realloc, size, header->size, old_reqsize, old_size, (long) header->sharedHeader->context->name); #endif return ret; }
static void getMemoryContextStat(MemoryContext context, MemoryContextCounters *stat) { AssertArg(MemoryContextIsValid(context)); /* Examine the context itself */ memset(stat, 0, sizeof(*stat)); (*context->methods->stats) (context, 0, false, stat); }
/* * Delete the MPool object and its related space. */ void mpool_delete(MPool *mpool) { Assert(mpool != NULL && mpool->context != NULL); Assert(MemoryContextIsValid(mpool->context)); mpool_reset(mpool); MemoryContextDelete(mpool->context); pfree(mpool); }
/* * 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 (child = context->firstchild; child != NULL; child = child->nextchild) MemoryContextReset(child); }
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); }
void MemoryContextCheck(MemoryContext context) { MemoryContext child; AssertArg(MemoryContextIsValid(context)); (*context->methods->check) (context); for (child = context->firstchild; child != NULL; child = child->nextchild) MemoryContextCheck(child); }
/* * 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)); /* save a function call in common case where there are no children */ if (context->firstchild != NULL) MemoryContextResetChildren(context); (*context->methods.reset) (context); }
/* * HandleMemoryAccountingGenerationOverflowChildren * Companion method for HandleMemoryAccountingGenerationOverflow(). Used for * walking the memory context tree. * * Parameters: * context: The memory context whose children will be descended */ static void HandleMemoryAccountingGenerationOverflowChildren(MemoryContext context) { MemoryContext child; AssertArg(MemoryContextIsValid(context)); for (child = context->firstchild; child != NULL; child = child->nextchild) { HandleMemoryAccountingGenerationOverflow(child); } }
/* * MemoryContextRegisterResetCallback * Register a function to be called before next context reset/delete. * Such callbacks will be called in reverse order of registration. * * The caller is responsible for allocating a MemoryContextCallback struct * to hold the info about this callback request, and for filling in the * "func" and "arg" fields in the struct to show what function to call with * what argument. Typically the callback struct should be allocated within * the specified context, since that means it will automatically be freed * when no longer needed. * * There is no API for deregistering a callback once registered. If you * want it to not do anything anymore, adjust the state pointed to by its * "arg" to indicate that. */ void MemoryContextRegisterResetCallback(MemoryContext context, MemoryContextCallback *cb) { AssertArg(MemoryContextIsValid(context)); /* Push onto head so this will be called before older registrants. */ cb->next = context->reset_cbs; context->reset_cbs = cb; /* Mark the context as non-reset (it probably is already). */ context->isReset = 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)); /* * MemoryContextDelete will delink the child from me, so just iterate as * long as there is a child. */ while (context->firstchild != NULL) MemoryContextDelete(context->firstchild); }
/* * HandleMemoryAccountingGenerationOverflow * Descends the memory context tree rooted at "context" and calls update_generation * on each context. The update_generation is supposed to look for every active chunks * and set their ownership to RolloverAccount.The update_generation method will also * set the generation of every active chunk to MemoryAccountingCurrentGeneration. * * Parameters: * context: The memory context whose children will be descended */ static void HandleMemoryAccountingGenerationOverflow(MemoryContext context) { AssertArg(MemoryContextIsValid(context)); /* save a function call in common case where there are no children */ if (context->firstchild != NULL) { HandleMemoryAccountingGenerationOverflowChildren(context); } (*context->methods.update_generation) (context); }
/* * MemoryContextReset * Release all space allocated within a context and delete all its * descendant contexts (but not the named context itself). */ void MemoryContextReset(MemoryContext context) { AssertArg(MemoryContextIsValid(context)); /* save a function call in common case where there are no children */ if (context->firstchild != NULL) MemoryContextDeleteChildren(context); /* save a function call if no pallocs since startup or last reset */ if (!context->isReset) MemoryContextResetOnly(context); }
/* * 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); context->isReset = false; return (*context->methods->alloc) (context, size); }
/* * 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); }
/* * Release all objects in the pool, and reset the memory context. */ void mpool_reset(MPool *mpool) { Assert(mpool != NULL && mpool->context != NULL); Assert(MemoryContextIsValid(mpool->context)); elog(DEBUG2, "MPool: total_bytes_allocated=" INT64_FORMAT ", bytes_used=" INT64_FORMAT ", bytes_wasted=" INT64_FORMAT, mpool->total_bytes_allocated, mpool->bytes_used, mpool->bytes_wasted); MemoryContextReset(mpool->context); mpool_init(mpool); }