blob: 04cabf80eeb3fe81b369a28d058296da76b8a7aa [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.
*/
/*-------------------------------------------------------------------------
*
* memutils.h
* This file contains declarations for memory allocation utility
* functions. These are functions that are not quite widely used
* enough to justify going in utils/palloc.h, but are still part
* of the API of the memory management subsystem.
*
*
* 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
*
* $PostgreSQL: pgsql/src/include/utils/memutils.h,v 1.60 2006/03/05 15:59:07 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef MEMUTILS_H
#define MEMUTILS_H
#include "nodes/memnodes.h"
#include "utils/memaccounting.h"
/*
* MaxAllocSize
* Quasi-arbitrary limit on size of allocations.
*
* Note:
* There is no guarantee that allocations smaller than MaxAllocSize
* will succeed. Allocation requests larger than MaxAllocSize will
* be summarily denied.
*
* XXX This is deliberately chosen to correspond to the limiting size
* of varlena objects under TOAST. See VARATT_MASK_SIZE in postgres.h.
*
* XXX Also, various places in aset.c assume they can compute twice an
* allocation's size without overflow, so beware of raising this.
*/
#define MaxAllocSize ((Size) 0x3fffffff) /* 1 gigabyte - 1 */
static inline bool AllocSizeIsValid(Size sz)
{
return (sz < MaxAllocSize);
}
/*
* Multiple chunks can share a SharedChunkHeader if their shared information
* such as owning memory context, memoryAccount, memory account generation etc.
* match. This sharing mechanism optimizes memory consumption by "refactoring"
* common chunk properties.
*/
typedef struct SharedChunkHeader
{
MemoryContext context; /* owning context */
struct MemoryAccount* memoryAccount; /* Which account to charge for this memory. */
/*
* The generation of "memoryAccount" pointer. If the generation
* is not equal to current memory account generation
* (MemoryAccountingCurrentGeneration), we do not
* release accounting through "memoryAccount". Instead, we
* release the accounting of RolloverMemoryAccount.
*/
uint16 memoryAccountGeneration;
/* Combined balance of all the chunks that are sharing this header */
int64 balance;
struct SharedChunkHeader *prev;
struct SharedChunkHeader *next;
} SharedChunkHeader;
/*
* All chunks allocated by any memory context manager are required to be
* preceded by a StandardChunkHeader at a spacing of STANDARDCHUNKHEADERSIZE.
* A currently-allocated chunk must contain a backpointer to its owning
* context as well as the allocated size of the chunk. The backpointer is
* used by pfree() and repalloc() to find the context to call. The allocated
* size is not absolutely essential, but it's expected to be needed by any
* reasonable implementation.
*
* NB: Chunks allocated from an AsetDirectContext have no StandardChunkHeader.
*/
typedef struct StandardChunkHeader
{
/*
* SharedChunkHeader stores all the "shared" details
* among multiple chunks, such as memoryAccount to charge,
* generation of memory account, memory context that owns this
* chunk etc.
*/
struct SharedChunkHeader* sharedHeader;
Size size; /* size of data space allocated in chunk */
#ifdef MEMORY_CONTEXT_CHECKING
/* when debugging memory usage, also store actual requested size */
Size requested_size;
#endif
#ifdef CDB_PALLOC_TAGS
const char *alloc_tag;
int alloc_n;
void *prev_chunk;
void *next_chunk;
#endif
} StandardChunkHeader;
#define STANDARDCHUNKHEADERSIZE MAXALIGN(sizeof(StandardChunkHeader))
/*--------------------
* 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.
*--------------------
*/
#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 */
typedef struct AllocBlockData *AllocBlock; /* forward reference */
typedef struct AllocChunkData *AllocChunk;
/*
* AllocSetContext is our standard implementation of MemoryContext.
*
* Note: isReset means there is nothing for AllocSetReset to do. This is
* different from the aset being physically empty (empty blocks list) because
* we may still have a keeper block. It's also different from the set being
* logically empty, because we don't attempt to detect pfree'ing the last
* active chunk.
*/
typedef struct AllocSetContext
{
MemoryContextData header; /* Standard memory-context fields */
/* Info about storage allocated in this context: */
AllocBlock blocks; /* head of list of blocks in this set */
AllocChunk freelist[ALLOCSET_NUM_FREELISTS]; /* free chunk lists */
bool isReset; /* T = no space alloced since last reset */
/* Allocation parameters for this context: */
Size initBlockSize; /* initial block size */
Size maxBlockSize; /* maximum block size */
Size nextBlockSize; /* next block size to allocate */
AllocBlock keeper; /* if not NULL, keep this block over resets */
/* Points to the head of the sharedHeaderList */
SharedChunkHeader *sharedHeaderList;
/* The memory account of this SharedChunkHeader is NULL */
SharedChunkHeader *nullAccountHeader;
#ifdef CDB_PALLOC_TAGS
/*
* allocList maintains a list of chunks (double linked list) that are
* currently allocated.
*/
AllocChunk allocList;
#endif
} AllocSetContext;
typedef AllocSetContext *AllocSet;
/*
* Standard top-level memory contexts.
*
* Only TopMemoryContext and ErrorContext are initialized by
* MemoryContextInit() itself.
*/
extern PGDLLIMPORT MemoryContext TopMemoryContext;
extern PGDLLIMPORT MemoryContext ErrorContext;
extern PGDLLIMPORT MemoryContext PostmasterContext;
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
extern PGDLLIMPORT MemoryContext MessageContext;
extern PGDLLIMPORT MemoryContext TopTransactionContext;
extern PGDLLIMPORT MemoryContext CurTransactionContext;
extern PGDLLIMPORT MemoryContext MemoryAccountMemoryContext;
/* These two are transient links to contexts owned by other objects: */
extern PGDLLIMPORT MemoryContext QueryContext;
extern PGDLLIMPORT MemoryContext PortalContext;
/*
* Memory-context-type-independent functions in mcxt.c
*/
extern void MemoryContextInit(void);
extern void MemoryContextReset(MemoryContext context);
extern void MemoryContextResetChildren(MemoryContext context);
extern void MemoryContextDeleteChildren(MemoryContext context);
extern void MemoryContextResetAndDeleteChildren(MemoryContext context);
extern Size GetMemoryChunkSpace(void *pointer);
extern MemoryContext GetMemoryChunkContext(void *pointer);
extern bool MemoryContextIsEmpty(MemoryContext context);
/* Statistics */
extern Size MemoryContextGetCurrentSpace(MemoryContext context);
extern Size MemoryContextGetPeakSpace(MemoryContext context);
extern Size MemoryContextSetPeakSpace(MemoryContext context, Size nbytes);
extern char *MemoryContextName(MemoryContext context, MemoryContext relativeTo,
char *buf, int bufsize);
#define MemoryContextDelete(context) (MemoryContextDeleteImpl(context, __FILE__, PG_FUNCNAME_MACRO, __LINE__))
extern void MemoryContextDeleteImpl(MemoryContext context, const char* sfile, const char *func, int sline);
#ifdef CDB_PALLOC_TAGS
extern void dump_memory_allocation(const char* fname);
extern void dump_memory_allocation_ctxt(FILE * ofile, void *ctxt);
#endif
#ifdef MEMORY_CONTEXT_CHECKING
extern void MemoryContextCheck(MemoryContext context);
#endif
extern bool MemoryContextContains(MemoryContext context, void *pointer);
extern bool MemoryContextContainsGenericAllocation(MemoryContext context, void *pointer);
/* Functions called only by context-type-specific memory managers... */
extern void MemoryContextNoteAlloc(MemoryContext context, Size nbytes);
extern void MemoryContextNoteFree(MemoryContext context, Size nbytes);
#ifdef _MSC_VER
__declspec(noreturn)
#endif
extern void MemoryContextError(int errorcode, MemoryContext context,
const char *sfile, int sline,
const char *fmt, ...)
__attribute__((__noreturn__));
/*
* This routine handles the context-type-independent part of memory
* context creation. It's intended to be called from context-type-
* specific creation routines, and noplace else.
*/
extern MemoryContext MemoryContextCreate(NodeTag tag, Size size,
MemoryContextMethods *methods,
MemoryContext parent,
const char *name);
/*
* Memory-context-type-specific functions
*/
/* aset.c */
extern MemoryContext AllocSetContextCreate(MemoryContext parent,
const char *name,
Size minContextSize,
Size initBlockSize,
Size maxBlockSize);
/* mpool.c */
typedef struct MPool MPool;
extern MPool *mpool_create(MemoryContext parent,
const char *name);
extern void *mpool_alloc(MPool *mpool, Size size);
extern void mpool_reset(MPool *mpool);
extern void mpool_delete(MPool *mpool);
extern uint64 mpool_total_bytes_allocated(MPool *mpool);
extern uint64 mpool_bytes_used(MPool *mpool);
/*
* Recommended default alloc parameters, suitable for "ordinary" contexts
* that might hold quite a lot of data.
*/
#define ALLOCSET_DEFAULT_MINSIZE 0
#define ALLOCSET_DEFAULT_INITSIZE (8 * 1024)
#define ALLOCSET_DEFAULT_MAXSIZE (8 * 1024 * 1024)
/*
* Recommended alloc parameters for "small" contexts that are not expected
* to contain much data (for example, a context to contain a query plan).
*/
#define ALLOCSET_SMALL_MINSIZE 0
#define ALLOCSET_SMALL_INITSIZE (1 * 1024)
#define ALLOCSET_SMALL_MAXSIZE (8 * 1024)
typedef struct SwitchedMemoryContext
{
MemoryContext oldContext;
MemoryContext newContext;
}
SwitchedMemoryContext;
/**
* Shorthand for doing an AllocSetContextCreate and then switching to the new context,
* using DEFAULT memory values
*/
static inline SwitchedMemoryContext
AllocSetCreateDefaultContextInCurrentAndSwitchTo(const char *name)
{
SwitchedMemoryContext res;
res.newContext = AllocSetContextCreate(CurrentMemoryContext, name,
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
res.oldContext = MemoryContextSwitchTo(res.newContext);
return res;
}
/**
* Shorthand for doing an AllocSetContextCreate and then switching to the new context,
* using SMALL memory values
*/
static inline SwitchedMemoryContext
AllocSetCreateSmallContextInCurrentAndSwitchTo(const char *name)
{
SwitchedMemoryContext res;
res.newContext = AllocSetContextCreate(CurrentMemoryContext, name,
ALLOCSET_SMALL_MINSIZE,
ALLOCSET_SMALL_INITSIZE,
ALLOCSET_SMALL_MAXSIZE);
res.oldContext = MemoryContextSwitchTo(res.newContext);
return res;
}
static inline void
DeleteAndRestoreSwitchedMemoryContext(SwitchedMemoryContext context)
{
MemoryContextSwitchTo(context.oldContext);
MemoryContextDelete(context.newContext);
}
/* asetdirect.c */
/*
* AsetDirectContextCreate
*
* Create a context which allocates directly from malloc().
*
* Limited functionality. Space can be freed only by resetting
* or deleting the MemoryContext.
*
* Allocations from this context are not preceded by a StandardChunkHeader.
* Therefore the caller must make certain never to pass an allocation obtained
* from an AsetDirectContext to any of the following functions:
* pfree()
* repalloc()
* GetMemoryChunkSpace()
* GetMemoryChunkContext()
* MemoryContextContains()
*/
extern MemoryContext AsetDirectContextCreate(MemoryContext parent, const char *name);
/*
* floor_log2_Size
*
* Returns the largest integer i such that 2**i <= sz
*/
int floor_log2_Size(Size sz); /* in utils/mmgr/mcxt.c */
static inline int
floor_log2_Size_inline(Size sz)
{
unsigned u;
int shift;
Assert(sz > 0);
if (sizeof(sz) > 4 &&
sz > (Size)0xffffffff)
{
u = (unsigned)((sz >> 16) >> 16);
shift = 32;
}
else
{
u = (unsigned)sz;
shift = 0;
}
if (u > 0xffff)
{
u >>= 16;
shift += 16;
}
if (u > 0xff)
{
u >>= 8;
shift += 8;
}
if (u > 0xf)
{
u >>= 4;
shift += 4;
}
if (u > 3)
{
u >>= 2;
shift += 2;
}
if (u > 1)
shift += 1;
Assert(sz >> shift == (Size)1);
return shift;
} /* floor_log2_Size_inline */
/*
* ceil_log2_Size
*
* Returns the smallest integer i such that sz <= 2**i
*/
int ceil_log2_Size(Size sz); /* in utils/mmgr/mcxt.c */
static inline int
ceil_log2_Size_inline(Size sz)
{
int shift = floor_log2_Size_inline(sz);
if (sz > (Size)1 << shift)
shift++;
Assert(sz <= (Size)1 << shift);
return shift;
} /* ceil_log2_Size_inline */
#endif /* MEMUTILS_H */