OSDN Git Service

Pgindent run for 8.0.
[pg-rex/syncrep.git] / src / backend / storage / ipc / shmem.c
1 /*-------------------------------------------------------------------------
2  *
3  * shmem.c
4  *        create shared memory and initialize shared memory data structures.
5  *
6  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/storage/ipc/shmem.c,v 1.80 2004/08/29 05:06:48 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 /*
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.
20  *
21  * NOTES:
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).
32  *
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.
48  *
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
55  *      unnecessary.
56  *
57  *              See InitSem() in sem.c for an example of how to use the
58  *      shmem index.
59  */
60
61 #include "postgres.h"
62
63 #include "access/transam.h"
64 #include "storage/pg_shmem.h"
65 #include "storage/spin.h"
66 #include "utils/tqual.h"
67
68
69 /* shared memory global variables */
70
71 static PGShmemHeader *ShmemSegHdr;              /* shared mem segment header */
72
73 SHMEM_OFFSET ShmemBase;                 /* start address of shared memory */
74
75 static SHMEM_OFFSET ShmemEnd;   /* end+1 address of shared memory */
76
77 NON_EXEC_STATIC slock_t *ShmemLock;             /* spinlock for shared memory
78                                                                                  * allocation */
79
80 NON_EXEC_STATIC slock_t *ShmemIndexLock;                /* spinlock for ShmemIndex */
81
82 NON_EXEC_STATIC void *ShmemIndexAlloc = NULL;   /* Memory actually
83                                                                                                  * allocated for
84                                                                                                  * ShmemIndex */
85
86 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
87
88 static bool ShmemBootstrap = false;             /* bootstrapping shmem index? */
89
90
91 /*
92  *      InitShmemAllocation() --- set up shared-memory allocation.
93  *
94  * Note: the argument should be declared "PGShmemHeader *seghdr",
95  * but we use void to avoid having to include ipc.h in shmem.h.
96  */
97 void
98 InitShmemAllocation(void *seghdr, bool init)
99 {
100         PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
101
102         /* Set up basic pointers to shared memory */
103         ShmemSegHdr = shmhdr;
104         ShmemBase = (SHMEM_OFFSET) shmhdr;
105         ShmemEnd = ShmemBase + shmhdr->totalsize;
106
107         if (init)
108         {
109                 /*
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.
113                  */
114                 ShmemLock = (slock_t *) (((char *) shmhdr) + shmhdr->freeoffset);
115                 shmhdr->freeoffset += MAXALIGN(sizeof(slock_t));
116                 Assert(shmhdr->freeoffset <= shmhdr->totalsize);
117
118                 ShmemIndexLock = (slock_t *) (((char *) shmhdr) + shmhdr->freeoffset);
119                 shmhdr->freeoffset += MAXALIGN(sizeof(slock_t));
120                 Assert(shmhdr->freeoffset <= shmhdr->totalsize);
121
122                 SpinLockInit(ShmemLock);
123                 SpinLockInit(ShmemIndexLock);
124
125                 /* ShmemIndex can't be set up yet (need LWLocks first) */
126                 ShmemIndex = (HTAB *) NULL;
127
128                 /*
129                  * Initialize ShmemVariableCache for transaction manager.
130                  */
131                 ShmemVariableCache = (VariableCache)
132                         ShmemAlloc(sizeof(*ShmemVariableCache));
133                 memset(ShmemVariableCache, 0, sizeof(*ShmemVariableCache));
134         }
135 }
136
137 /*
138  * ShmemAlloc -- allocate max-aligned chunk from shared memory
139  *
140  * Assumes ShmemLock and ShmemSegHdr are initialized.
141  *
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().
145  */
146 void *
147 ShmemAlloc(Size size)
148 {
149         uint32          newStart;
150         uint32          newFree;
151         void       *newSpace;
152
153         /* use volatile pointer to prevent code rearrangement */
154         volatile PGShmemHeader *shmemseghdr = ShmemSegHdr;
155
156         /*
157          * ensure all space is adequately aligned.
158          */
159         size = MAXALIGN(size);
160
161         Assert(shmemseghdr != NULL);
162
163         SpinLockAcquire(ShmemLock);
164
165         newStart = shmemseghdr->freeoffset;
166
167         /* extra alignment for large requests, since they are probably buffers */
168         if (size >= BLCKSZ)
169                 newStart = BUFFERALIGN(newStart);
170
171         newFree = newStart + size;
172         if (newFree <= shmemseghdr->totalsize)
173         {
174                 newSpace = (void *) MAKE_PTR(newStart);
175                 shmemseghdr->freeoffset = newFree;
176         }
177         else
178                 newSpace = NULL;
179
180         SpinLockRelease(ShmemLock);
181
182         if (!newSpace)
183                 ereport(WARNING,
184                                 (errcode(ERRCODE_OUT_OF_MEMORY),
185                                  errmsg("out of shared memory")));
186
187         return newSpace;
188 }
189
190 /*
191  * ShmemIsValid -- test if an offset refers to valid shared memory
192  *
193  * Returns TRUE if the pointer is valid.
194  */
195 bool
196 ShmemIsValid(unsigned long addr)
197 {
198         return (addr < ShmemEnd) && (addr >= ShmemBase);
199 }
200
201 /*
202  *      InitShmemIndex() --- set up shmem index table.
203  */
204 void
205 InitShmemIndex(void)
206 {
207         HASHCTL         info;
208         int                     hash_flags;
209         ShmemIndexEnt *result,
210                                 item;
211         bool            found;
212
213         /*
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.
218          */
219         ShmemBootstrap = true;
220
221         /* create the shared memory shmem index */
222         info.keysize = SHMEM_INDEX_KEYSIZE;
223         info.entrysize = sizeof(ShmemIndexEnt);
224         hash_flags = HASH_ELEM;
225
226         /* This will acquire the shmem index lock, but not release it. */
227         ShmemIndex = ShmemInitHash("ShmemIndex",
228                                                            SHMEM_INDEX_SIZE, SHMEM_INDEX_SIZE,
229                                                            &info, hash_flags);
230         if (!ShmemIndex)
231                 elog(FATAL, "could not initialize Shmem Index");
232
233         /*
234          * Now, create an entry in the hashtable for the index itself.
235          */
236         if (!IsUnderPostmaster)
237         {
238                 MemSet(item.key, 0, SHMEM_INDEX_KEYSIZE);
239                 strncpy(item.key, "ShmemIndex", SHMEM_INDEX_KEYSIZE);
240
241                 result = (ShmemIndexEnt *)
242                         hash_search(ShmemIndex, (void *) &item, HASH_ENTER, &found);
243                 if (!result)
244                         ereport(FATAL,
245                                         (errcode(ERRCODE_OUT_OF_MEMORY),
246                                          errmsg("out of shared memory")));
247
248                 Assert(ShmemBootstrap && !found);
249
250                 result->location = MAKE_OFFSET(ShmemIndex->hctl);
251                 result->size = SHMEM_INDEX_SIZE;
252
253                 ShmemBootstrap = false;
254         }
255
256         /* now release the lock acquired in ShmemInitStruct */
257         SpinLockRelease(ShmemIndexLock);
258 }
259
260 /*
261  * ShmemInitHash -- Create/Attach to and initialize
262  *              shared memory hash table.
263  *
264  * We assume caller is doing some kind of synchronization
265  * so that two people dont try to create/initialize the
266  * table at once.
267  */
268 HTAB *
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 */
274 {
275         bool            found;
276         void       *location;
277
278         /*
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.
282          *
283          * The shared memory allocator must be specified too.
284          */
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;
288
289         /* look it up in the shmem index */
290         location = ShmemInitStruct(name,
291                                         sizeof(HASHHDR) + infoP->dsize * sizeof(HASHSEGMENT),
292                                                            &found);
293
294         /*
295          * shmem index is corrupted.    Let someone else give the error
296          * message since they have more information
297          */
298         if (location == NULL)
299                 return NULL;
300
301         /*
302          * if it already exists, attach to it rather than allocate and
303          * initialize new space
304          */
305         if (found)
306                 hash_flags |= HASH_ATTACH;
307
308         /* Now provide the header and directory pointers */
309         infoP->hctl = (HASHHDR *) location;
310         infoP->dir = (HASHSEGMENT *) (((char *) location) + sizeof(HASHHDR));
311
312         return hash_create(name, init_size, infoP, hash_flags);
313 }
314
315 /*
316  * ShmemInitStruct -- Create/attach to a structure in shared
317  *              memory.
318  *
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
323  *              table is returned.
324  *
325  *      Returns: real pointer to the object.  FoundPtr is TRUE if
326  *              the object is already in the shmem index (hence, already
327  *              initialized).
328  */
329 void *
330 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
331 {
332         ShmemIndexEnt *result,
333                                 item;
334         void       *structPtr;
335
336         strncpy(item.key, name, SHMEM_INDEX_KEYSIZE);
337         item.location = BAD_LOCATION;
338
339         SpinLockAcquire(ShmemIndexLock);
340
341         if (!ShmemIndex)
342         {
343                 if (IsUnderPostmaster)
344                 {
345                         /* Must be initializing a (non-standalone) backend */
346                         Assert(strcmp(name, "ShmemIndex") == 0);
347                         Assert(ShmemBootstrap);
348                         Assert(ShmemIndexAlloc);
349                         *foundPtr = TRUE;
350                 }
351                 else
352                 {
353                         /*
354                          * If the shmem index doesn't exist, we are bootstrapping: we
355                          * must be trying to init the shmem index itself.
356                          *
357                          * Notice that the ShmemIndexLock is held until the shmem index
358                          * has been completely initialized.
359                          */
360                         Assert(strcmp(name, "ShmemIndex") == 0);
361                         Assert(ShmemBootstrap);
362                         *foundPtr = FALSE;
363                         ShmemIndexAlloc = ShmemAlloc(size);
364                 }
365                 return ShmemIndexAlloc;
366         }
367
368         /* look it up in the shmem index */
369         result = (ShmemIndexEnt *)
370                 hash_search(ShmemIndex, (void *) &item, HASH_ENTER, foundPtr);
371
372         if (!result)
373         {
374                 SpinLockRelease(ShmemIndexLock);
375                 ereport(ERROR,
376                                 (errcode(ERRCODE_OUT_OF_MEMORY),
377                                  errmsg("out of shared memory")));
378                 return NULL;
379         }
380
381         if (*foundPtr)
382         {
383                 /*
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).
387                  */
388                 if (result->size != size)
389                 {
390                         SpinLockRelease(ShmemIndexLock);
391
392                         elog(WARNING, "ShmemIndex entry size is wrong");
393                         /* let caller print its message too */
394                         return NULL;
395                 }
396                 structPtr = (void *) MAKE_PTR(result->location);
397         }
398         else
399         {
400                 /* It isn't in the table yet. allocate and initialize it */
401                 structPtr = ShmemAlloc(size);
402                 if (!structPtr)
403                 {
404                         /* out of memory */
405                         Assert(ShmemIndex);
406                         hash_search(ShmemIndex, (void *) &item, HASH_REMOVE, NULL);
407                         SpinLockRelease(ShmemIndexLock);
408
409                         ereport(WARNING,
410                                         (errcode(ERRCODE_OUT_OF_MEMORY),
411                                          errmsg("could not allocate shared memory segment \"%s\"", name)));
412                         *foundPtr = FALSE;
413                         return NULL;
414                 }
415                 result->size = size;
416                 result->location = MAKE_OFFSET(structPtr);
417         }
418         Assert(ShmemIsValid((unsigned long) structPtr));
419
420         SpinLockRelease(ShmemIndexLock);
421         return structPtr;
422 }