1 /*-------------------------------------------------------------------------
4 * create shared memory and initialize shared memory data structures.
6 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/storage/ipc/shmem.c,v 1.73 2003/10/16 20:59:35 tgl 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 static slock_t *ShmemLock; /* spinlock for shared memory allocation */
79 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
81 static bool ShmemBootstrap = false; /* bootstrapping shmem index? */
85 * InitShmemAllocation() --- set up shared-memory allocation.
87 * Note: the argument should be declared "PGShmemHeader *seghdr",
88 * but we use void to avoid having to include ipc.h in shmem.h.
91 InitShmemAllocation(void *seghdr)
93 PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
95 /* Set up basic pointers to shared memory */
97 ShmemBase = (SHMEM_OFFSET) shmhdr;
98 ShmemEnd = ShmemBase + shmhdr->totalsize;
101 * Initialize the spinlock used by ShmemAlloc. We have to do the
102 * space allocation the hard way, since ShmemAlloc can't be called
105 ShmemLock = (slock_t *) (((char *) shmhdr) + shmhdr->freeoffset);
106 shmhdr->freeoffset += MAXALIGN(sizeof(slock_t));
107 Assert(shmhdr->freeoffset <= shmhdr->totalsize);
109 SpinLockInit(ShmemLock);
111 /* ShmemIndex can't be set up yet (need LWLocks first) */
112 ShmemIndex = (HTAB *) NULL;
115 * Initialize ShmemVariableCache for transaction manager.
117 ShmemVariableCache = (VariableCache)
118 ShmemAlloc(sizeof(*ShmemVariableCache));
119 memset(ShmemVariableCache, 0, sizeof(*ShmemVariableCache));
123 * ShmemAlloc -- allocate max-aligned chunk from shared memory
125 * Assumes ShmemLock and ShmemSegHdr are initialized.
127 * Returns: real pointer to memory or NULL if we are out
128 * of space. Has to return a real pointer in order
129 * to be compatible with malloc().
132 ShmemAlloc(Size size)
138 /* use volatile pointer to prevent code rearrangement */
139 volatile PGShmemHeader *shmemseghdr = ShmemSegHdr;
142 * ensure all space is adequately aligned.
144 size = MAXALIGN(size);
146 Assert(shmemseghdr != NULL);
148 SpinLockAcquire(ShmemLock);
150 newStart = shmemseghdr->freeoffset;
152 /* extra alignment for large requests, since they are probably buffers */
154 newStart = BUFFERALIGN(newStart);
156 newFree = newStart + size;
157 if (newFree <= shmemseghdr->totalsize)
159 newSpace = (void *) MAKE_PTR(newStart);
160 shmemseghdr->freeoffset = newFree;
165 SpinLockRelease(ShmemLock);
169 (errcode(ERRCODE_OUT_OF_MEMORY),
170 errmsg("out of shared memory")));
176 * ShmemIsValid -- test if an offset refers to valid shared memory
178 * Returns TRUE if the pointer is valid.
181 ShmemIsValid(unsigned long addr)
183 return (addr < ShmemEnd) && (addr >= ShmemBase);
187 * InitShmemIndex() --- set up shmem index table.
194 ShmemIndexEnt *result,
199 * Since ShmemInitHash calls ShmemInitStruct, which expects the
200 * ShmemIndex hashtable to exist already, we have a bit of a
201 * circularity problem in initializing the ShmemIndex itself. We set
202 * ShmemBootstrap to tell ShmemInitStruct to fake it.
204 ShmemBootstrap = true;
206 /* create the shared memory shmem index */
207 info.keysize = SHMEM_INDEX_KEYSIZE;
208 info.entrysize = sizeof(ShmemIndexEnt);
209 hash_flags = HASH_ELEM;
211 /* This will acquire the shmem index lock, but not release it. */
212 ShmemIndex = ShmemInitHash("ShmemIndex",
213 SHMEM_INDEX_SIZE, SHMEM_INDEX_SIZE,
216 elog(FATAL, "could not initialize Shmem Index");
219 * Now, create an entry in the hashtable for the index itself.
221 MemSet(item.key, 0, SHMEM_INDEX_KEYSIZE);
222 strncpy(item.key, "ShmemIndex", SHMEM_INDEX_KEYSIZE);
224 result = (ShmemIndexEnt *)
225 hash_search(ShmemIndex, (void *) &item, HASH_ENTER, &found);
228 (errcode(ERRCODE_OUT_OF_MEMORY),
229 errmsg("out of shared memory")));
231 Assert(ShmemBootstrap && !found);
233 result->location = MAKE_OFFSET(ShmemIndex->hctl);
234 result->size = SHMEM_INDEX_SIZE;
236 ShmemBootstrap = false;
238 /* now release the lock acquired in ShmemInitStruct */
239 LWLockRelease(ShmemIndexLock);
243 * ShmemInitHash -- Create/Attach to and initialize
244 * shared memory hash table.
248 * assume caller is doing some kind of synchronization
249 * so that two people dont try to create/initialize the
253 ShmemInitHash(const char *name, /* table string name for shmem index */
254 long init_size, /* initial table size */
255 long max_size, /* max size of the table */
256 HASHCTL *infoP, /* info about key and bucket size */
257 int hash_flags) /* info about infoP */
263 * Hash tables allocated in shared memory have a fixed directory; it
264 * can't grow or other backends wouldn't be able to find it. So, make
265 * sure we make it big enough to start with.
267 * The shared memory allocator must be specified too.
269 infoP->dsize = infoP->max_dsize = hash_select_dirsize(max_size);
270 infoP->alloc = ShmemAlloc;
271 hash_flags |= HASH_SHARED_MEM | HASH_DIRSIZE;
273 /* look it up in the shmem index */
274 location = ShmemInitStruct(name,
275 sizeof(HASHHDR) + infoP->dsize * sizeof(HASHSEGMENT),
279 * shmem index is corrupted. Let someone else give the error
280 * message since they have more information
282 if (location == NULL)
286 * if it already exists, attach to it rather than allocate and
287 * initialize new space
290 hash_flags |= HASH_ATTACH;
292 /* Now provide the header and directory pointers */
293 infoP->hctl = (HASHHDR *) location;
294 infoP->dir = (HASHSEGMENT *) (((char *) location) + sizeof(HASHHDR));
296 return hash_create(name, init_size, infoP, hash_flags);
300 * ShmemInitStruct -- Create/attach to a structure in shared
303 * This is called during initialization to find or allocate
304 * a data structure in shared memory. If no other processes
305 * have created the structure, this routine allocates space
306 * for it. If it exists already, a pointer to the existing
309 * Returns: real pointer to the object. FoundPtr is TRUE if
310 * the object is already in the shmem index (hence, already
314 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
316 ShmemIndexEnt *result,
320 strncpy(item.key, name, SHMEM_INDEX_KEYSIZE);
321 item.location = BAD_LOCATION;
323 LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
328 * If the shmem index doesn't exist, we are bootstrapping: we must
329 * be trying to init the shmem index itself.
331 * Notice that the ShmemIndexLock is held until the shmem index has
332 * been completely initialized.
334 Assert(strcmp(name, "ShmemIndex") == 0);
335 Assert(ShmemBootstrap);
337 return ShmemAlloc(size);
340 /* look it up in the shmem index */
341 result = (ShmemIndexEnt *)
342 hash_search(ShmemIndex, (void *) &item, HASH_ENTER, foundPtr);
346 LWLockRelease(ShmemIndexLock);
348 (errcode(ERRCODE_OUT_OF_MEMORY),
349 errmsg("out of shared memory")));
356 * Structure is in the shmem index so someone else has allocated
357 * it already. The size better be the same as the size we are
358 * trying to initialize to or there is a name conflict (or worse).
360 if (result->size != size)
362 LWLockRelease(ShmemIndexLock);
364 elog(WARNING, "ShmemIndex entry size is wrong");
365 /* let caller print its message too */
368 structPtr = (void *) MAKE_PTR(result->location);
372 /* It isn't in the table yet. allocate and initialize it */
373 structPtr = ShmemAlloc(size);
378 hash_search(ShmemIndex, (void *) &item, HASH_REMOVE, NULL);
379 LWLockRelease(ShmemIndexLock);
382 (errcode(ERRCODE_OUT_OF_MEMORY),
383 errmsg("could not allocate shared memory segment \"%s\"", name)));
388 result->location = MAKE_OFFSET(structPtr);
390 Assert(ShmemIsValid((unsigned long) structPtr));
392 LWLockRelease(ShmemIndexLock);