~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/backend/storage/ipc/shmem.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * shmem.c
 
4
 *        create shared memory and initialize shared memory data structures.
 
5
 *
 
6
 * Portions Copyright (c) 1996-2005, 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.82 2004-12-31 22:00:56 pgsql 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 don't try to create/initialize the
 
266
 * table at once.
 
267
 *
 
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).
 
272
 *
 
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.
 
276
 */
 
277
HTAB *
 
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 */
 
283
{
 
284
        bool            found;
 
285
        void       *location;
 
286
 
 
287
        /*
 
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.
 
291
         *
 
292
         * The shared memory allocator must be specified too.
 
293
         */
 
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;
 
297
 
 
298
        /* look it up in the shmem index */
 
299
        location = ShmemInitStruct(name,
 
300
                                        sizeof(HASHHDR) + infoP->dsize * sizeof(HASHSEGMENT),
 
301
                                                           &found);
 
302
 
 
303
        /*
 
304
         * shmem index is corrupted.    Let someone else give the error
 
305
         * message since they have more information
 
306
         */
 
307
        if (location == NULL)
 
308
                return NULL;
 
309
 
 
310
        /*
 
311
         * if it already exists, attach to it rather than allocate and
 
312
         * initialize new space
 
313
         */
 
314
        if (found)
 
315
                hash_flags |= HASH_ATTACH;
 
316
 
 
317
        /* Now provide the header and directory pointers */
 
318
        infoP->hctl = (HASHHDR *) location;
 
319
        infoP->dir = (HASHSEGMENT *) (((char *) location) + sizeof(HASHHDR));
 
320
 
 
321
        return hash_create(name, init_size, infoP, hash_flags);
 
322
}
 
323
 
 
324
/*
 
325
 * ShmemInitStruct -- Create/attach to a structure in shared
 
326
 *              memory.
 
327
 *
 
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
 
332
 *              table is returned.
 
333
 *
 
334
 *      Returns: real pointer to the object.  FoundPtr is TRUE if
 
335
 *              the object is already in the shmem index (hence, already
 
336
 *              initialized).
 
337
 */
 
338
void *
 
339
ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
340
{
 
341
        ShmemIndexEnt *result,
 
342
                                item;
 
343
        void       *structPtr;
 
344
 
 
345
        strncpy(item.key, name, SHMEM_INDEX_KEYSIZE);
 
346
        item.location = BAD_LOCATION;
 
347
 
 
348
        SpinLockAcquire(ShmemIndexLock);
 
349
 
 
350
        if (!ShmemIndex)
 
351
        {
 
352
                if (IsUnderPostmaster)
 
353
                {
 
354
                        /* Must be initializing a (non-standalone) backend */
 
355
                        Assert(strcmp(name, "ShmemIndex") == 0);
 
356
                        Assert(ShmemBootstrap);
 
357
                        Assert(ShmemIndexAlloc);
 
358
                        *foundPtr = TRUE;
 
359
                }
 
360
                else
 
361
                {
 
362
                        /*
 
363
                         * If the shmem index doesn't exist, we are bootstrapping: we
 
364
                         * must be trying to init the shmem index itself.
 
365
                         *
 
366
                         * Notice that the ShmemIndexLock is held until the shmem index
 
367
                         * has been completely initialized.
 
368
                         */
 
369
                        Assert(strcmp(name, "ShmemIndex") == 0);
 
370
                        Assert(ShmemBootstrap);
 
371
                        *foundPtr = FALSE;
 
372
                        ShmemIndexAlloc = ShmemAlloc(size);
 
373
                }
 
374
                return ShmemIndexAlloc;
 
375
        }
 
376
 
 
377
        /* look it up in the shmem index */
 
378
        result = (ShmemIndexEnt *)
 
379
                hash_search(ShmemIndex, (void *) &item, HASH_ENTER, foundPtr);
 
380
 
 
381
        if (!result)
 
382
        {
 
383
                SpinLockRelease(ShmemIndexLock);
 
384
                ereport(ERROR,
 
385
                                (errcode(ERRCODE_OUT_OF_MEMORY),
 
386
                                 errmsg("out of shared memory")));
 
387
                return NULL;
 
388
        }
 
389
 
 
390
        if (*foundPtr)
 
391
        {
 
392
                /*
 
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).
 
396
                 */
 
397
                if (result->size != size)
 
398
                {
 
399
                        SpinLockRelease(ShmemIndexLock);
 
400
 
 
401
                        elog(WARNING, "ShmemIndex entry size is wrong");
 
402
                        /* let caller print its message too */
 
403
                        return NULL;
 
404
                }
 
405
                structPtr = (void *) MAKE_PTR(result->location);
 
406
        }
 
407
        else
 
408
        {
 
409
                /* It isn't in the table yet. allocate and initialize it */
 
410
                structPtr = ShmemAlloc(size);
 
411
                if (!structPtr)
 
412
                {
 
413
                        /* out of memory */
 
414
                        Assert(ShmemIndex);
 
415
                        hash_search(ShmemIndex, (void *) &item, HASH_REMOVE, NULL);
 
416
                        SpinLockRelease(ShmemIndexLock);
 
417
 
 
418
                        ereport(WARNING,
 
419
                                        (errcode(ERRCODE_OUT_OF_MEMORY),
 
420
                                         errmsg("could not allocate shared memory segment \"%s\"", name)));
 
421
                        *foundPtr = FALSE;
 
422
                        return NULL;
 
423
                }
 
424
                result->size = size;
 
425
                result->location = MAKE_OFFSET(structPtr);
 
426
        }
 
427
        Assert(ShmemIsValid((unsigned long) structPtr));
 
428
 
 
429
        SpinLockRelease(ShmemIndexLock);
 
430
        return structPtr;
 
431
}