Revert dubious message wording change.
[PostgreSQL.git] / src / backend / utils / mmgr / aset.c
blobebed54e6ffb99c8fae06bb46c847074ce3a6dc0f
1 /*-------------------------------------------------------------------------
3 * aset.c
4 * Allocation set definitions.
6 * AllocSet is our standard implementation of the abstract MemoryContext
7 * type.
10 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
11 * Portions Copyright (c) 1994, Regents of the University of California
13 * IDENTIFICATION
14 * $PostgreSQL$
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
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.
35 * Further improvement 12/00: as the code stood, request sizes in the
36 * midrange between "small" and "large" were handled very inefficiently,
37 * because any sufficiently large free chunk would be used to satisfy a
38 * request, even if it was much larger than necessary. This led to more
39 * and more wasted space in allocated chunks over time. To fix, get rid
40 * of the midrange behavior: we now handle only "small" power-of-2-size
41 * chunks as chunks. Anything "large" is passed off to malloc(). Change
42 * the number of freelists to change the small/large boundary.
45 * About CLOBBER_FREED_MEMORY:
47 * If this symbol is defined, all freed memory is overwritten with 0x7F's.
48 * This is useful for catching places that reference already-freed memory.
50 * About MEMORY_CONTEXT_CHECKING:
52 * Since we usually round request sizes up to the next power of 2, there
53 * is often some unused space immediately after a requested data area.
54 * Thus, if someone makes the common error of writing past what they've
55 * requested, the problem is likely to go unnoticed ... until the day when
56 * there *isn't* any wasted space, perhaps because of different memory
57 * alignment on a new platform, or some other effect. To catch this sort
58 * of problem, the MEMORY_CONTEXT_CHECKING option stores 0x7E just beyond
59 * the requested space whenever the request is less than the actual chunk
60 * size, and verifies that the byte is undamaged when the chunk is freed.
62 *-------------------------------------------------------------------------
65 #include "postgres.h"
67 #include "utils/memutils.h"
69 /* Define this to detail debug alloc information */
70 /* #define HAVE_ALLOCINFO */
72 /*--------------------
73 * Chunk freelist k holds chunks of size 1 << (k + ALLOC_MINBITS),
74 * for k = 0 .. ALLOCSET_NUM_FREELISTS-1.
76 * Note that all chunks in the freelists have power-of-2 sizes. This
77 * improves recyclability: we may waste some space, but the wasted space
78 * should stay pretty constant as requests are made and released.
80 * A request too large for the last freelist is handled by allocating a
81 * dedicated block from malloc(). The block still has a block header and
82 * chunk header, but when the chunk is freed we'll return the whole block
83 * to malloc(), not put it on our freelists.
85 * CAUTION: ALLOC_MINBITS must be large enough so that
86 * 1<<ALLOC_MINBITS is at least MAXALIGN,
87 * or we may fail to align the smallest chunks adequately.
88 * 8-byte alignment is enough on all currently known machines.
90 * With the current parameters, request sizes up to 8K are treated as chunks,
91 * larger requests go into dedicated blocks. Change ALLOCSET_NUM_FREELISTS
92 * to adjust the boundary point.
93 *--------------------
96 #define ALLOC_MINBITS 3 /* smallest chunk size is 8 bytes */
97 #define ALLOCSET_NUM_FREELISTS 11
98 #define ALLOC_CHUNK_LIMIT (1 << (ALLOCSET_NUM_FREELISTS-1+ALLOC_MINBITS))
99 /* Size of largest chunk that we use a fixed size for */
101 /*--------------------
102 * The first block allocated for an allocset has size initBlockSize.
103 * Each time we have to allocate another block, we double the block size
104 * (if possible, and without exceeding maxBlockSize), so as to reduce
105 * the bookkeeping load on malloc().
107 * Blocks allocated to hold oversize chunks do not follow this rule, however;
108 * they are just however big they need to be to hold that single chunk.
109 *--------------------
112 #define ALLOC_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData))
113 #define ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData))
115 typedef struct AllocBlockData *AllocBlock; /* forward reference */
116 typedef struct AllocChunkData *AllocChunk;
119 * AllocPointer
120 * Aligned pointer which may be a member of an allocation set.
122 typedef void *AllocPointer;
125 * AllocSetContext is our standard implementation of MemoryContext.
127 * Note: isReset means there is nothing for AllocSetReset to do. This is
128 * different from the aset being physically empty (empty blocks list) because
129 * we may still have a keeper block. It's also different from the set being
130 * logically empty, because we don't attempt to detect pfree'ing the last
131 * active chunk.
133 typedef struct AllocSetContext
135 MemoryContextData header; /* Standard memory-context fields */
136 /* Info about storage allocated in this context: */
137 AllocBlock blocks; /* head of list of blocks in this set */
138 AllocChunk freelist[ALLOCSET_NUM_FREELISTS]; /* free chunk lists */
139 bool isReset; /* T = no space alloced since last reset */
140 /* Allocation parameters for this context: */
141 Size initBlockSize; /* initial block size */
142 Size maxBlockSize; /* maximum block size */
143 Size nextBlockSize; /* next block size to allocate */
144 Size allocChunkLimit; /* effective chunk size limit */
145 AllocBlock keeper; /* if not NULL, keep this block over resets */
146 } AllocSetContext;
148 typedef AllocSetContext *AllocSet;
151 * AllocBlock
152 * An AllocBlock is the unit of memory that is obtained by aset.c
153 * from malloc(). It contains one or more AllocChunks, which are
154 * the units requested by palloc() and freed by pfree(). AllocChunks
155 * cannot be returned to malloc() individually, instead they are put
156 * on freelists by pfree() and re-used by the next palloc() that has
157 * a matching request size.
159 * AllocBlockData is the header data for a block --- the usable space
160 * within the block begins at the next alignment boundary.
162 typedef struct AllocBlockData
164 AllocSet aset; /* aset that owns this block */
165 AllocBlock next; /* next block in aset's blocks list */
166 char *freeptr; /* start of free space in this block */
167 char *endptr; /* end of space in this block */
168 } AllocBlockData;
171 * AllocChunk
172 * The prefix of each piece of memory in an AllocBlock
174 * NB: this MUST match StandardChunkHeader as defined by utils/memutils.h.
176 typedef struct AllocChunkData
178 /* aset is the owning aset if allocated, or the freelist link if free */
179 void *aset;
180 /* size is always the size of the usable space in the chunk */
181 Size size;
182 #ifdef MEMORY_CONTEXT_CHECKING
183 /* when debugging memory usage, also store actual requested size */
184 /* this is zero in a free chunk */
185 Size requested_size;
186 #endif
187 } AllocChunkData;
190 * AllocPointerIsValid
191 * True iff pointer is valid allocation pointer.
193 #define AllocPointerIsValid(pointer) PointerIsValid(pointer)
196 * AllocSetIsValid
197 * True iff set is valid allocation set.
199 #define AllocSetIsValid(set) PointerIsValid(set)
201 #define AllocPointerGetChunk(ptr) \
202 ((AllocChunk)(((char *)(ptr)) - ALLOC_CHUNKHDRSZ))
203 #define AllocChunkGetPointer(chk) \
204 ((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ))
207 * These functions implement the MemoryContext API for AllocSet contexts.
209 static void *AllocSetAlloc(MemoryContext context, Size size);
210 static void AllocSetFree(MemoryContext context, void *pointer);
211 static void *AllocSetRealloc(MemoryContext context, void *pointer, Size size);
212 static void AllocSetInit(MemoryContext context);
213 static void AllocSetReset(MemoryContext context);
214 static void AllocSetDelete(MemoryContext context);
215 static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer);
216 static bool AllocSetIsEmpty(MemoryContext context);
217 static void AllocSetStats(MemoryContext context, int level);
219 #ifdef MEMORY_CONTEXT_CHECKING
220 static void AllocSetCheck(MemoryContext context);
221 #endif
224 * This is the virtual function table for AllocSet contexts.
226 static MemoryContextMethods AllocSetMethods = {
227 AllocSetAlloc,
228 AllocSetFree,
229 AllocSetRealloc,
230 AllocSetInit,
231 AllocSetReset,
232 AllocSetDelete,
233 AllocSetGetChunkSpace,
234 AllocSetIsEmpty,
235 AllocSetStats
236 #ifdef MEMORY_CONTEXT_CHECKING
237 ,AllocSetCheck
238 #endif
242 /* ----------
243 * Debug macros
244 * ----------
246 #ifdef HAVE_ALLOCINFO
247 #define AllocFreeInfo(_cxt, _chunk) \
248 fprintf(stderr, "AllocFree: %s: %p, %d\n", \
249 (_cxt)->header.name, (_chunk), (_chunk)->size)
250 #define AllocAllocInfo(_cxt, _chunk) \
251 fprintf(stderr, "AllocAlloc: %s: %p, %d\n", \
252 (_cxt)->header.name, (_chunk), (_chunk)->size)
253 #else
254 #define AllocFreeInfo(_cxt, _chunk)
255 #define AllocAllocInfo(_cxt, _chunk)
256 #endif
258 /* ----------
259 * AllocSetFreeIndex -
261 * Depending on the size of an allocation compute which freechunk
262 * list of the alloc set it belongs to. Caller must have verified
263 * that size <= ALLOC_CHUNK_LIMIT.
264 * ----------
266 static inline int
267 AllocSetFreeIndex(Size size)
269 int idx = 0;
271 if (size > 0)
273 size = (size - 1) >> ALLOC_MINBITS;
274 while (size != 0)
276 idx++;
277 size >>= 1;
279 Assert(idx < ALLOCSET_NUM_FREELISTS);
282 return idx;
285 #ifdef RANDOMIZE_ALLOCATED_MEMORY
288 * Fill a just-allocated piece of memory with "random" data. It's not really
289 * very random, just a repeating sequence with a length that's prime. What
290 * we mainly want out of it is to have a good probability that two palloc's
291 * of the same number of bytes start out containing different data.
293 static void
294 randomize_mem(char *ptr, size_t size)
296 static int save_ctr = 1;
297 int ctr;
299 ctr = save_ctr;
300 while (size-- > 0)
302 *ptr++ = ctr;
303 if (++ctr > 251)
304 ctr = 1;
306 save_ctr = ctr;
308 #endif /* RANDOMIZE_ALLOCATED_MEMORY */
312 * Public routines
317 * AllocSetContextCreate
318 * Create a new AllocSet context.
320 * parent: parent context, or NULL if top-level context
321 * name: name of context (for debugging --- string will be copied)
322 * minContextSize: minimum context size
323 * initBlockSize: initial allocation block size
324 * maxBlockSize: maximum allocation block size
326 MemoryContext
327 AllocSetContextCreate(MemoryContext parent,
328 const char *name,
329 Size minContextSize,
330 Size initBlockSize,
331 Size maxBlockSize)
333 AllocSet context;
335 /* Do the type-independent part of context creation */
336 context = (AllocSet) MemoryContextCreate(T_AllocSetContext,
337 sizeof(AllocSetContext),
338 &AllocSetMethods,
339 parent,
340 name);
343 * Make sure alloc parameters are reasonable, and save them.
345 * We somewhat arbitrarily enforce a minimum 1K block size.
347 initBlockSize = MAXALIGN(initBlockSize);
348 if (initBlockSize < 1024)
349 initBlockSize = 1024;
350 maxBlockSize = MAXALIGN(maxBlockSize);
351 if (maxBlockSize < initBlockSize)
352 maxBlockSize = initBlockSize;
353 context->initBlockSize = initBlockSize;
354 context->maxBlockSize = maxBlockSize;
355 context->nextBlockSize = initBlockSize;
358 * Compute the allocation chunk size limit for this context. It can't be
359 * more than ALLOC_CHUNK_LIMIT because of the fixed number of freelists.
360 * If maxBlockSize is small then requests exceeding the maxBlockSize
361 * should be treated as large chunks, too. We have to have
362 * allocChunkLimit a power of two, because the requested and
363 * actually-allocated sizes of any chunk must be on the same side of the
364 * limit, else we get confused about whether the chunk is "big".
366 context->allocChunkLimit = ALLOC_CHUNK_LIMIT;
367 while (context->allocChunkLimit >
368 (Size) (maxBlockSize - ALLOC_BLOCKHDRSZ - ALLOC_CHUNKHDRSZ))
369 context->allocChunkLimit >>= 1;
372 * Grab always-allocated space, if requested
374 if (minContextSize > ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)
376 Size blksize = MAXALIGN(minContextSize);
377 AllocBlock block;
379 block = (AllocBlock) malloc(blksize);
380 if (block == NULL)
382 MemoryContextStats(TopMemoryContext);
383 ereport(ERROR,
384 (errcode(ERRCODE_OUT_OF_MEMORY),
385 errmsg("out of memory"),
386 errdetail("Failed while creating memory context \"%s\".",
387 name)));
389 block->aset = context;
390 block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
391 block->endptr = ((char *) block) + blksize;
392 block->next = context->blocks;
393 context->blocks = block;
394 /* Mark block as not to be released at reset time */
395 context->keeper = block;
398 context->isReset = true;
400 return (MemoryContext) context;
404 * AllocSetInit
405 * Context-type-specific initialization routine.
407 * This is called by MemoryContextCreate() after setting up the
408 * generic MemoryContext fields and before linking the new context
409 * into the context tree. We must do whatever is needed to make the
410 * new context minimally valid for deletion. We must *not* risk
411 * failure --- thus, for example, allocating more memory is not cool.
412 * (AllocSetContextCreate can allocate memory when it gets control
413 * back, however.)
415 static void
416 AllocSetInit(MemoryContext context)
419 * Since MemoryContextCreate already zeroed the context node, we don't
420 * have to do anything here: it's already OK.
425 * AllocSetReset
426 * Frees all memory which is allocated in the given set.
428 * Actually, this routine has some discretion about what to do.
429 * It should mark all allocated chunks freed, but it need not necessarily
430 * give back all the resources the set owns. Our actual implementation is
431 * that we hang onto any "keeper" block specified for the set. In this way,
432 * we don't thrash malloc() when a context is repeatedly reset after small
433 * allocations, which is typical behavior for per-tuple contexts.
435 static void
436 AllocSetReset(MemoryContext context)
438 AllocSet set = (AllocSet) context;
439 AllocBlock block;
441 AssertArg(AllocSetIsValid(set));
443 /* Nothing to do if no pallocs since startup or last reset */
444 if (set->isReset)
445 return;
447 #ifdef MEMORY_CONTEXT_CHECKING
448 /* Check for corruption and leaks before freeing */
449 AllocSetCheck(context);
450 #endif
452 /* Clear chunk freelists */
453 MemSetAligned(set->freelist, 0, sizeof(set->freelist));
455 block = set->blocks;
457 /* New blocks list is either empty or just the keeper block */
458 set->blocks = set->keeper;
460 while (block != NULL)
462 AllocBlock next = block->next;
464 if (block == set->keeper)
466 /* Reset the block, but don't return it to malloc */
467 char *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ;
469 #ifdef CLOBBER_FREED_MEMORY
470 /* Wipe freed memory for debugging purposes */
471 memset(datastart, 0x7F, block->freeptr - datastart);
472 #endif
473 block->freeptr = datastart;
474 block->next = NULL;
476 else
478 /* Normal case, release the block */
479 #ifdef CLOBBER_FREED_MEMORY
480 /* Wipe freed memory for debugging purposes */
481 memset(block, 0x7F, block->freeptr - ((char *) block));
482 #endif
483 free(block);
485 block = next;
488 /* Reset block size allocation sequence, too */
489 set->nextBlockSize = set->initBlockSize;
491 set->isReset = true;
495 * AllocSetDelete
496 * Frees all memory which is allocated in the given set,
497 * in preparation for deletion of the set.
499 * Unlike AllocSetReset, this *must* free all resources of the set.
500 * But note we are not responsible for deleting the context node itself.
502 static void
503 AllocSetDelete(MemoryContext context)
505 AllocSet set = (AllocSet) context;
506 AllocBlock block = set->blocks;
508 AssertArg(AllocSetIsValid(set));
510 #ifdef MEMORY_CONTEXT_CHECKING
511 /* Check for corruption and leaks before freeing */
512 AllocSetCheck(context);
513 #endif
515 /* Make it look empty, just in case... */
516 MemSetAligned(set->freelist, 0, sizeof(set->freelist));
517 set->blocks = NULL;
518 set->keeper = NULL;
520 while (block != NULL)
522 AllocBlock next = block->next;
524 #ifdef CLOBBER_FREED_MEMORY
525 /* Wipe freed memory for debugging purposes */
526 memset(block, 0x7F, block->freeptr - ((char *) block));
527 #endif
528 free(block);
529 block = next;
534 * AllocSetAlloc
535 * Returns pointer to allocated memory of given size; memory is added
536 * to the set.
538 static void *
539 AllocSetAlloc(MemoryContext context, Size size)
541 AllocSet set = (AllocSet) context;
542 AllocBlock block;
543 AllocChunk chunk;
544 int fidx;
545 Size chunk_size;
546 Size blksize;
548 AssertArg(AllocSetIsValid(set));
551 * If requested size exceeds maximum for chunks, allocate an entire block
552 * for this request.
554 if (size > set->allocChunkLimit)
556 chunk_size = MAXALIGN(size);
557 blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
558 block = (AllocBlock) malloc(blksize);
559 if (block == NULL)
561 MemoryContextStats(TopMemoryContext);
562 ereport(ERROR,
563 (errcode(ERRCODE_OUT_OF_MEMORY),
564 errmsg("out of memory"),
565 errdetail("Failed on request of size %lu.",
566 (unsigned long) size)));
568 block->aset = set;
569 block->freeptr = block->endptr = ((char *) block) + blksize;
571 chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ);
572 chunk->aset = set;
573 chunk->size = chunk_size;
574 #ifdef MEMORY_CONTEXT_CHECKING
575 chunk->requested_size = size;
576 /* set mark to catch clobber of "unused" space */
577 if (size < chunk_size)
578 ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
579 #endif
580 #ifdef RANDOMIZE_ALLOCATED_MEMORY
581 /* fill the allocated space with junk */
582 randomize_mem((char *) AllocChunkGetPointer(chunk), size);
583 #endif
586 * Stick the new block underneath the active allocation block, so that
587 * we don't lose the use of the space remaining therein.
589 if (set->blocks != NULL)
591 block->next = set->blocks->next;
592 set->blocks->next = block;
594 else
596 block->next = NULL;
597 set->blocks = block;
600 set->isReset = false;
602 AllocAllocInfo(set, chunk);
603 return AllocChunkGetPointer(chunk);
607 * Request is small enough to be treated as a chunk. Look in the
608 * corresponding free list to see if there is a free chunk we could reuse.
609 * If one is found, remove it from the free list, make it again a member
610 * of the alloc set and return its data address.
612 fidx = AllocSetFreeIndex(size);
613 chunk = set->freelist[fidx];
614 if (chunk != NULL)
616 Assert(chunk->size >= size);
618 set->freelist[fidx] = (AllocChunk) chunk->aset;
620 chunk->aset = (void *) set;
622 #ifdef MEMORY_CONTEXT_CHECKING
623 chunk->requested_size = size;
624 /* set mark to catch clobber of "unused" space */
625 if (size < chunk->size)
626 ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
627 #endif
628 #ifdef RANDOMIZE_ALLOCATED_MEMORY
629 /* fill the allocated space with junk */
630 randomize_mem((char *) AllocChunkGetPointer(chunk), size);
631 #endif
633 /* isReset must be false already */
634 Assert(!set->isReset);
636 AllocAllocInfo(set, chunk);
637 return AllocChunkGetPointer(chunk);
641 * Choose the actual chunk size to allocate.
643 chunk_size = (1 << ALLOC_MINBITS) << fidx;
644 Assert(chunk_size >= size);
647 * If there is enough room in the active allocation block, we will put the
648 * chunk into that block. Else must start a new one.
650 if ((block = set->blocks) != NULL)
652 Size availspace = block->endptr - block->freeptr;
654 if (availspace < (chunk_size + ALLOC_CHUNKHDRSZ))
657 * The existing active (top) block does not have enough room for
658 * the requested allocation, but it might still have a useful
659 * amount of space in it. Once we push it down in the block list,
660 * we'll never try to allocate more space from it. So, before we
661 * do that, carve up its free space into chunks that we can put on
662 * the set's freelists.
664 * Because we can only get here when there's less than
665 * ALLOC_CHUNK_LIMIT left in the block, this loop cannot iterate
666 * more than ALLOCSET_NUM_FREELISTS-1 times.
668 while (availspace >= ((1 << ALLOC_MINBITS) + ALLOC_CHUNKHDRSZ))
670 Size availchunk = availspace - ALLOC_CHUNKHDRSZ;
671 int a_fidx = AllocSetFreeIndex(availchunk);
674 * In most cases, we'll get back the index of the next larger
675 * freelist than the one we need to put this chunk on. The
676 * exception is when availchunk is exactly a power of 2.
678 if (availchunk != (1 << (a_fidx + ALLOC_MINBITS)))
680 a_fidx--;
681 Assert(a_fidx >= 0);
682 availchunk = (1 << (a_fidx + ALLOC_MINBITS));
685 chunk = (AllocChunk) (block->freeptr);
687 block->freeptr += (availchunk + ALLOC_CHUNKHDRSZ);
688 availspace -= (availchunk + ALLOC_CHUNKHDRSZ);
690 chunk->size = availchunk;
691 #ifdef MEMORY_CONTEXT_CHECKING
692 chunk->requested_size = 0; /* mark it free */
693 #endif
694 chunk->aset = (void *) set->freelist[a_fidx];
695 set->freelist[a_fidx] = chunk;
698 /* Mark that we need to create a new block */
699 block = NULL;
704 * Time to create a new regular (multi-chunk) block?
706 if (block == NULL)
708 Size required_size;
711 * The first such block has size initBlockSize, and we double the
712 * space in each succeeding block, but not more than maxBlockSize.
714 blksize = set->nextBlockSize;
715 set->nextBlockSize <<= 1;
716 if (set->nextBlockSize > set->maxBlockSize)
717 set->nextBlockSize = set->maxBlockSize;
720 * If initBlockSize is less than ALLOC_CHUNK_LIMIT, we could need more
721 * space... but try to keep it a power of 2.
723 required_size = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
724 while (blksize < required_size)
725 blksize <<= 1;
727 /* Try to allocate it */
728 block = (AllocBlock) malloc(blksize);
731 * We could be asking for pretty big blocks here, so cope if malloc
732 * fails. But give up if there's less than a meg or so available...
734 while (block == NULL && blksize > 1024 * 1024)
736 blksize >>= 1;
737 if (blksize < required_size)
738 break;
739 block = (AllocBlock) malloc(blksize);
742 if (block == NULL)
744 MemoryContextStats(TopMemoryContext);
745 ereport(ERROR,
746 (errcode(ERRCODE_OUT_OF_MEMORY),
747 errmsg("out of memory"),
748 errdetail("Failed on request of size %lu.",
749 (unsigned long) size)));
752 block->aset = set;
753 block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
754 block->endptr = ((char *) block) + blksize;
757 * If this is the first block of the set, make it the "keeper" block.
758 * Formerly, a keeper block could only be created during context
759 * creation, but allowing it to happen here lets us have fast reset
760 * cycling even for contexts created with minContextSize = 0; that way
761 * we don't have to force space to be allocated in contexts that might
762 * never need any space. Don't mark an oversize block as a keeper,
763 * however.
765 if (set->keeper == NULL && blksize == set->initBlockSize)
766 set->keeper = block;
768 block->next = set->blocks;
769 set->blocks = block;
773 * OK, do the allocation
775 chunk = (AllocChunk) (block->freeptr);
777 block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
778 Assert(block->freeptr <= block->endptr);
780 chunk->aset = (void *) set;
781 chunk->size = chunk_size;
782 #ifdef MEMORY_CONTEXT_CHECKING
783 chunk->requested_size = size;
784 /* set mark to catch clobber of "unused" space */
785 if (size < chunk->size)
786 ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
787 #endif
788 #ifdef RANDOMIZE_ALLOCATED_MEMORY
789 /* fill the allocated space with junk */
790 randomize_mem((char *) AllocChunkGetPointer(chunk), size);
791 #endif
793 set->isReset = false;
795 AllocAllocInfo(set, chunk);
796 return AllocChunkGetPointer(chunk);
800 * AllocSetFree
801 * Frees allocated memory; memory is removed from the set.
803 static void
804 AllocSetFree(MemoryContext context, void *pointer)
806 AllocSet set = (AllocSet) context;
807 AllocChunk chunk = AllocPointerGetChunk(pointer);
809 AllocFreeInfo(set, chunk);
811 #ifdef MEMORY_CONTEXT_CHECKING
812 /* Test for someone scribbling on unused space in chunk */
813 if (chunk->requested_size < chunk->size)
814 if (((char *) pointer)[chunk->requested_size] != 0x7E)
815 elog(WARNING, "detected write past chunk end in %s %p",
816 set->header.name, chunk);
817 #endif
819 if (chunk->size > set->allocChunkLimit)
822 * Big chunks are certain to have been allocated as single-chunk
823 * blocks. Find the containing block and return it to malloc().
825 AllocBlock block = set->blocks;
826 AllocBlock prevblock = NULL;
828 while (block != NULL)
830 if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ))
831 break;
832 prevblock = block;
833 block = block->next;
835 if (block == NULL)
836 elog(ERROR, "could not find block containing chunk %p", chunk);
837 /* let's just make sure chunk is the only one in the block */
838 Assert(block->freeptr == ((char *) block) +
839 (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ));
841 /* OK, remove block from aset's list and free it */
842 if (prevblock == NULL)
843 set->blocks = block->next;
844 else
845 prevblock->next = block->next;
846 #ifdef CLOBBER_FREED_MEMORY
847 /* Wipe freed memory for debugging purposes */
848 memset(block, 0x7F, block->freeptr - ((char *) block));
849 #endif
850 free(block);
852 else
854 /* Normal case, put the chunk into appropriate freelist */
855 int fidx = AllocSetFreeIndex(chunk->size);
857 chunk->aset = (void *) set->freelist[fidx];
859 #ifdef CLOBBER_FREED_MEMORY
860 /* Wipe freed memory for debugging purposes */
861 memset(pointer, 0x7F, chunk->size);
862 #endif
864 #ifdef MEMORY_CONTEXT_CHECKING
865 /* Reset requested_size to 0 in chunks that are on freelist */
866 chunk->requested_size = 0;
867 #endif
868 set->freelist[fidx] = chunk;
873 * AllocSetRealloc
874 * Returns new pointer to allocated memory of given size; this memory
875 * is added to the set. Memory associated with given pointer is copied
876 * into the new memory, and the old memory is freed.
878 static void *
879 AllocSetRealloc(MemoryContext context, void *pointer, Size size)
881 AllocSet set = (AllocSet) context;
882 AllocChunk chunk = AllocPointerGetChunk(pointer);
883 Size oldsize = chunk->size;
885 #ifdef MEMORY_CONTEXT_CHECKING
886 /* Test for someone scribbling on unused space in chunk */
887 if (chunk->requested_size < oldsize)
888 if (((char *) pointer)[chunk->requested_size] != 0x7E)
889 elog(WARNING, "detected write past chunk end in %s %p",
890 set->header.name, chunk);
891 #endif
893 /* isReset must be false already */
894 Assert(!set->isReset);
897 * Chunk sizes are aligned to power of 2 in AllocSetAlloc(). Maybe the
898 * allocated area already is >= the new size. (In particular, we always
899 * fall out here if the requested size is a decrease.)
901 if (oldsize >= size)
903 #ifdef MEMORY_CONTEXT_CHECKING
904 #ifdef RANDOMIZE_ALLOCATED_MEMORY
905 /* We can only fill the extra space if we know the prior request */
906 if (size > chunk->requested_size)
907 randomize_mem((char *) AllocChunkGetPointer(chunk) + chunk->requested_size,
908 size - chunk->requested_size);
909 #endif
911 chunk->requested_size = size;
912 /* set mark to catch clobber of "unused" space */
913 if (size < oldsize)
914 ((char *) pointer)[size] = 0x7E;
915 #endif
916 return pointer;
919 if (oldsize > set->allocChunkLimit)
922 * The chunk must have been allocated as a single-chunk block. Find
923 * the containing block and use realloc() to make it bigger with
924 * minimum space wastage.
926 AllocBlock block = set->blocks;
927 AllocBlock prevblock = NULL;
928 Size chksize;
929 Size blksize;
931 while (block != NULL)
933 if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ))
934 break;
935 prevblock = block;
936 block = block->next;
938 if (block == NULL)
939 elog(ERROR, "could not find block containing chunk %p", chunk);
940 /* let's just make sure chunk is the only one in the block */
941 Assert(block->freeptr == ((char *) block) +
942 (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ));
944 /* Do the realloc */
945 chksize = MAXALIGN(size);
946 blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
947 block = (AllocBlock) realloc(block, blksize);
948 if (block == NULL)
950 MemoryContextStats(TopMemoryContext);
951 ereport(ERROR,
952 (errcode(ERRCODE_OUT_OF_MEMORY),
953 errmsg("out of memory"),
954 errdetail("Failed on request of size %lu.",
955 (unsigned long) size)));
957 block->freeptr = block->endptr = ((char *) block) + blksize;
959 /* Update pointers since block has likely been moved */
960 chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ);
961 if (prevblock == NULL)
962 set->blocks = block;
963 else
964 prevblock->next = block;
965 chunk->size = chksize;
967 #ifdef MEMORY_CONTEXT_CHECKING
968 #ifdef RANDOMIZE_ALLOCATED_MEMORY
969 /* We can only fill the extra space if we know the prior request */
970 randomize_mem((char *) AllocChunkGetPointer(chunk) + chunk->requested_size,
971 size - chunk->requested_size);
972 #endif
974 chunk->requested_size = size;
975 /* set mark to catch clobber of "unused" space */
976 if (size < chunk->size)
977 ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
978 #endif
980 return AllocChunkGetPointer(chunk);
982 else
985 * Small-chunk case. We just do this by brute force, ie, allocate a
986 * new chunk and copy the data. Since we know the existing data isn't
987 * huge, this won't involve any great memcpy expense, so it's not
988 * worth being smarter. (At one time we tried to avoid memcpy when it
989 * was possible to enlarge the chunk in-place, but that turns out to
990 * misbehave unpleasantly for repeated cycles of
991 * palloc/repalloc/pfree: the eventually freed chunks go into the
992 * wrong freelist for the next initial palloc request, and so we leak
993 * memory indefinitely. See pgsql-hackers archives for 2007-08-11.)
995 AllocPointer newPointer;
997 /* allocate new chunk */
998 newPointer = AllocSetAlloc((MemoryContext) set, size);
1000 /* transfer existing data (certain to fit) */
1001 memcpy(newPointer, pointer, oldsize);
1003 /* free old chunk */
1004 AllocSetFree((MemoryContext) set, pointer);
1006 return newPointer;
1011 * AllocSetGetChunkSpace
1012 * Given a currently-allocated chunk, determine the total space
1013 * it occupies (including all memory-allocation overhead).
1015 static Size
1016 AllocSetGetChunkSpace(MemoryContext context, void *pointer)
1018 AllocChunk chunk = AllocPointerGetChunk(pointer);
1020 return chunk->size + ALLOC_CHUNKHDRSZ;
1024 * AllocSetIsEmpty
1025 * Is an allocset empty of any allocated space?
1027 static bool
1028 AllocSetIsEmpty(MemoryContext context)
1030 AllocSet set = (AllocSet) context;
1033 * For now, we say "empty" only if the context is new or just reset. We
1034 * could examine the freelists to determine if all space has been freed,
1035 * but it's not really worth the trouble for present uses of this
1036 * functionality.
1038 if (set->isReset)
1039 return true;
1040 return false;
1044 * AllocSetStats
1045 * Displays stats about memory consumption of an allocset.
1047 static void
1048 AllocSetStats(MemoryContext context, int level)
1050 AllocSet set = (AllocSet) context;
1051 long nblocks = 0;
1052 long nchunks = 0;
1053 long totalspace = 0;
1054 long freespace = 0;
1055 AllocBlock block;
1056 AllocChunk chunk;
1057 int fidx;
1058 int i;
1060 for (block = set->blocks; block != NULL; block = block->next)
1062 nblocks++;
1063 totalspace += block->endptr - ((char *) block);
1064 freespace += block->endptr - block->freeptr;
1066 for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++)
1068 for (chunk = set->freelist[fidx]; chunk != NULL;
1069 chunk = (AllocChunk) chunk->aset)
1071 nchunks++;
1072 freespace += chunk->size + ALLOC_CHUNKHDRSZ;
1076 for (i = 0; i < level; i++)
1077 fprintf(stderr, " ");
1079 fprintf(stderr,
1080 "%s: %lu total in %ld blocks; %lu free (%ld chunks); %lu used\n",
1081 set->header.name, totalspace, nblocks, freespace, nchunks,
1082 totalspace - freespace);
1086 #ifdef MEMORY_CONTEXT_CHECKING
1089 * AllocSetCheck
1090 * Walk through chunks and check consistency of memory.
1092 * NOTE: report errors as WARNING, *not* ERROR or FATAL. Otherwise you'll
1093 * find yourself in an infinite loop when trouble occurs, because this
1094 * routine will be entered again when elog cleanup tries to release memory!
1096 static void
1097 AllocSetCheck(MemoryContext context)
1099 AllocSet set = (AllocSet) context;
1100 char *name = set->header.name;
1101 AllocBlock block;
1103 for (block = set->blocks; block != NULL; block = block->next)
1105 char *bpoz = ((char *) block) + ALLOC_BLOCKHDRSZ;
1106 long blk_used = block->freeptr - bpoz;
1107 long blk_data = 0;
1108 long nchunks = 0;
1111 * Empty block - empty can be keeper-block only
1113 if (!blk_used)
1115 if (set->keeper != block)
1116 elog(WARNING, "problem in alloc set %s: empty block %p",
1117 name, block);
1121 * Chunk walker
1123 while (bpoz < block->freeptr)
1125 AllocChunk chunk = (AllocChunk) bpoz;
1126 Size chsize,
1127 dsize;
1128 char *chdata_end;
1130 chsize = chunk->size; /* aligned chunk size */
1131 dsize = chunk->requested_size; /* real data */
1132 chdata_end = ((char *) chunk) + (ALLOC_CHUNKHDRSZ + dsize);
1135 * Check chunk size
1137 if (dsize > chsize)
1138 elog(WARNING, "problem in alloc set %s: req size > alloc size for chunk %p in block %p",
1139 name, chunk, block);
1140 if (chsize < (1 << ALLOC_MINBITS))
1141 elog(WARNING, "problem in alloc set %s: bad size %lu for chunk %p in block %p",
1142 name, (unsigned long) chsize, chunk, block);
1144 /* single-chunk block? */
1145 if (chsize > set->allocChunkLimit &&
1146 chsize + ALLOC_CHUNKHDRSZ != blk_used)
1147 elog(WARNING, "problem in alloc set %s: bad single-chunk %p in block %p",
1148 name, chunk, block);
1151 * If chunk is allocated, check for correct aset pointer. (If it's
1152 * free, the aset is the freelist pointer, which we can't check as
1153 * easily...)
1155 if (dsize > 0 && chunk->aset != (void *) set)
1156 elog(WARNING, "problem in alloc set %s: bogus aset link in block %p, chunk %p",
1157 name, block, chunk);
1160 * Check for overwrite of "unallocated" space in chunk
1162 if (dsize > 0 && dsize < chsize && *chdata_end != 0x7E)
1163 elog(WARNING, "problem in alloc set %s: detected write past chunk end in block %p, chunk %p",
1164 name, block, chunk);
1166 blk_data += chsize;
1167 nchunks++;
1169 bpoz += ALLOC_CHUNKHDRSZ + chsize;
1172 if ((blk_data + (nchunks * ALLOC_CHUNKHDRSZ)) != blk_used)
1173 elog(WARNING, "problem in alloc set %s: found inconsistent memory block %p",
1174 name, block);
1178 #endif /* MEMORY_CONTEXT_CHECKING */