1
0
mirror of https://github.com/postgres/postgres.git synced 2026-01-27 21:43:08 +03:00
Files
postgres/src/include/utils/memutils.h
Bruce Momjian 451c43974f Update copyright for 2026
Backpatch-through: 14
2026-01-01 13:24:10 -05:00

323 lines
10 KiB
C

/*-------------------------------------------------------------------------
*
* 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) 1996-2026, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/utils/memutils.h
*
*-------------------------------------------------------------------------
*/
#ifndef MEMUTILS_H
#define MEMUTILS_H
#include "nodes/memnodes.h"
/*
* MaxAllocSize, MaxAllocHugeSize
* Quasi-arbitrary limits on size of allocations.
*
* Note:
* There is no guarantee that smaller allocations will succeed, but
* larger requests will be summarily denied.
*
* palloc() enforces MaxAllocSize, chosen to correspond to the limiting size
* of varlena objects under TOAST. See VARSIZE_4B() and related macros in
* varatt.h. Many datatypes assume that any allocatable size can be
* represented in a varlena header. This limit also permits a caller to use
* an "int" variable for an index into or length of an allocation. Callers
* careful to avoid these hazards can access the higher limit with
* MemoryContextAllocHuge(). Both limits permit code to assume that it may
* compute twice an allocation's size without overflow.
*/
#define MaxAllocSize ((Size) 0x3fffffff) /* 1 gigabyte - 1 */
#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)
/*
* 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;
/* This is a transient link to the active portal's memory context: */
extern PGDLLIMPORT MemoryContext PortalContext;
/*
* Memory-context-type-independent functions in mcxt.c
*/
extern void MemoryContextInit(void);
extern void MemoryContextReset(MemoryContext context);
extern void MemoryContextDelete(MemoryContext context);
extern void MemoryContextResetOnly(MemoryContext context);
extern void MemoryContextResetChildren(MemoryContext context);
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);
extern Size MemoryContextMemAllocated(MemoryContext context, bool recurse);
extern void MemoryContextMemConsumed(MemoryContext context,
MemoryContextCounters *consumed);
extern void MemoryContextStats(MemoryContext context);
extern void MemoryContextStatsDetail(MemoryContext context,
int max_level, int max_children,
bool print_to_stderr);
extern void MemoryContextAllowInCriticalSection(MemoryContext context,
bool allow);
#ifdef MEMORY_CONTEXT_CHECKING
extern void MemoryContextCheck(MemoryContext context);
#endif
/* Handy macro for copying and assigning context ID ... but note double eval */
#define MemoryContextCopyAndSetIdentifier(cxt, id) \
MemoryContextSetIdentifier(cxt, MemoryContextStrdup(cxt, id))
extern void HandleLogMemoryContextInterrupt(void);
extern void ProcessLogMemoryContextInterrupt(void);
/*
* Memory-context-type-specific functions
*/
/* aset.c */
extern MemoryContext AllocSetContextCreateInternal(MemoryContext parent,
const char *name,
Size minContextSize,
Size initBlockSize,
Size maxBlockSize);
/*
* This wrapper macro exists to check for non-constant strings used as context
* names; that's no longer supported. (Use MemoryContextSetIdentifier if you
* want to provide a variable identifier.)
*/
#ifdef HAVE__BUILTIN_CONSTANT_P
#define AllocSetContextCreate(parent, name, ...) \
(StaticAssertExpr(__builtin_constant_p(name), \
"memory context names must be constant strings"), \
AllocSetContextCreateInternal(parent, name, __VA_ARGS__))
#else
#define AllocSetContextCreate \
AllocSetContextCreateInternal
#endif
/* slab.c */
extern MemoryContext SlabContextCreate(MemoryContext parent,
const char *name,
Size blockSize,
Size chunkSize);
/* generation.c */
extern MemoryContext GenerationContextCreate(MemoryContext parent,
const char *name,
Size minContextSize,
Size initBlockSize,
Size maxBlockSize);
/* bump.c */
extern MemoryContext BumpContextCreate(MemoryContext parent,
const char *name,
Size minContextSize,
Size initBlockSize,
Size maxBlockSize);
/*
* 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)
#define ALLOCSET_DEFAULT_SIZES \
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE
/*
* Recommended alloc parameters for "small" contexts that are never 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)
#define ALLOCSET_SMALL_SIZES \
ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE
/*
* Recommended alloc parameters for contexts that should start out small,
* but might sometimes grow big.
*/
#define ALLOCSET_START_SMALL_SIZES \
ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE
/*
* Threshold above which a request in an AllocSet context is certain to be
* allocated separately (and thereby have constant allocation overhead).
* Few callers should be interested in this, but tuplesort/tuplestore need
* to know it.
*/
#define ALLOCSET_SEPARATE_THRESHOLD 8192
#define SLAB_DEFAULT_BLOCK_SIZE (8 * 1024)
#define SLAB_LARGE_BLOCK_SIZE (8 * 1024 * 1024)
/*
* pg_memory_is_all_zeros
*
* Test if a memory region starting at "ptr" and of size "len" is full of
* zeroes.
*
* The test is divided into multiple cases for safety reason and multiple
* phases for efficiency.
*
* Case 1: len < sizeof(size_t) bytes, then byte-by-byte comparison.
* Case 2: len < (sizeof(size_t) * 8 - 1) bytes:
* - Phase 1: byte-by-byte comparison, until the pointer is aligned.
* - Phase 2: size_t comparisons, with aligned pointers, up to the last
* location possible.
* - Phase 3: byte-by-byte comparison, until the end location.
* Case 3: len >= (sizeof(size_t) * 8) bytes, same as case 2 except that an
* additional phase is placed between Phase 1 and Phase 2, with
* (8 * sizeof(size_t)) comparisons using bitwise OR to encourage
* compilers to use SIMD instructions if available, up to the last
* aligned location possible.
*
* Case 1 and Case 2 are mandatory to ensure that we won't read beyond the
* memory area. This is portable for 32-bit and 64-bit architectures.
*
* Caller must ensure that "ptr" is not NULL.
*/
static inline bool
pg_memory_is_all_zeros(const void *ptr, size_t len)
{
const unsigned char *p = (const unsigned char *) ptr;
const unsigned char *end = &p[len];
const unsigned char *aligned_end = (const unsigned char *)
((uintptr_t) end & (~(sizeof(size_t) - 1)));
if (len < sizeof(size_t))
{
while (p < end)
{
if (*p++ != 0)
return false;
}
return true;
}
/* "len" in the [sizeof(size_t), sizeof(size_t) * 8 - 1] range */
if (len < sizeof(size_t) * 8)
{
/* Compare bytes until the pointer "p" is aligned */
while (((uintptr_t) p & (sizeof(size_t) - 1)) != 0)
{
if (p == end)
return true;
if (*p++ != 0)
return false;
}
/*
* Compare remaining size_t-aligned chunks.
*
* There is no risk to read beyond the memory area, as "aligned_end"
* cannot be higher than "end".
*/
for (; p < aligned_end; p += sizeof(size_t))
{
if (*(size_t *) p != 0)
return false;
}
/* Compare remaining bytes until the end */
while (p < end)
{
if (*p++ != 0)
return false;
}
return true;
}
/* "len" in the [sizeof(size_t) * 8, inf) range */
/* Compare bytes until the pointer "p" is aligned */
while (((uintptr_t) p & (sizeof(size_t) - 1)) != 0)
{
if (p == end)
return true;
if (*p++ != 0)
return false;
}
/*
* Compare 8 * sizeof(size_t) chunks at once.
*
* For performance reasons, we manually unroll this loop and purposefully
* use bitwise-ORs to combine each comparison. This prevents boolean
* short-circuiting and lets the compiler know that it's safe to access
* all 8 elements regardless of the result of the other comparisons. This
* seems to be enough to coax a few compilers into using SIMD
* instructions.
*/
for (; p < aligned_end - (sizeof(size_t) * 7); p += sizeof(size_t) * 8)
{
if ((((size_t *) p)[0] != 0) | (((size_t *) p)[1] != 0) |
(((size_t *) p)[2] != 0) | (((size_t *) p)[3] != 0) |
(((size_t *) p)[4] != 0) | (((size_t *) p)[5] != 0) |
(((size_t *) p)[6] != 0) | (((size_t *) p)[7] != 0))
return false;
}
/*
* Compare remaining size_t-aligned chunks.
*
* There is no risk to read beyond the memory area, as "aligned_end"
* cannot be higher than "end".
*/
for (; p < aligned_end; p += sizeof(size_t))
{
if (*(size_t *) p != 0)
return false;
}
/* Compare remaining bytes until the end */
while (p < end)
{
if (*p++ != 0)
return false;
}
return true;
}
#endif /* MEMUTILS_H */