* src/pbc_merge.c:
[parrot.git] / src / gc / dod.c
blobf9d4d10aa1f4adcec8fcaac304fdb41bac83f626
1 /*
2 Copyright (C) 2001-2008, The Perl Foundation.
3 $Id$
5 =head1 NAME
7 src/gc/dod.c - Dead object destruction of the various headers
9 =head1 DESCRIPTION
11 This file implements I<dead object destruction>. This is documented in
12 PDD 9 with supplementary notes in F<docs/dev/dod.pod> and
13 F<docs/memory_internals.pod>.
15 It's possible to turn on/off the checking of the system stack and
16 processor registers. The actual checking is set up in F<src/cpu_dep.c>
17 and is performed in the function C<trace_memory_block> here.
19 There's also a verbose mode for garbage collection.
21 =head1 FUNCTIONS
23 =over 4
25 =cut
29 #define DOD_C_SOURCE
30 #include "parrot/parrot.h"
31 #include "parrot/dod.h"
33 /* HEADERIZER HFILE: include/parrot/dod.h */
35 /* HEADERIZER BEGIN: static */
36 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
38 static void clear_live_bits(ARGIN(const Small_Object_Pool *pool))
39 __attribute__nonnull__(1);
41 PARROT_CONST_FUNCTION
42 static size_t find_common_mask(PARROT_INTERP, size_t val1, size_t val2)
43 __attribute__nonnull__(1);
45 static void mark_special(PARROT_INTERP, ARGIN(PMC *obj))
46 __attribute__nonnull__(1)
47 __attribute__nonnull__(2);
49 static int sweep_cb(PARROT_INTERP,
50 ARGMOD(Small_Object_Pool *pool),
51 int flag,
52 ARGMOD(void *arg))
53 __attribute__nonnull__(1)
54 __attribute__nonnull__(2)
55 __attribute__nonnull__(4)
56 FUNC_MODIFIES(*pool)
57 FUNC_MODIFIES(*arg);
59 static int trace_active_PMCs(PARROT_INTERP, int trace_stack)
60 __attribute__nonnull__(1);
62 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
63 /* HEADERIZER END: static */
66 /* Set this to 1 to see if unanchored objects are found in system areas.
67 * Please note: these objects might be bogus
69 #define GC_VERBOSE 0
71 #if ! DISABLE_GC_DEBUG
72 /* Set when walking the system stack */
73 int CONSERVATIVE_POINTER_CHASING = 0;
74 #endif
78 =item C<static void mark_special>
80 Marks the children of a special PMC. Handles the marking necessary
81 for shared PMCs, and ensures timely marking of high-priority PMCs.
82 Ensures PMC_EXT structures are properly organized for garbage
83 collection.
85 =cut
89 static void
90 mark_special(PARROT_INTERP, ARGIN(PMC *obj))
92 int hi_prio;
93 Arenas *arena_base;
96 * If the object is shared, we have to use the arena and dod
97 * pointers of the originating interpreter.
99 * We are possibly changing another interpreter's data here, so
100 * the mark phase of DOD must run only on one interpreter of a pool
101 * at a time. However, freeing unused objects can occur in parallel.
102 * And: to be sure that a shared object is dead, we have to finish
103 * the mark phase of all interpreters in a pool that might reference
104 * the object.
106 if (PObj_is_PMC_shared_TEST(obj)) {
107 interp = PMC_sync(obj)->owner;
108 PARROT_ASSERT(interp);
109 /* XXX FIXME hack */
110 if (!interp->arena_base->dod_mark_ptr)
111 interp->arena_base->dod_mark_ptr = obj;
114 arena_base = interp->arena_base;
116 if (PObj_needs_early_DOD_TEST(obj))
117 ++arena_base->num_early_PMCs_seen;
119 if (PObj_high_priority_DOD_TEST(obj) && arena_base->dod_trace_ptr) {
120 /* set obj's parent to high priority */
121 PObj_high_priority_DOD_SET(arena_base->dod_trace_ptr);
122 hi_prio = 1;
124 else
125 hi_prio = 0;
127 if (obj->pmc_ext) {
128 PMC * const tptr = arena_base->dod_trace_ptr;
130 ++arena_base->num_extended_PMCs;
132 * XXX this basically invalidates the high-priority marking
133 * of PMCs by putting all PMCs onto the front of the list.
134 * The reason for this is the by far better cache locality
135 * when aggregates and their contents are marked "together".
137 * To enable high priority marking again we should probably
138 * use a second pointer chain, which is, when not empty,
139 * processed first.
141 if (hi_prio && tptr) {
142 if (PMC_next_for_GC(tptr) == tptr) {
143 PMC_next_for_GC(obj) = obj;
145 else {
146 /* put it at the head of the list */
147 PMC_next_for_GC(obj) = PMC_next_for_GC(tptr);
150 PMC_next_for_GC(tptr) = (PMC*)obj;
152 else {
153 /* put it on the end of the list */
154 PMC_next_for_GC(arena_base->dod_mark_ptr) = obj;
156 /* Explicitly make the tail of the linked list be
157 * self-referential */
158 arena_base->dod_mark_ptr = PMC_next_for_GC(obj) = obj;
161 else if (PObj_custom_mark_TEST(obj)) {
162 PObj_get_FLAGS(obj) |= PObj_custom_GC_FLAG;
163 VTABLE_mark(interp, obj);
169 =item C<void pobject_lives>
171 Marks the PObj as "alive" for the Garbage Collector. Takes a pointer to a
172 PObj, and performs necessary marking to ensure the PMC and it's direct
173 children nodes are marked alive. Implementation is generally dependant on
174 the particular garbage collector in use.
176 =cut
180 PARROT_API
181 void
182 pobject_lives(PARROT_INTERP, ARGMOD(PObj *obj))
184 PARROT_ASSERT(obj);
185 #if PARROT_GC_GMS
186 do {
187 if (!PObj_live_TEST(obj) && \
188 PObj_to_GMSH(obj)->gen->gen_no >= interp->gc_generation) \
189 parrot_gc_gms_pobject_lives(interp, obj); \
190 } while (0);
191 #else /* not PARROT_GC_GMS */
193 /* if object is live or on free list return */
194 if (PObj_is_live_or_free_TESTALL(obj))
195 return;
197 # if ! DISABLE_GC_DEBUG
198 # if GC_VERBOSE
199 if (CONSERVATIVE_POINTER_CHASING)
200 fprintf(stderr, "GC Warning! Unanchored %s %p found in system areas \n",
201 PObj_is_PMC_TEST(obj) ? "PMC" : "Buffer", obj);
203 # endif
204 # endif
205 /* mark it live */
206 PObj_live_SET(obj);
208 /* if object is a PMC and its real_self pointer points to another
209 * PMC, we must mark that. */
210 if (PObj_is_PMC_TEST(obj)) {
211 PMC * const p = (PMC *)obj;
213 if (p->real_self != p)
214 pobject_lives(interp, (PObj *)p->real_self);
216 /* if object is a PMC and contains buffers or PMCs, then attach the PMC
217 * to the chained mark list. */
218 if (PObj_is_special_PMC_TEST(obj))
219 mark_special(interp, p);
221 # ifndef NDEBUG
222 else if (p->pmc_ext && PMC_metadata(p))
223 fprintf(stderr, "GC: error obj %p (%s) has properties\n",
224 (void *)p, (char*)p->vtable->whoami->strstart);
225 # endif
227 # if GC_VERBOSE
228 /* buffer GC_DEBUG stuff */
229 if (GC_DEBUG(interp) && PObj_report_TEST(obj))
230 fprintf(stderr, "GC: buffer %p pointing to %p marked live\n",
231 obj, PObj_bufstart((Buffer *)obj));
232 # endif
233 #endif /* PARROT_GC_GMS */
238 =item C<int Parrot_dod_trace_root>
240 Traces the root set. Returns 0 if it's a lazy DOD run and all objects
241 that need timely destruction were found.
243 C<trace_stack> can have these values:
245 0 ... trace normal roots, no system areas
246 1 ... trace whole root set
247 2 ... trace system areas only
249 =cut
254 Parrot_dod_trace_root(PARROT_INTERP, int trace_stack)
256 Arenas * const arena_base = interp->arena_base;
257 parrot_context_t *ctx;
259 /* note: adding locals here did cause increased DOD runs */
260 unsigned int i = 0;
262 if (trace_stack == 2) {
263 trace_system_areas(interp);
264 return 0;
267 if (interp->profile)
268 Parrot_dod_profile_start(interp);
270 /* We have to start somewhere; the interpreter globals is a good place */
271 if (!arena_base->dod_mark_start) {
272 arena_base->dod_mark_start
273 = arena_base->dod_mark_ptr
274 = interp->iglobals;
277 /* mark it as used */
278 pobject_lives(interp, (PObj *)interp->iglobals);
280 /* mark the current context. */
281 ctx = CONTEXT(interp);
282 mark_context(interp, ctx);
284 /* mark the dynamic environment. */
285 mark_stack(interp, interp->dynamic_env);
288 * mark vtable->data
290 * XXX these PMCs are constant and shouldn't get collected
291 * but t/library/dumper* fails w/o this marking.
293 * It seems that the Class PMC gets DODed - these should
294 * get created as constant PMCs.
296 mark_vtables(interp);
298 /* mark the root_namespace */
299 pobject_lives(interp, (PObj *)interp->root_namespace);
301 /* mark the concurrency scheduler */
302 if (interp->scheduler)
303 pobject_lives(interp, (PObj *)interp->scheduler);
305 /* s. packfile.c */
306 mark_const_subs(interp);
308 /* mark caches and freelists */
309 mark_object_cache(interp);
311 /* Now mark the class hash */
312 pobject_lives(interp, (PObj *)interp->class_hash);
314 /* Mark the registry */
315 PARROT_ASSERT(interp->DOD_registry);
316 pobject_lives(interp, (PObj *)interp->DOD_registry);
318 /* Mark the transaction log */
319 /* XXX do this more generically? */
320 if (interp->thread_data && interp->thread_data->stm_log)
321 Parrot_STM_mark_transaction(interp);
323 /* Walk the iodata */
324 Parrot_IOData_mark(interp, interp->piodata);
326 /* quick check if we can already bail out */
327 if (arena_base->lazy_dod && arena_base->num_early_PMCs_seen >=
328 arena_base->num_early_DOD_PMCs)
329 return 0;
331 /* Find important stuff on the system stack */
332 if (trace_stack)
333 trace_system_areas(interp);
335 if (interp->profile)
336 Parrot_dod_profile_end(interp, PARROT_PROF_DOD_p1);
338 return 1;
344 =item C<static int trace_active_PMCs>
346 Performs a full trace run and marks all the PMCs as active if they
347 are. Returns whether the run completed, that is, whether it's safe
348 to proceed with GC.
350 =cut
354 static int
355 trace_active_PMCs(PARROT_INTERP, int trace_stack)
357 if (!Parrot_dod_trace_root(interp, trace_stack))
358 return 0;
360 /* Okay, we've marked the whole root set, and should have a good-sized
361 * list of things to look at. Run through it */
362 return Parrot_dod_trace_children(interp, (size_t) -1);
367 =item C<int Parrot_dod_trace_children>
369 Returns whether the tracing process has completed.
371 =cut
376 Parrot_dod_trace_children(PARROT_INTERP, size_t how_many)
378 Arenas * const arena_base = interp->arena_base;
379 const int lazy_dod = arena_base->lazy_dod;
380 PMC *current = arena_base->dod_mark_start;
382 const UINTVAL mask = PObj_data_is_PMC_array_FLAG | PObj_custom_mark_FLAG;
385 * First phase of mark is finished. Now if we are the owner
386 * of a shared pool, we must run the mark phase of other
387 * interpreters in our pool, so that live shared PMCs in that
388 * interpreter are appended to our mark_ptrs chain.
390 * If there is a count of shared PMCs and we have already seen
391 * all these, we could skip that.
393 if (interp->profile)
394 Parrot_dod_profile_start(interp);
396 pt_DOD_mark_root_finished(interp);
398 do {
399 const UINTVAL bits = PObj_get_FLAGS(current) & mask;
400 PMC *next;
402 if (lazy_dod && arena_base->num_early_PMCs_seen >=
403 arena_base->num_early_DOD_PMCs) {
404 return 0;
407 arena_base->dod_trace_ptr = current;
409 /* short-term hack to color objects black */
410 PObj_get_FLAGS(current) |= PObj_custom_GC_FLAG;
412 /* clearing the flag is much more expensive then testing */
413 if (!PObj_needs_early_DOD_TEST(current))
414 PObj_high_priority_DOD_CLEAR(current);
416 /* mark properties */
417 if (PMC_metadata(current))
418 pobject_lives(interp, (PObj *)PMC_metadata(current));
420 /* Start by checking if there's anything at all. This assumes that the
421 * largest percentage of PMCs won't have anything in their data
422 * pointer that we need to trace. */
423 if (bits) {
424 if (bits == PObj_data_is_PMC_array_FLAG)
425 Parrot_dod_trace_pmc_data(interp, current);
426 else {
427 /* All that's left is the custom */
428 PARROT_ASSERT(!PObj_on_free_list_TEST(current));
429 VTABLE_mark(interp, current);
433 next = PMC_next_for_GC(current);
435 if (!PMC_IS_NULL(next) && next == current)
436 break;
438 current = next;
439 } while (--how_many > 0);
441 arena_base->dod_mark_start = current;
442 arena_base->dod_trace_ptr = NULL;
444 if (interp->profile)
445 Parrot_dod_profile_end(interp, PARROT_PROF_DOD_p2);
447 return 1;
452 =item C<void Parrot_dod_trace_pmc_data>
454 If the PMC is an array of PMCs, trace all elements in the array as children.
455 Touches each object in the array to mark it as being alive. To determine
456 whether a PMC is an array to be marked in this way, it is tested for the
457 C<PObj_data_is_PMC_array_FLAG> flag.
459 =cut
463 void
464 Parrot_dod_trace_pmc_data(PARROT_INTERP, ARGIN(PMC *p))
466 /* malloced array of PMCs */
467 PMC ** const data = PMC_data_typed(p, PMC **);
469 if (data) {
470 INTVAL i;
472 for (i = PMC_int_val(p) - 1; i >= 0; --i)
473 if (data[i])
474 pobject_lives(interp, (PObj *)data[i]);
478 #ifdef GC_IS_MALLOC
482 =item C<void clear_cow>
484 Clears the COW ref count.
486 =cut
490 void
491 clear_cow(PARROT_INTERP, ARGMOD(Small_Object_Pool *pool), int cleanup)
493 const UINTVAL object_size = pool->object_size;
494 Small_Object_Arena *cur_arena;
496 /* clear refcount for COWable objects. */
497 for (cur_arena = pool->last_Arena;
498 NULL != cur_arena; cur_arena = cur_arena->prev) {
499 UINTVAL i;
500 Buffer *b = cur_arena->start_objects;
502 for (i = 0; i < cur_arena->used; i++) {
503 if (!PObj_on_free_list_TEST(b)) {
504 if (cleanup) {
505 /* clear COWed external FLAG */
506 PObj_external_CLEAR(b);
508 /* if cleanup (Parrot_destroy) constants are dead too */
509 PObj_constant_CLEAR(b);
510 PObj_live_CLEAR(b);
513 if (PObj_COW_TEST(b) && PObj_bufstart(b) &&
514 !PObj_external_TEST(b)) {
515 INTVAL * const refcount = PObj_bufrefcountptr(b);
516 *refcount = 0;
520 b = (Buffer *)((char *)b + object_size);
527 =item C<void used_cow>
529 Finds other users of COW's C<bufstart>.
531 =cut
535 void
536 used_cow(PARROT_INTERP, ARGMOD(Small_Object_Pool *pool), int cleanup)
538 const UINTVAL object_size = pool->object_size;
539 Small_Object_Arena *cur_arena;
541 for (cur_arena = pool->last_Arena;
542 NULL != cur_arena; cur_arena = cur_arena->prev) {
543 const Buffer *b = cur_arena->start_objects;
544 UINTVAL i;
546 for (i = 0; i < cur_arena->used; i++) {
547 if (!PObj_on_free_list_TEST(b) &&
548 PObj_COW_TEST(b) &&
549 PObj_bufstart(b) &&
550 !PObj_external_TEST(b)) {
552 INTVAL * const refcount = PObj_bufrefcountptr(b);
554 /* mark users of this bufstart by incrementing refcount */
555 if (PObj_live_TEST(b))
556 *refcount = 1 << 29; /* ~infinite usage */
557 else
558 (*refcount)++; /* dead usage */
561 b = (Buffer *)((char *)b + object_size);
565 #endif /* GC_IS_MALLOC */
569 =item C<void Parrot_dod_sweep>
571 Puts any buffers/PMCs that are now unused onto the pool's free list. If
572 C<GC_IS_MALLOC>, bufstart gets freed too, if possible. Avoids buffers that
573 are immune from collection (i.e. constant).
575 =cut
579 void
580 Parrot_dod_sweep(PARROT_INTERP, ARGMOD(Small_Object_Pool *pool))
582 UINTVAL total_used = 0;
583 const UINTVAL object_size = pool->object_size;
585 Small_Object_Arena *cur_arena;
586 dod_object_fn_type dod_object = pool->dod_object;
588 #if GC_VERBOSE
589 if (Interp_trace_TEST(interp, 1)) {
590 Interp * const tracer = interp->debugger;
591 PMC *pio = PIO_STDERR(interp);
593 PIO_flush(interp, pio);
595 if (tracer) {
596 pio = PIO_STDERR(tracer);
597 PIO_flush(tracer, pio);
600 #endif
602 /* Run through all the buffer header pools and mark */
603 for (cur_arena = pool->last_Arena; cur_arena; cur_arena = cur_arena->prev) {
604 Buffer *b = (Buffer *)cur_arena->start_objects;
605 UINTVAL i;
607 /* loop only while there are objects in the arena */
608 for (i = cur_arena->total_objects; i; i--) {
610 if (PObj_on_free_list_TEST(b))
611 ; /* if it's on free list, do nothing */
612 else if (PObj_live_TEST(b)) {
613 total_used++;
614 PObj_live_CLEAR(b);
615 PObj_get_FLAGS(b) &= ~PObj_custom_GC_FLAG;
617 else {
618 /* it must be dead */
620 #if GC_VERBOSE
621 if (Interp_trace_TEST(interp, 1)) {
622 fprintf(stderr, "Freeing pobject %p\n", b);
623 if (PObj_is_PMC_TEST(b)) {
624 fprintf(stderr, "\t = PMC type %s\n",
625 (char*) ((PMC*)b)->vtable->whoami->strstart);
628 #endif
630 if (PObj_is_shared_TEST(b)) {
631 /* only mess with shared objects if we
632 * (and thus everyone) is suspended for
633 * a GC run.
634 * XXX wrong thing to do with "other" GCs
636 if (!(interp->thread_data &&
637 (interp->thread_data->state &
638 THREAD_STATE_SUSPENDED_GC))) {
639 ++total_used;
640 goto next;
644 dod_object(interp, pool, b);
646 pool->add_free_object(interp, pool, b);
648 next:
649 b = (Buffer *)((char *)b + object_size);
653 pool->num_free_objects = pool->total_objects - total_used;
658 =item C<void Parrot_dod_free_pmc>
660 Frees a PMC that is no longer being used. Calls a custom C<destroy>
661 VTABLE method if one is available. If the PMC uses a PMC_EXT
662 structure, that is freed as well.
664 =cut
668 void
669 Parrot_dod_free_pmc(PARROT_INTERP, SHIM(Small_Object_Pool *pool),
670 ARGMOD(PObj *p))
672 PMC * const pmc = (PMC *)p;
673 Arenas * const arena_base = interp->arena_base;
675 /* TODO collect objects with finalizers */
676 if (PObj_needs_early_DOD_TEST(p))
677 --arena_base->num_early_DOD_PMCs;
679 if (PObj_active_destroy_TEST(p))
680 VTABLE_destroy(interp, pmc);
682 if (PObj_is_PMC_EXT_TEST(p))
683 Parrot_free_pmc_ext(interp, pmc);
685 #ifndef NDEBUG
687 pmc->pmc_ext = (PMC_EXT *)0xdeadbeef;
688 pmc->vtable = (VTABLE *)0xdeadbeef;
689 PMC_pmc_val(pmc) = (PMC *)0xdeadbeef;
691 #endif
697 =item C<void Parrot_free_pmc_ext>
699 Frees the C<PMC_EXT> structure attached to a PMC, if it exists.
701 =cut
705 void
706 Parrot_free_pmc_ext(PARROT_INTERP, ARGMOD(PMC *p))
708 /* if the PMC has a PMC_EXT structure, return it to the pool/arena */
709 Arenas * const arena_base = interp->arena_base;
710 Small_Object_Pool * const ext_pool = arena_base->pmc_ext_pool;
712 if (PObj_is_PMC_shared_TEST(p) && PMC_sync(p)) {
713 MUTEX_DESTROY(PMC_sync(p)->pmc_lock);
714 mem_internal_free(PMC_sync(p));
715 PMC_sync(p) = NULL;
718 if (p->pmc_ext)
719 ext_pool->add_free_object(interp, ext_pool, p->pmc_ext);
721 ext_pool->num_free_objects++;
723 p->pmc_ext = NULL;
728 =item C<void Parrot_dod_free_sysmem>
730 If the PMC uses memory allocated directly from the system, this function
731 frees that memory.
733 =cut
737 void
738 Parrot_dod_free_sysmem(SHIM_INTERP, SHIM(Small_Object_Pool *pool),
739 ARGMOD(PObj *b))
741 /* has sysmem allocated, e.g. string_pin */
742 if (PObj_sysmem_TEST(b) && PObj_bufstart(b))
743 mem_sys_free(PObj_bufstart(b));
745 PObj_bufstart(b) = NULL;
746 PObj_buflen(b) = 0;
751 =item C<void Parrot_dod_free_buffer_malloc>
753 Frees the given buffer, returning the storage space to the operating system
754 and removing it from Parrot's memory management system. If the buffer is COW,
755 The buffer is not freed if the reference count is greater then 1.
757 =cut
761 void
762 Parrot_dod_free_buffer_malloc(SHIM_INTERP, SHIM(Small_Object_Pool *pool),
763 ARGMOD(PObj *b))
766 /* free allocated space at (int *)bufstart - 1, but not if it used COW or is
767 * external */
768 PObj_buflen(b) = 0;
770 if (!PObj_bufstart(b) || PObj_is_external_or_free_TESTALL(b))
771 return;
773 if (PObj_COW_TEST(b)) {
774 INTVAL * const refcount = PObj_bufrefcountptr(b);
776 if (--(*refcount) == 0) {
777 mem_sys_free(refcount); /* the actual bufstart */
780 else
781 mem_sys_free(PObj_bufrefcountptr(b));
786 =item C<void Parrot_dod_free_buffer>
788 Frees a buffer, returning it to the memory pool for Parrot to possibly
789 reuse later.
791 =cut
795 void
796 Parrot_dod_free_buffer(SHIM_INTERP, ARGMOD(Small_Object_Pool *pool), ARGMOD(PObj *b))
798 Memory_Pool * const mem_pool = (Memory_Pool *)pool->mem_pool;
800 /* XXX Jarkko reported that on irix pool->mem_pool was NULL, which really
801 * shouldn't happen */
802 if (mem_pool) {
803 if (!PObj_COW_TEST(b))
804 mem_pool->guaranteed_reclaimable += PObj_buflen(b);
806 mem_pool->possibly_reclaimable += PObj_buflen(b);
809 PObj_buflen(b) = 0;
812 #ifndef PLATFORM_STACK_WALK
816 =item C<static size_t find_common_mask>
818 Finds a mask covering the longest common bit-prefix of C<val1>
819 and C<val2>.
821 =cut
825 PARROT_CONST_FUNCTION
826 static size_t
827 find_common_mask(PARROT_INTERP, size_t val1, size_t val2)
829 int i;
830 const int bound = sizeof (size_t) * 8;
832 /* Shifting a value by its size (in bits) or larger is undefined behaviour.
833 So need an explicit check to return 0 if there is no prefix, rather than
834 attempting to rely on (say) 0xFFFFFFFF << 32 being 0. */
835 for (i = 0; i < bound; i++) {
836 if (val1 == val2)
837 return ~(size_t)0 << i;
839 val1 >>= 1;
840 val2 >>= 1;
843 if (val1 == val2) {
844 PARROT_ASSERT(i == bound);
845 return 0;
848 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INTERP_ERROR,
849 "Unexpected condition in find_common_mask()!\n");
854 =item C<void trace_mem_block>
856 Traces the memory block between C<lo_var_ptr> and C<hi_var_ptr>.
857 Attempt to find pointers to PObjs or buffers, and mark them as "alive"
858 if found. See src/cpu_dep.c for more information about tracing memory
859 areas.
861 =cut
865 void
866 trace_mem_block(PARROT_INTERP, size_t lo_var_ptr, size_t hi_var_ptr)
868 size_t prefix;
869 ptrdiff_t cur_var_ptr;
871 const size_t buffer_min = get_min_buffer_address(interp);
872 const size_t buffer_max = get_max_buffer_address(interp);
873 const size_t pmc_min = get_min_pmc_address(interp);
874 const size_t pmc_max = get_max_pmc_address(interp);
876 const size_t mask =
877 find_common_mask(interp,
878 buffer_min < pmc_min ? buffer_min : pmc_min,
879 buffer_max > pmc_max ? buffer_max : pmc_max);
881 if (!lo_var_ptr || !hi_var_ptr)
882 return;
884 if (lo_var_ptr < hi_var_ptr) {
885 const size_t tmp_ptr = hi_var_ptr;
886 hi_var_ptr = lo_var_ptr;
887 lo_var_ptr = tmp_ptr;
890 /* Get the expected prefix */
891 prefix = mask & buffer_min;
893 for (cur_var_ptr = hi_var_ptr;
894 (ptrdiff_t)cur_var_ptr < (ptrdiff_t)lo_var_ptr;
895 cur_var_ptr = (size_t)((ptrdiff_t)cur_var_ptr + sizeof (void *))) {
896 const size_t ptr = *(size_t *)cur_var_ptr;
898 /* Do a quick approximate range check by bit-masking */
899 if ((ptr & mask) == prefix || !prefix) {
900 /* Note that what we find via the stack or registers are not
901 * guaranteed to be live pmcs/buffers, and could very well have
902 * had their bufstart/vtable destroyed due to the linked list of
903 * free headers... */
904 if (pmc_min <= ptr && ptr < pmc_max &&
905 is_pmc_ptr(interp, (void *)ptr)) {
906 /* ...so ensure that pobject_lives checks PObj_on_free_list_FLAG
907 * before adding it to the next_for_GC list, to have
908 * vtable->mark() called. */
909 pobject_lives(interp, (PObj *)ptr);
911 else if (buffer_min <= ptr && ptr < buffer_max &&
912 is_buffer_ptr(interp, (void *)ptr)) {
913 /* ...and since pobject_lives doesn't care about bufstart, it
914 * doesn't really matter if it sets a flag */
915 pobject_lives(interp, (PObj *)ptr);
920 return;
922 #endif
926 =item C<static void clear_live_bits>
928 Runs through all PMC arenas and clear live bits. This is used to reset
929 the GC system after a full system sweep.
931 =cut
935 static void
936 clear_live_bits(ARGIN(const Small_Object_Pool *pool))
938 Small_Object_Arena *arena;
939 const UINTVAL object_size = pool->object_size;
941 for (arena = pool->last_Arena; arena; arena = arena->prev) {
942 Buffer *b = (Buffer *)arena->start_objects;
943 UINTVAL i;
945 for (i = 0; i < arena->used; i++) {
946 PObj_live_CLEAR(b);
947 b = (Buffer *)((char *)b + object_size);
955 =item C<void Parrot_dod_clear_live_bits>
957 Resets the PMC pool, so all objects are marked as "White". This
958 is done after a GC run to reset the system and prepare for the
959 next mark phase.
961 =cut
965 void
966 Parrot_dod_clear_live_bits(PARROT_INTERP)
968 Small_Object_Pool * const pool = interp->arena_base->pmc_pool;
969 clear_live_bits(pool);
974 =item C<void Parrot_dod_profile_start>
976 Records the start time of a DOD run when profiling is enabled.
978 =cut
982 void
983 Parrot_dod_profile_start(PARROT_INTERP)
985 if (Interp_flags_TEST(interp, PARROT_PROFILE_FLAG))
986 interp->profile->dod_time = Parrot_floatval_time();
991 =item C<void Parrot_dod_profile_end>
993 Records the end time of the DOD part C<what> run when profiling is
994 enabled. Also record start time of next part.
996 =cut
1000 void
1001 Parrot_dod_profile_end(PARROT_INTERP, int what)
1003 if (Interp_flags_TEST(interp, PARROT_PROFILE_FLAG)) {
1004 RunProfile * const profile = interp->profile;
1005 const FLOATVAL now = Parrot_floatval_time();
1007 profile->data[what].numcalls++;
1008 profile->data[what].time += now - profile->dod_time;
1011 * we've recorded the time of a DOD/GC piece from
1012 * dod_time until now, so add this to the start of the
1013 * currently executing opcode, which hasn't run this
1014 * interval.
1016 profile->starttime += now - profile->dod_time;
1018 /* prepare start for next step */
1019 profile->dod_time = now;
1025 =item C<void Parrot_dod_ms_run_init>
1027 Prepares the collector for a mark & sweep DOD run. This is the
1028 initializer function for the MS garbage collector.
1030 =cut
1034 void
1035 Parrot_dod_ms_run_init(PARROT_INTERP)
1037 Arenas * const arena_base = interp->arena_base;
1039 arena_base->dod_trace_ptr = NULL;
1040 arena_base->dod_mark_start = NULL;
1041 arena_base->num_early_PMCs_seen = 0;
1042 arena_base->num_extended_PMCs = 0;
1047 =item C<static int sweep_cb>
1049 Sweeps the given pool for the MS collector. This function also ends
1050 the profiling timer, if profiling is enabled. Returns the total number
1051 of objects freed.
1053 =cut
1057 static int
1058 sweep_cb(PARROT_INTERP, ARGMOD(Small_Object_Pool *pool), int flag,
1059 ARGMOD(void *arg))
1061 int * const total_free = (int *) arg;
1063 #ifdef GC_IS_MALLOC
1064 if (flag & POOL_BUFFER)
1065 used_cow(interp, pool, 0);
1066 #endif
1068 Parrot_dod_sweep(interp, pool);
1070 #ifdef GC_IS_MALLOC
1071 if (flag & POOL_BUFFER)
1072 clear_cow(interp, pool, 0);
1073 #endif
1075 if (interp->profile && (flag & POOL_PMC))
1076 Parrot_dod_profile_end(interp, PARROT_PROF_DOD_cp);
1078 *total_free += pool->num_free_objects;
1080 return 0;
1085 =item C<void Parrot_dod_ms_run>
1087 Runs the stop-the-world mark & sweep (MS) collector.
1089 =cut
1093 void
1094 Parrot_dod_ms_run(PARROT_INTERP, int flags)
1096 Arenas * const arena_base = interp->arena_base;
1098 /* XXX these should go into the interpreter */
1099 int total_free = 0;
1101 if (arena_base->DOD_block_level)
1102 return;
1104 if (interp->debugger) {
1106 * if the other interpreter did a DOD run, it can set
1107 * live bits of shared objects, but these aren't reset, because
1108 * they are in a different arena. When now such a PMC points to
1109 * other non-shared object, these wouldn't be marked and hence
1110 * collected.
1112 Parrot_dod_clear_live_bits(interp);
1116 * the sync sweep is always at the end, so that
1117 * the live bits are cleared
1119 if (flags & GC_finish_FLAG) {
1120 clear_live_bits(interp->arena_base->pmc_pool);
1121 clear_live_bits(interp->arena_base->constant_pmc_pool);
1123 Parrot_dod_sweep(interp, interp->arena_base->pmc_pool);
1124 Parrot_dod_sweep(interp, interp->arena_base->constant_pmc_pool);
1126 return;
1129 ++arena_base->DOD_block_level;
1130 arena_base->lazy_dod = flags & GC_lazy_FLAG;
1132 /* tell the threading system that we're doing DOD mark */
1133 pt_DOD_start_mark(interp);
1134 Parrot_dod_ms_run_init(interp);
1136 /* compact STRING pools to collect free headers and allocated buffers */
1137 Parrot_go_collect(interp);
1139 /* Now go trace the PMCs */
1140 if (trace_active_PMCs(interp, flags & GC_trace_stack_FLAG)) {
1141 int ignored;
1143 arena_base->dod_trace_ptr = NULL;
1144 arena_base->dod_mark_ptr = NULL;
1146 /* mark is now finished */
1147 pt_DOD_stop_mark(interp);
1149 /* Now put unused PMCs and Buffers on the free list */
1150 ignored = Parrot_forall_header_pools(interp, POOL_BUFFER | POOL_PMC,
1151 (void*)&total_free, sweep_cb);
1152 UNUSED(ignored);
1154 if (interp->profile)
1155 Parrot_dod_profile_end(interp, PARROT_PROF_DOD_cb);
1157 else {
1158 pt_DOD_stop_mark(interp); /* XXX */
1160 /* successful lazy DOD count */
1161 ++arena_base->lazy_dod_runs;
1163 Parrot_dod_clear_live_bits(interp);
1164 if (interp->profile)
1165 Parrot_dod_profile_end(interp, PARROT_PROF_DOD_p2);
1168 /* Note it */
1169 arena_base->dod_runs++;
1170 --arena_base->DOD_block_level;
1172 return;
1178 =item C<void Parrot_do_dod_run>
1180 Calls the configured garbage collector to find and reclaim unused
1181 headers.
1183 =cut
1187 void
1188 Parrot_do_dod_run(PARROT_INTERP, UINTVAL flags)
1190 interp->arena_base->do_gc_mark(interp, flags);
1191 parrot_gc_context(interp);
1196 =back
1198 =head1 SEE ALSO
1200 F<include/parrot/dod.h>, F<src/cpu_dep.c>, F<docs/dev/dod.dev> and
1201 F<docs/pdds/pdd09_gc.pod>.
1203 =head1 HISTORY
1205 Initial version by Mike Lambert on 2002.05.27.
1207 =cut
1212 * Local variables:
1213 * c-file-style: "parrot"
1214 * End:
1215 * vim: expandtab shiftwidth=4: