[t][TT #1119] Convert t/op/bitwise.t to PIR
[parrot.git] / src / gc / alloc_resources.c
blob0967e224aea9f9940420733f2483fc3a3a2e8079
1 /*
2 Copyright (C) 2001-2009, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 src/gc/alloc_resources.c - Allocate and deallocate buffer resources such as
8 STRINGS.
10 =head1 DESCRIPTION
12 Functions to manage non-PObj memory, including strings and buffers.
14 =head2 Parrot Memory Management Code
16 =over 4
18 =cut
22 #include "parrot/parrot.h"
23 #include "gc_private.h"
26 #define RECLAMATION_FACTOR 0.20
27 #define WE_WANT_EVER_GROWING_ALLOCATIONS 0
29 /* show allocated blocks on stderr */
30 #define RESOURCE_DEBUG 0
31 #define RESOURCE_DEBUG_SIZE 1000000
33 #define POOL_SIZE 65536 * 2
35 typedef void (*compact_f) (Interp *, Variable_Size_Pool *);
37 /* HEADERIZER HFILE: src/gc/gc_private.h */
39 /* HEADERIZER BEGIN: static */
40 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
42 static void alloc_new_block(PARROT_INTERP,
43 size_t size,
44 ARGMOD(Variable_Size_Pool *pool),
45 ARGIN(const char *why))
46 __attribute__nonnull__(1)
47 __attribute__nonnull__(3)
48 __attribute__nonnull__(4)
49 FUNC_MODIFIES(*pool);
51 PARROT_CANNOT_RETURN_NULL
52 PARROT_WARN_UNUSED_RESULT
53 static const char* buffer_location(PARROT_INTERP, ARGIN(const PObj *b))
54 __attribute__nonnull__(1)
55 __attribute__nonnull__(2);
57 static void check_fixed_size_obj_pool(ARGMOD(Fixed_Size_Pool * pool))
58 __attribute__nonnull__(1)
59 FUNC_MODIFIES(* pool);
61 static void check_memory_system(PARROT_INTERP)
62 __attribute__nonnull__(1);
64 static void check_var_size_obj_pool(ARGMOD(Variable_Size_Pool *pool))
65 __attribute__nonnull__(1)
66 FUNC_MODIFIES(*pool);
68 static void debug_print_buf(PARROT_INTERP, ARGIN(const Buffer *b))
69 __attribute__nonnull__(1)
70 __attribute__nonnull__(2);
72 PARROT_MALLOC
73 PARROT_CANNOT_RETURN_NULL
74 static Variable_Size_Pool * new_memory_pool(
75 size_t min_block,
76 NULLOK(compact_f compact));
78 #define ASSERT_ARGS_alloc_new_block __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
79 PARROT_ASSERT_ARG(interp) \
80 , PARROT_ASSERT_ARG(pool) \
81 , PARROT_ASSERT_ARG(why))
82 #define ASSERT_ARGS_buffer_location __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
83 PARROT_ASSERT_ARG(interp) \
84 , PARROT_ASSERT_ARG(b))
85 #define ASSERT_ARGS_check_fixed_size_obj_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
86 PARROT_ASSERT_ARG(pool))
87 #define ASSERT_ARGS_check_memory_system __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
88 PARROT_ASSERT_ARG(interp))
89 #define ASSERT_ARGS_check_var_size_obj_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
90 PARROT_ASSERT_ARG(pool))
91 #define ASSERT_ARGS_debug_print_buf __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
92 PARROT_ASSERT_ARG(interp) \
93 , PARROT_ASSERT_ARG(b))
94 #define ASSERT_ARGS_new_memory_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
95 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
96 /* HEADERIZER END: static */
101 =item C<static void alloc_new_block(PARROT_INTERP, size_t size,
102 Variable_Size_Pool *pool, const char *why)>
104 Allocate a new memory block. We allocate either the requested size or the
105 default size, whichever is larger. Add the new block to the given memory
106 pool. The given C<char *why> text is used for debugging.
108 =cut
112 static void
113 alloc_new_block(PARROT_INTERP, size_t size, ARGMOD(Variable_Size_Pool *pool),
114 ARGIN(const char *why))
116 ASSERT_ARGS(alloc_new_block)
117 Memory_Block *new_block;
119 const size_t alloc_size = (size > pool->minimum_block_size)
120 ? size : pool->minimum_block_size;
122 #if RESOURCE_DEBUG
123 fprintf(stderr, "new_block (%s) size %u -> %u\n",
124 why, size, alloc_size);
125 #else
126 UNUSED(why)
127 #endif
129 /* Allocate a new block. Header info's on the front */
130 new_block = (Memory_Block *)mem_internal_allocate_zeroed(
131 sizeof (Memory_Block) + alloc_size);
133 if (!new_block) {
134 fprintf(stderr, "out of mem allocsize = %d\n", (int)alloc_size);
135 exit(EXIT_FAILURE);
138 new_block->free = alloc_size;
139 new_block->size = alloc_size;
141 new_block->next = NULL;
142 new_block->start = (char *)new_block + sizeof (Memory_Block);
143 new_block->top = new_block->start;
145 /* Note that we've allocated it */
146 interp->mem_pools->memory_allocated += alloc_size;
148 /* If this is for a public pool, add it to the list */
149 new_block->prev = pool->top_block;
151 /* If we're not first, then tack us on the list */
152 if (pool->top_block)
153 pool->top_block->next = new_block;
155 pool->top_block = new_block;
156 pool->total_allocated += alloc_size;
161 =item C<void * mem_allocate(PARROT_INTERP, size_t size, Variable_Size_Pool
162 *pool)>
164 Allocates memory for headers.
166 Alignment problems history:
168 See L<http://archive.develooper.com/perl6-internals%40perl.org/msg12310.html>
169 for details.
171 - return aligned pointer *if needed*
172 - return strings et al at unaligned i.e. void* boundaries
173 - remember alignment in a buffer header bit
174 use this in compaction code
175 - reduce alignment to a reasonable value i.e. MALLOC_ALIGNMENT
176 aka 2*sizeof (size_t) or just 8 (TODO make a config hint)
178 Buffer memory layout:
180 +-----------------+
181 | ref_count |f | # GC header
182 obj->bufstart -> +-----------------+
183 | data |
186 * if PObj_is_COWable is set, then we have
187 - a ref_count, {inc, dec}remented by 2 always
188 - the lo bit 'f' means 'is being forwarded" - what TAIL_flag was
190 * if PObj_align_FLAG is set, obj->bufstart is aligned like discussed above
191 * obj->buflen is the usable length excluding the optional GC part.
193 =cut
197 PARROT_MALLOC
198 PARROT_CANNOT_RETURN_NULL
199 void *
200 mem_allocate(PARROT_INTERP, size_t size, ARGMOD(Variable_Size_Pool *pool))
202 ASSERT_ARGS(mem_allocate)
203 void *return_val;
205 /* we always should have one block at least */
206 PARROT_ASSERT(pool->top_block);
208 /* If not enough room, try to find some */
209 if (pool->top_block->free < size) {
211 * force a GC mark run to get live flags set
212 * for incremental M&S collection is run from there
213 * but only if there may be something worth collecting!
214 * TODO pass required allocation size to the GC system,
215 * so that collection can be skipped if needed
217 if (!interp->mem_pools->gc_mark_block_level
218 && interp->mem_pools->mem_allocs_since_last_collect) {
219 Parrot_gc_mark_and_sweep(interp, GC_trace_stack_FLAG);
221 if (interp->gc_sys->sys_type != INF) {
222 /* Compact the pool if allowed and worthwhile */
223 if (pool->compact) {
224 /* don't bother reclaiming if it's only a small amount */
225 if ((pool->possibly_reclaimable * pool->reclaim_factor +
226 pool->guaranteed_reclaimable) > size) {
227 (*pool->compact) (interp, pool);
232 if (pool->top_block->free < size) {
233 if (pool->minimum_block_size < 65536 * 16)
234 pool->minimum_block_size *= 2;
236 * TODO - Big blocks
238 * Mark the block as big block (it has just one item)
239 * And don't set big blocks as the top_block.
241 alloc_new_block(interp, size, pool, "compact failed");
243 interp->mem_pools->mem_allocs_since_last_collect++;
245 if (pool->top_block->free < size) {
246 fprintf(stderr, "out of mem\n");
247 exit(EXIT_FAILURE);
252 /* TODO inline the fast path */
253 return_val = pool->top_block->top;
254 pool->top_block->top += size;
255 pool->top_block->free -= size;
257 return return_val;
262 =item C<static const char* buffer_location(PARROT_INTERP, const PObj *b)>
264 Recturns a constant string representing the location of the given
265 PObj C<b> in one of the PMC registers. If the PMC is not located
266 in one of the PMC registers of the current context, returns the
267 string C<"???">.
269 =cut
273 #if RESOURCE_DEBUG
274 PARROT_CANNOT_RETURN_NULL
275 PARROT_WARN_UNUSED_RESULT
276 static const char*
277 buffer_location(PARROT_INTERP, ARGIN(const PObj *b))
279 ASSERT_ARGS(buffer_location)
280 int i;
281 static char reg[10];
283 Parrot_Context* const ctx = CONTEXT(interp);
285 for (i = 0; i < ctx->n_regs_used[REGNO_STR]; ++i) {
286 PObj * const obj = (PObj *) CTX_REG_STR(interp, ctx, i);
287 if (obj == b) {
288 sprintf(reg, "S%d", i);
289 return reg;
293 return "???";
298 =item C<static void debug_print_buf(PARROT_INTERP, const Buffer *b)>
300 Prints a debug statement with information about the given PObj C<b>.
301 =cut
305 static void
306 debug_print_buf(PARROT_INTERP, ARGIN(const Buffer *b))
308 ASSERT_ARGS(debug_print_buf)
309 fprintf(stderr, "found %p, len %d, flags 0x%08x at %s\n",
310 b, (int)Buffer_buflen(b), (uint)PObj_get_FLAGS(b),
311 buffer_location(interp, b));
313 #endif
317 =back
319 =head2 Compaction Code
321 =over 4
323 =item C<void compact_pool(PARROT_INTERP, Variable_Size_Pool *pool)>
325 Compact the string buffer pool. Does not perform a GC scan, or mark items
326 as being alive in any way.
328 =cut
332 void
333 compact_pool(PARROT_INTERP, ARGMOD(Variable_Size_Pool *pool))
335 ASSERT_ARGS(compact_pool)
336 INTVAL j;
337 UINTVAL total_size;
339 Memory_Block *new_block; /* A pointer to our working block */
340 char *cur_spot; /* Where we're currently copying to */
342 Fixed_Size_Arena *cur_buffer_arena;
343 Memory_Pools * const mem_pools = interp->mem_pools;
345 /* Bail if we're blocked */
346 if (mem_pools->gc_sweep_block_level)
347 return;
349 ++mem_pools->gc_sweep_block_level;
351 /* We're collecting */
352 mem_pools->mem_allocs_since_last_collect = 0;
353 mem_pools->header_allocs_since_last_collect = 0;
354 mem_pools->gc_collect_runs++;
356 /* total - reclaimable == currently used. Add a minimum block to the
357 * current amount, so we can avoid having to allocate it in the future. */
359 Memory_Block *cur_block = pool->top_block;
361 total_size = 0;
363 while (cur_block) {
365 * TODO - Big blocks
367 * Currently all available blocks are compacted into on new
368 * block with total_size. This is more than suboptimal, if
369 * the block has just one live item from a big allocation.
371 * But currently it's unknown if the buffer memory is alive
372 * as the live bits are in Buffer headers. We have to run the
373 * compaction loop below to check liveness. OTOH if this
374 * compaction is running through all the buffer headers, there
375 * is no relation to the block.
378 * Moving the life bit into the buffer thus also solves this
379 * problem easily.
381 total_size += cur_block->size - cur_block->free;
382 cur_block = cur_block->prev;
386 * XXX for some reason the guarantee isn't correct
387 * TODO check why
390 /* total_size -= pool->guaranteed_reclaimable; */
392 /* this makes for ever increasing allocations but fewer collect runs */
393 #if WE_WANT_EVER_GROWING_ALLOCATIONS
394 total_size += pool->minimum_block_size;
395 #endif
397 /* Snag a block big enough for everything */
398 alloc_new_block(interp, total_size, pool, "inside compact");
400 new_block = pool->top_block;
402 /* Start at the beginning */
403 cur_spot = new_block->start;
405 /* Run through all the Buffer header pools and copy */
406 for (j = (INTVAL)mem_pools->num_sized - 1; j >= 0; --j) {
407 Fixed_Size_Pool * const header_pool = mem_pools->sized_header_pools[j];
408 UINTVAL object_size;
410 if (!header_pool)
411 continue;
413 object_size = header_pool->object_size;
415 for (cur_buffer_arena = header_pool->last_Arena;
416 cur_buffer_arena;
417 cur_buffer_arena = cur_buffer_arena->prev) {
418 Buffer *b = (Buffer *) cur_buffer_arena->start_objects;
419 UINTVAL i;
420 const size_t objects_end = cur_buffer_arena->used;
422 for (i = objects_end; i; --i) {
423 INTVAL *ref_count = NULL;
425 /* ! (on_free_list | constant | external | sysmem) */
426 if (Buffer_buflen(b) && PObj_is_movable_TESTALL(b)) {
427 ptrdiff_t offset = 0;
428 #if RESOURCE_DEBUG
429 if (Buffer_buflen(b) >= RESOURCE_DEBUG_SIZE)
430 debug_print_buf(interp, b);
431 #endif
433 /* we can't perform the math all the time, because
434 * strstart might be in unallocated memory */
435 if (PObj_is_COWable_TEST(b)) {
436 ref_count = Buffer_bufrefcountptr(b);
438 if (PObj_is_string_TEST(b)) {
439 offset = (ptrdiff_t)((STRING *)b)->strstart -
440 (ptrdiff_t)Buffer_bufstart(b);
444 /* buffer has already been moved; just change the header */
445 if (PObj_COW_TEST(b) &&
446 (ref_count && *ref_count & Buffer_moved_FLAG)) {
447 /* Find out who else references our data */
448 Buffer * const hdr = *((Buffer **)Buffer_bufstart(b));
451 PARROT_ASSERT(PObj_is_COWable_TEST(b));
453 /* Make sure they know that we own it too */
454 PObj_COW_SET(hdr);
456 /* TODO incr ref_count, after fixing string too
457 * Now make sure we point to where the other guy does */
458 Buffer_bufstart(b) = Buffer_bufstart(hdr);
460 /* And if we're a string, update strstart */
461 /* Somewhat of a hack, but if we get per-pool
462 * collections, it should help ease the pain */
463 if (PObj_is_string_TEST(b)) {
464 ((STRING *)b)->strstart = (char *)Buffer_bufstart(b) +
465 offset;
468 else {
469 cur_spot = aligned_mem(b, cur_spot);
471 if (PObj_is_COWable_TEST(b)) {
472 INTVAL * const new_ref_count = ((INTVAL*) cur_spot) - 1;
473 *new_ref_count = 2;
476 /* Copy our memory to the new pool */
477 memcpy(cur_spot, Buffer_bufstart(b), Buffer_buflen(b));
479 /* If we're COW */
480 if (PObj_COW_TEST(b)) {
481 PARROT_ASSERT(PObj_is_COWable_TEST(b));
483 /* Let the old buffer know how to find us */
484 *((Buffer **)Buffer_bufstart(b)) = b;
486 /* No guarantees that our data is still COW, so
487 * assume not, and let the above code fix-up */
488 PObj_COW_CLEAR(b);
490 /* Finally, let the tail know that we've moved, so
491 * that any other references can know to look for
492 * us and not re-copy */
493 if (ref_count)
494 *ref_count |= Buffer_moved_FLAG;
497 Buffer_bufstart(b) = cur_spot;
499 if (PObj_is_string_TEST(b)) {
500 ((STRING *)b)->strstart = (char *)Buffer_bufstart(b) +
501 offset;
504 cur_spot += Buffer_buflen(b);
507 b = (Buffer *)((char *)b + object_size);
512 /* Okay, we're done with the copy. Set the bits in the pool struct */
513 /* First, where we allocate next */
514 new_block->top = cur_spot;
516 PARROT_ASSERT(new_block->size >= (size_t)new_block->top -
517 (size_t)new_block->start);
519 /* How much is free. That's the total size minus the amount we used */
520 new_block->free = new_block->size - (new_block->top - new_block->start);
522 mem_pools->memory_collected += (new_block->top - new_block->start);
524 /* Now we're done. We're already on the pool's free list, so let us be the
525 * only one on the free list and free the rest */
527 Memory_Block *cur_block = new_block->prev;
529 PARROT_ASSERT(new_block == pool->top_block);
531 while (cur_block) {
532 Memory_Block * const next_block = cur_block->prev;
534 /* Note that we don't have it any more */
535 mem_pools->memory_allocated -= cur_block->size;
537 /* We know the pool body and pool header are a single chunk, so
538 * this is enough to get rid of 'em both */
539 mem_internal_free(cur_block);
540 cur_block = next_block;
543 /* Set our new pool as the only pool */
544 new_block->prev = NULL;
545 pool->total_allocated = total_size;
548 pool->guaranteed_reclaimable = 0;
549 pool->possibly_reclaimable = 0;
551 --mem_pools->gc_sweep_block_level;
556 =item C<size_t aligned_size(const Buffer *buffer, size_t len)>
558 Determines the size of Buffer C<buffer> which has nominal length C<len>.
559 The actual size in RAM of the Buffer might be different because of
560 alignment issues.
562 =cut
566 PARROT_PURE_FUNCTION
567 PARROT_WARN_UNUSED_RESULT
568 size_t
569 aligned_size(ARGIN(const Buffer *buffer), size_t len)
571 ASSERT_ARGS(aligned_size)
572 if (PObj_is_COWable_TEST(buffer))
573 len += sizeof (void*);
574 if (PObj_aligned_TEST(buffer))
575 len = (len + BUFFER_ALIGN_1) & BUFFER_ALIGN_MASK;
576 else
577 len = (len + WORD_ALIGN_1) & WORD_ALIGN_MASK;
578 return len;
583 =item C<char * aligned_mem(const Buffer *buffer, char *mem)>
585 Returns a pointer to the aligned allocated storage for Buffer C<buffer>,
586 which might not be the same as the pointer to C<buffeR> because of
587 memory alignment.
589 =cut
593 PARROT_CANNOT_RETURN_NULL
594 PARROT_WARN_UNUSED_RESULT
595 char *
596 aligned_mem(ARGIN(const Buffer *buffer), ARGIN(char *mem))
598 ASSERT_ARGS(aligned_mem)
599 if (PObj_is_COWable_TEST(buffer))
600 mem += sizeof (void*);
601 if (PObj_aligned_TEST(buffer))
602 mem = (char*)(((unsigned long)(mem + BUFFER_ALIGN_1)) &
603 BUFFER_ALIGN_MASK);
604 else
605 mem = (char*)(((unsigned long)(mem + WORD_ALIGN_1)) & WORD_ALIGN_MASK);
607 return mem;
612 =item C<size_t aligned_string_size(size_t len)>
614 Determines the size of a string of length C<len> in RAM, accounting for
615 alignment.
617 =cut
621 PARROT_CONST_FUNCTION
622 PARROT_WARN_UNUSED_RESULT
623 size_t
624 aligned_string_size(size_t len)
626 ASSERT_ARGS(aligned_string_size)
627 len += sizeof (void*);
628 len = (len + WORD_ALIGN_1) & WORD_ALIGN_MASK;
629 return len;
634 =back
636 =head2 Parrot Re/Allocate Code
638 =over 4
640 =item C<static Variable_Size_Pool * new_memory_pool(size_t min_block, compact_f
641 compact)>
643 Allocate a new C<Variable_Size_Pool> structures, and set some initial values.
644 return a pointer to the new pool.
646 =cut
650 PARROT_MALLOC
651 PARROT_CANNOT_RETURN_NULL
652 static Variable_Size_Pool *
653 new_memory_pool(size_t min_block, NULLOK(compact_f compact))
655 ASSERT_ARGS(new_memory_pool)
656 Variable_Size_Pool * const pool = mem_internal_allocate_typed(Variable_Size_Pool);
658 pool->top_block = NULL;
659 pool->compact = compact;
660 pool->minimum_block_size = min_block;
661 pool->total_allocated = 0;
662 pool->guaranteed_reclaimable = 0;
663 pool->possibly_reclaimable = 0;
664 pool->reclaim_factor = RECLAMATION_FACTOR;
666 return pool;
671 =item C<void initialize_var_size_pools(PARROT_INTERP)>
673 Initialize the managed memory pools. Parrot maintains two C<Variable_Size_Pool>
674 structures, the general memory pool and the constant string pool. Create
675 and initialize both pool structures, and allocate initial blocks of memory
676 for both.
678 =cut
682 void
683 initialize_var_size_pools(PARROT_INTERP)
685 ASSERT_ARGS(initialize_var_size_pools)
686 Memory_Pools * const mem_pools = interp->mem_pools;
688 mem_pools->memory_pool = new_memory_pool(POOL_SIZE, &compact_pool);
689 alloc_new_block(interp, POOL_SIZE, mem_pools->memory_pool, "init");
691 /* Constant strings - not compacted */
692 mem_pools->constant_string_pool = new_memory_pool(POOL_SIZE, NULL);
693 alloc_new_block(interp, POOL_SIZE, mem_pools->constant_string_pool, "init");
699 =item C<void merge_pools(Variable_Size_Pool *dest, Variable_Size_Pool *source)>
701 Merge two memory pools together. Do this by moving all memory blocks
702 from the C<*source> pool into the C<*dest> pool. The C<source> pool
703 is emptied, but is not destroyed here.
705 =cut
709 void
710 merge_pools(ARGMOD(Variable_Size_Pool *dest), ARGMOD(Variable_Size_Pool *source))
712 ASSERT_ARGS(merge_pools)
713 Memory_Block *cur_block;
715 cur_block = source->top_block;
717 while (cur_block) {
718 Memory_Block * const next_block = cur_block->prev;
720 if (cur_block->free == cur_block->size)
721 mem_internal_free(cur_block);
722 else {
723 cur_block->next = NULL;
724 cur_block->prev = dest->top_block;
726 dest->top_block = cur_block;
727 dest->total_allocated += cur_block->size;
729 cur_block = next_block;
732 dest->guaranteed_reclaimable += source->guaranteed_reclaimable;
733 dest->possibly_reclaimable += dest->possibly_reclaimable;
735 source->top_block = NULL;
736 source->total_allocated = 0;
737 source->possibly_reclaimable = 0;
738 source->guaranteed_reclaimable = 0;
743 =item C<static void check_memory_system(PARROT_INTERP)>
745 Checks the memory system of parrot on any corruptions, including
746 the string system.
748 =cut
752 static void
753 check_memory_system(PARROT_INTERP)
755 ASSERT_ARGS(check_memory_system)
756 size_t i;
757 Memory_Pools * const mem_pools = interp->mem_pools;
759 check_var_size_obj_pool(mem_pools->memory_pool);
760 check_var_size_obj_pool(mem_pools->constant_string_pool);
761 check_fixed_size_obj_pool(mem_pools->pmc_pool);
762 check_fixed_size_obj_pool(mem_pools->constant_pmc_pool);
763 check_fixed_size_obj_pool(mem_pools->string_header_pool);
764 check_fixed_size_obj_pool(mem_pools->constant_string_header_pool);
766 for (i = 0; i < mem_pools->num_sized; i++) {
767 Fixed_Size_Pool * pool = mem_pools->sized_header_pools[i];
768 if (pool != NULL && pool != mem_pools->string_header_pool)
769 check_fixed_size_obj_pool(pool);
775 =item C<static void check_fixed_size_obj_pool(Fixed_Size_Pool * pool)>
777 Checks a small object pool, if it contains buffer it checks the buffers also.
779 =cut
783 static void
784 check_fixed_size_obj_pool(ARGMOD(Fixed_Size_Pool * pool))
786 ASSERT_ARGS(check_fixed_size_obj_pool)
787 size_t total_objects;
788 size_t last_free_list_count;
789 Fixed_Size_Arena * arena_walker;
790 size_t free_objects;
791 PObj * object;
792 size_t i;
793 size_t count;
794 GC_MS_PObj_Wrapper * pobj_walker;
796 count = 10000000; /*detect unendless loop just use big enough number*/
798 total_objects = pool->total_objects;
799 last_free_list_count = 1;
800 free_objects = 0;
802 arena_walker = pool->last_Arena;
803 while (arena_walker != NULL) {
804 total_objects -= arena_walker->total_objects;
805 object = (PObj*)arena_walker->start_objects;
806 for (i = 0; i < arena_walker->total_objects; ++i) {
807 if (PObj_on_free_list_TEST(object)) {
808 ++free_objects;
809 pobj_walker = (GC_MS_PObj_Wrapper*)object;
810 if (pobj_walker->next_ptr == NULL)
811 /* should happen only once at the end */
812 --last_free_list_count;
813 else {
814 /* next item on free list should also be flaged as free item */
815 pobj_walker = (GC_MS_PObj_Wrapper*)pobj_walker->next_ptr;
816 PARROT_ASSERT(PObj_on_free_list_TEST((PObj*)pobj_walker));
819 else if (pool->mem_pool != NULL) {
820 /*then it means we are a buffer*/
821 check_buffer_ptr((Buffer*)object, pool->mem_pool);
823 object = (PObj*)((char *)object + pool->object_size);
824 PARROT_ASSERT(--count);
826 /*check the list*/
827 if (arena_walker->prev != NULL)
828 PARROT_ASSERT(arena_walker->prev->next == arena_walker);
829 arena_walker = arena_walker->prev;
830 PARROT_ASSERT(--count);
833 count = 10000000;
835 PARROT_ASSERT(free_objects == pool->num_free_objects);
837 pobj_walker = (GC_MS_PObj_Wrapper*)pool->free_list;
838 while (pobj_walker != NULL) {
839 PARROT_ASSERT(pool->start_arena_memory <= (size_t)pobj_walker);
840 PARROT_ASSERT(pool->end_arena_memory > (size_t)pobj_walker);
841 PARROT_ASSERT(PObj_on_free_list_TEST((PObj*)pobj_walker));
842 --free_objects;
843 pobj_walker = (GC_MS_PObj_Wrapper*)pobj_walker->next_ptr;
844 PARROT_ASSERT(--count);
847 PARROT_ASSERT(total_objects == 0);
848 PARROT_ASSERT(last_free_list_count == 0 || pool->num_free_objects == 0);
849 PARROT_ASSERT(free_objects == 0);
854 =item C<static void check_var_size_obj_pool(Variable_Size_Pool *pool)>
856 Checks a memory pool, containing buffer data
858 =cut
862 static void
863 check_var_size_obj_pool(ARGMOD(Variable_Size_Pool *pool))
865 ASSERT_ARGS(check_var_size_obj_pool)
866 size_t count;
867 Memory_Block * block_walker;
868 count = 10000000; /*detect unendless loop just use big enough number*/
870 block_walker = (Memory_Block *)pool->top_block;
871 while (block_walker != NULL) {
872 PARROT_ASSERT(block_walker->start == (char *)block_walker +
873 sizeof (Memory_Block));
874 PARROT_ASSERT((size_t)(block_walker->top -
875 block_walker->start) == block_walker->size - block_walker->free);
877 /*check the list*/
878 if (block_walker->prev != NULL)
879 PARROT_ASSERT(block_walker->prev->next == block_walker);
880 block_walker = block_walker->prev;
881 PARROT_ASSERT(--count);
887 =item C<void check_buffer_ptr(Buffer * pobj, Variable_Size_Pool * pool)>
889 Checks wether the buffer is within the bounds of the memory pool
891 =cut
895 void
896 check_buffer_ptr(ARGMOD(Buffer * pobj), ARGMOD(Variable_Size_Pool * pool))
898 ASSERT_ARGS(check_buffer_ptr)
899 Memory_Block * cur_block = pool->top_block;
900 char * bufstart;
902 bufstart = (char*)Buffer_bufstart(pobj);
904 if (bufstart == NULL && Buffer_buflen(pobj) == 0)
905 return;
907 if (PObj_external_TEST(pobj) || PObj_sysmem_TEST(pobj)) {
908 /*buffer does not come from the memory pool*/
909 if (PObj_is_string_TEST(pobj)) {
910 PARROT_ASSERT(((STRING *) pobj)->strstart >=
911 (char *) Buffer_bufstart(pobj));
912 PARROT_ASSERT(((STRING *) pobj)->strstart +
913 ((STRING *) pobj)->strlen <=
914 (char *) Buffer_bufstart(pobj) + Buffer_buflen(pobj));
916 return;
919 if (PObj_is_COWable_TEST(pobj))
920 bufstart -= sizeof (void*);
922 while (cur_block) {
923 if ((char *)bufstart >= cur_block->start &&
924 (char *)Buffer_bufstart(pobj) +
925 Buffer_buflen(pobj) < cur_block->start + cur_block->size) {
926 if (PObj_is_string_TEST(pobj)) {
927 PARROT_ASSERT(((STRING *)pobj)->strstart >=
928 (char *)Buffer_bufstart(pobj));
929 PARROT_ASSERT(((STRING *)pobj)->strstart +
930 ((STRING *)pobj)->strlen <= (char *)Buffer_bufstart(pobj) +
931 Buffer_buflen(pobj));
933 return;
935 cur_block = cur_block->prev;
937 PARROT_ASSERT(0);
942 =back
944 =head1 SEE ALSO
946 F<src/gc/memory.c>.
948 =head1 HISTORY
950 Initial version by Dan on 2001.10.2.
952 =cut
957 * Local variables:
958 * c-file-style: "parrot"
959 * End:
960 * vim: expandtab shiftwidth=4: