blob: 9727f52f91d62edb7867e2e4ec8d69d90d760602 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*-------------------------------------------------------------------------
*
* asetdirect.c
* A specialized implementation of the abstract MemoryContext type,
* which allocates directly from malloc() and does not support pfree().
*
* Portions Copyright (c) 2007-2008, Greenplum inc
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "utils/memutils.h"
#include "cdb/cdbptrbuf.h" /* CdbPtrBuf */
/* Define this to detail debug alloc information */
/* #define HAVE_ALLOCINFO */
#ifdef CDB_PALLOC_CALLER_ID
#define CDB_MCXT_WHERE(context) (context)->callerFile, (context)->callerLine
#else
#define CDB_MCXT_WHERE(context) __FILE__, __LINE__
#endif
/*
* AsetDirectContext
*/
typedef struct AsetDirectContext
{
MemoryContextData header; /* standard memory-context fields */
Size size_total; /* total size of all allocated areas */
unsigned narea_total; /* number of allocated areas */
CdbPtrBuf areas; /* collection of allocated area ptrs */
/* variably-sized array, must be last */
CdbPtrBuf_Ptr areaspace[10];
} AsetDirectContext;
#define ASETDIRECTCONTEXT_BYTES(nareaspace) \
(MAXALIGN(SIZEOF_VARSTRUCT(nareaspace, AsetDirectContext, areaspace)))
/*
* These functions implement the MemoryContext API for AsetDirect contexts.
*/
static void *AsetDirectAlloc(MemoryContext context, Size size);
static void AsetDirectInit(MemoryContext context);
static void AsetDirectReset(MemoryContext context);
static void AsetDirectDelete(MemoryContext context);
static bool AsetDirectIsEmpty(MemoryContext context);
static void AsetDirectStats(MemoryContext context, uint64 *nBlocks, uint64 *nChunks,
uint64 *currentAvailable, uint64 *allAllocated, uint64 *allFreed, uint64 *maxHeld);
#ifdef MEMORY_CONTEXT_CHECKING
static void AsetDirectCheck(MemoryContext context);
#endif
/*
* This is the virtual function table for AsetDirect contexts.
*/
static MemoryContextMethods AsetDirectMethods = {
AsetDirectAlloc,
NULL, /* pfree */
NULL, /* repalloc */
AsetDirectInit,
AsetDirectReset,
AsetDirectDelete,
NULL, /* GetChunkSpace */
AsetDirectIsEmpty,
AsetDirectStats,
#ifdef MEMORY_CONTEXT_CHECKING
AsetDirectCheck
#endif
};
/* ----------
* Debug macros
* ----------
*/
#ifdef HAVE_ALLOCINFO
#define AllocAllocInfo(_cxt, _chunk) \
fprintf(stderr, "AsetDirectAlloc: %s: %p, %d\n", \
(_cxt)->header.name, (_chunk), (_cxt)->chunksize)
#else
#define AllocAllocInfo(_cxt, _chunk)
#endif
/*
* Public routines
*/
/*
* AsetDirectContextCreate
* Create a new AsetDirect context.
*
* parent: parent context, or NULL if top-level context
* name: name of context (for debugging --- string will be copied)
*/
MemoryContext
AsetDirectContextCreate(MemoryContext parent, const char *name)
{
AsetDirectContext *set;
Size namesize = MAXALIGN(strlen(name) + 1);
Size allocsize;
Size setsize; /* #bytes to request for new context */
int nareaspace; /* num of slots in areaspace array */
/*
* Determine amount of memory to request for the AsetDirectContext struct.
*
* Assume the total allocation will be rounded up to a power of 2, and
* will include the AsetDirectContext with variably sized 'areaspace' array
* and the context 'name' string. Size the 'areaspace' array to use up any
* extra space in the expected allocation.
*/
allocsize = 1 << ceil_log2_Size(MAXALIGN(sizeof(AsetDirectContext)) + namesize);
nareaspace = VARELEMENTS_TO_FIT(allocsize - namesize, AsetDirectContext, areaspace);
setsize = ASETDIRECTCONTEXT_BYTES(nareaspace);
/*
* Create the new memory context and hook up to parent context.
*/
set = (AsetDirectContext *)MemoryContextCreate(T_AsetDirectContext,
setsize,
&AsetDirectMethods,
parent,
name);
/*
* Initialize empty collection of ptrs to allocated areas.
*/
CdbPtrBuf_Init(&set->areas,
set->areaspace,
nareaspace,
50, /* num_cells_expand */
set->header.parent);
return (MemoryContext)set;
} /* AsetDirectContextCreate */
/*
* AsetDirectInit
* 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.
* (AsetDirectContextCreate can allocate memory when it gets control
* back, however.)
*/
static void
AsetDirectInit(MemoryContext context)
{
/*
* Since MemoryContextCreate already zeroed the context node, we don't
* have to do anything here: it's already OK.
*/
} /* AsetDirectInit */
/*
* AsetDirectReset
* Frees all memory which is allocated in the given set.
*/
static void
AsetDirectReset(MemoryContext context)
{
AsetDirectContext *set = (AsetDirectContext *)context;
CdbPtrBuf_Iterator it;
CdbPtrBuf_Ptr *pp;
CdbPtrBuf_Ptr p;
Assert(set && IsA(set, AsetDirectContext));
Assert(CdbPtrBuf_IsOk(&set->areas));
/* Free allocated areas. */
CdbPtrBuf_Iterator_Init(&it, &set->areas);
while (NULL != (pp = CdbPtrBuf_Iterator_NextCell(&it)))
{
p = *pp;
*pp = NULL;
if (p)
{
#ifdef CLOBBER_FREED_MEMORY
/* Wipe first few bytes of freed memory for debugging purposes */
memset(p, 0x7F, MAXALIGN(1)); /* don't know actual size of area */
#endif
free(p);
}
}
/* Empty the 'areas' collection. */
CdbPtrBuf_Reset(&set->areas);
/* Update statistics. */
MemoryContextNoteFree(&set->header, set->size_total);
set->narea_total = 0;
set->size_total = 0;
} /* AsetDirectReset */
/*
* AsetDirectDelete
* Frees all memory which is allocated in the given set,
* in preparation for deletion of the set.
*
* Unlike AsetDirectReset, this *must* free all resources of the set.
* But note we are not responsible for deleting the context node itself.
*/
static void
AsetDirectDelete(MemoryContext context)
{
AsetDirectReset(context);
} /* AsetDirectDelete */
/*
* AsetDirectAlloc
* Returns pointer to allocated memory of given size; memory is added
* to the set.
*/
static void *
AsetDirectAlloc(MemoryContext context, Size size)
{
AsetDirectContext *set = (AsetDirectContext *)context;
CdbPtrBuf_Ptr *pp;
Assert(set && IsA(set, AsetDirectContext));
if (size < MAXALIGN(1))
size = MAXALIGN(1);
/* Obtain a slot in 'areas' collection to point to the new allocation. */
pp = CdbPtrBuf_Append(&set->areas, NULL);
/* Allocate the memory. */
*pp = malloc(size);
if (!*pp)
MemoryContextError(ERRCODE_OUT_OF_MEMORY,
&set->header, CDB_MCXT_WHERE(&set->header),
"Out of memory. Failed on request of size %lu bytes.",
(unsigned long)size);
/* Update statistics. */
set->size_total += size;
set->narea_total++;
MemoryContextNoteAlloc(&set->header, size);
AllocAllocInfo(set, chunk);
return *pp;
} /* AsetDirectAlloc */
/*
* AsetDirectIsEmpty
* Is an allocset empty of any allocated space?
*/
static bool
AsetDirectIsEmpty(MemoryContext context)
{
AsetDirectContext *set = (AsetDirectContext *)context;
return set->narea_total == 0;
} /* AsetDirectIsEmpty */
/*
* AsetDirectStats
* Returns stats about memory consumption of an AsetDirectContext.
*
* Input parameters:
* context: the context of interest
*
* Output parameters:
* nBlocks: number of blocks in the context
* nChunks: number of chunks in the context
*
* currentAvailable: free space across all blocks
*
* allAllocated: total bytes allocated during lifetime (including
* blocks that was dropped later on, e.g., freeing a large chunk
* in an exclusive block would drop the block)
*
* allFreed: total bytes that was freed during lifetime
* maxHeld: maximum bytes held during lifetime
*/
static void
AsetDirectStats(MemoryContext context, uint64 *nBlocks, uint64 *nChunks,
uint64 *currentAvailable, uint64 *allAllocated, uint64 *allFreed, uint64 *maxHeld)
{
AsetDirectContext *set = (AsetDirectContext *)context;
Assert(set && IsA(set, AsetDirectContext));
*nBlocks = 0;
*nChunks = set->narea_total;
*currentAvailable = 0;
*allAllocated = set->header.allBytesAlloc;
*allFreed = set->header.allBytesFreed;
*maxHeld = set->header.maxBytesHeld;
}
#ifdef MEMORY_CONTEXT_CHECKING
/*
* AsetDirectCheck
* Walk through chunks and check consistency of memory.
*
* NOTE: report errors as WARNING, *not* ERROR or FATAL. Otherwise you'll
* find yourself in an infinite loop when trouble occurs, because this
* routine will be entered again when elog cleanup tries to release memory!
*/
static void
AsetDirectCheck(MemoryContext context)
{
AsetDirectContext *set = (AsetDirectContext *)context;
const char *name = set->header.name;
if (!IsA(set, AsetDirectContext))
elog(WARNING, "problem in alloc set %s: type=%d",
name, set->header.type);
else if (!CdbPtrBuf_IsOk(&set->areas))
elog(WARNING, "problem in alloc set %s: CdbPtrBuf error",
name);
else if (set->narea_total < 0 ||
set->narea_total > CdbPtrBuf_Length(&set->areas))
elog(WARNING, "problem in alloc set %s: narea=%d",
name, set->narea_total);
} /* AsetDirectCheck */
#endif /* MEMORY_CONTEXT_CHECKING */