1 /*-------------------------------------------------------------------------
4 * create shared memory and initialize shared memory data structures.
6 * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/storage/ipc/shmem.c,v 1.80 2004/08/29 05:06:48 momjian Exp $
13 *-------------------------------------------------------------------------
16 * POSTGRES processes share one or more regions of shared memory.
17 * The shared memory is created by a postmaster and is inherited
18 * by each backend via fork(). The routines in this file are used for
19 * allocating and binding to shared memory data structures.
22 * (a) There are three kinds of shared memory data structures
23 * available to POSTGRES: fixed-size structures, queues and hash
24 * tables. Fixed-size structures contain things like global variables
25 * for a module and should never be allocated after the process
26 * initialization phase. Hash tables have a fixed maximum size, but
27 * their actual size can vary dynamically. When entries are added
28 * to the table, more space is allocated. Queues link data structures
29 * that have been allocated either as fixed size structures or as hash
30 * buckets. Each shared data structure has a string name to identify
31 * it (assigned in the module that declares it).
33 * (b) During initialization, each module looks for its
34 * shared data structures in a hash table called the "Shmem Index".
35 * If the data structure is not present, the caller can allocate
36 * a new one and initialize it. If the data structure is present,
37 * the caller "attaches" to the structure by initializing a pointer
38 * in the local address space.
39 * The shmem index has two purposes: first, it gives us
40 * a simple model of how the world looks when a backend process
41 * initializes. If something is present in the shmem index,
42 * it is initialized. If it is not, it is uninitialized. Second,
43 * the shmem index allows us to allocate shared memory on demand
44 * instead of trying to preallocate structures and hard-wire the
45 * sizes and locations in header files. If you are using a lot
46 * of shared memory in a lot of different places (and changing
47 * things during development), this is important.
49 * (c) memory allocation model: shared memory can never be
50 * freed, once allocated. Each hash table has its own free list,
51 * so hash buckets can be reused when an item is deleted. However,
52 * if one hash table grows very large and then shrinks, its space
53 * cannot be redistributed to other tables. We could build a simple
54 * hash bucket garbage collector if need be. Right now, it seems
57 * See InitSem() in sem.c for an example of how to use the
63 #include "access/transam.h"
64 #include "storage/pg_shmem.h"
65 #include "storage/spin.h"
66 #include "utils/tqual.h"
69 /* shared memory global variables */
71 static PGShmemHeader *ShmemSegHdr; /* shared mem segment header */
73 SHMEM_OFFSET ShmemBase; /* start address of shared memory */
75 static SHMEM_OFFSET ShmemEnd; /* end+1 address of shared memory */
77 NON_EXEC_STATIC slock_t *ShmemLock; /* spinlock for shared memory
80 NON_EXEC_STATIC slock_t *ShmemIndexLock; /* spinlock for ShmemIndex */
82 NON_EXEC_STATIC void *ShmemIndexAlloc = NULL; /* Memory actually
86 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
88 static bool ShmemBootstrap = false; /* bootstrapping shmem index? */
92 * InitShmemAllocation() --- set up shared-memory allocation.
94 * Note: the argument should be declared "PGShmemHeader *seghdr",
95 * but we use void to avoid having to include ipc.h in shmem.h.
98 InitShmemAllocation(void *seghdr, bool init)
100 PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
102 /* Set up basic pointers to shared memory */
103 ShmemSegHdr = shmhdr;
104 ShmemBase = (SHMEM_OFFSET) shmhdr;
105 ShmemEnd = ShmemBase + shmhdr->totalsize;
110 * Initialize the spinlocks used by ShmemAlloc/ShmemInitStruct. We
111 * have to do the space allocation the hard way, since ShmemAlloc
112 * can't be called yet.
114 ShmemLock = (slock_t *) (((char *) shmhdr) + shmhdr->freeoffset);
115 shmhdr->freeoffset += MAXALIGN(sizeof(slock_t));
116 Assert(shmhdr->freeoffset <= shmhdr->totalsize);
118 ShmemIndexLock = (slock_t *) (((char *) shmhdr) + shmhdr->freeoffset);
119 shmhdr->freeoffset += MAXALIGN(sizeof(slock_t));
120 Assert(shmhdr->freeoffset <= shmhdr->totalsize);
122 SpinLockInit(ShmemLock);
123 SpinLockInit(ShmemIndexLock);
125 /* ShmemIndex can't be set up yet (need LWLocks first) */
126 ShmemIndex = (HTAB *) NULL;
129 * Initialize ShmemVariableCache for transaction manager.
131 ShmemVariableCache = (VariableCache)
132 ShmemAlloc(sizeof(*ShmemVariableCache));
133 memset(ShmemVariableCache, 0, sizeof(*ShmemVariableCache));
138 * ShmemAlloc -- allocate max-aligned chunk from shared memory
140 * Assumes ShmemLock and ShmemSegHdr are initialized.
142 * Returns: real pointer to memory or NULL if we are out
143 * of space. Has to return a real pointer in order
144 * to be compatible with malloc().
147 ShmemAlloc(Size size)
153 /* use volatile pointer to prevent code rearrangement */
154 volatile PGShmemHeader *shmemseghdr = ShmemSegHdr;
157 * ensure all space is adequately aligned.
159 size = MAXALIGN(size);
161 Assert(shmemseghdr != NULL);
163 SpinLockAcquire(ShmemLock);
165 newStart = shmemseghdr->freeoffset;
167 /* extra alignment for large requests, since they are probably buffers */
169 newStart = BUFFERALIGN(newStart);
171 newFree = newStart + size;
172 if (newFree <= shmemseghdr->totalsize)
174 newSpace = (void *) MAKE_PTR(newStart);
175 shmemseghdr->freeoffset = newFree;
180 SpinLockRelease(ShmemLock);
184 (errcode(ERRCODE_OUT_OF_MEMORY),
185 errmsg("out of shared memory")));
191 * ShmemIsValid -- test if an offset refers to valid shared memory
193 * Returns TRUE if the pointer is valid.
196 ShmemIsValid(unsigned long addr)
198 return (addr < ShmemEnd) && (addr >= ShmemBase);
202 * InitShmemIndex() --- set up shmem index table.
209 ShmemIndexEnt *result,
214 * Since ShmemInitHash calls ShmemInitStruct, which expects the
215 * ShmemIndex hashtable to exist already, we have a bit of a
216 * circularity problem in initializing the ShmemIndex itself. We set
217 * ShmemBootstrap to tell ShmemInitStruct to fake it.
219 ShmemBootstrap = true;
221 /* create the shared memory shmem index */
222 info.keysize = SHMEM_INDEX_KEYSIZE;
223 info.entrysize = sizeof(ShmemIndexEnt);
224 hash_flags = HASH_ELEM;
226 /* This will acquire the shmem index lock, but not release it. */
227 ShmemIndex = ShmemInitHash("ShmemIndex",
228 SHMEM_INDEX_SIZE, SHMEM_INDEX_SIZE,
231 elog(FATAL, "could not initialize Shmem Index");
234 * Now, create an entry in the hashtable for the index itself.
236 if (!IsUnderPostmaster)
238 MemSet(item.key, 0, SHMEM_INDEX_KEYSIZE);
239 strncpy(item.key, "ShmemIndex", SHMEM_INDEX_KEYSIZE);
241 result = (ShmemIndexEnt *)
242 hash_search(ShmemIndex, (void *) &item, HASH_ENTER, &found);
245 (errcode(ERRCODE_OUT_OF_MEMORY),
246 errmsg("out of shared memory")));
248 Assert(ShmemBootstrap && !found);
250 result->location = MAKE_OFFSET(ShmemIndex->hctl);
251 result->size = SHMEM_INDEX_SIZE;
253 ShmemBootstrap = false;
256 /* now release the lock acquired in ShmemInitStruct */
257 SpinLockRelease(ShmemIndexLock);
261 * ShmemInitHash -- Create/Attach to and initialize
262 * shared memory hash table.
264 * We assume caller is doing some kind of synchronization
265 * so that two people dont try to create/initialize the
269 ShmemInitHash(const char *name, /* table string name for shmem index */
270 long init_size, /* initial table size */
271 long max_size, /* max size of the table */
272 HASHCTL *infoP, /* info about key and bucket size */
273 int hash_flags) /* info about infoP */
279 * Hash tables allocated in shared memory have a fixed directory; it
280 * can't grow or other backends wouldn't be able to find it. So, make
281 * sure we make it big enough to start with.
283 * The shared memory allocator must be specified too.
285 infoP->dsize = infoP->max_dsize = hash_select_dirsize(max_size);
286 infoP->alloc = ShmemAlloc;
287 hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
289 /* look it up in the shmem index */
290 location = ShmemInitStruct(name,
291 sizeof(HASHHDR) + infoP->dsize * sizeof(HASHSEGMENT),
295 * shmem index is corrupted. Let someone else give the error
296 * message since they have more information
298 if (location == NULL)
302 * if it already exists, attach to it rather than allocate and
303 * initialize new space
306 hash_flags |= HASH_ATTACH;
308 /* Now provide the header and directory pointers */
309 infoP->hctl = (HASHHDR *) location;
310 infoP->dir = (HASHSEGMENT *) (((char *) location) + sizeof(HASHHDR));
312 return hash_create(name, init_size, infoP, hash_flags);
316 * ShmemInitStruct -- Create/attach to a structure in shared
319 * This is called during initialization to find or allocate
320 * a data structure in shared memory. If no other processes
321 * have created the structure, this routine allocates space
322 * for it. If it exists already, a pointer to the existing
325 * Returns: real pointer to the object. FoundPtr is TRUE if
326 * the object is already in the shmem index (hence, already
330 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
332 ShmemIndexEnt *result,
336 strncpy(item.key, name, SHMEM_INDEX_KEYSIZE);
337 item.location = BAD_LOCATION;
339 SpinLockAcquire(ShmemIndexLock);
343 if (IsUnderPostmaster)
345 /* Must be initializing a (non-standalone) backend */
346 Assert(strcmp(name, "ShmemIndex") == 0);
347 Assert(ShmemBootstrap);
348 Assert(ShmemIndexAlloc);
354 * If the shmem index doesn't exist, we are bootstrapping: we
355 * must be trying to init the shmem index itself.
357 * Notice that the ShmemIndexLock is held until the shmem index
358 * has been completely initialized.
360 Assert(strcmp(name, "ShmemIndex") == 0);
361 Assert(ShmemBootstrap);
363 ShmemIndexAlloc = ShmemAlloc(size);
365 return ShmemIndexAlloc;
368 /* look it up in the shmem index */
369 result = (ShmemIndexEnt *)
370 hash_search(ShmemIndex, (void *) &item, HASH_ENTER, foundPtr);
374 SpinLockRelease(ShmemIndexLock);
376 (errcode(ERRCODE_OUT_OF_MEMORY),
377 errmsg("out of shared memory")));
384 * Structure is in the shmem index so someone else has allocated
385 * it already. The size better be the same as the size we are
386 * trying to initialize to or there is a name conflict (or worse).
388 if (result->size != size)
390 SpinLockRelease(ShmemIndexLock);
392 elog(WARNING, "ShmemIndex entry size is wrong");
393 /* let caller print its message too */
396 structPtr = (void *) MAKE_PTR(result->location);
400 /* It isn't in the table yet. allocate and initialize it */
401 structPtr = ShmemAlloc(size);
406 hash_search(ShmemIndex, (void *) &item, HASH_REMOVE, NULL);
407 SpinLockRelease(ShmemIndexLock);
410 (errcode(ERRCODE_OUT_OF_MEMORY),
411 errmsg("could not allocate shared memory segment \"%s\"", name)));
416 result->location = MAKE_OFFSET(structPtr);
418 Assert(ShmemIsValid((unsigned long) structPtr));
420 SpinLockRelease(ShmemIndexLock);