PostgreSQL Source Code git master
Loading...
Searching...
No Matches
shm_toc.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * shm_toc.c
4 * shared memory segment table of contents
5 *
6 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 * src/backend/storage/ipc/shm_toc.c
10 *
11 *-------------------------------------------------------------------------
12 */
13
14#include "postgres.h"
15
16#include "port/atomics.h"
17#include "storage/shm_toc.h"
18#include "storage/spin.h"
19
20typedef struct shm_toc_entry
21{
22 uint64 key; /* Arbitrary identifier */
23 Size offset; /* Offset, in bytes, from TOC start */
25
26struct shm_toc
27{
28 uint64 toc_magic; /* Magic number identifying this TOC */
29 slock_t toc_mutex; /* Spinlock for mutual exclusion */
30 Size toc_total_bytes; /* Bytes managed by this TOC */
31 Size toc_allocated_bytes; /* Bytes allocated of those managed */
32 uint32 toc_nentry; /* Number of entries in TOC */
34};
35
36/*
37 * Initialize a region of shared memory with a table of contents.
38 */
39shm_toc *
40shm_toc_create(uint64 magic, void *address, Size nbytes)
41{
42 shm_toc *toc = (shm_toc *) address;
43
44 Assert(nbytes > offsetof(shm_toc, toc_entry));
45 toc->toc_magic = magic;
47
48 /*
49 * The alignment code in shm_toc_allocate() assumes that the starting
50 * value is buffer-aligned.
51 */
52 toc->toc_total_bytes = BUFFERALIGN_DOWN(nbytes);
53 toc->toc_allocated_bytes = 0;
54 toc->toc_nentry = 0;
55
56 return toc;
57}
58
59/*
60 * Attach to an existing table of contents. If the magic number found at
61 * the target address doesn't match our expectations, return NULL.
62 */
63shm_toc *
64shm_toc_attach(uint64 magic, void *address)
65{
66 shm_toc *toc = (shm_toc *) address;
67
68 if (toc->toc_magic != magic)
69 return NULL;
70
72 Assert(toc->toc_total_bytes > offsetof(shm_toc, toc_entry));
73
74 return toc;
75}
76
77/*
78 * Allocate shared memory from a segment managed by a table of contents.
79 *
80 * This is not a full-blown allocator; there's no way to free memory. It's
81 * just a way of dividing a single physical shared memory segment into logical
82 * chunks that may be used for different purposes.
83 *
84 * We allocate backwards from the end of the segment, so that the TOC entries
85 * can grow forward from the start of the segment.
86 */
87void *
89{
90 Size total_bytes;
92 Size nentry;
94
95 /*
96 * Make sure request is well-aligned. XXX: MAXALIGN is not enough,
97 * because atomic ops might need a wider alignment. We don't have a
98 * proper definition for the minimum to make atomic ops safe, but
99 * BUFFERALIGN ought to be enough.
100 */
101 nbytes = BUFFERALIGN(nbytes);
102
104
105 total_bytes = toc->toc_total_bytes;
107 nentry = toc->toc_nentry;
108 toc_bytes = offsetof(shm_toc, toc_entry) + nentry * sizeof(shm_toc_entry)
110
111 /* Check for memory exhaustion and overflow. */
112 if (toc_bytes + nbytes > total_bytes || toc_bytes + nbytes < toc_bytes)
113 {
117 errmsg("out of shared memory")));
118 }
119 toc->toc_allocated_bytes += nbytes;
120
122
123 return ((char *) toc) + (total_bytes - allocated_bytes - nbytes);
124}
125
126/*
127 * Return the number of bytes that can still be allocated.
128 */
129Size
131{
132 Size total_bytes;
134 Size nentry;
136
138 total_bytes = toc->toc_total_bytes;
140 nentry = toc->toc_nentry;
142
143 toc_bytes = offsetof(shm_toc, toc_entry) + nentry * sizeof(shm_toc_entry);
144 Assert(allocated_bytes + BUFFERALIGN(toc_bytes) <= total_bytes);
145 return total_bytes - (allocated_bytes + BUFFERALIGN(toc_bytes));
146}
147
148/*
149 * Insert a TOC entry.
150 *
151 * The idea here is that the process setting up the shared memory segment will
152 * register the addresses of data structures within the segment using this
153 * function. Each data structure will be identified using a 64-bit key, which
154 * is assumed to be a well-known or discoverable integer. Other processes
155 * accessing the shared memory segment can pass the same key to
156 * shm_toc_lookup() to discover the addresses of those data structures.
157 *
158 * Since the shared memory segment may be mapped at different addresses within
159 * different backends, we store relative rather than absolute pointers.
160 *
161 * This won't scale well to a large number of keys. Hopefully, that isn't
162 * necessary; if it proves to be, we might need to provide a more sophisticated
163 * data structure here. But the real idea here is just to give someone mapping
164 * a dynamic shared memory the ability to find the bare minimum number of
165 * pointers that they need to bootstrap. If you're storing a lot of stuff in
166 * the TOC, you're doing it wrong.
167 */
168void
169shm_toc_insert(shm_toc *toc, uint64 key, void *address)
170{
171 Size total_bytes;
173 Size nentry;
175 Size offset;
176
177 /* Relativize pointer. */
178 Assert(address > (void *) toc);
179 offset = ((char *) address) - (char *) toc;
180
182
183 total_bytes = toc->toc_total_bytes;
185 nentry = toc->toc_nentry;
186
187#ifdef USE_ASSERT_CHECKING
188 /* Verify no duplicate keys */
189 for (Size i = 0; i < nentry; i++)
190 Assert(toc->toc_entry[i].key != key);
191#endif
192
193 toc_bytes = offsetof(shm_toc, toc_entry) + nentry * sizeof(shm_toc_entry)
195
196 /* Check for memory exhaustion and overflow. */
197 if (toc_bytes + sizeof(shm_toc_entry) > total_bytes ||
198 toc_bytes + sizeof(shm_toc_entry) < toc_bytes ||
199 nentry >= PG_UINT32_MAX)
200 {
204 errmsg("out of shared memory")));
205 }
206
207 Assert(offset < total_bytes);
208 toc->toc_entry[nentry].key = key;
209 toc->toc_entry[nentry].offset = offset;
210
211 /*
212 * By placing a write barrier after filling in the entry and before
213 * updating the number of entries, we make it safe to read the TOC
214 * unlocked.
215 */
217
218 toc->toc_nentry++;
219
221}
222
223/*
224 * Look up a TOC entry.
225 *
226 * If the key is not found, returns NULL if noError is true, otherwise
227 * throws elog(ERROR).
228 *
229 * Unlike the other functions in this file, this operation acquires no lock;
230 * it uses only barriers. It probably wouldn't hurt concurrency very much even
231 * if it did get a lock, but since it's reasonably likely that a group of
232 * worker processes could each read a series of entries from the same TOC
233 * right around the same time, there seems to be some value in avoiding it.
234 */
235void *
237{
238 uint32 nentry;
239 uint32 i;
240
241 /*
242 * Read the number of entries before we examine any entry. We assume that
243 * reading a uint32 is atomic.
244 */
245 nentry = toc->toc_nentry;
247
248 /* Now search for a matching entry. */
249 for (i = 0; i < nentry; ++i)
250 {
251 if (toc->toc_entry[i].key == key)
252 return ((char *) toc) + toc->toc_entry[i].offset;
253 }
254
255 /* No matching entry was found. */
256 if (!noError)
257 elog(ERROR, "could not find key " UINT64_FORMAT " in shm TOC at %p",
258 key, toc);
259 return NULL;
260}
261
262/*
263 * Estimate how much shared memory will be required to store a TOC and its
264 * dependent data structures.
265 */
266Size
268{
269 Size sz;
270
271 sz = offsetof(shm_toc, toc_entry);
272 sz = add_size(sz, mul_size(e->number_of_keys, sizeof(shm_toc_entry)));
273 sz = add_size(sz, e->space_for_chunks);
274
275 return BUFFERALIGN(sz);
276}
#define pg_read_barrier()
Definition atomics.h:154
#define pg_write_barrier()
Definition atomics.h:155
#define PG_UINT32_MAX
Definition c.h:733
#define BUFFERALIGN_DOWN(LEN)
Definition c.h:968
#define BUFFERALIGN(LEN)
Definition c.h:957
#define Assert(condition)
Definition c.h:1002
#define FLEXIBLE_ARRAY_MEMBER
Definition c.h:617
#define UINT64_FORMAT
Definition c.h:694
uint64_t uint64
Definition c.h:684
uint32_t uint32
Definition c.h:683
size_t Size
Definition c.h:748
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
int i
Definition isn.c:77
Size add_size(Size s1, Size s2)
Definition mcxt.c:1733
Size mul_size(Size s1, Size s2)
Definition mcxt.c:1752
static char * errmsg
e
static int fb(int x)
void * shm_toc_allocate(shm_toc *toc, Size nbytes)
Definition shm_toc.c:88
Size shm_toc_estimate(shm_toc_estimator *e)
Definition shm_toc.c:267
shm_toc * shm_toc_create(uint64 magic, void *address, Size nbytes)
Definition shm_toc.c:40
Size shm_toc_freespace(shm_toc *toc)
Definition shm_toc.c:130
void shm_toc_insert(shm_toc *toc, uint64 key, void *address)
Definition shm_toc.c:169
void * shm_toc_lookup(shm_toc *toc, uint64 key, bool noError)
Definition shm_toc.c:236
shm_toc * shm_toc_attach(uint64 magic, void *address)
Definition shm_toc.c:64
static void SpinLockRelease(volatile slock_t *lock)
Definition spin.h:62
static void SpinLockAcquire(volatile slock_t *lock)
Definition spin.h:56
static void SpinLockInit(volatile slock_t *lock)
Definition spin.h:50
Definition shm_toc.c:21
Size offset
Definition shm_toc.c:23
uint64 key
Definition shm_toc.c:22
Size toc_total_bytes
Definition shm_toc.c:30
uint32 toc_nentry
Definition shm_toc.c:32
shm_toc_entry toc_entry[FLEXIBLE_ARRAY_MEMBER]
Definition shm_toc.c:33
slock_t toc_mutex
Definition shm_toc.c:29
uint64 toc_magic
Definition shm_toc.c:28
Size toc_allocated_bytes
Definition shm_toc.c:31