mirror of
https://github.com/postgres/postgres.git
synced 2026-02-18 01:59:53 -05:00
Robert Haas reported that his older clang compiler didn't like the two
Asserts which were verifying that the given MemoryContextMethodID was <=
MEMORY_CONTEXT_METHODID_MASK when building with
-Wtautological-constant-out-of-range-compare. In my (David's) opinion,
the compiler is wrong to warn about that. Newer versions of clang don't
warn about the out of range enum value, so perhaps this was a bug that has
now been fixed. To keep older clang versions happy, let's just cast the
enum value to int to stop the compiler complaining.
The main reason for the Asserts mentioned above to exist are to inform
future developers which are adding new MemoryContexts if they run out of
bit space in MemoryChunk to store the MemoryContextMethodID. As pointed
out by Tom Lane, it seems wise to also add a comment to the header for
that enum to document the restriction on these enum values.
Additionally, also fix an incorrect usage of UINT64CONST() which was
introduced in c6e0fe1f2.
Author: Robert Haas, David Rowley
Discussion: https://postgr.es/m/CA+TgmoYGG2C7Vbw1cjkQRRBL3zOk8SmhrQnsJgzscX=N9AwPrw@mail.gmail.com
237 lines
8.1 KiB
C
237 lines
8.1 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* 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((int) 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((int) 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 */
|