]> granicus.if.org Git - postgresql/blob - src/backend/utils/mmgr/aset.c
12affe6731faad5ed1e526f6f4261bcb4d7ec23d
[postgresql] / src / backend / utils / mmgr / aset.c
1 /*-------------------------------------------------------------------------
2  *
3  * aset.c
4  *        Allocation set definitions.
5  *
6  * AllocSet is our standard implementation of the abstract MemoryContext
7  * type.
8  *
9  *
10  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
11  * Portions Copyright (c) 1994, Regents of the University of California
12  *
13  * IDENTIFICATION
14  *        $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.31 2000/07/12 05:15:20 tgl Exp $
15  *
16  * NOTE:
17  *      This is a new (Feb. 05, 1999) implementation of the allocation set
18  *      routines. AllocSet...() does not use OrderedSet...() any more.
19  *      Instead it manages allocations in a block pool by itself, combining
20  *      many small allocations in a few bigger blocks. AllocSetFree() normally
21  *      doesn't free() memory really. It just add's the free'd area to some
22  *      list for later reuse by AllocSetAlloc(). All memory blocks are free()'d
23  *      at once on AllocSetReset(), which happens when the memory context gets
24  *      destroyed.
25  *                              Jan Wieck
26  *
27  *      Performance improvement from Tom Lane, 8/99: for extremely large request
28  *      sizes, we do want to be able to give the memory back to free() as soon
29  *      as it is pfree()'d.  Otherwise we risk tying up a lot of memory in
30  *      freelist entries that might never be usable.  This is specially needed
31  *      when the caller is repeatedly repalloc()'ing a block bigger and bigger;
32  *      the previous instances of the block were guaranteed to be wasted until
33  *      AllocSetReset() under the old way.
34  *-------------------------------------------------------------------------
35  */
36
37 #include "postgres.h"
38
39 #include "utils/memutils.h"
40
41 /* Define this to detail debug alloc information 
42  */
43 /*#define HAVE_ALLOCINFO        1*/
44
45 /*
46  * AllocSetContext is defined in nodes/memnodes.h.
47  */
48 typedef AllocSetContext *AllocSet;
49
50 /*
51  * AllocPointer
52  *              Aligned pointer which may be a member of an allocation set.
53  */
54 typedef void *AllocPointer;
55
56 /*
57  * AllocBlock
58  *              An AllocBlock is the unit of memory that is obtained by aset.c
59  *              from malloc().  It contains one or more AllocChunks, which are
60  *              the units requested by palloc() and freed by pfree().  AllocChunks
61  *              cannot be returned to malloc() individually, instead they are put
62  *              on freelists by pfree() and re-used by the next palloc() that has
63  *              a matching request size.
64  *
65  *              AllocBlockData is the header data for a block --- the usable space
66  *              within the block begins at the next alignment boundary.
67  */
68 typedef struct AllocBlockData
69 {
70         AllocSet        aset;                   /* aset that owns this block */
71         AllocBlock      next;                   /* next block in aset's blocks list */
72         char       *freeptr;            /* start of free space in this block */
73         char       *endptr;                     /* end of space in this block */
74 } AllocBlockData;
75
76 /*
77  * AllocChunk
78  *              The prefix of each piece of memory in an AllocBlock
79  *
80  * NB: this MUST match StandardChunkHeader as defined by utils/memutils.h.
81  */
82 typedef struct AllocChunkData
83 {
84         /* aset is the owning aset if allocated, or the freelist link if free */
85         void    *aset;
86         /* size is always the size of the usable space in the chunk */
87         Size    size;
88 #ifdef MEMORY_CONTEXT_CHECKING
89         Size    data_size;      
90 #endif                  
91 } AllocChunkData;
92
93 /*
94  * AllocPointerIsValid
95  *              True iff pointer is valid allocation pointer.
96  */
97 #define AllocPointerIsValid(pointer) PointerIsValid(pointer)
98
99 /*
100  * AllocSetIsValid
101  *              True iff set is valid allocation set.
102  */
103 #define AllocSetIsValid(set) PointerIsValid(set)
104
105 /*--------------------
106  * Chunk freelist k holds chunks of size 1 << (k + ALLOC_MINBITS),
107  * for k = 0 .. ALLOCSET_NUM_FREELISTS-2.
108  * The last freelist holds all larger free chunks.      Those chunks come in
109  * varying sizes depending on the request size, whereas smaller chunks are
110  * coerced to powers of 2 to improve their "recyclability".
111  *
112  * CAUTION: ALLOC_MINBITS must be large enough so that
113  * 1<<ALLOC_MINBITS is at least MAXALIGN,
114  * or we may fail to align the smallest chunks adequately.
115  * 16-byte alignment is enough on all currently known machines.
116  *--------------------
117  */
118
119 #define ALLOC_MINBITS           4       /* smallest chunk size is 16 bytes */
120 #define ALLOC_SMALLCHUNK_LIMIT  (1 << (ALLOCSET_NUM_FREELISTS-2+ALLOC_MINBITS))
121 /* Size of largest chunk that we use a fixed size for */
122
123 /*--------------------
124  * The first block allocated for an allocset has size initBlockSize.
125  * Each time we have to allocate another block, we double the block size
126  * (if possible, and without exceeding maxBlockSize), so as to reduce
127  * the bookkeeping load on malloc().
128  *
129  * Blocks allocated to hold oversize chunks do not follow this rule, however;
130  * they are just however big they need to be to hold that single chunk.
131  * AllocSetAlloc has some freedom about whether to consider a chunk larger
132  * than ALLOC_SMALLCHUNK_LIMIT to be "oversize".  We require all chunks
133  * >= ALLOC_BIGCHUNK_LIMIT to be allocated as single-chunk blocks; those
134  * chunks are treated specially by AllocSetFree and AllocSetRealloc.  For
135  * request sizes between ALLOC_SMALLCHUNK_LIMIT and ALLOC_BIGCHUNK_LIMIT,
136  * AllocSetAlloc has discretion whether to put the request into an existing
137  * block or make a single-chunk block.
138  *
139  * We must have initBlockSize > ALLOC_SMALLCHUNK_LIMIT and
140  * ALLOC_BIGCHUNK_LIMIT > ALLOC_SMALLCHUNK_LIMIT.
141  *--------------------
142  */
143
144 #define ALLOC_BIGCHUNK_LIMIT    (64 * 1024)
145 /* Chunks >= ALLOC_BIGCHUNK_LIMIT are immediately free()d by pfree() */
146
147 #define ALLOC_BLOCKHDRSZ        MAXALIGN(sizeof(AllocBlockData))
148 #define ALLOC_CHUNKHDRSZ        MAXALIGN(sizeof(AllocChunkData))
149
150 /* Min safe value of allocation block size */
151 #define ALLOC_MIN_BLOCK_SIZE  \
152         (ALLOC_SMALLCHUNK_LIMIT + ALLOC_CHUNKHDRSZ + ALLOC_BLOCKHDRSZ)
153
154 #define AllocPointerGetChunk(ptr)       \
155                                         ((AllocChunk)(((char *)(ptr)) - ALLOC_CHUNKHDRSZ))
156 #define AllocChunkGetPointer(chk)       \
157                                         ((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ))
158 #define AllocPointerGetAset(ptr)        ((AllocSet)(AllocPointerGetChunk(ptr)->aset))
159 #define AllocPointerGetSize(ptr)        (AllocPointerGetChunk(ptr)->size)
160
161 /*
162  * These functions implement the MemoryContext API for AllocSet contexts.
163  */
164 static void *AllocSetAlloc(MemoryContext context, Size size);
165 static void AllocSetFree(MemoryContext context, void *pointer);
166 static void *AllocSetRealloc(MemoryContext context, void *pointer, Size size);
167 static void AllocSetInit(MemoryContext context);
168 static void AllocSetReset(MemoryContext context);
169 static void AllocSetDelete(MemoryContext context);
170
171 #ifdef MEMORY_CONTEXT_CHECKING
172 static void AllocSetCheck(MemoryContext context);
173 #endif
174
175 static void AllocSetStats(MemoryContext context);
176
177 /*
178  * This is the virtual function table for AllocSet contexts.
179  */
180 static MemoryContextMethods AllocSetMethods = {
181         AllocSetAlloc,
182         AllocSetFree,
183         AllocSetRealloc,
184         AllocSetInit,
185         AllocSetReset,
186         AllocSetDelete,
187 #ifdef MEMORY_CONTEXT_CHECKING
188         AllocSetCheck,
189 #endif
190         AllocSetStats
191 };
192
193
194 /* ----------
195  * Debug macros
196  * ----------
197  */
198 #ifdef HAVE_ALLOCINFO
199 #define AllocFreeInfo(_cxt, _chunk) \
200                         fprintf(stderr, "AllocFree: %s: %p, %d\n", \
201                                 (_cxt)->header.name, (_chunk), (_chunk)->size)
202 #define AllocAllocInfo(_cxt, _chunk) \
203                         fprintf(stderr, "AllocAlloc: %s: %p, %d\n", \
204                                 (_cxt)->header.name, (_chunk), (_chunk)->size)
205 #else
206 #define AllocFreeInfo(_cxt, _chunk)
207 #define AllocAllocInfo(_cxt, _chunk)
208 #endif  
209
210 /* ----------
211  * AllocSetFreeIndex -
212  *
213  *              Depending on the size of an allocation compute which freechunk
214  *              list of the alloc set it belongs to.
215  * ----------
216  */
217 static inline int
218 AllocSetFreeIndex(Size size)
219 {
220         int                     idx = 0;
221
222         if (size > 0)
223         {
224                 size = (size - 1) >> ALLOC_MINBITS;
225                 while (size != 0 && idx < ALLOCSET_NUM_FREELISTS - 1)
226                 {
227                         idx++;
228                         size >>= 1;
229                 }
230         }
231
232         return idx;
233 }
234
235
236 /*
237  * Public routines
238  */
239
240
241 /*
242  * AllocSetContextCreate
243  *              Create a new AllocSet context.
244  *
245  * parent: parent context, or NULL if top-level context
246  * name: name of context (for debugging --- string will be copied)
247  * minContextSize: minimum context size
248  * initBlockSize: initial allocation block size
249  * maxBlockSize: maximum allocation block size
250  */
251 MemoryContext
252 AllocSetContextCreate(MemoryContext parent,
253                                           const char *name,
254                                           Size minContextSize,
255                                           Size initBlockSize,
256                                           Size maxBlockSize)
257 {
258         AllocSet        context;
259
260         /* Do the type-independent part of context creation */
261         context = (AllocSet) MemoryContextCreate(T_AllocSetContext,
262                                                                                          sizeof(AllocSetContext),
263                                                                                          &AllocSetMethods,
264                                                                                          parent,
265                                                                                          name);
266         /*
267          * Make sure alloc parameters are safe, and save them
268          */
269         initBlockSize = MAXALIGN(initBlockSize);
270         if (initBlockSize < ALLOC_MIN_BLOCK_SIZE)
271                 initBlockSize = ALLOC_MIN_BLOCK_SIZE;
272         maxBlockSize = MAXALIGN(maxBlockSize);
273         if (maxBlockSize < initBlockSize)
274                 maxBlockSize = initBlockSize;
275         context->initBlockSize = initBlockSize;
276         context->maxBlockSize = maxBlockSize;
277
278         /*
279          * Grab always-allocated space, if requested
280          */
281         if (minContextSize > ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)
282         {
283                 Size            blksize = MAXALIGN(minContextSize);
284                 AllocBlock      block;
285
286                 block = (AllocBlock) malloc(blksize);
287                 if (block == NULL)
288                         elog(ERROR, "Memory exhausted in AllocSetContextCreate()");
289                 block->aset = context;
290                 block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
291                 block->endptr = ((char *) block) + blksize;
292                 block->next = context->blocks;
293                 context->blocks = block;
294                 /* Mark block as not to be released at reset time */
295                 context->keeper = block;
296
297 #ifdef MEMORY_CONTEXT_CHECKING
298                 /* mark memory for memory leak searching */
299                 memset(block->freeptr, 0x7F, blksize - ALLOC_BLOCKHDRSZ);
300 #endif
301         }
302
303         return (MemoryContext) context;
304 }
305
306 /*
307  * AllocSetInit
308  *              Context-type-specific initialization routine.
309  *
310  * This is called by MemoryContextCreate() after setting up the
311  * generic MemoryContext fields and before linking the new context
312  * into the context tree.  We must do whatever is needed to make the
313  * new context minimally valid for deletion.  We must *not* risk
314  * failure --- thus, for example, allocating more memory is not cool.
315  * (AllocSetContextCreate can allocate memory when it gets control
316  * back, however.)
317  */
318 static void
319 AllocSetInit(MemoryContext context)
320 {
321         /*
322          * Since MemoryContextCreate already zeroed the context node,
323          * we don't have to do anything here: it's already OK.
324          */
325 }
326
327 /*
328  * AllocSetReset
329  *              Frees all memory which is allocated in the given set.
330  *
331  * Actually, this routine has some discretion about what to do.
332  * It should mark all allocated chunks freed, but it need not
333  * necessarily give back all the resources the set owns.  Our
334  * actual implementation is that we hang on to any "keeper"
335  * block specified for the set.
336  */
337 static void
338 AllocSetReset(MemoryContext context)
339 {
340         AllocSet        set = (AllocSet) context;
341         AllocBlock      block = set->blocks;
342
343         AssertArg(AllocSetIsValid(set));
344
345         while (block != NULL)
346         {
347                 AllocBlock      next = block->next;
348
349                 if (block == set->keeper)
350                 {
351                         /* Reset the block, but don't return it to malloc */
352                         char   *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ;
353
354 #ifdef CLOBBER_FREED_MEMORY
355                         /* Wipe freed memory for debugging purposes */
356                         memset(datastart, 0x7F, ((char *) block->freeptr) - datastart);
357 #endif
358                         block->freeptr = datastart;
359                         block->next = NULL;
360                 }
361                 else
362                 {
363                         /* Normal case, release the block */
364 #ifdef CLOBBER_FREED_MEMORY
365                         /* Wipe freed memory for debugging purposes */
366                         memset(block, 0x7F, ((char *) block->freeptr) - ((char *) block));
367 #endif
368                         free(block);
369                 }
370                 block = next;
371         }
372
373         /* Now blocks list is either empty or just the keeper block */
374         set->blocks = set->keeper;
375         /* Clear chunk freelists in any case */
376         MemSet(set->freelist, 0, sizeof(set->freelist));
377 }
378
379 /*
380  * AllocSetDelete
381  *              Frees all memory which is allocated in the given set,
382  *              in preparation for deletion of the set.
383  *
384  * Unlike AllocSetReset, this *must* free all resources of the set.
385  * But note we are not responsible for deleting the context node itself.
386  */
387 static void
388 AllocSetDelete(MemoryContext context)
389 {
390         AllocSet        set = (AllocSet) context;
391         AllocBlock      block = set->blocks;
392
393         AssertArg(AllocSetIsValid(set));
394
395         while (block != NULL)
396         {
397                 AllocBlock      next = block->next;
398
399 #ifdef CLOBBER_FREED_MEMORY
400                 /* Wipe freed memory for debugging purposes */
401                 memset(block, 0x7F, ((char *) block->endptr) - ((char *) block));
402 #endif
403                 free(block);
404                 block = next;
405         }
406
407         /* Make it look empty, just in case... */
408         set->blocks = NULL;
409         MemSet(set->freelist, 0, sizeof(set->freelist));
410         set->keeper = NULL;
411 }
412
413 /*
414  * AllocSetAlloc
415  *              Returns pointer to allocated memory of given size; memory is added
416  *              to the set.
417  */
418 static void *
419 AllocSetAlloc(MemoryContext context, Size size)
420 {
421         AllocSet        set = (AllocSet) context;
422         AllocBlock      block;
423         AllocChunk      chunk;
424         AllocChunk      priorfree = NULL;
425         int             fidx;
426         Size            chunk_size;
427         Size            blksize;
428
429         AssertArg(AllocSetIsValid(set));
430
431         /*
432          * Small size can be in free list 
433          */
434         if (size < ALLOC_BIGCHUNK_LIMIT)
435         {
436                 /*
437                  * Lookup in the corresponding free list if there is a free chunk we
438                  * could reuse
439                  */
440                 fidx = AllocSetFreeIndex(size);
441                 for (chunk = set->freelist[fidx]; chunk; chunk = (AllocChunk) chunk->aset)
442                 {
443                         if (chunk->size >= size)
444                                 break;
445                         priorfree = chunk;
446                 }
447
448                 /*
449                  * If one is found, remove it from the free list, make it again a
450                  * member of the alloc set and return its data address.
451                  */
452                 if (chunk != NULL)
453                 {
454                         if (priorfree == NULL)
455                                 set->freelist[fidx] = (AllocChunk) chunk->aset;
456                         else
457                                 priorfree->aset = chunk->aset;
458         
459                         chunk->aset = (void *) set;
460
461 #ifdef MEMORY_CONTEXT_CHECKING
462                         chunk->data_size = size;
463 #endif
464                         AllocAllocInfo(set, chunk);
465                         return AllocChunkGetPointer(chunk);
466                 }
467         } 
468         else
469                 /* Big chunk
470                  */
471                 fidx = ALLOCSET_NUM_FREELISTS - 1;
472
473         /*
474          * Choose the actual chunk size to allocate.
475          */
476         if (size > ALLOC_SMALLCHUNK_LIMIT)
477                 chunk_size = MAXALIGN(size);
478         else
479                 chunk_size = 1 << (fidx + ALLOC_MINBITS);
480         Assert(chunk_size >= size);
481
482         /*
483          * If there is enough room in the active allocation block, *and* the
484          * chunk is less than ALLOC_BIGCHUNK_LIMIT, put the chunk into the
485          * active allocation block.
486          */
487         if ((block = set->blocks) != NULL)
488         {
489                 Size            have_free = block->endptr - block->freeptr;
490
491                 if (have_free < (chunk_size + ALLOC_CHUNKHDRSZ) ||
492                         chunk_size >= ALLOC_BIGCHUNK_LIMIT)
493                         block = NULL;
494         }
495
496         /*
497          * Otherwise, if requested size exceeds smallchunk limit, allocate an
498          * entire separate block for this allocation.  In particular, we will
499          * always take this path if the requested size exceeds bigchunk limit.
500          */
501         if (block == NULL && size > ALLOC_SMALLCHUNK_LIMIT)
502         {
503                 blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
504                 block = (AllocBlock) malloc(blksize);
505                 if (block == NULL)
506                         elog(ERROR, "Memory exhausted in AllocSetAlloc()");
507                 block->aset = set;
508                 block->freeptr = block->endptr = ((char *) block) + blksize;
509
510                 chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ);
511                 chunk->aset = set;
512                 chunk->size = chunk_size;
513
514                 /*
515                  * Try to stick the block underneath the active allocation block,
516                  * so that we don't lose the use of the space remaining therein.
517                  */
518                 if (set->blocks != NULL)
519                 {
520                         block->next = set->blocks->next;
521                         set->blocks->next = block;
522                 }
523                 else
524                 {
525                         block->next = NULL;
526                         set->blocks = block;
527                 }
528                 
529 #ifdef MEMORY_CONTEXT_CHECKING
530                 chunk->data_size = size;
531                 /* mark memory for memory leak searching */
532                 memset(AllocChunkGetPointer(chunk), 0x7F, chunk->size);         
533 #endif
534                 AllocAllocInfo(set, chunk);
535                 return AllocChunkGetPointer(chunk);
536         }
537
538         /*
539          * Time to create a new regular (multi-chunk) block?
540          */
541         if (block == NULL)
542         {
543                 if (set->blocks == NULL)
544                 {
545                         blksize = set->initBlockSize;
546                         block = (AllocBlock) malloc(blksize);
547                 }
548                 else
549                 {
550                         /* Get size of prior block */
551                         blksize = set->blocks->endptr - ((char *) set->blocks);
552
553                         /*
554                          * Special case: if very first allocation was for a large
555                          * chunk (or we have a small "keeper" block), could have an
556                          * undersized top block.  Do something reasonable.
557                          */
558                         if (blksize < set->initBlockSize)
559                                 blksize = set->initBlockSize;
560                         else
561                         {
562                                 /* Crank it up, but not past max */
563                                 blksize <<= 1;
564                                 if (blksize > set->maxBlockSize)
565                                         blksize = set->maxBlockSize;
566                         }
567                         /* Try to allocate it */
568                         block = (AllocBlock) malloc(blksize);
569
570                         /*
571                          * We could be asking for pretty big blocks here, so cope if
572                          * malloc fails.  But give up if there's less than a meg or so
573                          * available...
574                          */
575                         while (block == NULL && blksize > 1024 * 1024)
576                         {
577                                 blksize >>= 1;
578                                 block = (AllocBlock) malloc(blksize);
579                         }
580                 }
581
582                 if (block == NULL)
583                         elog(ERROR, "Memory exhausted in AllocSetAlloc()");
584                         
585                 block->aset = set;
586                 block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
587                 block->endptr = ((char *) block) + blksize;
588
589 #ifdef MEMORY_CONTEXT_CHECKING
590                 /* mark memory for memory leak searching */
591                 memset(block->freeptr, 0x7F, blksize - ALLOC_BLOCKHDRSZ);
592 #endif
593                 block->next = set->blocks;
594
595                 set->blocks = block;
596         }
597
598         /*
599          * OK, do the allocation
600          */
601         chunk = (AllocChunk) (block->freeptr);
602         chunk->aset = (void *) set;
603         chunk->size = chunk_size;
604         
605 #ifdef MEMORY_CONTEXT_CHECKING
606         chunk->data_size = size;
607 #endif  
608         block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
609         Assert(block->freeptr <= block->endptr);
610
611         AllocAllocInfo(set, chunk);
612         return AllocChunkGetPointer(chunk);
613 }
614
615 /*
616  * AllocSetFree
617  *              Frees allocated memory; memory is removed from the set.
618  */
619 static void
620 AllocSetFree(MemoryContext context, void *pointer)
621 {
622         AllocSet        set = (AllocSet) context;
623         AllocChunk      chunk = AllocPointerGetChunk(pointer);
624
625 #if defined(CLOBBER_FREED_MEMORY) || defined(MEMORY_CONTEXT_CHECKING)
626         /* Wipe freed memory for debugging purposes or for memory leak 
627          * searching (in freelist[] must be mark memory
628          */
629         memset(pointer, 0x7F, chunk->size);
630 #endif
631
632         AllocFreeInfo(set, chunk);
633
634         if (chunk->size >= ALLOC_BIGCHUNK_LIMIT)
635         {
636                 /*
637                  * Big chunks are certain to have been allocated as single-chunk
638                  * blocks.      Find the containing block and return it to malloc().
639                  */
640                 AllocBlock      block = set->blocks;
641                 AllocBlock      prevblock = NULL;
642
643                 while (block != NULL)
644                 {
645                         if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ))
646                                 break;
647                         prevblock = block;
648                         block = block->next;
649                 }
650                 if (block == NULL)
651                         elog(ERROR, "AllocSetFree: cannot find block containing chunk");
652                 /* let's just make sure chunk is the only one in the block */
653                 Assert(block->freeptr == ((char *) block) +
654                            (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ));
655                 /* OK, remove block from aset's list and free it */
656                 if (prevblock == NULL)
657                         set->blocks = block->next;
658                 else
659                         prevblock->next = block->next;
660 #ifdef CLOBBER_FREED_MEMORY
661                 /* Wipe freed memory for debugging purposes */
662                 memset(block, 0x7F, ((char *) block->endptr) - ((char *) block));
663 #endif
664                 free(block);
665         }
666         else
667         {
668                 /* Normal case, put the chunk into appropriate freelist */
669                 int                     fidx = AllocSetFreeIndex(chunk->size);
670
671                 chunk->aset = (void *) set->freelist[fidx];
672
673 #ifdef MEMORY_CONTEXT_CHECKING
674                 chunk->data_size = 0;
675 #endif          
676                 set->freelist[fidx] = chunk;
677         }
678 }
679
680 /*
681  * AllocSetRealloc
682  *              Returns new pointer to allocated memory of given size; this memory
683  *              is added to the set.  Memory associated with given pointer is copied
684  *              into the new memory, and the old memory is freed.
685  */
686 static void *
687 AllocSetRealloc(MemoryContext context, void *pointer, Size size)
688 {
689         AllocSet        set = (AllocSet) context;
690         Size            oldsize;
691
692         /*
693          * Chunk sizes are aligned to power of 2 in AllocSetAlloc(). Maybe the
694          * allocated area already is >= the new size.  (In particular, we
695          * always fall out here if the requested size is a decrease.)
696          */
697         oldsize = AllocPointerGetSize(pointer);
698         if (oldsize >= size)
699         {
700 #ifdef MEMORY_CONTEXT_CHECKING          
701                 AllocChunk      chunk = AllocPointerGetChunk(pointer);
702
703                 /* mark memory for memory leak searching */
704                 memset(((char *) chunk) + (ALLOC_CHUNKHDRSZ + size), 
705                                 0x7F, chunk->size - size);
706                 chunk->data_size = size;
707 #endif
708                 return pointer;
709         }
710
711         if (oldsize >= ALLOC_BIGCHUNK_LIMIT)
712         {
713
714                 /*
715                  * If the chunk is already >= bigchunk limit, then it must have
716                  * been allocated as a single-chunk block.      Find the containing
717                  * block and use realloc() to make it bigger with minimum space
718                  * wastage.
719                  */
720                 AllocChunk      chunk = AllocPointerGetChunk(pointer);
721                 AllocBlock      block = set->blocks;
722                 AllocBlock      prevblock = NULL;
723                 Size            blksize;
724 #ifdef MEMORY_CONTEXT_CHECKING          
725                 Size            data_size = size;
726 #endif
727
728                 while (block != NULL)
729                 {
730                         if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ))
731                                 break;
732                         prevblock = block;
733                         block = block->next;
734                 }
735                 if (block == NULL)
736                         elog(ERROR, "AllocSetRealloc: cannot find block containing chunk");
737                 /* let's just make sure chunk is the only one in the block */
738                 Assert(block->freeptr == ((char *) block) +
739                            (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ));
740
741                 /* Do the realloc */
742                 size = MAXALIGN(size);
743                 blksize = size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
744                 block = (AllocBlock) realloc(block, blksize);
745                 if (block == NULL)
746                         elog(ERROR, "Memory exhausted in AllocSetReAlloc()");
747                 block->freeptr = block->endptr = ((char *) block) + blksize;
748
749                 /* Update pointers since block has likely been moved */
750                 chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ);
751                 if (prevblock == NULL)
752                         set->blocks = block;
753                 else
754                         prevblock->next = block;
755                 chunk->size = size;
756                 
757 #ifdef MEMORY_CONTEXT_CHECKING
758                 /* mark memory for memory leak searching */
759                 memset(((char *) chunk) + (ALLOC_CHUNKHDRSZ + data_size), 
760                                 0x7F, size - data_size);
761                 chunk->data_size = data_size;
762 #endif
763                 return AllocChunkGetPointer(chunk);
764         }
765         else
766         {
767                 /* Normal small-chunk case: just do it by brute force. */
768
769                 /* allocate new chunk */
770                 AllocPointer newPointer = AllocSetAlloc((MemoryContext) set, size);
771
772                 /* transfer existing data (certain to fit) */
773                 memcpy(newPointer, pointer, oldsize);
774
775                 /* free old chunk */
776                 AllocSetFree((MemoryContext) set, pointer);
777
778                 return newPointer;
779         }
780 }
781
782 /*
783  * AllocSetStats
784  *              Displays stats about memory consumption of an allocset.
785  */
786 static void
787 AllocSetStats(MemoryContext context)
788 {
789         AllocSet        set = (AllocSet) context;
790         long            nblocks = 0;
791         long            nchunks = 0;
792         long            totalspace = 0;
793         long            freespace = 0;
794         AllocBlock      block;
795         AllocChunk      chunk;
796         int                     fidx;
797
798         for (block = set->blocks; block != NULL; block = block->next)
799         {
800                 nblocks++;
801                 totalspace += block->endptr - ((char *) block);
802                 freespace += block->endptr - block->freeptr;
803         }
804         for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++)
805         {
806                 for (chunk = set->freelist[fidx]; chunk != NULL;
807                          chunk = (AllocChunk) chunk->aset)
808                 {
809                         nchunks++;
810                         freespace += chunk->size + ALLOC_CHUNKHDRSZ;
811                 }
812         }
813         fprintf(stderr,
814                         "%s: %ld total in %ld blocks; %ld free (%ld chunks); %ld used\n",
815                         set->header.name, totalspace, nblocks, freespace, nchunks,
816                         totalspace - freespace);
817 }
818
819
820 /* 
821  * AllocSetCheck
822  *              Walk on chunks and check consistence of memory. 
823  */
824 #ifdef MEMORY_CONTEXT_CHECKING
825
826 static void 
827 AllocSetCheck(MemoryContext context)
828 {
829         AllocSet        set = (AllocSet) context;       
830         AllocBlock      block = NULL;
831         AllocChunk      chunk = NULL;
832         char            *name = set->header.name;
833
834         for (block = set->blocks; block != NULL; block = block->next)
835         {       
836                 char    *bpoz = ((char *) block) + ALLOC_BLOCKHDRSZ;
837         /*      long    blk_size = block->endptr - ((char *) block);*/
838                 long    blk_free = block->endptr - block->freeptr; 
839                 long    blk_used = block->freeptr - bpoz;
840                 long    blk_data = 0;
841                 long    nchunks  = 0;
842
843                 /*
844                  * Empty block - empty can be keeper-block only
845                  */
846                 if (!blk_used)
847                 {
848                         if (set->keeper == block)
849                                 continue;
850                         else    
851                                 elog(ERROR, "AllocSetCheck(): %s: empty block %p", 
852                                                 name, block);
853                 }       
854                 
855                 /*
856                  * Chunk walker
857                  */     
858                 do {
859                         Size    chsize,
860                                 dsize;
861                         char    *chdata_end,
862                                 *chend;
863                                 
864                         chunk = (AllocChunk) bpoz;
865                         
866                         chsize = chunk->size;           /* align chunk size */
867                         dsize = chunk->data_size;       /* real data */
868                         
869                         chdata_end = ((char *) chunk) + (ALLOC_CHUNKHDRSZ + dsize);
870                         chend = ((char *) chunk) + (ALLOC_CHUNKHDRSZ + chsize);
871                         
872                         if (!dsize && chsize < dsize)
873                                 elog(ERROR, "AllocSetCheck(): %s: internal error for chunk %p in block %p",
874                                                 name, chunk, block);                    
875                         /*
876                          * Check chunk size
877                          */
878                         if (chsize < (1 << ALLOC_MINBITS))
879                                 elog(ERROR, "AllocSetCheck(): %s: bad size '%d' for chunk %p in block %p",
880                                                 name, chsize, chunk, block);
881                                                 
882                         /* single-chunk block */
883                         if (chsize >= ALLOC_BIGCHUNK_LIMIT &&
884                             chsize + ALLOC_CHUNKHDRSZ != blk_used)
885                                 elog(ERROR, "AllocSetCheck(): %s: bad singel-chunk %p in block %p",
886                                                 name, chunk, block);
887                                                 
888                         /*
889                          * Check in-chunk leak
890                          */             
891                         if (dsize < chsize && *chdata_end != 0x7F)
892                         {
893                                 fprintf(stderr, "\n--- Leak %p ---\n", chdata_end);
894                                 fprintf(stderr, "Chunk dump size: %ld (chunk-header %ld + chunk-size: %d), data must be: %d\n--- dump begin ---\n", 
895                                         chsize + ALLOC_CHUNKHDRSZ, 
896                                         ALLOC_CHUNKHDRSZ, chsize, dsize);
897                                         
898                                 fwrite((void *) chunk, chsize+ALLOC_CHUNKHDRSZ, sizeof(char), stderr);
899                                 fputs("\n--- dump end ---\n", stderr);
900                                 
901                                 elog(ERROR, "AllocSetCheck(): %s: found in-chunk memory leak (block %p; chunk %p; leak at %p",
902                                                 name, block, chunk, chdata_end);
903                         }
904                         
905                         /*
906                          * Check block-freeptr leak 
907                          */
908                         if (chend == block->freeptr && blk_free && 
909                                                         *chdata_end != 0x7F) {
910                                 
911                                 fprintf(stderr, "\n--- Leak %p ---\n", chdata_end);
912                                 fprintf(stderr, "Dump size: %ld (chunk-header %ld + chunk-size: %d + block-freespace: %ld), data must be: %d\n--- dump begin ---\n", 
913                                         chsize + ALLOC_CHUNKHDRSZ + blk_free, 
914                                         ALLOC_CHUNKHDRSZ, chsize, blk_free, dsize);
915                                         
916                                 fwrite((void *) chunk, chsize+ALLOC_CHUNKHDRSZ+blk_free, sizeof(char), stderr);
917                                 fputs("\n--- dump end ---\n", stderr);
918                                 
919                                 elog(ERROR, "AllocSetCheck(): %s: found block-freeptr memory leak (block %p; chunk %p; leak at %p",
920                                                 name, block, chunk, chdata_end);
921                         }                       
922                         
923                         blk_data += chsize;
924                         nchunks++;
925                         
926                         if (chend < block->freeptr)
927                                 bpoz += ALLOC_CHUNKHDRSZ + chsize;
928                         else
929                                 break;
930
931                 } while(block->freeptr > bpoz); /* chunk walker */              
932         
933                 
934                 if ((blk_data + (nchunks * ALLOC_CHUNKHDRSZ)) != blk_used)
935
936                         elog(ERROR, "AllocSetCheck(): %s: found non-consistent memory block %p",
937                                         name, block);
938         }
939 }
940 #endif