OSDN Git Service

Update copyright for 2009.
[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-2009, 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.102 2009/01/01 17:23:47 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() (or, in some ports, via other OS-specific
19  * methods).  The routines in this file are used for allocating and
20  * binding to shared memory data structures.
21  *
22  * NOTES:
23  *              (a) There are three kinds of shared memory data structures
24  *      available to POSTGRES: fixed-size structures, queues and hash
25  *      tables.  Fixed-size structures contain things like global variables
26  *      for a module and should never be allocated after the shared memory
27  *      initialization phase.  Hash tables have a fixed maximum size, but
28  *      their actual size can vary dynamically.  When entries are added
29  *      to the table, more space is allocated.  Queues link data structures
30  *      that have been allocated either within fixed-size structures or as hash
31  *      buckets.  Each shared data structure has a string name to identify
32  *      it (assigned in the module that declares it).
33  *
34  *              (b) During initialization, each module looks for its
35  *      shared data structures in a hash table called the "Shmem Index".
36  *      If the data structure is not present, the caller can allocate
37  *      a new one and initialize it.  If the data structure is present,
38  *      the caller "attaches" to the structure by initializing a pointer
39  *      in the local address space.
40  *              The shmem index has two purposes: first, it gives us
41  *      a simple model of how the world looks when a backend process
42  *      initializes.  If something is present in the shmem index,
43  *      it is initialized.      If it is not, it is uninitialized.      Second,
44  *      the shmem index allows us to allocate shared memory on demand
45  *      instead of trying to preallocate structures and hard-wire the
46  *      sizes and locations in header files.  If you are using a lot
47  *      of shared memory in a lot of different places (and changing
48  *      things during development), this is important.
49  *
50  *              (c) In standard Unix-ish environments, individual backends do not
51  *      need to re-establish their local pointers into shared memory, because
52  *      they inherit correct values of those variables via fork() from the
53  *      postmaster.  However, this does not work in the EXEC_BACKEND case.
54  *      In ports using EXEC_BACKEND, new backends have to set up their local
55  *      pointers using the method described in (b) above.
56  *
57  *              (d) memory allocation model: shared memory can never be
58  *      freed, once allocated.   Each hash table has its own free list,
59  *      so hash buckets can be reused when an item is deleted.  However,
60  *      if one hash table grows very large and then shrinks, its space
61  *      cannot be redistributed to other tables.  We could build a simple
62  *      hash bucket garbage collector if need be.  Right now, it seems
63  *      unnecessary.
64  */
65
66 #include "postgres.h"
67
68 #include "access/transam.h"
69 #include "miscadmin.h"
70 #include "storage/lwlock.h"
71 #include "storage/pg_shmem.h"
72 #include "storage/shmem.h"
73 #include "storage/spin.h"
74
75
76 /* shared memory global variables */
77
78 static PGShmemHeader *ShmemSegHdr;              /* shared mem segment header */
79
80 static void *ShmemBase;                 /* start address of shared memory */
81
82 static void *ShmemEnd;                  /* end+1 address of shared memory */
83
84 slock_t    *ShmemLock;                  /* spinlock for shared memory and LWLock
85                                                                  * allocation */
86
87 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
88
89
90 /*
91  *      InitShmemAccess() --- set up basic pointers to shared memory.
92  *
93  * Note: the argument should be declared "PGShmemHeader *seghdr",
94  * but we use void to avoid having to include ipc.h in shmem.h.
95  */
96 void
97 InitShmemAccess(void *seghdr)
98 {
99         PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
100
101         ShmemSegHdr = shmhdr;
102         ShmemBase = (void *) shmhdr;
103         ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
104 }
105
106 /*
107  *      InitShmemAllocation() --- set up shared-memory space allocation.
108  *
109  * This should be called only in the postmaster or a standalone backend.
110  */
111 void
112 InitShmemAllocation(void)
113 {
114         PGShmemHeader *shmhdr = ShmemSegHdr;
115
116         Assert(shmhdr != NULL);
117
118         /*
119          * Initialize the spinlock used by ShmemAlloc.  We have to do the space
120          * allocation the hard way, since obviously ShmemAlloc can't be called
121          * yet.
122          */
123         ShmemLock = (slock_t *) (((char *) shmhdr) + shmhdr->freeoffset);
124         shmhdr->freeoffset += MAXALIGN(sizeof(slock_t));
125         Assert(shmhdr->freeoffset <= shmhdr->totalsize);
126
127         SpinLockInit(ShmemLock);
128
129         /* ShmemIndex can't be set up yet (need LWLocks first) */
130         shmhdr->index = NULL;
131         ShmemIndex = (HTAB *) NULL;
132
133         /*
134          * Initialize ShmemVariableCache for transaction manager. (This doesn't
135          * really belong here, but not worth moving.)
136          */
137         ShmemVariableCache = (VariableCache)
138                 ShmemAlloc(sizeof(*ShmemVariableCache));
139         memset(ShmemVariableCache, 0, sizeof(*ShmemVariableCache));
140 }
141
142 /*
143  * ShmemAlloc -- allocate max-aligned chunk from shared memory
144  *
145  * Assumes ShmemLock and ShmemSegHdr are initialized.
146  *
147  * Returns: real pointer to memory or NULL if we are out
148  *              of space.  Has to return a real pointer in order
149  *              to be compatible with malloc().
150  */
151 void *
152 ShmemAlloc(Size size)
153 {
154         Size            newStart;
155         Size            newFree;
156         void       *newSpace;
157
158         /* use volatile pointer to prevent code rearrangement */
159         volatile PGShmemHeader *shmemseghdr = ShmemSegHdr;
160
161         /*
162          * ensure all space is adequately aligned.
163          */
164         size = MAXALIGN(size);
165
166         Assert(shmemseghdr != NULL);
167
168         SpinLockAcquire(ShmemLock);
169
170         newStart = shmemseghdr->freeoffset;
171
172         /* extra alignment for large requests, since they are probably buffers */
173         if (size >= BLCKSZ)
174                 newStart = BUFFERALIGN(newStart);
175
176         newFree = newStart + size;
177         if (newFree <= shmemseghdr->totalsize)
178         {
179                 newSpace = (void *) ((char *) ShmemBase + newStart);
180                 shmemseghdr->freeoffset = newFree;
181         }
182         else
183                 newSpace = NULL;
184
185         SpinLockRelease(ShmemLock);
186
187         if (!newSpace)
188                 ereport(WARNING,
189                                 (errcode(ERRCODE_OUT_OF_MEMORY),
190                                  errmsg("out of shared memory")));
191
192         return newSpace;
193 }
194
195 /*
196  * ShmemAddrIsValid -- test if an address refers to shared memory
197  *
198  * Returns TRUE if the pointer points within the shared memory segment.
199  */
200 bool
201 ShmemAddrIsValid(void *addr)
202 {
203         return (addr >= ShmemBase) && (addr < ShmemEnd);
204 }
205
206 /*
207  *      InitShmemIndex() --- set up or attach to shmem index table.
208  */
209 void
210 InitShmemIndex(void)
211 {
212         HASHCTL         info;
213         int                     hash_flags;
214
215         /*
216          * Since ShmemInitHash calls ShmemInitStruct, which expects the ShmemIndex
217          * hashtable to exist already, we have a bit of a circularity problem in
218          * initializing the ShmemIndex itself.  The special "ShmemIndex" hash
219          * table name will tell ShmemInitStruct to fake it.
220          */
221
222         /* create the shared memory shmem index */
223         info.keysize = SHMEM_INDEX_KEYSIZE;
224         info.entrysize = sizeof(ShmemIndexEnt);
225         hash_flags = HASH_ELEM;
226
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 /*
235  * ShmemInitHash -- Create and initialize, or attach to, a
236  *              shared memory hash table.
237  *
238  * We assume caller is doing some kind of synchronization
239  * so that two people don't try to create/initialize the
240  * table at once.
241  *
242  * max_size is the estimated maximum number of hashtable entries.  This is
243  * not a hard limit, but the access efficiency will degrade if it is
244  * exceeded substantially (since it's used to compute directory size and
245  * the hash table buckets will get overfull).
246  *
247  * init_size is the number of hashtable entries to preallocate.  For a table
248  * whose maximum size is certain, this should be equal to max_size; that
249  * ensures that no run-time out-of-shared-memory failures can occur.
250  */
251 HTAB *
252 ShmemInitHash(const char *name, /* table string name for shmem index */
253                           long init_size,       /* initial table size */
254                           long max_size,        /* max size of the table */
255                           HASHCTL *infoP,       /* info about key and bucket size */
256                           int hash_flags)       /* info about infoP */
257 {
258         bool            found;
259         void       *location;
260
261         /*
262          * Hash tables allocated in shared memory have a fixed directory; it can't
263          * grow or other backends wouldn't be able to find it. So, make sure we
264          * make it big enough to start with.
265          *
266          * The shared memory allocator must be specified too.
267          */
268         infoP->dsize = infoP->max_dsize = hash_select_dirsize(max_size);
269         infoP->alloc = ShmemAlloc;
270         hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
271
272         /* look it up in the shmem index */
273         location = ShmemInitStruct(name,
274                                                            hash_get_shared_size(infoP, hash_flags),
275                                                            &found);
276
277         /*
278          * If fail, shmem index is corrupted.  Let caller give the error message
279          * since it has more information
280          */
281         if (location == NULL)
282                 return NULL;
283
284         /*
285          * if it already exists, attach to it rather than allocate and initialize
286          * new space
287          */
288         if (found)
289                 hash_flags |= HASH_ATTACH;
290
291         /* Pass location of hashtable header to hash_create */
292         infoP->hctl = (HASHHDR *) location;
293
294         return hash_create(name, init_size, infoP, hash_flags);
295 }
296
297 /*
298  * ShmemInitStruct -- Create/attach to a structure in shared
299  *              memory.
300  *
301  *      This is called during initialization to find or allocate
302  *              a data structure in shared memory.      If no other process
303  *              has created the structure, this routine allocates space
304  *              for it.  If it exists already, a pointer to the existing
305  *              table is returned.
306  *
307  *      Returns: real pointer to the object.  FoundPtr is TRUE if
308  *              the object is already in the shmem index (hence, already
309  *              initialized).
310  */
311 void *
312 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
313 {
314         ShmemIndexEnt *result;
315         void       *structPtr;
316
317         LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
318
319         if (!ShmemIndex)
320         {
321                 PGShmemHeader *shmemseghdr = ShmemSegHdr;
322
323                 Assert(strcmp(name, "ShmemIndex") == 0);
324                 if (IsUnderPostmaster)
325                 {
326                         /* Must be initializing a (non-standalone) backend */
327                         Assert(shmemseghdr->index != NULL);
328                         structPtr = shmemseghdr->index;
329                         *foundPtr = TRUE;
330                 }
331                 else
332                 {
333                         /*
334                          * If the shmem index doesn't exist, we are bootstrapping: we must
335                          * be trying to init the shmem index itself.
336                          *
337                          * Notice that the ShmemIndexLock is released before the shmem
338                          * index has been initialized.  This should be OK because no other
339                          * process can be accessing shared memory yet.
340                          */
341                         Assert(shmemseghdr->index == NULL);
342                         structPtr = ShmemAlloc(size);
343                         shmemseghdr->index = structPtr;
344                         *foundPtr = FALSE;
345                 }
346                 LWLockRelease(ShmemIndexLock);
347                 return structPtr;
348         }
349
350         /* look it up in the shmem index */
351         result = (ShmemIndexEnt *)
352                 hash_search(ShmemIndex, name, HASH_ENTER_NULL, foundPtr);
353
354         if (!result)
355         {
356                 LWLockRelease(ShmemIndexLock);
357                 ereport(ERROR,
358                                 (errcode(ERRCODE_OUT_OF_MEMORY),
359                                  errmsg("out of shared memory")));
360         }
361
362         if (*foundPtr)
363         {
364                 /*
365                  * Structure is in the shmem index so someone else has allocated it
366                  * already.  The size better be the same as the size we are trying to
367                  * initialize to or there is a name conflict (or worse).
368                  */
369                 if (result->size != size)
370                 {
371                         LWLockRelease(ShmemIndexLock);
372
373                         elog(WARNING, "ShmemIndex entry size is wrong");
374                         /* let caller print its message too */
375                         return NULL;
376                 }
377                 structPtr = result->location;
378         }
379         else
380         {
381                 /* It isn't in the table yet. allocate and initialize it */
382                 structPtr = ShmemAlloc(size);
383                 if (!structPtr)
384                 {
385                         /* out of memory */
386                         Assert(ShmemIndex);
387                         hash_search(ShmemIndex, name, HASH_REMOVE, NULL);
388                         LWLockRelease(ShmemIndexLock);
389
390                         ereport(WARNING,
391                                         (errcode(ERRCODE_OUT_OF_MEMORY),
392                                          errmsg("could not allocate shared memory segment \"%s\"",
393                                                         name)));
394                         *foundPtr = FALSE;
395                         return NULL;
396                 }
397                 result->size = size;
398                 result->location = structPtr;
399         }
400         Assert(ShmemAddrIsValid(structPtr));
401
402         LWLockRelease(ShmemIndexLock);
403         return structPtr;
404 }
405
406
407 /*
408  * Add two Size values, checking for overflow
409  */
410 Size
411 add_size(Size s1, Size s2)
412 {
413         Size            result;
414
415         result = s1 + s2;
416         /* We are assuming Size is an unsigned type here... */
417         if (result < s1 || result < s2)
418                 ereport(ERROR,
419                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
420                                  errmsg("requested shared memory size overflows size_t")));
421         return result;
422 }
423
424 /*
425  * Multiply two Size values, checking for overflow
426  */
427 Size
428 mul_size(Size s1, Size s2)
429 {
430         Size            result;
431
432         if (s1 == 0 || s2 == 0)
433                 return 0;
434         result = s1 * s2;
435         /* We are assuming Size is an unsigned type here... */
436         if (result / s2 != s1)
437                 ereport(ERROR,
438                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
439                                  errmsg("requested shared memory size overflows size_t")));
440         return result;
441 }