Age Owner Branch data TLA Line data Source code
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 : :
90 : : #include "utils/memutils_internal.h"
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 */
132 : : } MemoryChunk;
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
594 drowley@postgresql.o 169 :CBC 303912068 : MemoryChunkSetHdrMask(MemoryChunk *chunk, void *block,
170 : : Size value, MemoryContextMethodID methodid)
171 : : {
172 : 303912068 : Size blockoffset = (char *) chunk - (char *) block;
173 : :
479 174 [ - + ]: 303912068 : Assert((char *) chunk >= (char *) block);
7 drowley@postgresql.o 175 [ - + ]:GNC 303912068 : Assert((blockoffset & MEMORYCHUNK_BLOCKOFFSET_MASK) == blockoffset);
594 drowley@postgresql.o 176 [ - + ]:CBC 303912068 : Assert(value <= MEMORYCHUNK_MAX_VALUE);
592 177 [ - + ]: 303912068 : Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK);
178 : :
594 179 : 303912068 : chunk->hdrmask = (((uint64) blockoffset) << MEMORYCHUNK_BLOCKOFFSET_BASEBIT) |
180 : 303912068 : (((uint64) value) << MEMORYCHUNK_VALUE_BASEBIT) |
181 : : methodid;
182 : 303912068 : }
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
190 : 7666736 : MemoryChunkSetHdrMaskExternal(MemoryChunk *chunk,
191 : : MemoryContextMethodID methodid)
192 : : {
592 193 [ - + ]: 7666736 : Assert((int) methodid <= MEMORY_CONTEXT_METHODID_MASK);
194 : :
594 195 : 7666736 : chunk->hdrmask = MEMORYCHUNK_MAGIC | (((uint64) 1) << MEMORYCHUNK_EXTERNAL_BASEBIT) |
196 : : methodid;
197 : 7666736 : }
198 : :
199 : : /*
200 : : * MemoryChunkIsExternal
201 : : * Return true if 'chunk' is marked as external.
202 : : */
203 : : static inline bool
204 : 2457456736 : MemoryChunkIsExternal(MemoryChunk *chunk)
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 [ + + - + ]: 2457456736 : Assert(!HdrMaskIsExternal(chunk->hdrmask) ||
211 : : HdrMaskCheckMagic(chunk->hdrmask));
212 : :
213 : 2457456736 : 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
222 : 2609978109 : MemoryChunkGetValue(MemoryChunk *chunk)
223 : : {
224 [ - + ]: 2609978109 : Assert(!HdrMaskIsExternal(chunk->hdrmask));
225 : :
226 : 2609978109 : 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 *
235 : 2426665103 : MemoryChunkGetBlock(MemoryChunk *chunk)
236 : : {
237 [ - + ]: 2426665103 : Assert(!HdrMaskIsExternal(chunk->hdrmask));
238 : :
239 : 2426665103 : 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 */
|