The attached patch fixes a bunch of bugs. They are: >From before, rolled into this patch: + Creates a new flag, immortal, which is intended for GC use only, so it shouldn't be set in the init() function. This is used to prevent the GC from dod'ing the object. + PerlString now stores the string pointer in data instead of cache.struct_val . + buffer_ptrs work properly now, and check that they point to something before calling buffer_lives on it.
New fixes in this patch: + new_string_header nulls out the bufstart before returning it. This was causing problems when 'bufstart = parrot_allocate...' was causing a do_collect to be run, and it was referencing invalid memory. I also made new_pmc_header null out the data field as well, for consistency, and removed the nulling-out in pmc_new*. + Changed a conditional in stack_entry so that it looks up entry in the correct place in a borderline case. + Fixed the same thing that Peter Gibb's patched fixed wrt the GC linked list bug, but in what I think is a more efficient way. the next_for_GC linked list now *requires* that the tail element point to itself. This hack (I think this is a good hack, not a bad one, if documented) determines the 'end-of-list'. This allows the same logic to be used in mark_used to determine if the element is on the linked list. It avoids the need to add additional conditional branches into mark_used, which is probably a hotspot of the memory manager (haven't verified this, however). Please let me know if there are any issues with this that I should address. Mike Lambert
Index: parrot/pmc.c =================================================================== RCS file: /cvs/public/parrot/pmc.c,v retrieving revision 1.11 diff -u -r1.11 pmc.c --- parrot/pmc.c 10 Mar 2002 21:19:46 -0000 1.11 +++ parrot/pmc.c 29 Mar 2002 08:09:23 -0000 @@ -39,8 +39,8 @@ return NULL; } - pmc->flags = 0; - pmc->data = 0; + /* Ensure the PMC survives DOD during this function */ + pmc->flags |= PMC_immortal_FLAG; pmc->vtable = &(Parrot_base_vtables[base_type]); @@ -53,6 +53,9 @@ } pmc->vtable->init(interpreter, pmc, 0); + + /* Let the caller track this PMC */ + pmc->flags &= ~PMC_immortal_FLAG; return pmc; } @@ -67,8 +70,8 @@ return NULL; } - pmc->flags = 0; - pmc->data = 0; + /* Ensure the PMC survives DOD during this function */ + pmc->flags |= PMC_immortal_FLAG; pmc->vtable = &(Parrot_base_vtables[base_type]); @@ -81,6 +84,9 @@ } pmc->vtable->init(interpreter, pmc, size); + + /* Let the caller track this PMC */ + pmc->flags &= ~PMC_immortal_FLAG; return pmc; } Index: parrot/resources.c =================================================================== RCS file: /cvs/public/parrot/resources.c,v retrieving revision 1.35 diff -u -r1.35 resources.c --- parrot/resources.c 26 Mar 2002 16:33:01 -0000 1.35 +++ parrot/resources.c 29 Mar 2002 08:09:23 -0000 @@ -139,6 +139,8 @@ interpreter->active_PMCs++; /* Mark it live */ return_me->flags = PMC_live_FLAG; + /* Don't let it point to garbage memory */ + return_me->data = NULL; /* Return it */ return return_me; } @@ -242,6 +244,8 @@ interpreter->active_Buffers++; /* Mark it live */ return_me->flags = BUFFER_live_FLAG; + /* Don't let it point to garbage memory */ + return_me->bufstart = NULL; /* Return it */ return return_me; } @@ -348,6 +352,9 @@ /* Now put it on the end of the list */ current_end_of_list->next_for_GC = used_pmc; + /* Explicitly make the tail of the linked list be self-referential */ + used_pmc->next_for_GC = used_pmc; + /* return the PMC we were passed as the new end of the list */ return used_pmc; } @@ -355,20 +362,20 @@ /* Do a full trace run and mark all the PMCs as active if they are */ static void trace_active_PMCs(struct Parrot_Interp *interpreter) { - PMC *last, *current; /* Pointers to the last marked PMC and the - currently being processed PMC. */ + PMC *last, *current, *prev; /* Pointers to the last marked PMC, the + currently being processed PMC, and in + the previously processed PMC in a loop. */ unsigned int i, j, chunks_traced; Stack_chunk *cur_stack, *start_stack; struct PRegChunk *cur_chunk; + /* We have to start somewhere, and the global stash is a good place */ last = current = interpreter->perl_stash->stash_hash; + /* mark it as used and get an updated end of list */ last = mark_used(current, last); - /* Wipe out the next for gc bit, otherwise we'll never get anywhere */ - last->next_for_GC = NULL; - /* Now, go run through the PMC registers and mark them as live */ /* First mark the current set. */ for (i=0; i < NUM_REGISTERS; i++) { @@ -407,7 +414,8 @@ /* Okay, we've marked the whole root set, and should have a good-sized list 'o things to look at. Run through it */ - for (; current; current = current->next_for_GC) { + prev = NULL; + for (; current != prev; current = current->next_for_GC) { UINTVAL mask = PMC_is_PMC_ptr_FLAG | PMC_is_buffer_ptr_FLAG; UINTVAL bits = current->flags & mask; @@ -420,7 +428,9 @@ } else { if (bits == PMC_is_buffer_ptr_FLAG) { - buffer_lives(current->data); + if (current->data) { + buffer_lives(current->data); + } } else { /* The only thing left is "buffer of PMCs" */ @@ -434,6 +444,7 @@ } } } + prev = current; } } @@ -498,7 +509,7 @@ PMC *pmc_array = cur_arena->start_PMC; for (i = 0; i < cur_arena->used; i++) { /* If it's not live or on the free list, put it on the free list */ - if (!(pmc_array[i].flags & (PMC_live_FLAG | + if (!(pmc_array[i].flags & (PMC_live_FLAG | PMC_immortal_FLAG | PMC_on_free_list_FLAG))) { add_pmc_to_free(interpreter, interpreter->arena_base->pmc_pool, @@ -645,6 +656,8 @@ interpreter->active_Buffers++; /* Mark it live */ return_me->flags = BUFFER_live_FLAG; + /* Don't let it point to garbage memory */ + return_me->bufstart = NULL; /* Return it */ return return_me; } @@ -826,6 +839,7 @@ if (NULL == interpreter) { return mem_sys_allocate(size); } + /* Make sure we round up to a multiple of 16 */ size += 16; size &= ~0x0f; Index: parrot/stacks.c =================================================================== RCS file: /cvs/public/parrot/stacks.c,v retrieving revision 1.25 diff -u -r1.25 stacks.c --- parrot/stacks.c 22 Mar 2002 20:24:02 -0000 1.25 +++ parrot/stacks.c 29 Mar 2002 08:09:24 -0000 @@ -76,7 +76,7 @@ } else { chunk = stack_base->prev; /* Start at top */ - while (offset > chunk->used && chunk != stack_base) { + while (offset >= chunk->used && chunk != stack_base) { offset -= chunk->used; chunk = chunk->prev; } Index: parrot/classes/perlint.pmc =================================================================== RCS file: /cvs/public/parrot/classes/perlint.pmc,v retrieving revision 1.17 diff -u -r1.17 perlint.pmc --- parrot/classes/perlint.pmc 10 Mar 2002 21:18:13 -0000 1.17 +++ parrot/classes/perlint.pmc 29 Mar 2002 08:09:25 -0000 @@ -119,27 +119,27 @@ void set_string (PMC * value) { SELF->vtable = &(Parrot_base_vtables[enum_class_PerlString]); - SELF->cache.struct_val = value->cache.struct_val; + SELF->data = value->cache.struct_val; } void set_string_native (STRING * value) { SELF->vtable = &(Parrot_base_vtables[enum_class_PerlString]); - SELF->cache.struct_val = value; + SELF->data = value; } void set_string_unicode (STRING * value) { SELF->vtable = &(Parrot_base_vtables[enum_class_PerlString]); - SELF->cache.struct_val = value; + SELF->data = value; } void set_string_other (STRING * value) { SELF->vtable = &(Parrot_base_vtables[enum_class_PerlString]); - SELF->cache.struct_val = value; + SELF->data = value; } void set_string_same (PMC * value) { SELF->vtable = &(Parrot_base_vtables[enum_class_PerlString]); - SELF->cache.struct_val = value->cache.struct_val; + SELF->data = value->cache.struct_val; } void set_value (void* value) { Index: parrot/classes/perlnum.pmc =================================================================== RCS file: /cvs/public/parrot/classes/perlnum.pmc,v retrieving revision 1.19 diff -u -r1.19 perlnum.pmc --- parrot/classes/perlnum.pmc 10 Mar 2002 21:18:13 -0000 1.19 +++ parrot/classes/perlnum.pmc 29 Mar 2002 08:09:26 -0000 @@ -117,27 +117,27 @@ void set_string (PMC * value) { SELF->vtable = &(Parrot_base_vtables[enum_class_PerlString]); - SELF->cache.struct_val = value->cache.struct_val; + SELF->data = value->cache.struct_val; } void set_string_native (STRING * value) { SELF->vtable = &(Parrot_base_vtables[enum_class_PerlString]); - SELF->cache.struct_val = value; + SELF->data = value; } void set_string_unicode (STRING * value) { SELF->vtable = &(Parrot_base_vtables[enum_class_PerlString]); - SELF->cache.struct_val = value; + SELF->data = value; } void set_string_other (STRING * value) { SELF->vtable = &(Parrot_base_vtables[enum_class_PerlString]); - SELF->cache.struct_val = value; + SELF->data = value; } void set_string_same (PMC * value) { SELF->vtable = &(Parrot_base_vtables[enum_class_PerlString]); - SELF->cache.struct_val = value->cache.struct_val; + SELF->data = value->cache.struct_val; } void set_value (void* value) { Index: parrot/classes/perlstring.pmc =================================================================== RCS file: /cvs/public/parrot/classes/perlstring.pmc,v retrieving revision 1.18 diff -u -r1.18 perlstring.pmc --- parrot/classes/perlstring.pmc 10 Mar 2002 21:18:13 -0000 1.18 +++ parrot/classes/perlstring.pmc 29 Mar 2002 08:09:26 -0000 @@ -23,12 +23,13 @@ } void init (INTVAL size) { - SELF->cache.struct_val = string_make(INTERP,NULL,0,NULL,0,NULL); + SELF->data = string_make(INTERP,NULL,0,NULL,0,NULL); + SELF->flags = PMC_is_buffer_ptr_FLAG; } void clone (PMC* dest) { dest->vtable = SELF->vtable; - dest->cache.struct_val = string_copy(INTERP,SELF->cache.struct_val); + dest->data = string_copy(INTERP,SELF->data); } void morph (INTVAL type) { @@ -43,34 +44,34 @@ } void destroy () { - string_destroy(SELF->cache.struct_val); + string_destroy(SELF->data); } INTVAL get_integer () { - STRING* s = (STRING*) SELF->cache.struct_val; + STRING* s = (STRING*) SELF->data; return string_to_int(s); } FLOATVAL get_number () { - STRING* s = (STRING*) SELF->cache.struct_val; + STRING* s = (STRING*) SELF->data; return string_to_num(s); } STRING* get_string () { - return (STRING*)SELF->cache.struct_val; + return (STRING*)SELF->data; } BOOLVAL get_bool () { - return string_bool(SELF->cache.struct_val); + return string_bool(SELF->data); } void* get_value () { - return &SELF->cache; + return &SELF->data; } BOOLVAL is_same (PMC* other) { - STRING* s1 = (STRING*)SELF->cache.struct_val; - STRING* s2 = (STRING*)other->cache.struct_val; + STRING* s1 = (STRING*)SELF->data; + STRING* s2 = (STRING*)other->data; return (BOOLVAL)( other->vtable == SELF->vtable && s1->bufused == s2->bufused && (memcmp(s1->bufstart,s2->bufstart,(size_t)s1->bufused)==0)); @@ -113,29 +114,29 @@ } void set_string (PMC * value) { - SELF->cache.struct_val = - string_copy(INTERP, (STRING*)value->cache.struct_val); + SELF->data = + string_copy(INTERP, (STRING*)value->data); } void set_string_native (STRING * value) { - SELF->cache.struct_val = string_copy(INTERP, value); + SELF->data = string_copy(INTERP, value); } void set_string_unicode (STRING * value) { - SELF->cache.struct_val = string_copy(INTERP, value); + SELF->data = string_copy(INTERP, value); } void set_string_other (STRING * value) { - SELF->cache.struct_val = string_copy(INTERP, value); + SELF->data = string_copy(INTERP, value); } void set_string_same (PMC * value) { - SELF->cache.struct_val = - string_copy(INTERP, (STRING*)value->cache.struct_val); + SELF->data = + string_copy(INTERP, (STRING*)value->data); } void set_value (void* value) { - SELF->cache.struct_val = value; + SELF->data = value; } void add (PMC * value, PMC* dest) { @@ -347,54 +348,54 @@ } void concatenate (PMC * value, PMC* dest) { - STRING* s = string_copy(INTERP, (STRING*)SELF->cache.struct_val); - dest->cache.struct_val = + STRING* s = string_copy(INTERP, (STRING*)SELF->data); + dest->data = string_concat(INTERP, s, value->vtable->get_string(INTERP, value), 0 ); - /* don't destroy s, as it is dest->cache.struct_val */ + /* don't destroy s, as it is dest->data */ } void concatenate_native (STRING * value, PMC* dest) { - STRING* s = string_copy(INTERP, (STRING*)SELF->cache.struct_val); - dest->cache.struct_val = + STRING* s = string_copy(INTERP, (STRING*)SELF->data); + dest->data = string_concat(INTERP, s, value, 0 ); - /* don't destroy s, as it is dest->cache.struct_val */ + /* don't destroy s, as it is dest->data */ } void concatenate_unicode (STRING * value, PMC* dest) { - STRING* s = string_copy(INTERP, (STRING*)SELF->cache.struct_val); - dest->cache.struct_val = + STRING* s = string_copy(INTERP, (STRING*)SELF->data); + dest->data = string_concat(INTERP, s, value, 0 ); - /* don't destroy s, as it is dest->cache.struct_val */ + /* don't destroy s, as it is dest->data */ } void concatenate_other (STRING * value, PMC* dest) { - STRING* s = string_copy(INTERP, (STRING*)SELF->cache.struct_val); - dest->cache.struct_val = + STRING* s = string_copy(INTERP, (STRING*)SELF->data); + dest->data = string_concat(INTERP, s, value, 0 ); - /* don't destroy s, as it is dest->cache.struct_val */ + /* don't destroy s, as it is dest->data */ } void concatenate_same (PMC * value, PMC* dest) { - dest->cache.struct_val = + dest->data = string_concat(INTERP, - SELF->cache.struct_val, - value->cache.struct_val, + SELF->data, + value->data, 0 ); } @@ -402,7 +403,7 @@ /* == operation */ BOOLVAL is_equal (PMC* value) { return (BOOLVAL)( 0 == string_compare(INTERP, - SELF->cache.struct_val, + SELF->data, value->vtable->get_string(INTERP, value) )); } @@ -430,40 +431,40 @@ void repeat (PMC * value, PMC* dest) { dest->vtable = &Parrot_base_vtables[enum_class_PerlString]; - dest->cache.struct_val = - string_repeat(INTERP, SELF->cache.struct_val, + dest->data = + string_repeat(INTERP, SELF->data, (UINTVAL)value->vtable->get_integer(INTERP, value), NULL ); } void repeat_native (STRING * value, PMC* dest) { dest->vtable = &Parrot_base_vtables[enum_class_PerlString]; - dest->cache.struct_val = - string_repeat(INTERP, SELF->cache.struct_val, + dest->data = + string_repeat(INTERP, SELF->data, (UINTVAL)string_to_int(value), NULL ); } void repeat_unicode (STRING * value, PMC* dest) { dest->vtable = &Parrot_base_vtables[enum_class_PerlString]; - dest->cache.struct_val = - string_repeat(INTERP, SELF->cache.struct_val, + dest->data = + string_repeat(INTERP, SELF->data, (UINTVAL)string_to_int(value), NULL ); } void repeat_other (STRING * value, PMC* dest) { dest->vtable = &Parrot_base_vtables[enum_class_PerlString]; - dest->cache.struct_val = - string_repeat(INTERP, SELF->cache.struct_val, + dest->data = + string_repeat(INTERP, SELF->data, (UINTVAL)string_to_int(value), NULL ); } void repeat_same (PMC * value, PMC* dest) { dest->vtable = &Parrot_base_vtables[enum_class_PerlString]; - dest->cache.struct_val = - string_repeat(INTERP, SELF->cache.struct_val, + dest->data = + string_repeat(INTERP, SELF->data, (UINTVAL)value->vtable->get_integer(INTERP, value), NULL ); } Index: parrot/include/parrot/pmc.h =================================================================== RCS file: /cvs/public/parrot/include/parrot/pmc.h,v retrieving revision 1.24 diff -u -r1.24 pmc.h --- parrot/include/parrot/pmc.h 15 Mar 2002 19:45:00 -0000 1.24 +++ parrot/include/parrot/pmc.h 29 Mar 2002 08:09:27 -0000 @@ -38,6 +38,12 @@ DPOINTER *struct_val; } cache; SYNC *synchronize; + /* This flag determines the next PMC in the 'used' list during + dead object detection in the GC. It is a linked list, which is + only valid in trace_active_PMCs. Also, the linked list is + guaranteed to have the tail element's next_for_GC point to itself, + which makes much of the logic and checks simpler. We then have to + check for PMC->next_for_GC == PMC to find the end of list. */ PMC *next_for_GC; /* Yeah, the GC data should be out of band, but that makes things really slow when actually marking things for @@ -98,7 +104,10 @@ /* Our refcount */ PMC_refcount_field = 1 << 16 | 1 << 17, /* Constant flag */ - PMC_constant_FLAG = 1 << 18 + PMC_constant_FLAG = 1 << 18, + /* Immortal flag, for ensuring a PMC survives DOD. Used internally + * by the GC: should not be used in PMC code. */ + PMC_immortal_FLAG = 1 << 19 } PMC_flags; /* XXX add various bit test macros once we have need of them */