diff options
Diffstat (limited to 'src/include')
-rw-r--r-- | src/include/nodes/memnodes.h | 7 | ||||
-rw-r--r-- | src/include/utils/memutils.h | 51 | ||||
-rw-r--r-- | src/include/utils/memutils_internal.h | 127 | ||||
-rw-r--r-- | src/include/utils/memutils_memorychunk.h | 237 |
4 files changed, 372 insertions, 50 deletions
diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h index 16cd56da6db..63d07358cdb 100644 --- a/src/include/nodes/memnodes.h +++ b/src/include/nodes/memnodes.h @@ -59,11 +59,12 @@ typedef struct MemoryContextMethods { void *(*alloc) (MemoryContext context, Size size); /* call this free_p in case someone #define's free() */ - void (*free_p) (MemoryContext context, void *pointer); - void *(*realloc) (MemoryContext context, void *pointer, Size size); + void (*free_p) (void *pointer); + void *(*realloc) (void *pointer, Size size); void (*reset) (MemoryContext context); void (*delete_context) (MemoryContext context); - Size (*get_chunk_space) (MemoryContext context, void *pointer); + MemoryContext (*get_chunk_context) (void *pointer); + Size (*get_chunk_space) (void *pointer); bool (*is_empty) (MemoryContext context); void (*stats) (MemoryContext context, MemoryStatsPrintFunc printfunc, void *passthru, diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h index 495d1af2010..52bc41ec53b 100644 --- a/src/include/utils/memutils.h +++ b/src/include/utils/memutils.h @@ -41,8 +41,11 @@ #define AllocSizeIsValid(size) ((Size) (size) <= MaxAllocSize) +/* Must be less than SIZE_MAX */ #define MaxAllocHugeSize (SIZE_MAX / 2) +#define InvalidAllocSize SIZE_MAX + #define AllocHugeSizeIsValid(size) ((Size) (size) <= MaxAllocHugeSize) @@ -79,6 +82,7 @@ extern void MemoryContextDeleteChildren(MemoryContext context); extern void MemoryContextSetIdentifier(MemoryContext context, const char *id); extern void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent); +extern MemoryContext GetMemoryChunkContext(void *pointer); extern Size GetMemoryChunkSpace(void *pointer); extern MemoryContext MemoryContextGetParent(MemoryContext context); extern bool MemoryContextIsEmpty(MemoryContext context); @@ -98,53 +102,6 @@ extern bool MemoryContextContains(MemoryContext context, void *pointer); #define MemoryContextCopyAndSetIdentifier(cxt, id) \ MemoryContextSetIdentifier(cxt, MemoryContextStrdup(cxt, id)) -/* - * GetMemoryChunkContext - * Given a currently-allocated chunk, determine the context - * it belongs to. - * - * All chunks allocated by any memory context manager are required to be - * preceded by the corresponding MemoryContext stored, without padding, in the - * preceding sizeof(void*) bytes. A currently-allocated chunk must contain a - * backpointer to its owning context. The backpointer is used by pfree() and - * repalloc() to find the context to call. - */ -#ifndef FRONTEND -static inline MemoryContext -GetMemoryChunkContext(void *pointer) -{ - MemoryContext context; - - /* - * 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 context. - */ - context = *(MemoryContext *) (((char *) pointer) - sizeof(void *)); - - AssertArg(MemoryContextIsValid(context)); - - return context; -} -#endif - -/* - * 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 void MemoryContextCreate(MemoryContext node, - NodeTag tag, - const MemoryContextMethods *methods, - MemoryContext parent, - const char *name); - extern void HandleLogMemoryContextInterrupt(void); extern void ProcessLogMemoryContextInterrupt(void); diff --git a/src/include/utils/memutils_internal.h b/src/include/utils/memutils_internal.h new file mode 100644 index 00000000000..9a9f52ef164 --- /dev/null +++ b/src/include/utils/memutils_internal.h @@ -0,0 +1,127 @@ +/*------------------------------------------------------------------------- + * + * memutils_internal.h + * This file contains declarations for memory allocation utility + * functions for internal use. + * + * + * Portions Copyright (c) 2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/utils/memutils_internal.h + * + *------------------------------------------------------------------------- + */ + +#ifndef MEMUTILS_INTERNAL_H +#define MEMUTILS_INTERNAL_H + +#include "utils/memutils.h" + +/* These functions implement the MemoryContext API for AllocSet context. */ +extern void *AllocSetAlloc(MemoryContext context, Size size); +extern void AllocSetFree(void *pointer); +extern void *AllocSetRealloc(void *pointer, Size size); +extern void AllocSetReset(MemoryContext context); +extern void AllocSetDelete(MemoryContext context); +extern MemoryContext AllocSetGetChunkContext(void *pointer); +extern Size AllocSetGetChunkSpace(void *pointer); +extern bool AllocSetIsEmpty(MemoryContext context); +extern void AllocSetStats(MemoryContext context, + MemoryStatsPrintFunc printfunc, void *passthru, + MemoryContextCounters *totals, + bool print_to_stderr); +#ifdef MEMORY_CONTEXT_CHECKING +extern void AllocSetCheck(MemoryContext context); +#endif + +/* These functions implement the MemoryContext API for Generation context. */ +extern void *GenerationAlloc(MemoryContext context, Size size); +extern void GenerationFree(void *pointer); +extern void *GenerationRealloc(void *pointer, Size size); +extern void GenerationReset(MemoryContext context); +extern void GenerationDelete(MemoryContext context); +extern MemoryContext GenerationGetChunkContext(void *pointer); +extern Size GenerationGetChunkSpace(void *pointer); +extern bool GenerationIsEmpty(MemoryContext context); +extern void GenerationStats(MemoryContext context, + MemoryStatsPrintFunc printfunc, void *passthru, + MemoryContextCounters *totals, + bool print_to_stderr); +#ifdef MEMORY_CONTEXT_CHECKING +extern void GenerationCheck(MemoryContext context); +#endif + + +/* These functions implement the MemoryContext API for Slab context. */ +extern void *SlabAlloc(MemoryContext context, Size size); +extern void SlabFree(void *pointer); +extern void *SlabRealloc(void *pointer, Size size); +extern void SlabReset(MemoryContext context); +extern void SlabDelete(MemoryContext context); +extern MemoryContext SlabGetChunkContext(void *pointer); +extern Size SlabGetChunkSpace(void *pointer); +extern bool SlabIsEmpty(MemoryContext context); +extern void SlabStats(MemoryContext context, + MemoryStatsPrintFunc printfunc, void *passthru, + MemoryContextCounters *totals, + bool print_to_stderr); +#ifdef MEMORY_CONTEXT_CHECKING +extern void SlabCheck(MemoryContext context); +#endif + +/* + * MemoryContextMethodID + * A unique identifier for each MemoryContext implementation which + * indicates the index into the mcxt_methods[] array. See mcxt.c. + */ +typedef enum MemoryContextMethodID +{ + MCTX_ASET_ID, + MCTX_GENERATION_ID, + MCTX_SLAB_ID, +} MemoryContextMethodID; + +/* + * The number of bits that 8-byte memory chunk headers can use to encode the + * MemoryContextMethodID. + */ +#define MEMORY_CONTEXT_METHODID_BITS 3 +#define MEMORY_CONTEXT_METHODID_MASK \ + UINT64CONST((1 << MEMORY_CONTEXT_METHODID_BITS) - 1) + +/* + * 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 void MemoryContextCreate(MemoryContext node, + NodeTag tag, + MemoryContextMethodID method_id, + MemoryContext parent, + const char *name); + +/* + * GetMemoryChunkMethodID + * Return the MemoryContextMethodID from the uint64 chunk header which + * directly precedes 'pointer'. + */ +static inline MemoryContextMethodID +GetMemoryChunkMethodID(void *pointer) +{ + uint64 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)); + + header = *((uint64 *) ((char *) pointer - sizeof(uint64))); + + return (MemoryContextMethodID) (header & MEMORY_CONTEXT_METHODID_MASK); +} + +#endif /* MEMUTILS_INTERNAL_H */ diff --git a/src/include/utils/memutils_memorychunk.h b/src/include/utils/memutils_memorychunk.h new file mode 100644 index 00000000000..685c177b681 --- /dev/null +++ b/src/include/utils/memutils_memorychunk.h @@ -0,0 +1,237 @@ +/*------------------------------------------------------------------------- + * + * memutils_memorychunk.h + * Here we define a struct named MemoryChunk which implementations of + * MemoryContexts may use as a header for chunks of memory they allocate. + * + * MemoryChunk provides a lightweight header that a MemoryContext can use to + * store a reference back to the block the which the given chunk is allocated + * on and also an additional 30-bits to store another value such as the size + * of the allocated chunk. + * + * Although MemoryChunks are used by each of our MemoryContexts, future + * implementations may choose to implement their own method for storing chunk + * headers. The only requirement is that the header ends with an 8-byte value + * which the least significant 3-bits of are set to the MemoryContextMethodID + * of the given context. + * + * By default, a MemoryChunk is 8 bytes in size, however, when + * MEMORY_CONTEXT_CHECKING is defined the header becomes 16 bytes in size due + * to the additional requested_size field. The MemoryContext may use this + * field for whatever they wish, but it is intended to be used for additional + * checks which are only done in MEMORY_CONTEXT_CHECKING builds. + * + * The MemoryChunk contains a uint64 field named 'hdrmask'. This field is + * used to encode 4 separate pieces of information. Starting with the least + * significant bits of 'hdrmask', the bit space is reserved as follows: + * + * 1. 3-bits to indicate the MemoryContextMethodID as defined by + * MEMORY_CONTEXT_METHODID_MASK + * 2. 1-bit to denote an "external" chunk (see below) + * 3. 30-bits reserved for the MemoryContext to use for anything it + * requires. Most MemoryContext likely want to store the size of the + * chunk here. + * 4. 30-bits for the number of bytes that must be subtracted from the chunk + * to obtain the address of the block that the chunk is stored on. + * + * In some cases, for example when memory allocations become large, it's + * possible fields 3 and 4 above are not large enough to store the values + * required for the chunk. In this case, the MemoryContext can choose to mark + * the chunk as "external" by calling the MemoryChunkSetExternal() function. + * When this is done, fields 3 and 4 are unavailable for use by the + * MemoryContext and it's up to the MemoryContext itself to devise its own + * method for getting the reference to the block. + * + * Interface: + * + * MemoryChunkSetHdrMask: + * Used to set up a non-external MemoryChunk. + * + * MemoryChunkSetHdrMaskExternal: + * Used to set up an externally managed MemoryChunk. + * + * MemoryChunkIsExternal: + * Determine if the given MemoryChunk is externally managed, i.e. + * MemoryChunkSetHdrMaskExternal() was called on the chunk. + * + * MemoryChunkGetValue: + * For non-external chunks, return the stored 30-bit value as it was set + * in the call to MemoryChunkSetHdrMask(). + * + * MemoryChunkGetBlock: + * For non-external chunks, return a pointer to the block as it was set + * in the call to MemoryChunkSetHdrMask(). + * + * Also exports: + * MEMORYCHUNK_MAX_VALUE + * MEMORYCHUNK_MAX_BLOCKOFFSET + * PointerGetMemoryChunk + * MemoryChunkGetPointer + * + * Portions Copyright (c) 2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/utils/memutils_memorychunk.h + * + *------------------------------------------------------------------------- + */ + +#ifndef MEMUTILS_MEMORYCHUNK_H +#define MEMUTILS_MEMORYCHUNK_H + +#include "utils/memutils_internal.h" + + /* + * The maximum allowed value that MemoryContexts can store in the value + * field. Must be 1 less than a power of 2. + */ +#define MEMORYCHUNK_MAX_VALUE UINT64CONST(0x3FFFFFFF) + +/* + * The maximum distance in bytes that a MemoryChunk can be offset from the + * block that is storing the chunk. Must be 1 less than a power of 2. + */ +#define MEMORYCHUNK_MAX_BLOCKOFFSET UINT64CONST(0x3FFFFFFF) + +/* define the least significant base-0 bit of each portion of the hdrmask */ +#define MEMORYCHUNK_EXTERNAL_BASEBIT MEMORY_CONTEXT_METHODID_BITS +#define MEMORYCHUNK_VALUE_BASEBIT (MEMORYCHUNK_EXTERNAL_BASEBIT + 1) +#define MEMORYCHUNK_BLOCKOFFSET_BASEBIT (MEMORYCHUNK_VALUE_BASEBIT + 30) + +/* + * A magic number for storing in the free bits of an external chunk. This + * must mask out the bits used for storing the MemoryContextMethodID and the + * external bit. + */ +#define MEMORYCHUNK_MAGIC (UINT64CONST(0xB1A8DB858EB6EFBA) >> \ + MEMORYCHUNK_VALUE_BASEBIT << \ + MEMORYCHUNK_VALUE_BASEBIT) + +typedef struct MemoryChunk +{ +#ifdef MEMORY_CONTEXT_CHECKING + Size requested_size; +#endif + + /* bitfield for storing details about the chunk */ + uint64 hdrmask; /* must be last */ +} MemoryChunk; + +/* Get the MemoryChunk from the pointer */ +#define PointerGetMemoryChunk(p) \ + ((MemoryChunk *) ((char *) (p) - sizeof(MemoryChunk))) +/* Get the pointer from the MemoryChunk */ +#define MemoryChunkGetPointer(c) \ + ((void *) ((char *) (c) + sizeof(MemoryChunk))) + +/* private macros for making the inline functions below more simple */ +#define HdrMaskIsExternal(hdrmask) \ + ((hdrmask) & (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT)) +#define HdrMaskGetValue(hdrmask) \ + (((hdrmask) >> MEMORYCHUNK_VALUE_BASEBIT) & MEMORYCHUNK_MAX_VALUE) + +/* + * We should have used up all the bits here, so the compiler is likely to + * optimize out the & MEMORYCHUNK_MAX_BLOCKOFFSET. + */ +#define HdrMaskBlockOffset(hdrmask) \ + (((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_MAX_BLOCKOFFSET) + +/* For external chunks only, check the magic number matches */ +#define HdrMaskCheckMagic(hdrmask) \ + (MEMORYCHUNK_MAGIC == \ + ((hdrmask) >> MEMORYCHUNK_VALUE_BASEBIT << MEMORYCHUNK_VALUE_BASEBIT)) +/* + * MemoryChunkSetHdrMask + * Store the given 'block', 'chunk_size' and 'methodid' in the given + * MemoryChunk. + * + * The number of bytes between 'block' and 'chunk' must be <= + * MEMORYCHUNK_MAX_BLOCKOFFSET. + * 'value' must be <= MEMORYCHUNK_MAX_VALUE. + */ +static inline void +MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block, + Size value, MemoryContextMethodID methodid) +{ + Size blockoffset = (char *) chunk - (char *) block; + + Assert((char *) chunk > (char *) block); + Assert(blockoffset <= MEMORYCHUNK_MAX_BLOCKOFFSET); + Assert(value <= MEMORYCHUNK_MAX_VALUE); + Assert(methodid <= MEMORY_CONTEXT_METHODID_MASK); + + chunk->hdrmask = (((uint64) blockoffset) << MEMORYCHUNK_BLOCKOFFSET_BASEBIT) | + (((uint64) value) << MEMORYCHUNK_VALUE_BASEBIT) | + methodid; +} + +/* + * MemoryChunkSetHdrMaskExternal + * Set 'chunk' as an externally managed chunk. Here we only record the + * MemoryContextMethodID and set the external chunk bit. + */ +static inline void +MemoryChunkSetHdrMaskExternal(MemoryChunk *chunk, + MemoryContextMethodID methodid) +{ + Assert(methodid <= MEMORY_CONTEXT_METHODID_MASK); + + chunk->hdrmask = MEMORYCHUNK_MAGIC | (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT) | + methodid; +} + +/* + * MemoryChunkIsExternal + * Return true if 'chunk' is marked as external. + */ +static inline bool +MemoryChunkIsExternal(MemoryChunk *chunk) +{ + /* + * External chunks should always store MEMORYCHUNK_MAGIC in the upper + * portion of the hdrmask, check that nothing has stomped on that. + */ + Assert(!HdrMaskIsExternal(chunk->hdrmask) || + HdrMaskCheckMagic(chunk->hdrmask)); + + return HdrMaskIsExternal(chunk->hdrmask); +} + +/* + * MemoryChunkGetValue + * For non-external chunks, returns the value field as it was set in + * MemoryChunkSetHdrMask. + */ +static inline Size +MemoryChunkGetValue(MemoryChunk *chunk) +{ + Assert(!HdrMaskIsExternal(chunk->hdrmask)); + + return HdrMaskGetValue(chunk->hdrmask); +} + +/* + * MemoryChunkGetBlock + * For non-external chunks, returns the pointer to the block as was set + * in MemoryChunkSetHdrMask. + */ +static inline void * +MemoryChunkGetBlock(MemoryChunk *chunk) +{ + Assert(!HdrMaskIsExternal(chunk->hdrmask)); + + return (void *) ((char *) chunk - HdrMaskBlockOffset(chunk->hdrmask)); +} + +/* cleanup all internal definitions */ +#undef MEMORYCHUNK_EXTERNAL_BASEBIT +#undef MEMORYCHUNK_VALUE_BASEBIT +#undef MEMORYCHUNK_BLOCKOFFSET_BASEBIT +#undef MEMORYCHUNK_MAGIC +#undef HdrMaskIsExternal +#undef HdrMaskGetValue +#undef HdrMaskBlockOffset +#undef HdrMaskCheckMagic + +#endif /* MEMUTILS_MEMORYCHUNK_H */ |