PostgreSQL Source Code  git master
memutils_memorychunk.h
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * memutils_memorychunk.h
4  * Here we define a struct named MemoryChunk which implementations of
5  * MemoryContexts may use as a header for chunks of memory they allocate.
6  *
7  * MemoryChunk provides a lightweight header that a MemoryContext can use to
8  * store a reference back to the block which the given chunk is allocated on
9  * and also an additional 30-bits to store another value such as the size of
10  * the allocated chunk.
11  *
12  * Although MemoryChunks are used by each of our MemoryContexts, future
13  * implementations may choose to implement their own method for storing chunk
14  * headers. The only requirement is that the header ends with an 8-byte value
15  * which the least significant 4-bits of are set to the MemoryContextMethodID
16  * of the given context.
17  *
18  * By default, a MemoryChunk is 8 bytes in size, however, when
19  * MEMORY_CONTEXT_CHECKING is defined the header becomes 16 bytes in size due
20  * to the additional requested_size field. The MemoryContext may use this
21  * field for whatever they wish, but it is intended to be used for additional
22  * checks which are only done in MEMORY_CONTEXT_CHECKING builds.
23  *
24  * The MemoryChunk contains a uint64 field named 'hdrmask'. This field is
25  * used to encode 4 separate pieces of information. Starting with the least
26  * significant bits of 'hdrmask', the bit space is reserved as follows:
27  *
28  * 1. 4-bits to indicate the MemoryContextMethodID as defined by
29  * MEMORY_CONTEXT_METHODID_MASK
30  * 2. 1-bit to denote an "external" chunk (see below)
31  * 3. 30-bits reserved for the MemoryContext to use for anything it
32  * requires. Most MemoryContexts likely want to store the size of the
33  * chunk here.
34  * 4. 30-bits for the number of bytes that must be subtracted from the chunk
35  * to obtain the address of the block that the chunk is stored on.
36  *
37  * If you're paying close attention, you'll notice this adds up to 65 bits
38  * rather than 64 bits. This is because the highest-order bit of #3 is the
39  * same bit as the lowest-order bit of #4. We can do this as we insist that
40  * the chunk and block pointers are both MAXALIGNed, therefore the relative
41  * offset between those will always be a MAXALIGNed value which means the
42  * lowest order bit is always 0. When fetching the chunk to block offset we
43  * mask out the lowest-order bit to ensure it's still zero.
44  *
45  * In some cases, for example when memory allocations become large, it's
46  * possible fields 3 and 4 above are not large enough to store the values
47  * required for the chunk. In this case, the MemoryContext can choose to mark
48  * the chunk as "external" by calling the MemoryChunkSetHdrMaskExternal()
49  * function. When this is done, fields 3 and 4 are unavailable for use by the
50  * MemoryContext and it's up to the MemoryContext itself to devise its own
51  * method for getting the reference to the block.
52  *
53  * Interface:
54  *
55  * MemoryChunkSetHdrMask:
56  * Used to set up a non-external MemoryChunk.
57  *
58  * MemoryChunkSetHdrMaskExternal:
59  * Used to set up an externally managed MemoryChunk.
60  *
61  * MemoryChunkIsExternal:
62  * Determine if the given MemoryChunk is externally managed, i.e.
63  * MemoryChunkSetHdrMaskExternal() was called on the chunk.
64  *
65  * MemoryChunkGetValue:
66  * For non-external chunks, return the stored 30-bit value as it was set
67  * in the call to MemoryChunkSetHdrMask().
68  *
69  * MemoryChunkGetBlock:
70  * For non-external chunks, return a pointer to the block as it was set
71  * in the call to MemoryChunkSetHdrMask().
72  *
73  * Also exports:
74  * MEMORYCHUNK_MAX_VALUE
75  * MEMORYCHUNK_MAX_BLOCKOFFSET
76  * PointerGetMemoryChunk
77  * MemoryChunkGetPointer
78  *
79  * Portions Copyright (c) 2022-2024, PostgreSQL Global Development Group
80  * Portions Copyright (c) 1994, Regents of the University of California
81  *
82  * src/include/utils/memutils_memorychunk.h
83  *
84  *-------------------------------------------------------------------------
85  */
86 
87 #ifndef MEMUTILS_MEMORYCHUNK_H
88 #define MEMUTILS_MEMORYCHUNK_H
89 
91 
92  /*
93  * The maximum allowed value that MemoryContexts can store in the value
94  * field. Must be 1 less than a power of 2.
95  */
96 #define MEMORYCHUNK_MAX_VALUE UINT64CONST(0x3FFFFFFF)
97 
98 /*
99  * The maximum distance in bytes that a MemoryChunk can be offset from the
100  * block that is storing the chunk. Must be 1 less than a power of 2.
101  */
102 #define MEMORYCHUNK_MAX_BLOCKOFFSET UINT64CONST(0x3FFFFFFF)
103 
104 /*
105  * As above, but mask out the lowest-order (always zero) bit as this is shared
106  * with the MemoryChunkGetValue field.
107  */
108 #define MEMORYCHUNK_BLOCKOFFSET_MASK UINT64CONST(0x3FFFFFFE)
109 
110 /* define the least significant base-0 bit of each portion of the hdrmask */
111 #define MEMORYCHUNK_EXTERNAL_BASEBIT MEMORY_CONTEXT_METHODID_BITS
112 #define MEMORYCHUNK_VALUE_BASEBIT (MEMORYCHUNK_EXTERNAL_BASEBIT + 1)
113 #define MEMORYCHUNK_BLOCKOFFSET_BASEBIT (MEMORYCHUNK_VALUE_BASEBIT + 29)
114 
115 /*
116  * A magic number for storing in the free bits of an external chunk. This
117  * must mask out the bits used for storing the MemoryContextMethodID and the
118  * external bit.
119  */
120 #define MEMORYCHUNK_MAGIC (UINT64CONST(0xB1A8DB858EB6EFBA) >> \
121  MEMORYCHUNK_VALUE_BASEBIT << \
122  MEMORYCHUNK_VALUE_BASEBIT)
123 
124 typedef struct MemoryChunk
125 {
126 #ifdef MEMORY_CONTEXT_CHECKING
127  Size requested_size;
128 #endif
129 
130  /* bitfield for storing details about the chunk */
131  uint64 hdrmask; /* must be last */
133 
134 /* Get the MemoryChunk from the pointer */
135 #define PointerGetMemoryChunk(p) \
136  ((MemoryChunk *) ((char *) (p) - sizeof(MemoryChunk)))
137 /* Get the pointer from the MemoryChunk */
138 #define MemoryChunkGetPointer(c) \
139  ((void *) ((char *) (c) + sizeof(MemoryChunk)))
140 
141 /* private macros for making the inline functions below more simple */
142 #define HdrMaskIsExternal(hdrmask) \
143  ((hdrmask) & (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT))
144 #define HdrMaskGetValue(hdrmask) \
145  (((hdrmask) >> MEMORYCHUNK_VALUE_BASEBIT) & MEMORYCHUNK_MAX_VALUE)
146 
147 /*
148  * Shift the block offset down to the 0th bit position and mask off the single
149  * bit that's shared with the MemoryChunkGetValue field.
150  */
151 #define HdrMaskBlockOffset(hdrmask) \
152  (((hdrmask) >> MEMORYCHUNK_BLOCKOFFSET_BASEBIT) & MEMORYCHUNK_BLOCKOFFSET_MASK)
153 
154 /* For external chunks only, check the magic number matches */
155 #define HdrMaskCheckMagic(hdrmask) \
156  (MEMORYCHUNK_MAGIC == \
157  ((hdrmask) >> MEMORYCHUNK_VALUE_BASEBIT << MEMORYCHUNK_VALUE_BASEBIT))
158 /*
159  * MemoryChunkSetHdrMask
160  * Store the given 'block', 'chunk_size' and 'methodid' in the given
161  * MemoryChunk.
162  *
163  * The number of bytes between 'block' and 'chunk' must be <=
164  * MEMORYCHUNK_MAX_BLOCKOFFSET.
165  * 'value' must be <= MEMORYCHUNK_MAX_VALUE.
166  * Both 'chunk' and 'block' must be MAXALIGNed pointers.
167  */
168 static inline void
170  Size value, MemoryContextMethodID methodid)
171 {
172  Size blockoffset = (char *) chunk - (char *) block;
173 
174  Assert((char *) chunk >= (char *) block);
175  Assert((blockoffset & MEMORYCHUNK_BLOCKOFFSET_MASK) == blockoffset);
177  Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK);
178 
179  chunk->hdrmask = (((uint64) blockoffset) << MEMORYCHUNK_BLOCKOFFSET_BASEBIT) |
180  (((uint64) value) << MEMORYCHUNK_VALUE_BASEBIT) |
181  methodid;
182 }
183 
184 /*
185  * MemoryChunkSetHdrMaskExternal
186  * Set 'chunk' as an externally managed chunk. Here we only record the
187  * MemoryContextMethodID and set the external chunk bit.
188  */
189 static inline void
191  MemoryContextMethodID methodid)
192 {
193  Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK);
194 
195  chunk->hdrmask = MEMORYCHUNK_MAGIC | (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT) |
196  methodid;
197 }
198 
199 /*
200  * MemoryChunkIsExternal
201  * Return true if 'chunk' is marked as external.
202  */
203 static inline bool
205 {
206  /*
207  * External chunks should always store MEMORYCHUNK_MAGIC in the upper
208  * portion of the hdrmask, check that nothing has stomped on that.
209  */
210  Assert(!HdrMaskIsExternal(chunk->hdrmask) ||
211  HdrMaskCheckMagic(chunk->hdrmask));
212 
213  return HdrMaskIsExternal(chunk->hdrmask);
214 }
215 
216 /*
217  * MemoryChunkGetValue
218  * For non-external chunks, returns the value field as it was set in
219  * MemoryChunkSetHdrMask.
220  */
221 static inline Size
223 {
224  Assert(!HdrMaskIsExternal(chunk->hdrmask));
225 
226  return HdrMaskGetValue(chunk->hdrmask);
227 }
228 
229 /*
230  * MemoryChunkGetBlock
231  * For non-external chunks, returns the pointer to the block as was set
232  * in MemoryChunkSetHdrMask.
233  */
234 static inline void *
236 {
237  Assert(!HdrMaskIsExternal(chunk->hdrmask));
238 
239  return (void *) ((char *) chunk - HdrMaskBlockOffset(chunk->hdrmask));
240 }
241 
242 /* cleanup all internal definitions */
243 #undef MEMORYCHUNK_BLOCKOFFSET_MASK
244 #undef MEMORYCHUNK_EXTERNAL_BASEBIT
245 #undef MEMORYCHUNK_VALUE_BASEBIT
246 #undef MEMORYCHUNK_BLOCKOFFSET_BASEBIT
247 #undef MEMORYCHUNK_MAGIC
248 #undef HdrMaskIsExternal
249 #undef HdrMaskGetValue
250 #undef HdrMaskBlockOffset
251 #undef HdrMaskCheckMagic
252 
253 #endif /* MEMUTILS_MEMORYCHUNK_H */
#define Assert(condition)
Definition: c.h:858
size_t Size
Definition: c.h:605
uint64 chunk
static struct @155 value
#define MEMORY_CONTEXT_METHODID_MASK
MemoryContextMethodID
#define MEMORYCHUNK_MAX_VALUE
static Size MemoryChunkGetValue(MemoryChunk *chunk)
#define MEMORYCHUNK_VALUE_BASEBIT
#define MEMORYCHUNK_BLOCKOFFSET_MASK
#define MEMORYCHUNK_EXTERNAL_BASEBIT
#define HdrMaskBlockOffset(hdrmask)
static bool MemoryChunkIsExternal(MemoryChunk *chunk)
static void MemoryChunkSetHdrMaskExternal(MemoryChunk *chunk, MemoryContextMethodID methodid)
#define HdrMaskCheckMagic(hdrmask)
#define HdrMaskIsExternal(hdrmask)
struct MemoryChunk MemoryChunk
#define MEMORYCHUNK_BLOCKOFFSET_BASEBIT
static void * MemoryChunkGetBlock(MemoryChunk *chunk)
static void MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block, Size value, MemoryContextMethodID methodid)
#define MEMORYCHUNK_MAGIC
#define HdrMaskGetValue(hdrmask)