1
/*-------------------------------------------------------------------------
4
* create shared memory and initialize shared memory data structures.
6
* Portions Copyright (c) 1996-2005, 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.82 2004-12-31 22:00:56 pgsql 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 don't try to create/initialize the
268
* max_size is the estimated maximum number of hashtable entries. This is
269
* not a hard limit, but the access efficiency will degrade if it is
270
* exceeded substantially (since it's used to compute directory size and
271
* the hash table buckets will get overfull).
273
* init_size is the number of hashtable entries to preallocate. For a table
274
* whose maximum size is certain, this should be equal to max_size; that
275
* ensures that no run-time out-of-shared-memory failures can occur.
278
ShmemInitHash(const char *name, /* table string name for shmem index */
279
long init_size, /* initial table size */
280
long max_size, /* max size of the table */
281
HASHCTL *infoP, /* info about key and bucket size */
282
int hash_flags) /* info about infoP */
288
* Hash tables allocated in shared memory have a fixed directory; it
289
* can't grow or other backends wouldn't be able to find it. So, make
290
* sure we make it big enough to start with.
292
* The shared memory allocator must be specified too.
294
infoP->dsize = infoP->max_dsize = hash_select_dirsize(max_size);
295
infoP->alloc = ShmemAlloc;
296
hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
298
/* look it up in the shmem index */
299
location = ShmemInitStruct(name,
300
sizeof(HASHHDR) + infoP->dsize * sizeof(HASHSEGMENT),
304
* shmem index is corrupted. Let someone else give the error
305
* message since they have more information
307
if (location == NULL)
311
* if it already exists, attach to it rather than allocate and
312
* initialize new space
315
hash_flags |= HASH_ATTACH;
317
/* Now provide the header and directory pointers */
318
infoP->hctl = (HASHHDR *) location;
319
infoP->dir = (HASHSEGMENT *) (((char *) location) + sizeof(HASHHDR));
321
return hash_create(name, init_size, infoP, hash_flags);
325
* ShmemInitStruct -- Create/attach to a structure in shared
328
* This is called during initialization to find or allocate
329
* a data structure in shared memory. If no other processes
330
* have created the structure, this routine allocates space
331
* for it. If it exists already, a pointer to the existing
334
* Returns: real pointer to the object. FoundPtr is TRUE if
335
* the object is already in the shmem index (hence, already
339
ShmemInitStruct(const char *name, Size size, bool *foundPtr)
341
ShmemIndexEnt *result,
345
strncpy(item.key, name, SHMEM_INDEX_KEYSIZE);
346
item.location = BAD_LOCATION;
348
SpinLockAcquire(ShmemIndexLock);
352
if (IsUnderPostmaster)
354
/* Must be initializing a (non-standalone) backend */
355
Assert(strcmp(name, "ShmemIndex") == 0);
356
Assert(ShmemBootstrap);
357
Assert(ShmemIndexAlloc);
363
* If the shmem index doesn't exist, we are bootstrapping: we
364
* must be trying to init the shmem index itself.
366
* Notice that the ShmemIndexLock is held until the shmem index
367
* has been completely initialized.
369
Assert(strcmp(name, "ShmemIndex") == 0);
370
Assert(ShmemBootstrap);
372
ShmemIndexAlloc = ShmemAlloc(size);
374
return ShmemIndexAlloc;
377
/* look it up in the shmem index */
378
result = (ShmemIndexEnt *)
379
hash_search(ShmemIndex, (void *) &item, HASH_ENTER, foundPtr);
383
SpinLockRelease(ShmemIndexLock);
385
(errcode(ERRCODE_OUT_OF_MEMORY),
386
errmsg("out of shared memory")));
393
* Structure is in the shmem index so someone else has allocated
394
* it already. The size better be the same as the size we are
395
* trying to initialize to or there is a name conflict (or worse).
397
if (result->size != size)
399
SpinLockRelease(ShmemIndexLock);
401
elog(WARNING, "ShmemIndex entry size is wrong");
402
/* let caller print its message too */
405
structPtr = (void *) MAKE_PTR(result->location);
409
/* It isn't in the table yet. allocate and initialize it */
410
structPtr = ShmemAlloc(size);
415
hash_search(ShmemIndex, (void *) &item, HASH_REMOVE, NULL);
416
SpinLockRelease(ShmemIndexLock);
419
(errcode(ERRCODE_OUT_OF_MEMORY),
420
errmsg("could not allocate shared memory segment \"%s\"", name)));
425
result->location = MAKE_OFFSET(structPtr);
427
Assert(ShmemIsValid((unsigned long) structPtr));
429
SpinLockRelease(ShmemIndexLock);