diff --git a/src/array.c b/src/array.c index f62b346c21715eef13bab79770e1cb7a77c7c2de..77a8f64ae3b13d6592aed25dd1dece526c18f503 100644 --- a/src/array.c +++ b/src/array.c @@ -23,7 +23,7 @@ #include "stuff.h" #include "bignum.h" -RCSID("$Id: array.c,v 1.77 2000/07/11 03:45:09 mast Exp $"); +RCSID("$Id: array.c,v 1.78 2000/07/18 05:48:20 mast Exp $"); struct array empty_array= { @@ -1861,40 +1861,28 @@ void gc_mark_array_as_referenced(struct array *a) } } -static void low_gc_cycle_check_array(struct array *a) +void real_gc_cycle_check_array(struct array *a, int weak) { - int e; + GC_CYCLE_ENTER(a, weak) { + int e; #ifdef PIKE_DEBUG - if (a == &empty_array) fatal("Trying to gc cycle check empty_array.\n"); + if (a == &empty_array) fatal("Trying to gc cycle check empty_array.\n"); #endif - if (a->type_field & BIT_COMPLEX) - { - if (a->flags & ARRAY_WEAK_FLAG) - gc_recurse_weak_array(a, gc_cycle_check_weak_svalues); - else { - TYPE_FIELD t; - if ((t = gc_cycle_check_svalues(ITEM(a), a->size))) { - if(!(a->type_field & BIT_UNFINISHED) || a->refs!=1) - a->type_field = t; - else - a->type_field |= t; + if (a->type_field & BIT_COMPLEX) + { + if (a->flags & ARRAY_WEAK_FLAG) + gc_recurse_weak_array(a, gc_cycle_check_weak_svalues); + else { + TYPE_FIELD t; + if ((t = gc_cycle_check_svalues(ITEM(a), a->size))) { + if(!(a->type_field & BIT_UNFINISHED) || a->refs!=1) + a->type_field = t; + else + a->type_field |= t; + } } } - } -} - -void real_gc_cycle_check_array(struct array *a) -{ - GC_CYCLE_ENTER(a, 0) { - low_gc_cycle_check_array(a); - } GC_CYCLE_LEAVE; -} - -void real_gc_cycle_check_array_weak(struct array *a) -{ - GC_CYCLE_ENTER(a, 1) { - low_gc_cycle_check_array(a); } GC_CYCLE_LEAVE; } @@ -1948,28 +1936,26 @@ void gc_cycle_check_all_arrays(void) { struct array *a; for (a = gc_internal_array; a != &empty_array; a = a->next) { - real_gc_cycle_check_array(a); - run_lifo_queue(&gc_mark_queue); + real_gc_cycle_check_array(a, 0); + gc_cycle_run_queue(); } } +void gc_zap_ext_weak_refs_in_arrays(void) +{ + gc_mark_array_pos = empty_array.next; + while (gc_mark_array_pos != gc_internal_array && gc_ext_weak_refs) { + struct array *a = gc_mark_array_pos; + gc_mark_array_pos = a->next; + gc_mark_array_as_referenced(a); + } + discard_queue(&gc_mark_queue); +} + void gc_free_all_unreferenced_arrays(void) { struct array *a,*next; - if (gc_ext_weak_refs) { - /* Have to go through all marked things if we got external weak - * references to otherwise unreferenced things, so the mark - * functions can free those references. */ - gc_mark_array_pos = empty_array.next; - while (gc_mark_array_pos != gc_internal_array && gc_ext_weak_refs) { - struct array *a = gc_mark_array_pos; - gc_mark_array_pos = a->next; - gc_mark_array_as_referenced(a); - } - discard_queue(&gc_mark_queue); - } - for (a = gc_internal_array; a != &empty_array; a = next) { #ifdef PIKE_DEBUG diff --git a/src/array.h b/src/array.h index cd662a2ec749ddc9d519a74adfeb8805a93cd7a6..daa5a2d1190e4277b5d848355537a44b09e703b0 100644 --- a/src/array.h +++ b/src/array.h @@ -5,7 +5,7 @@ \*/ /* - * $Id: array.h,v 1.20 2000/06/09 22:43:04 mast Exp $ + * $Id: array.h,v 1.21 2000/07/18 05:48:20 mast Exp $ */ #ifndef ARRAY_H #define ARRAY_H @@ -165,9 +165,9 @@ void gc_mark_array_as_referenced(struct array *a); unsigned gc_touch_all_arrays(void); void gc_check_all_arrays(void); void gc_mark_all_arrays(void); -void real_gc_cycle_check_array(struct array *a); -void real_gc_cycle_check_array_weak(struct array *a); +void real_gc_cycle_check_array(struct array *a, int weak); void gc_cycle_check_all_arrays(void); +void gc_zap_ext_weak_refs_in_arrays(void); void gc_free_all_unreferenced_arrays(void); void debug_dump_type_field(TYPE_FIELD t); void debug_dump_array(struct array *a); @@ -177,9 +177,7 @@ struct array *explode_array(struct array *a, struct array *b); struct array *implode_array(struct array *a, struct array *b); /* Prototypes end here */ -#define gc_cycle_check_array(X) \ - enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_array, (X)) -#define gc_cycle_check_array_weak(X) \ - enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_array_weak, (X)) +#define gc_cycle_check_array(X, WEAK) \ + gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_array, (X), (WEAK)) #endif /* ARRAY_H */ diff --git a/src/gc.c b/src/gc.c index 86c923d79f1246e3b058b8b1d20c1cdeb667df39..59532f62af67d1d74952a938eb10076fbde9d712 100644 --- a/src/gc.c +++ b/src/gc.c @@ -29,7 +29,7 @@ struct callback *gc_evaluator_callback=0; #include "block_alloc.h" -RCSID("$Id: gc.c,v 1.106 2000/07/11 04:42:37 mast Exp $"); +RCSID("$Id: gc.c,v 1.107 2000/07/18 05:48:20 mast Exp $"); /* Run garbage collect approximately every time * 20 percent of all arrays, objects and programs is @@ -41,7 +41,7 @@ RCSID("$Id: gc.c,v 1.106 2000/07/11 04:42:37 mast Exp $"); #define MAX_ALLOC_THRESHOLD 10000000 #define MULTIPLIER 0.9 #define MARKER_CHUNK_SIZE 1023 -#define REF_CYCLE_CHUNK_SIZE 32 +#define GC_LINK_CHUNK_SIZE 31 /* The gc will free all things with no external references that isn't * referenced by undestructed objects with destroy() lfuns (known as @@ -82,6 +82,8 @@ RCSID("$Id: gc.c,v 1.106 2000/07/11 04:42:37 mast Exp $"); /* #define GC_VERBOSE */ /* #define GC_CYCLE_DEBUG */ +/* #define GC_STACK_DEBUG */ + #if defined(GC_VERBOSE) && !defined(PIKE_DEBUG) #undef GC_VERBOSE #endif @@ -98,33 +100,85 @@ int Pike_in_gc = 0; struct pike_queue gc_mark_queue; time_t last_gc; -static struct marker rec_list = {0, 0, 0}; -struct marker *gc_rec_last = &rec_list; -static struct marker *kill_list = 0; +struct gc_frame +{ + struct gc_frame *back; /* Previous stack frame. */ + void *data; + union { + struct { /* Pop frame. */ + struct gc_frame *prev; /* Previous frame in rec_list. */ + struct gc_frame *next; /* Next pointer in rec_list and kill_list. */ + unsigned INT16 cycle; /* Cycle id number. */ + } pop; + struct { /* Link frame. */ + gc_cycle_check_cb *checkfn; + int weak; + } link; + } u; + unsigned INT16 frameflags; +}; + +#define GC_POP_FRAME 0x01 +#define GC_WEAK_REF 0x02 +#define GC_STRONG_REF 0x04 +#define GC_OFF_STACK 0x08 +#ifdef PIKE_DEBUG +#define GC_LINK_FREED 0x10 +#define GC_FOLLOWED_NONSTRONG 0x20 +#endif + +#undef BLOCK_ALLOC_NEXT +#define BLOCK_ALLOC_NEXT back + +BLOCK_ALLOC(gc_frame,GC_LINK_CHUNK_SIZE) + +#define PREV(frame) ((frame)->u.pop.prev) +#define NEXT(frame) ((frame)->u.pop.next) +#define CYCLE(frame) ((frame)->u.pop.cycle) + +#ifdef PIKE_DEBUG +#define CHECK_POP_FRAME(frame) do { \ + if ((frame)->frameflags & GC_LINK_FREED) \ + gc_fatal((frame)->data, 0, "Accessing freed gc_frame.\n"); \ + if (!((frame)->frameflags & GC_POP_FRAME)) \ + gc_fatal((frame)->data, 0, #frame " is not a pop frame.\n"); \ + if (NEXT(PREV(frame)) != (frame)) \ + gc_fatal((frame)->data, 0, \ + "Pop frame pointers are inconsistent.\n"); \ +} while (0) +#else +#define CHECK_POP_FRAME(frame) do {} while (0) +#endif + +static struct gc_frame rec_list = {0, 0, {{0, 0, 0}}, GC_POP_FRAME}; +static struct gc_frame *gc_rec_last = &rec_list, *gc_rec_top = 0; +static struct gc_frame *kill_list = 0; + static unsigned last_cycle; size_t gc_ext_weak_refs; -/* rec_list is a linked list of the markers currently being recursed - * through in the cycle check pass. gc_rec_last points at the - * innermost marker being visited. A new marker is linked in after - * gc_rec_last, except when that's inside a cycle, in which case it's - * linked in after that cycle. A cycle is always treated as one atomic - * unit, e.g. it's either popped whole or not at all. +/* gc_frame objects are used as frames in a recursion stack during the + * cycle check pass. gc_rec_top points to the current top of the + * stack. When a thing is recursed, a pop frame is first pushed on the + * stack and then the gc_cycle_check_* function fills in with link + * frames for every reference the thing contains. * - * Two ranges of markers next to each other may swap places to break a - * cyclic reference at a chosen point. Some markers thus get a place - * earlier on the list even though their corresponding stack frames - * are later than some other markers they have references to. These - * markers are flagged to make them stay on the list when they're - * popped from the stack. They'll get popped eventually since all - * markers in the gap between the top one and it's previous - * gc_rec_last point are popped. + * rec_list is a double linked list of the pop frames on the stack, + * and that list represents the current prospective destruct order. + * gc_rec_last points at the last frame in the list and new frames are + * linked in after it. A cycle is always treated as one atomic unit, + * e.g. it's either popped whole or not at all. That means that + * rec_list may contain frames that are no longer on the stack. * - * Markers for live objects are linked into the beginning of kill_list + * A range of frames which always ends at the end of the list, may be + * rotated a number of slots to break a cyclic reference at a chosen + * point. The stack of link frames are rotated simultaneously. + * + * Frames for live objects are linked into the beginning of kill_list * when they're popped from rec_list. * * The cycle check functions might recurse another round through the - * markers that have been recursed already, to propagate the GC_LIVE + * frames that have been recursed already, to propagate the GC_LIVE * flag to things that have been found to be referenced from live * objects. rec_list is not touched at all in this extra round. */ @@ -141,17 +195,22 @@ struct callback *debug_add_gc_callback(callback_func call, return add_to_callback(&gc_callbacks, call, arg, free_func); } +static void gc_cycle_pop(void *a); + + +#undef BLOCK_ALLOC_NEXT +#define BLOCK_ALLOC_NEXT next #undef INIT_BLOCK #ifdef PIKE_DEBUG #define INIT_BLOCK(X) \ (X)->flags=(X)->refs=(X)->weak_refs=(X)->xrefs=0; \ (X)->saved_refs=-1; \ - (X)->cycle = (unsigned INT16) -1; \ - (X)->link = (struct marker *) -1; + (X)->frame = 0; #else #define INIT_BLOCK(X) \ - (X)->flags=(X)->refs=(X)->weak_refs=0; + (X)->flags=(X)->refs=(X)->weak_refs=0; \ + (X)->frame = 0; #endif PTR_HASH_ALLOC(marker,MARKER_CHUNK_SIZE) @@ -160,6 +219,7 @@ PTR_HASH_ALLOC(marker,MARKER_CHUNK_SIZE) int gc_in_cycle_check = 0; static unsigned weak_freed, checked, marked, cycle_checked, live_ref; +static unsigned max_gc_frames, num_gc_frames = 0; static unsigned gc_extra_refs = 0; void dump_gc_info(void) @@ -407,13 +467,32 @@ void describe_location(void *real_memblock, #endif } +static void describe_gc_frame(struct gc_frame *l) +{ + if (l->frameflags & GC_POP_FRAME) + fprintf(stderr, "back=%p, prev=%p, next=%p, data=%p, cycle=%u, flags=0x%02x", + l->back, PREV(l), NEXT(l), l->data, CYCLE(l), l->frameflags); + else + fprintf(stderr, "LINK back=%p, data=%p, weak=%d, flags=0x%02x", + l->back, l->data, l->u.link.weak, l->frameflags); +} + static void describe_marker(struct marker *m) { - if (m) + if (m) { fprintf(stderr, "marker at %p: flags=0x%06x, refs=%d, weak=%d, " - "xrefs=%d, saved=%d, cycle=%d, link=%p\n", + "xrefs=%d, saved=%d, frame=%p", m, m->flags, m->refs, m->weak_refs, - m->xrefs, m->saved_refs, m->cycle, m->link); + m->xrefs, m->saved_refs, m->frame); +#ifdef PIKE_DEBUG + if (m->frame) { + fputs(" [", stderr); + describe_gc_frame(m->frame); + putc(']', stderr); + } +#endif + putc('\n', stderr); + } else fprintf(stderr, "no marker\n"); } @@ -749,9 +828,9 @@ void debug_gc_touch(void *a) if (!(m->flags & GC_TOUCHED)) gc_fatal(a, 2, "An existing but untouched marker found " "for object in linked lists.\n"); - else if (m->flags & (GC_ON_STACK|GC_IN_REC_LIST|GC_DONT_POP| - GC_LIVE_RECURSE|GC_WEAK_REF|GC_STRONG_REF)) - gc_fatal(a, 2, "Marker still got flag from recurse list.\n"); + else if (m->flags & GC_LIVE_RECURSE || + (m->frame && m->frame->frameflags & (GC_WEAK_REF|GC_STRONG_REF))) + gc_fatal(a, 2, "Thing still got flag from recurse list.\n"); else if (m->flags & GC_MARKED) return; else if (!(m->flags & GC_NOT_REFERENCED) || m->flags & GC_XREFERENCED) @@ -866,6 +945,7 @@ INT32 real_gc_check_weak(void *a) static void init_gc(void) { init_marker_hash(); + get_marker(rec_list.data); /* Used to simplify fencepost conditions. */ } static void exit_gc(void) @@ -878,6 +958,10 @@ static void exit_gc(void) remove_marker(marker_hash_table[e]->data); #endif exit_marker_hash(); + free_all_gc_frame_blocks(); +#ifdef GC_VERBOSE + num_gc_frames = 0; +#endif } #ifdef PIKE_DEBUG @@ -1030,7 +1114,7 @@ int gc_do_weak_free(void *a) if (!a) fatal("Got null pointer.\n"); if (Pike_in_gc != GC_PASS_MARK && Pike_in_gc != GC_PASS_CYCLE && - Pike_in_gc != GC_PASS_FREE) + Pike_in_gc != GC_PASS_ZAP_WEAK) fatal("gc_do_weak_free() called in invalid gc pass.\n"); if (gc_debug) { if (!(m = find_marker(a))) @@ -1042,7 +1126,7 @@ int gc_do_weak_free(void *a) if (m->weak_refs > m->refs) gc_fatal(a, 0, "More weak references than internal references.\n"); - if (Pike_in_gc != GC_PASS_FREE) { + if (Pike_in_gc != GC_PASS_ZAP_WEAK) { if (m->weak_refs == -1) { gc_ext_weak_refs--; return 1; @@ -1060,6 +1144,22 @@ int gc_do_weak_free(void *a) return 0; } +void debug_really_free_gc_frame(struct gc_frame *l) +{ + if (l->frameflags & GC_LINK_FREED) + gc_fatal(l->data, 0, "Freeing freed gc_frame.\n"); + l->frameflags |= GC_LINK_FREED; + l->back = PREV(l) = NEXT(l) = (struct gc_frame *) -1; + really_free_gc_frame(l); +#ifdef GC_VERBOSE + num_gc_frames--; +#endif +} + +#else /* PIKE_DEBUG */ + +#define debug_really_free_gc_frame(l) really_free_gc_frame(l) + #endif /* PIKE_DEBUG */ int gc_mark(void *a) @@ -1068,22 +1168,22 @@ int gc_mark(void *a) #ifdef PIKE_DEBUG if (!a) fatal("Got null pointer.\n"); - if (Pike_in_gc != GC_PASS_MARK && Pike_in_gc != GC_PASS_FREE) + if (Pike_in_gc != GC_PASS_MARK && Pike_in_gc != GC_PASS_ZAP_WEAK) fatal("gc mark attempted in invalid pass.\n"); if (!*(INT32 *) a) gc_fatal(a, 0, "Marked a thing without refs.\n"); if (m->weak_refs == -1) gc_fatal(a, 0, "Marking thing scheduled for weak free.\n"); - if (Pike_in_gc == GC_PASS_FREE && !(m->flags & GC_MARKED)) - gc_fatal(a, 0, "gc_mark() called for thing in free pass " + if (Pike_in_gc == GC_PASS_ZAP_WEAK && !(m->flags & GC_MARKED)) + gc_fatal(a, 0, "gc_mark() called for thing in zap weak pass " "that wasn't marked before.\n"); #endif - if (Pike_in_gc == GC_PASS_FREE) - /* Things are visited in the free pass through the mark functions - * to free refs to internal things that only got weak external - * references. That happens only when a thing also have internal - * cyclic non-weak refs. */ + if (Pike_in_gc == GC_PASS_ZAP_WEAK) + /* Things are visited in the zap weak pass through the mark + * functions to free refs to internal things that only got weak + * external references. That happens only when a thing also have + * internal cyclic non-weak refs. */ if (m->flags & GC_FREE_VISITED) return 0; else { @@ -1109,10 +1209,75 @@ int gc_mark(void *a) } } +void gc_cycle_enqueue(gc_cycle_check_cb *checkfn, void *data, int weak) +{ + struct gc_frame *l = alloc_gc_frame(); +#ifdef GC_VERBOSE + if (++num_gc_frames > max_gc_frames) max_gc_frames = num_gc_frames; +#endif + l->data = data; + l->u.link.checkfn = checkfn; + l->u.link.weak = weak; + l->frameflags = 0; + l->back = gc_rec_top; +#ifdef GC_STACK_DEBUG + fprintf(stderr, "enqueue %p [%p]: ", l, gc_rec_top); + describe_gc_frame(l); + fputc('\n', stderr); +#endif + gc_rec_top = l; +} + +static struct gc_frame *gc_cycle_enqueue_pop(void *data) +{ + struct gc_frame *l = alloc_gc_frame(); +#ifdef GC_VERBOSE + if (++num_gc_frames > max_gc_frames) max_gc_frames = num_gc_frames; +#endif + l->data = data; + PREV(l) = gc_rec_last; + NEXT(l) = 0; + CYCLE(l) = 0; + l->frameflags = GC_POP_FRAME; + l->back = gc_rec_top; +#ifdef GC_STACK_DEBUG + fprintf(stderr, "enqueue %p [%p]: ", l, gc_rec_top); + describe_gc_frame(l); + fputc('\n', stderr); +#endif + gc_rec_top = l; + return l; +} + +void gc_cycle_run_queue() +{ + while (gc_rec_top) { +#ifdef GC_STACK_DEBUG + fprintf(stderr, "dequeue %p [%p]: ", gc_rec_top, gc_rec_top->back); + describe_gc_frame(gc_rec_top); + fputc('\n', stderr); +#endif + if (gc_rec_top->frameflags & GC_POP_FRAME) { + struct gc_frame *l = gc_rec_top->back; + gc_cycle_pop(gc_rec_top->data); + gc_rec_top = l; + } else { + struct gc_frame l = *gc_rec_top; +#ifdef PIKE_DEBUG + if (l.frameflags & GC_LINK_FREED) + gc_fatal(l.data, 0, "Accessing freed gc_frame.\n"); +#endif + debug_really_free_gc_frame(gc_rec_top); + gc_rec_top = l.back; + l.u.link.checkfn(l.data, l.u.link.weak); + } + } +} + #ifdef GC_CYCLE_DEBUG static int gc_cycle_indent = 0; #define CYCLE_DEBUG_MSG(M, TXT) do { \ - fprintf(stderr, "%*s%-33s %p [%p] ", gc_cycle_indent, "", \ + fprintf(stderr, "%*s%-35s %p [%p] ", gc_cycle_indent, "", \ (TXT), (M) ? (M)->data : 0, gc_rec_last->data); \ describe_marker(M); \ } while (0) @@ -1120,59 +1285,99 @@ static int gc_cycle_indent = 0; #define CYCLE_DEBUG_MSG(M, TXT) do {} while (0) #endif -static struct marker *break_cycle (struct marker *beg, struct marker *pos) +static void rotate_rec_list (struct gc_frame *beg, struct gc_frame *pos) +/* Rotates the marker list and the cycle stack so the bit from pos + * down to the end gets before the bit from beg down to pos. */ { - /* There's a cycle from beg to gc_rec_last which should be broken at - * pos. Do it by switching places of the markers before and after pos. */ - struct marker *p, *q; + struct gc_frame *l; + #ifdef PIKE_DEBUG + CHECK_POP_FRAME(beg); + CHECK_POP_FRAME(pos); if (beg == pos) gc_fatal(beg->data, 0, "Cycle already broken at requested position.\n"); + if (NEXT(gc_rec_last)) + gc_fatal(gc_rec_last->data, 0, "gc_rec_last not at end.\n"); #endif - if (beg->cycle) { - for (q = &rec_list; q->link->cycle != beg->cycle; q = q->link) {} - beg = q->link; - if (beg->cycle == pos->cycle) { +#ifdef GC_STACK_DEBUG + fprintf(stderr,"Stack before:\n"); + for (l = gc_rec_top; l; l = l->back) { + fprintf(stderr, " %p ", l); + describe_gc_frame(l); + fputc('\n', stderr); + } +#endif + + if (CYCLE(beg)) { + for (l = beg; CYCLE(PREV(l)) == CYCLE(beg); l = PREV(l)) + CHECK_POP_FRAME(l); + CHECK_POP_FRAME(l); + if (CYCLE(l) == CYCLE(pos)) { /* Breaking something previously marked as a cycle. Clear it * since we're no longer sure it's an ambigious cycle. */ - unsigned cycle = beg->cycle; - for (p = beg; p && p->cycle == cycle; p = p->link) - p->cycle = 0; + unsigned cycle = CYCLE(l); + for (; l && CYCLE(l) == cycle; l = NEXT(l)) { + CHECK_POP_FRAME(l); +#ifdef GC_CYCLE_DEBUG + if (CYCLE(l)) + CYCLE_DEBUG_MSG(find_marker(l->data), "> rotate_rec_list, clear cycle"); +#endif + CYCLE(l) = 0; + } } + else beg = l; /* Keep the cycle continuous. */ } - else - for (q = &rec_list; q->link != beg; q = q->link) {} - q->link = pos; + CYCLE_DEBUG_MSG(find_marker(beg->data), "> rotate_rec_list, begin at"); - CYCLE_DEBUG_MSG(beg, "> break_cycle, begin at"); - - for (p = beg; p->link != pos; p = p->link) {} - - for (q = pos;; q = q->link) { - q->flags |= GC_MOVED_BACK|GC_DONT_POP; - CYCLE_DEBUG_MSG(q, "> break_cycle, mark for don't pop"); - if (q == gc_rec_last) break; + { + struct gc_frame *b = beg, *p = pos, *old_rec_top; + while (b->frameflags & GC_OFF_STACK) { + if ((b = NEXT(b)) == pos) goto done; + CHECK_POP_FRAME(b); + } + while (p->frameflags & GC_OFF_STACK) { + if (!(p = NEXT(p))) goto done; + CHECK_POP_FRAME(p); + } + old_rec_top = gc_rec_top; + gc_rec_top = p->back; + p->back = b->back; + b->back = old_rec_top; } +done: { - struct marker *m = q->link; - q->link = beg; - p->link = m; + struct gc_frame *new_rec_last = PREV(pos); + NEXT(PREV(beg)) = pos; + PREV(pos) = PREV(beg); + NEXT(gc_rec_last) = beg; + PREV(beg) = gc_rec_last; + gc_rec_last = new_rec_last; + NEXT(gc_rec_last) = 0; } - if (beg->flags & GC_WEAK_REF) { - beg->flags &= ~GC_WEAK_REF; - pos->flags |= GC_WEAK_REF; - CYCLE_DEBUG_MSG(pos, "> break_cycle, moved weak flag"); + if (beg->frameflags & GC_WEAK_REF) { + beg->frameflags &= ~GC_WEAK_REF; + pos->frameflags |= GC_WEAK_REF; + CYCLE_DEBUG_MSG(get_marker(pos->data), "> rotate_rec_list, moved weak flag"); } - return beg; +#ifdef GC_STACK_DEBUG + fprintf(stderr,"Stack after:\n"); + for (l = gc_rec_top; l; l = l->back) { + fprintf(stderr, " %p ", l); + describe_gc_frame(l); + fputc('\n', stderr); + } +#endif } int gc_cycle_push(void *x, struct marker *m, int weak) { + struct marker *last = find_marker(gc_rec_last->data); + #ifdef PIKE_DEBUG if (!x) fatal("Got null pointer.\n"); if (m->data != x) fatal("Got wrong marker.\n"); @@ -1208,9 +1413,9 @@ int gc_cycle_push(void *x, struct marker *m, int weak) } #endif - if (gc_rec_last->flags & GC_LIVE_RECURSE) { + if (last->flags & GC_LIVE_RECURSE) { #ifdef PIKE_DEBUG - if (!(gc_rec_last->flags & GC_LIVE)) + if (!(last->flags & GC_LIVE)) gc_fatal(x, 0, "Doing live recursion from a dead thing.\n"); #endif @@ -1232,32 +1437,37 @@ int gc_cycle_push(void *x, struct marker *m, int weak) int flags; CYCLE_DEBUG_MSG(m, "gc_cycle_push, live rec done"); do { - gc_rec_last->flags &= ~GC_LIVE_RECURSE; + last->flags &= ~GC_LIVE_RECURSE; #ifdef GC_CYCLE_DEBUG gc_cycle_indent -= 2; - CYCLE_DEBUG_MSG(gc_rec_last, "> gc_cycle_push, unwinding live"); + CYCLE_DEBUG_MSG(find_marker(gc_rec_last->data), + "> gc_cycle_push, unwinding live"); #endif - gc_rec_last = (struct marker *) - dequeue_lifo(&gc_mark_queue, (queue_call) gc_set_rec_last); + while (1) { + struct gc_frame *l = gc_rec_top; #ifdef PIKE_DEBUG - if (!gc_rec_last) - fatal("Expected a gc_set_rec_last entry in gc_mark_queue.\n"); + if (!gc_rec_top) + fatal("Expected a gc_cycle_pop entry in gc_rec_top.\n"); #endif - } while (gc_rec_last->flags & GC_LIVE_RECURSE); - if (!dequeue_lifo(&gc_mark_queue, (queue_call) gc_cycle_pop)) { -#ifdef PIKE_DEBUG - fatal("Expected a gc_cycle_pop entry in gc_mark_queue.\n"); -#endif - } + gc_rec_top = l->back; + if (l->frameflags & GC_POP_FRAME) { + gc_rec_last = PREV(l); + debug_really_free_gc_frame(l); + break; + } + debug_really_free_gc_frame(l); + } + last = find_marker(gc_rec_last->data); + } while (last->flags & GC_LIVE_RECURSE); } return 0; } #ifdef PIKE_DEBUG - if (weak >= 0 && gc_rec_last->flags & GC_FOLLOWED_STRONG) - gc_fatal(x, 0, "Followed strong link too early.\n"); - if (weak < 0) gc_rec_last->flags |= GC_FOLLOWED_STRONG; + if (weak < 0 && gc_rec_last->frameflags & GC_FOLLOWED_NONSTRONG) + gc_fatal(x, 0, "Followed strong link too late.\n"); + if (weak >= 0) gc_rec_last->frameflags |= GC_FOLLOWED_NONSTRONG; #endif if (weak > 0) { @@ -1269,20 +1479,24 @@ int gc_cycle_push(void *x, struct marker *m, int weak) gc_ext_weak_refs--; } - if (m->flags & GC_IN_REC_LIST) { /* A cyclic reference is found. */ + if (m->frame && !(m->frame->frameflags & GC_OFF_STACK)) { + /* A cyclic reference is found. */ #ifdef PIKE_DEBUG - if (m == &rec_list || gc_rec_last == &rec_list) + if (gc_rec_last == &rec_list) gc_fatal(x, 0, "Cyclic ref involves dummy rec_list marker.\n"); + CHECK_POP_FRAME(gc_rec_last); + CHECK_POP_FRAME(m->frame); #endif - if (m != gc_rec_last) { - struct marker *p, *weak_ref = 0, *nonstrong_ref = 0; + if (m != last) { + struct gc_frame *p, *weak_ref = 0, *nonstrong_ref = 0; if (!weak) { - struct marker *q; + struct gc_frame *q; CYCLE_DEBUG_MSG(m, "gc_cycle_push, search normal"); - for (q = m, p = m->link; p; q = p, p = p->link) { - if (p->flags & (GC_WEAK_REF|GC_STRONG_REF)) { - if (p->flags & GC_WEAK_REF) weak_ref = p; + for (q = m->frame, p = NEXT(q);; q = p, p = NEXT(p)) { + CHECK_POP_FRAME(p); + if (p->frameflags & (GC_WEAK_REF|GC_STRONG_REF)) { + if (p->frameflags & GC_WEAK_REF) weak_ref = p; else if (!nonstrong_ref) nonstrong_ref = q; } if (p == gc_rec_last) break; @@ -1291,9 +1505,10 @@ int gc_cycle_push(void *x, struct marker *m, int weak) else if (weak < 0) { CYCLE_DEBUG_MSG(m, "gc_cycle_push, search strong"); - for (p = m->link; p; p = p->link) { - if (p->flags & GC_WEAK_REF) weak_ref = p; - if (!(p->flags & GC_STRONG_REF)) nonstrong_ref = p; + for (p = NEXT(m->frame);; p = NEXT(p)) { + CHECK_POP_FRAME(p); + if (p->frameflags & GC_WEAK_REF) weak_ref = p; + if (!(p->frameflags & GC_STRONG_REF)) nonstrong_ref = p; if (p == gc_rec_last) break; } #ifdef PIKE_DEBUG @@ -1303,105 +1518,83 @@ int gc_cycle_push(void *x, struct marker *m, int weak) } else { - struct marker *q; + struct gc_frame *q; CYCLE_DEBUG_MSG(m, "gc_cycle_push, search weak"); - for (q = m, p = m->link; p; q = p, p = p->link) { - if (!(p->flags & GC_WEAK_REF) && !nonstrong_ref) nonstrong_ref = q; + for (q = m->frame, p = NEXT(q);; q = p, p = NEXT(p)) { + CHECK_POP_FRAME(p); + if (!(p->frameflags & GC_WEAK_REF) && !nonstrong_ref) + nonstrong_ref = q; if (p == gc_rec_last) break; } } - if (p == gc_rec_last) { /* It was a backward reference. */ - if (weak_ref) { - /* The backward link is normal or strong and there are one - * or more weak links in the cycle. Let's break it at the - * last one (to ensure that a sequence of several weak links - * are broken at the last one). */ - CYCLE_DEBUG_MSG(weak_ref, "gc_cycle_push, weak break"); - break_cycle (m, weak_ref); - } - - else if (weak < 0) { - /* The backward link is strong. Must break the cycle at the - * last nonstrong link. */ - if (m->flags & GC_STRONG_REF) - nonstrong_ref->flags = - (nonstrong_ref->flags & ~GC_WEAK_REF) | GC_STRONG_REF; - else - m->flags = (m->flags & ~GC_WEAK_REF) | GC_STRONG_REF; - CYCLE_DEBUG_MSG(nonstrong_ref, "gc_cycle_push, nonstrong break"); - break_cycle (m, nonstrong_ref); - } + if (weak_ref) { + /* The backward link is normal or strong and there are one + * or more weak links in the cycle. Let's break it at the + * last one (to ensure that a sequence of several weak links + * are broken at the last one). */ + CYCLE_DEBUG_MSG(find_marker(weak_ref->data), + "gc_cycle_push, weak break"); + rotate_rec_list(m->frame, weak_ref); + } - else if (nonstrong_ref) { - /* Either a nonweak cycle with a strong link in it or a weak - * cycle with a nonweak link in it. Break before the first - * link that's stronger than the others. */ - if (nonstrong_ref != m) { - CYCLE_DEBUG_MSG(nonstrong_ref, "gc_cycle_push, weaker break"); - break_cycle (m, nonstrong_ref); - } - } + else if (weak < 0) { + /* The backward link is strong. Must break the cycle at the + * last nonstrong link. */ + if (m->frame->frameflags & GC_STRONG_REF) + nonstrong_ref->frameflags = + (nonstrong_ref->frameflags & ~GC_WEAK_REF) | GC_STRONG_REF; + else + m->frame->frameflags = + (m->frame->frameflags & ~GC_WEAK_REF) | GC_STRONG_REF; + CYCLE_DEBUG_MSG(find_marker(nonstrong_ref->data), + "gc_cycle_push, nonstrong break"); + rotate_rec_list(m->frame, nonstrong_ref); + } - else if (!weak) { - /* A normal cycle which will be destructed in arbitrary - * order. For reasons having to do with strong links we - * can't mark weak cycles this way. */ - unsigned cycle = m->cycle ? m->cycle : ++last_cycle; - if (cycle == gc_rec_last->cycle) - CYCLE_DEBUG_MSG(m, "gc_cycle_push, old cycle"); - else { - unsigned replace_cycle = gc_rec_last->cycle; - CYCLE_DEBUG_MSG(m, "gc_cycle_push, cycle"); - for (p = m;; p = p->link) { - p->cycle = cycle; - CYCLE_DEBUG_MSG(p, "> gc_cycle_push, mark cycle"); - if (p == gc_rec_last) break; - } - if (replace_cycle && replace_cycle != cycle) - for (p = p->link; p && p->cycle == replace_cycle; p = p->link) { - p->cycle = cycle; - CYCLE_DEBUG_MSG(p, "> gc_cycle_push, re-mark cycle"); - }}}} /* Mmm.. lisp ;) */ - - else { /* A forward reference. */ - CYCLE_DEBUG_MSG(m, "gc_cycle_push, forward ref"); - if (m->flags & GC_ON_STACK && - !(gc_rec_last->cycle && gc_rec_last->cycle == m->cycle)) { - /* Since it's not part of the same cycle it's a reference to - * a marker that has been swapped further down the list by - * break_cycle(). In that case we must mark gc_rec_last to - * stay on the list. */ - gc_rec_last->flags |= GC_DONT_POP; - CYCLE_DEBUG_MSG(gc_rec_last, "gc_cycle_push, mark for don't pop"); + else if (nonstrong_ref) { + /* Either a nonweak cycle with a strong link in it or a weak + * cycle with a nonweak link in it. Break before the first + * link that's stronger than the others. */ + if (nonstrong_ref != m->frame) { + CYCLE_DEBUG_MSG(find_marker(nonstrong_ref->data), + "gc_cycle_push, weaker break"); + rotate_rec_list(m->frame, nonstrong_ref); } } - } - } + + else if (!weak) { + /* A normal cycle which will be destructed in arbitrary + * order. For reasons having to do with strong links we + * can't mark weak cycles this way. */ + unsigned cycle = CYCLE(m->frame) ? CYCLE(m->frame) : ++last_cycle; + if (cycle == CYCLE(gc_rec_last)) + CYCLE_DEBUG_MSG(m, "gc_cycle_push, old cycle"); + else { + CYCLE_DEBUG_MSG(m, "gc_cycle_push, cycle"); + for (p = m->frame;; p = NEXT(p)) { + CYCLE(p) = cycle; + CYCLE_DEBUG_MSG(find_marker(p->data), "> gc_cycle_push, mark cycle"); + if (p == gc_rec_last) break; + }}}}} /* Mmm.. lisp ;) */ else if (!(m->flags & GC_CYCLE_CHECKED)) { - struct marker *p; - m->flags |= gc_rec_last->flags & GC_LIVE; - if (weak) { - if (weak > 0) m->flags |= GC_WEAK_REF; - else m->flags |= GC_STRONG_REF; - } + struct gc_frame *l, *prev_rec_last = gc_rec_last; #ifdef PIKE_DEBUG cycle_checked++; - if (m->flags & GC_ON_STACK) - gc_fatal(x, 0, "Recursing twice into thing.\n"); - if (m->flags & GC_LIVE_RECURSE) - gc_fatal(x, 0, "GC_LIVE_RECURSE set in normal recursion.\n"); + if (m->frame) + gc_fatal(x, 0, "Marker already got a frame.\n"); + if (NEXT(gc_rec_last)) + gc_fatal(gc_rec_last->data, 0, "Not at end of list.\n"); #endif - p = gc_rec_last; - if (p->cycle) - for (; p->link && p->link->cycle == p->cycle; p = p->link) {} - m->link = p->link; - p->link = m; - m->flags |= GC_CYCLE_CHECKED|GC_ON_STACK|GC_IN_REC_LIST; - m->cycle = 0; + NEXT(gc_rec_last) = m->frame = l = gc_cycle_enqueue_pop(x); + m->flags |= GC_CYCLE_CHECKED | (last->flags & GC_LIVE); + if (weak) { + if (weak > 0) l->frameflags |= GC_WEAK_REF; + else l->frameflags |= GC_STRONG_REF; + } #ifdef GC_CYCLE_DEBUG if (weak > 0) CYCLE_DEBUG_MSG(m, "gc_cycle_push, recurse weak"); @@ -1409,13 +1602,13 @@ int gc_cycle_push(void *x, struct marker *m, int weak) else CYCLE_DEBUG_MSG(m, "gc_cycle_push, recurse"); gc_cycle_indent += 2; #endif - gc_rec_last = m; + gc_rec_last = l; return 1; } /* Should normally not recurse now, but got to do that anyway if we * must mark live things. */ - if (!(gc_rec_last->flags & GC_LIVE) || m->flags & GC_LIVE) { + if (!(last->flags & GC_LIVE) || m->flags & GC_LIVE) { CYCLE_DEBUG_MSG(m, "gc_cycle_push, no recurse"); return 0; } @@ -1438,34 +1631,26 @@ live_recurse: } } - /* Recurse without linking m onto rec_list. */ + { + /* Recurse without linking onto rec_list. */ + struct gc_frame *l = gc_cycle_enqueue_pop(x); #ifdef GC_CYCLE_DEBUG - CYCLE_DEBUG_MSG(m, "gc_cycle_push, live recurse"); - gc_cycle_indent += 2; + CYCLE_DEBUG_MSG(m, "gc_cycle_push, live recurse"); + gc_cycle_indent += 2; #endif - gc_rec_last = m; + gc_rec_last = l; + } + return 1; } -/* Add an extra ref when a dead thing is popped. It's taken away in - * the free pass. This is done to not refcount garb the cycles - * themselves recursively, which in bad cases can consume a lot of C - * stack. */ -#define ADD_REF_IF_DEAD(M) \ - if (!(M->flags & GC_LIVE)) { \ - DO_IF_DEBUG( \ - if (M->flags & GC_GOT_DEAD_REF) \ - gc_fatal(M->data, 0, "A thing already got an extra dead cycle ref.\n"); \ - ); \ - gc_add_extra_ref(M->data); \ - M->flags |= GC_GOT_DEAD_REF; \ - } - -void gc_cycle_pop(void *a) +static void gc_cycle_pop(void *a) { - struct marker *m = find_marker(a), *p, *q, *base; + struct marker *m = find_marker(a); + struct gc_frame *here, *base, *p; #ifdef PIKE_DEBUG + if (!a) fatal("Got null pointer.\n"); if (Pike_in_gc != GC_PASS_CYCLE) fatal("GC cycle pop attempted in invalid pass.\n"); if (!(m->flags & GC_CYCLE_CHECKED)) @@ -1483,97 +1668,80 @@ void gc_cycle_pop(void *a) if (m->flags & GC_LIVE_RECURSE) { m->flags &= ~GC_LIVE_RECURSE; - CYCLE_DEBUG_MSG(m, "gc_cycle_pop, live pop"); + CYCLE_DEBUG_MSG(m, "gc_cycle_pop_live"); + gc_rec_last = PREV(gc_rec_top); + debug_really_free_gc_frame(gc_rec_top); return; } + here = m->frame; #ifdef PIKE_DEBUG - if (!(m->flags & GC_ON_STACK)) + if (!here || here->data != a) + gc_fatal(a, 0, "Marker being popped has no or invalid frame.\n"); + CHECK_POP_FRAME(here); + CHECK_POP_FRAME(gc_rec_last); + if (here->frameflags & GC_OFF_STACK) gc_fatal(a, 0, "Marker being popped isn't on stack.\n"); - if (m->flags & GC_GOT_DEAD_REF) - gc_fatal(a, 0, "Didn't expect a dead extra ref.\n"); -#endif - m->flags &= ~GC_ON_STACK; - - if (m->flags & GC_DONT_POP) { -#ifdef PIKE_DEBUG - if (!m->link) - gc_fatal(a, 0, "Found GC_DONT_POP marker on top of list.\n"); + here->back = (struct gc_frame *) -1; #endif - CYCLE_DEBUG_MSG(m, "gc_cycle_pop, keep in list"); - if (!(m->flags & GC_MOVED_BACK)) { - gc_rec_last->flags |= GC_DONT_POP; - CYCLE_DEBUG_MSG(gc_rec_last, "gc_cycle_pop, propagate don't pop"); - } - return; - } + here->frameflags |= GC_OFF_STACK; - for (base = gc_rec_last, p = base->link;; base = p, p = p->link) { - if (base == m) { -#ifdef GC_CYCLE_DEBUG + for (base = PREV(here), p = here;; base = p, p = NEXT(p)) { + if (base == here) { + /* Part of a cycle; wait until the cycle is complete before + * unlinking it from rec_list. */ + DO_IF_DEBUG(m->frame->back = (struct gc_frame *) -1); CYCLE_DEBUG_MSG(m, "gc_cycle_pop, keep cycle"); -#endif return; } - if (!(p->cycle && p->cycle == base->cycle)) + CHECK_POP_FRAME(p); + if (!(CYCLE(p) && CYCLE(p) == CYCLE(base))) break; } - q = base; - while (1) { - p = q->link; + gc_rec_last = base; + while ((p = NEXT(base))) { + struct marker *pm = find_marker(p->data); #ifdef PIKE_DEBUG - if (p->flags & (GC_ON_STACK|GC_LIVE_RECURSE)) - gc_fatal(p->data, 0, "Marker still got an on-stack flag at pop.\n"); + if (pm->frame != p) + gc_fatal(p->data, 0, "Bogus marker for thing being popped.\n"); + if (pm->flags & GC_GOT_DEAD_REF) + gc_fatal(p->data, 0, "Didn't expect a dead extra ref.\n"); #endif - - if (!p->cycle && !(p->flags & GC_LIVE_OBJ)) { - ADD_REF_IF_DEAD(p); - p->flags &= ~(GC_IN_REC_LIST|GC_DONT_POP|GC_WEAK_REF|GC_STRONG_REF); - q->link = p->link; - DO_IF_DEBUG(p->link = (struct marker *) -1); - CYCLE_DEBUG_MSG(p, "gc_cycle_pop, pop off"); + p->frameflags &= ~(GC_WEAK_REF|GC_STRONG_REF); + if (pm->flags & GC_LIVE_OBJ) { + /* This extra ref is taken away in the kill pass. */ + gc_add_extra_ref(p->data); + base = p; + DO_IF_DEBUG(PREV(p) = (struct gc_frame *) -1); + CYCLE_DEBUG_MSG(pm, "gc_cycle_pop, put on kill list"); } - else q = p; - - if (p == m) break; - } - - if (base != q) { - q = base; - while ((p = q->link)) { + else { + if (!(pm->flags & GC_LIVE)) { + /* Add an extra ref which is taken away in the free pass. This + * is done to not refcount garb the cycles themselves + * recursively, which in bad cases can consume a lot of C + * stack. */ #ifdef PIKE_DEBUG - if (!(p->flags & GC_IN_REC_LIST)) - gc_fatal(p->data, 0, "Marker being cycle popped doesn't have GC_IN_REC_LIST.\n"); - if (p->flags & GC_GOT_DEAD_REF) - gc_fatal(p->data, 0, "Didn't expect a dead extra ref.\n"); -#endif - p->flags &= ~GC_IN_REC_LIST; - - p->flags &= ~(GC_DONT_POP|GC_WEAK_REF|GC_STRONG_REF); - if (p->flags & GC_LIVE_OBJ) { - /* This extra ref is taken away in the kill pass. */ - gc_add_extra_ref(p->data); - q = p; - CYCLE_DEBUG_MSG(p, "gc_cycle_pop, put on kill list"); - } - else { - ADD_REF_IF_DEAD(p); - q->link = p->link; - DO_IF_DEBUG(p->link = (struct marker *) -1); - CYCLE_DEBUG_MSG(p, "gc_cycle_pop, cycle pop off"); + if (pm->flags & GC_GOT_DEAD_REF) + gc_fatal(pm->data, 0, + "A thing already got an extra dead cycle ref.\n"); +#endif + gc_add_extra_ref(pm->data); + pm->flags |= GC_GOT_DEAD_REF; } + NEXT(base) = NEXT(p); + CYCLE_DEBUG_MSG(pm, "gc_cycle_pop, pop off"); + pm->frame = 0; + debug_really_free_gc_frame(p); } - - q->link = kill_list; - kill_list = base->link; - base->link = p; } -} -void gc_set_rec_last(struct marker *m) -{ - gc_rec_last = m; + if (base != gc_rec_last) { + NEXT(base) = kill_list; + kill_list = NEXT(gc_rec_last); + NEXT(gc_rec_last) = 0; + } } void do_gc_recurse_svalues(struct svalue *s, int num) @@ -1622,17 +1790,17 @@ static void warn_bad_cycles() struct array *obj_arr = 0; if (!SETJMP(uwp)) { - struct marker *p; + struct gc_frame *p; unsigned cycle = 0; obj_arr = allocate_array(0); for (p = kill_list; p;) { - if ((cycle = p->cycle)) { + if ((cycle = CYCLE(p))) { push_object((struct object *) p->data); obj_arr = append_array(obj_arr, --sp); } - p = p->link; - if (p ? p->cycle != cycle : cycle) { + p = NEXT(p); + if (p ? CYCLE(p) != cycle : cycle) { if (obj_arr->size >= 2) { push_constant_text("gc"); push_constant_text("bad_cycle"); @@ -1786,6 +1954,7 @@ int do_gc(void) Pike_in_gc=GC_PASS_CYCLE; #ifdef PIKE_DEBUG obj_count = num_objects; + max_gc_frames = 0; #endif /* Now find all cycles in the internal structures */ @@ -1799,16 +1968,44 @@ int do_gc(void) gc_cycle_check_all_programs(); #ifdef PIKE_DEBUG - if (gc_mark_queue.first) - fatal("gc_mark_queue not empty at end of cycle check pass.\n"); - if (rec_list.link || gc_rec_last != &rec_list) + if (gc_rec_top) + fatal("gc_rec_top not empty at end of cycle check pass.\n"); + if (NEXT(&rec_list) || gc_rec_last != &rec_list || gc_rec_top) fatal("Recurse list not empty or inconsistent after cycle check pass.\n"); #endif GC_VERBOSE_DO(fprintf(stderr, "| cycle: %u internal things visited, %u cycle ids used,\n" - "| %u weak references freed, %d things really freed\n", - cycle_checked, last_cycle, weak_freed, obj_count - num_objects)); + "| %u weak references freed, %d things really freed,\n" + "| space for %u gc frames used\n", + cycle_checked, last_cycle, weak_freed, obj_count - num_objects, + max_gc_frames)); + + if (gc_ext_weak_refs) { + size_t to_free = gc_ext_weak_refs; +#ifdef PIKE_DEBUG + obj_count = num_objects; +#endif + Pike_in_gc = GC_PASS_ZAP_WEAK; + /* Zap weak references from external to internal things. That + * doesn't occur very often; only when something have both + * external weak refs and nonweak cyclic refs from internal + * things. */ + gc_zap_ext_weak_refs_in_arrays(); + /* Multisets handled as arrays. */ + if (gc_ext_weak_refs) { + gc_zap_ext_weak_refs_in_mappings(); + if (gc_ext_weak_refs) { + gc_zap_ext_weak_refs_in_programs(); + if (gc_ext_weak_refs) + gc_zap_ext_weak_refs_in_objects(); + } + } + GC_VERBOSE_DO( + fprintf(stderr, + "| zap weak: freed %u external weak refs, %d things really freed\n", + to_free - gc_ext_weak_refs, obj_count - num_objects)); + } /* Thread switches, object alloc/free and reference changes are * allowed again now. */ @@ -1851,11 +2048,15 @@ int do_gc(void) #ifdef PIKE_DEBUG destroy_count = 0; #endif - for (; kill_list; kill_list = kill_list->link) { + for (; kill_list; kill_list = NEXT(kill_list)) { struct object *o = (struct object *) kill_list->data; #ifdef PIKE_DEBUG - if ((kill_list->flags & (GC_LIVE|GC_LIVE_OBJ)) != (GC_LIVE|GC_LIVE_OBJ)) + if ((get_marker(kill_list->data)->flags & (GC_LIVE|GC_LIVE_OBJ)) != + (GC_LIVE|GC_LIVE_OBJ)) gc_fatal(o, 0, "Invalid thing in kill list.\n"); + if (o->parent && !o->parent->prog && + get_marker(o->parent)->flags & GC_LIVE_OBJ) + gc_fatal(o, 0, "GC destructed parent prematurely.\n"); #endif GC_VERBOSE_DO(fprintf(stderr, "| Killing %p with %d refs\n", o, o->refs)); diff --git a/src/gc.h b/src/gc.h index 146b2ceddbcc4e4c8bf672aa43696671c4a977c6..9194dea97658640e0ccf73cfd05d5d399d910727 100644 --- a/src/gc.h +++ b/src/gc.h @@ -1,5 +1,5 @@ /* - * $Id: gc.h,v 1.54 2000/07/11 03:45:10 mast Exp $ + * $Id: gc.h,v 1.55 2000/07/18 05:48:20 mast Exp $ */ #ifndef GC_H #define GC_H @@ -61,23 +61,20 @@ extern void *gc_svalue_location; num_objects-- ; \ }while(0) +struct gc_frame; + struct marker { struct marker *next; - struct marker *link; /* Next pointer used in rec_list and destroy_list. */ + struct gc_frame *frame; /* Pointer into the cycle check stack. */ void *data; INT32 refs; /* Internal references. */ INT32 weak_refs; /* Weak (implying internal) references. */ #ifdef PIKE_DEBUG INT32 xrefs; /* Known external references. */ - INT32 saved_refs; /* Object refcount during check and mark pass. */ + INT32 saved_refs; /* Object refcount during check pass. */ #endif - unsigned INT16 cycle; /* Cycle id number. */ -#ifdef PIKE_DEBUG - unsigned INT32 flags; -#else unsigned INT16 flags; -#endif }; #define GC_MARKED 0x0001 @@ -85,30 +82,25 @@ struct marker #define GC_CYCLE_CHECKED 0x0004 #define GC_LIVE 0x0008 #define GC_LIVE_OBJ 0x0010 -#define GC_ON_STACK 0x0020 -#define GC_IN_REC_LIST 0x0040 -#define GC_MOVED_BACK 0x0080 -#define GC_DONT_POP 0x0100 -#define GC_LIVE_RECURSE 0x0200 -#define GC_WEAK_REF 0x0400 -#define GC_STRONG_REF 0x0800 -#define GC_GOT_DEAD_REF 0x1000 -#define GC_FREE_VISITED 0x2000 - -/* Debug mode flags. */ -#define GC_TOUCHED 0x010000 -#define GC_IS_REFERENCED 0x020000 -#define GC_XREFERENCED 0x040000 -#define GC_DO_FREE 0x080000 -#define GC_GOT_EXTRA_REF 0x100000 -#define GC_FOLLOWED_STRONG 0x200000 +#define GC_LIVE_RECURSE 0x0020 +#define GC_GOT_DEAD_REF 0x0040 +#define GC_FREE_VISITED 0x0080 + +#ifdef PIKE_DEBUG +#define GC_TOUCHED 0x0100 +#define GC_IS_REFERENCED 0x0200 +#define GC_XREFERENCED 0x0400 +#define GC_DO_FREE 0x0800 +#define GC_GOT_EXTRA_REF 0x1000 +#endif #include "block_alloc_h.h" PTR_HASH_ALLOC(marker,MARKER_CHUNK_SIZE) -extern struct marker *gc_rec_last; extern size_t gc_ext_weak_refs; +typedef void gc_cycle_check_cb (void *data, int weak); + /* Prototypes begin here */ struct callback *debug_add_gc_callback(callback_func call, void *arg, @@ -138,7 +130,6 @@ int gc_external_mark3(void *a, void *in, char *where); int gc_do_weak_free(void *a); int gc_mark(void *a); int gc_cycle_push(void *x, struct marker *m, int weak); -void gc_cycle_pop(void *a); void gc_set_rec_last(struct marker *m); void do_gc_recurse_svalues(struct svalue *s, int num); void do_gc_recurse_short_svalue(union anything *u, TYPE_T type); @@ -146,6 +137,8 @@ int gc_do_free(void *a); int gc_is_internal(void *a); int do_gc(void); void f__gc_status(INT32 args); +void gc_cycle_enqueue(gc_cycle_check_cb *checkfn, void *data, int weak); +void gc_cycle_run_queue(); /* Prototypes end here */ #define gc_fatal \ @@ -167,9 +160,10 @@ void f__gc_status(INT32 args); (Pike_in_gc == GC_PASS_MARK ? \ gc_mark_weak_short_svalue((U), (T)) : gc_cycle_check_weak_short_svalue((U), (T))) -#define GC_RECURSE_THING(V,T) \ +#define GC_RECURSE_THING(V, T) \ (Pike_in_gc == GC_PASS_MARK ? \ - PIKE_CONCAT3(gc_mark_, T, _as_referenced)(V) : PIKE_CONCAT(gc_cycle_check_, T)(V)) + PIKE_CONCAT3(gc_mark_, T, _as_referenced)(V) : \ + PIKE_CONCAT(gc_cycle_check_, T)(V, 0)) #define gc_recurse_array(V) GC_RECURSE_THING((V), array) #define gc_recurse_mapping(V) GC_RECURSE_THING((V), mapping) #define gc_recurse_multiset(V) GC_RECURSE_THING((V), multiset) @@ -189,7 +183,7 @@ void f__gc_status(INT32 args); #define gc_is_referenced(X) debug_gc_is_referenced(debug_malloc_pass(X)) #else #define gc_is_referenced(X) !(get_marker(X)->flags & GC_NOT_REFERENCED) -#define gc_do_weak_free(X) (Pike_in_gc != GC_PASS_FREE ? \ +#define gc_do_weak_free(X) (Pike_in_gc != GC_PASS_ZAP_WEAK ? \ get_marker(X)->weak_refs == -1 : \ !(get_marker(X)->flags & GC_MARKED)) #endif @@ -210,6 +204,7 @@ void f__gc_status(INT32 args); #define GC_PASS_CHECK 100 #define GC_PASS_MARK 200 #define GC_PASS_CYCLE 250 +#define GC_PASS_ZAP_WEAK 270 #define GC_PASS_FREE 300 #define GC_PASS_KILL 400 #define GC_PASS_DESTRUCT 500 @@ -223,8 +218,8 @@ extern int gc_in_cycle_check; /* Use WEAK < 0 for strong links. The gc makes these assumptions about * those: - * 1. All strong links are recursed after any other links, i.e. - * strong links should be pushed first into the lifo queue. + * 1. All strong links are recursed before any other links, i.e. + * strong links must be passed to gc_cycle_check_* last. * 2. There can never be a cycle consisting of only strong links. */ @@ -232,42 +227,29 @@ extern int gc_in_cycle_check; void *_thing_ = (X); \ struct marker *_m_ = get_marker(_thing_); \ if (!(_m_->flags & GC_MARKED)) { \ - struct marker *_old_last_ = gc_rec_last; \ - if (gc_cycle_push(_thing_, _m_, (WEAK))) { \ - DO_IF_DEBUG( \ - if (gc_in_cycle_check) \ - fatal("Recursing immediately in gc cycle check.\n"); \ - gc_in_cycle_check = 1; \ - ); \ - enqueue_lifo(&gc_mark_queue, \ - (queue_call) gc_cycle_pop, _thing_); \ - enqueue_lifo(&gc_mark_queue, \ - (queue_call) gc_set_rec_last, _old_last_); \ - { + DO_IF_DEBUG( \ + if (gc_in_cycle_check) \ + fatal("Recursing immediately in gc cycle check.\n"); \ + gc_in_cycle_check = 1; \ + ); \ + if (gc_cycle_push(_thing_, _m_, (WEAK))) { #define GC_CYCLE_ENTER_OBJECT(X, WEAK) do { \ struct object *_thing_ = (X); \ struct marker *_m_ = get_marker(_thing_); \ if (!(_m_->flags & GC_MARKED)) { \ - struct marker *_old_last_ = gc_rec_last; \ if (_thing_->prog && FIND_LFUN(_thing_->prog, LFUN_DESTROY) != -1) \ _m_->flags |= GC_LIVE|GC_LIVE_OBJ; \ - if (gc_cycle_push(_thing_, _m_, (WEAK))) { \ - DO_IF_DEBUG( \ - if (gc_in_cycle_check) \ - fatal("Recursing immediately in gc cycle check.\n"); \ - gc_in_cycle_check = 1; \ - ); \ - enqueue_lifo(&gc_mark_queue, \ - (queue_call) gc_cycle_pop, _thing_); \ - enqueue_lifo(&gc_mark_queue, \ - (queue_call) gc_set_rec_last, _old_last_); \ - { + DO_IF_DEBUG( \ + if (gc_in_cycle_check) \ + fatal("Recursing immediately in gc cycle check.\n"); \ + gc_in_cycle_check = 1; \ + ); \ + if (gc_cycle_push(_thing_, _m_, (WEAK))) { #define GC_CYCLE_LEAVE \ - } \ - DO_IF_DEBUG(gc_in_cycle_check = 0); \ } \ + DO_IF_DEBUG(gc_in_cycle_check = 0); \ } \ } while (0) diff --git a/src/mapping.c b/src/mapping.c index 93b6bd3d19eb29f25f6980118055ebcaa4f802d8..07c7dfa6983862e06e95fd2961b45d682d53b05d 100644 --- a/src/mapping.c +++ b/src/mapping.c @@ -5,7 +5,7 @@ \*/ /**/ #include "global.h" -RCSID("$Id: mapping.c,v 1.92 2000/07/11 03:45:10 mast Exp $"); +RCSID("$Id: mapping.c,v 1.93 2000/07/18 05:48:20 mast Exp $"); #include "main.h" #include "object.h" #include "mapping.h" @@ -846,7 +846,7 @@ void check_mapping_for_destruct(struct mapping *m) fatal("Zero refs in mapping->data\n"); if(d_flag>1) check_mapping(m); debug_malloc_touch(m); - if (Pike_in_gc > GC_PASS_PREPARE && Pike_in_gc < GC_PASS_FREE && + if (Pike_in_gc > GC_PASS_PREPARE && Pike_in_gc < GC_PASS_ZAP_WEAK && Pike_in_gc != GC_PASS_MARK) fatal("check_mapping_for_destruct called in wrong pass inside gc.\n"); #endif @@ -1910,45 +1910,33 @@ void gc_mark_mapping_as_referenced(struct mapping *m) } } -void low_gc_cycle_check_mapping(struct mapping *m) +void real_gc_cycle_check_mapping(struct mapping *m, int weak) { + GC_CYCLE_ENTER(m, weak) { #ifdef PIKE_DEBUG - if(m->data->refs <=0) - fatal("Zero refs in mapping->data\n"); + if(m->data->refs <=0) + fatal("Zero refs in mapping->data\n"); #endif - if((m->data->ind_types | m->data->val_types) & BIT_COMPLEX) - { - INT32 e; - struct keypair *k; + if((m->data->ind_types | m->data->val_types) & BIT_COMPLEX) + { + INT32 e; + struct keypair *k; - if (m->flags & MAPPING_FLAG_WEAK) - gc_recurse_weak_mapping(m, gc_cycle_check_weak_svalues); - else - NEW_MAPPING_LOOP(m->data) - { - if (gc_cycle_check_svalues(&k->ind, 1) || - gc_cycle_check_svalues(&k->val, 1)) { + if (m->flags & MAPPING_FLAG_WEAK) + gc_recurse_weak_mapping(m, gc_cycle_check_weak_svalues); + else + NEW_MAPPING_LOOP(m->data) + { + if (gc_cycle_check_svalues(&k->ind, 1) || + gc_cycle_check_svalues(&k->val, 1)) { #ifdef PIKE_DEBUG - fatal("Looks like check_mapping_for_destruct " - "didn't do its job properly.\n"); + fatal("Looks like check_mapping_for_destruct " + "didn't do its job properly.\n"); #endif + } } - } - } -} - -void real_gc_cycle_check_mapping(struct mapping *m) -{ - GC_CYCLE_ENTER(m, 0) { - low_gc_cycle_check_mapping(m); - } GC_CYCLE_LEAVE; -} - -void real_gc_cycle_check_mapping_weak(struct mapping *m) -{ - GC_CYCLE_ENTER(m, 1) { - low_gc_cycle_check_mapping(m); + } } GC_CYCLE_LEAVE; } @@ -2049,11 +2037,22 @@ void gc_cycle_check_all_mappings(void) { struct mapping *m; for (m = gc_internal_mapping; m; m = m->next) { - real_gc_cycle_check_mapping(m); - run_lifo_queue(&gc_mark_queue); + real_gc_cycle_check_mapping(m, 0); + gc_cycle_run_queue(); } } +void gc_zap_ext_weak_refs_in_mappings(void) +{ + gc_mark_mapping_pos = first_mapping; + while (gc_mark_mapping_pos != gc_internal_mapping && gc_ext_weak_refs) { + struct mapping *m = gc_mark_mapping_pos; + gc_mark_mapping_pos = m->next; + gc_mark_mapping_as_referenced(m); + } + discard_queue(&gc_mark_queue); +} + void gc_free_all_unreferenced_mappings(void) { INT32 e; @@ -2061,19 +2060,6 @@ void gc_free_all_unreferenced_mappings(void) struct mapping *m,*next; struct mapping_data *md; - if (gc_ext_weak_refs) { - /* Have to go through all marked things if we got external weak - * references to otherwise unreferenced things, so the mark - * functions can free those references. */ - gc_mark_mapping_pos = first_mapping; - while (gc_mark_mapping_pos != gc_internal_mapping && gc_ext_weak_refs) { - struct mapping *m = gc_mark_mapping_pos; - gc_mark_mapping_pos = m->next; - gc_mark_mapping_as_referenced(m); - } - discard_queue(&gc_mark_queue); - } - for(m=gc_internal_mapping;m;m=next) { if(gc_do_free(m)) diff --git a/src/mapping.h b/src/mapping.h index 0bd51726ba47c19057af9cb98f3e09f0d8f1d6b0..b2c3ae334d717251d695dc19e322bffe6f706077 100644 --- a/src/mapping.h +++ b/src/mapping.h @@ -5,7 +5,7 @@ \*/ /* - * $Id: mapping.h,v 1.26 2000/07/06 23:25:26 mast Exp $ + * $Id: mapping.h,v 1.27 2000/07/18 05:48:20 mast Exp $ */ #ifndef MAPPING_H #define MAPPING_H @@ -150,9 +150,9 @@ void gc_mark_mapping_as_referenced(struct mapping *m); unsigned gc_touch_all_mappings(void); void gc_check_all_mappings(void); void gc_mark_all_mappings(void); -void real_gc_cycle_check_mapping(struct mapping *m); -void real_gc_cycle_check_mapping_weak(struct mapping *m); +void real_gc_cycle_check_mapping(struct mapping *m, int weak); void gc_cycle_check_all_mappings(void); +void gc_zap_ext_weak_refs_in_mappings(void); void gc_free_all_unreferenced_mappings(void); void simple_describe_mapping(struct mapping *m); void debug_dump_mapping(struct mapping *m); @@ -161,9 +161,7 @@ void zap_all_mappings(void); #define allocate_mapping(X) dmalloc_touch(struct mapping *,debug_allocate_mapping(X)) -#define gc_cycle_check_mapping(X) \ - enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_mapping, (X)) -#define gc_cycle_check_mapping_weak(X) \ - enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_mapping_weak, (X)) +#define gc_cycle_check_mapping(X, WEAK) \ + gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_mapping, (X), (WEAK)) #endif /* MAPPING_H */ diff --git a/src/multiset.c b/src/multiset.c index 8340d086adc207a8e398885b3def62e9ad4e1b2e..7041a49da918781c49ba38cc6ba11aa27a1f3515 100644 --- a/src/multiset.c +++ b/src/multiset.c @@ -16,7 +16,7 @@ #include "gc.h" #include "security.h" -RCSID("$Id: multiset.c,v 1.24 2000/07/11 03:45:10 mast Exp $"); +RCSID("$Id: multiset.c,v 1.25 2000/07/18 05:48:20 mast Exp $"); struct multiset *first_multiset; @@ -307,17 +307,10 @@ void gc_mark_multiset_as_referenced(struct multiset *l) } } -void real_gc_cycle_check_multiset(struct multiset *l) +void real_gc_cycle_check_multiset(struct multiset *l, int weak) { - GC_CYCLE_ENTER(l, 0) { - gc_cycle_check_array(l->ind); - } GC_CYCLE_LEAVE; -} - -void real_gc_cycle_check_multiset_weak(struct multiset *l) -{ - GC_CYCLE_ENTER(l, 1) { - gc_cycle_check_array(l->ind); + GC_CYCLE_ENTER(l, weak) { + gc_cycle_check_array(l->ind, 0); } GC_CYCLE_LEAVE; } @@ -360,8 +353,8 @@ void gc_cycle_check_all_multisets(void) { struct multiset *l; for (l = gc_internal_multiset; l; l = l->next) { - real_gc_cycle_check_multiset(l); - run_lifo_queue(&gc_mark_queue); + real_gc_cycle_check_multiset(l, 0); + gc_cycle_run_queue(); } } @@ -369,9 +362,6 @@ void gc_free_all_unreferenced_multisets(void) { struct multiset *l,*next; - /* No gc_ext_weak_refs stuff here; it's been taken care of by - * gc_free_all_unreferenced_arrays(). */ - for(l=gc_internal_multiset;l;l=next) { if(gc_do_free(l)) diff --git a/src/multiset.h b/src/multiset.h index 85bb71b1a62d30a5fb5456bb26dc7bfeef402652..b7ee4a003d084bccc022dee3997bb5d82f258845 100644 --- a/src/multiset.h +++ b/src/multiset.h @@ -5,7 +5,7 @@ \*/ /* - * $Id: multiset.h,v 1.12 2000/07/06 23:25:26 mast Exp $ + * $Id: multiset.h,v 1.13 2000/07/18 05:48:20 mast Exp $ */ #ifndef MULTISET_H #define MULTISET_H @@ -56,16 +56,13 @@ void gc_mark_multiset_as_referenced(struct multiset *l); unsigned gc_touch_all_multisets(void); void gc_check_all_multisets(void); void gc_mark_all_multisets(void); -void real_gc_cycle_check_multiset(struct multiset *l); -void real_gc_cycle_check_multiset_weak(struct multiset *l); +void real_gc_cycle_check_multiset(struct multiset *l, int weak); void gc_cycle_check_all_multisets(void); void gc_free_all_unreferenced_multisets(void); void count_memory_in_multisets(INT32 *num_, INT32 *size_); /* Prototypes end here */ -#define gc_cycle_check_multiset(X) \ - enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_multiset, (X)) -#define gc_cycle_check_multiset_weak(X) \ - enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_multiset_weak, (X)) +#define gc_cycle_check_multiset(X, WEAK) \ + gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_multiset, (X), (WEAK)) #endif /* MULTISET_H */ diff --git a/src/object.c b/src/object.c index 13b58c32cc0ffc47f49ffc40cf32b14cf005a3c0..5bd94cfbf7838f76b1d0bbde395f5e5535a45f4f 100644 --- a/src/object.c +++ b/src/object.c @@ -5,7 +5,7 @@ \*/ /**/ #include "global.h" -RCSID("$Id: object.c,v 1.135 2000/07/11 03:45:10 mast Exp $"); +RCSID("$Id: object.c,v 1.136 2000/07/18 05:48:20 mast Exp $"); #include "object.h" #include "dynamic_buffer.h" #include "interpret.h" @@ -1279,80 +1279,61 @@ void gc_mark_object_as_referenced(struct object *o) } } -static void low_gc_cycle_check_object(struct object *o) +void real_gc_cycle_check_object(struct object *o, int weak) { - int e; - struct program *p = o->prog; + GC_CYCLE_ENTER_OBJECT(o, weak) { + int e; + struct program *p = o->prog; - if (p) { + if (p) { #if 0 - struct object *o2; - for (o2 = gc_internal_object; o2 && o2 != o; o2 = o2->next) {} - if (!o2) fatal("Object not on gc_internal_object list.\n"); + struct object *o2; + for (o2 = gc_internal_object; o2 && o2 != o; o2 = o2->next) {} + if (!o2) fatal("Object not on gc_internal_object list.\n"); #endif - /* This must be first. */ - if(o->parent) - gc_cycle_check_object_strong(o->parent); + LOW_PUSH_FRAME(o); - LOW_PUSH_FRAME(o); - - for(e=p->num_inherits-1; e>=0; e--) - { - int q; + for(e=p->num_inherits-1; e>=0; e--) + { + int q; - LOW_SET_FRAME_CONTEXT(p->inherits[e]); + LOW_SET_FRAME_CONTEXT(p->inherits[e]); - if(pike_frame->context.prog->gc_recurse_func) - pike_frame->context.prog->gc_recurse_func(o); + if(pike_frame->context.prog->gc_recurse_func) + pike_frame->context.prog->gc_recurse_func(o); - for(q=0;q<(int)pike_frame->context.prog->num_variable_index;q++) - { - int d=pike_frame->context.prog->variable_index[q]; - - if(pike_frame->context.prog->identifiers[d].run_time_type == T_MIXED) + for(q=0;q<(int)pike_frame->context.prog->num_variable_index;q++) { - struct svalue *s; - s=(struct svalue *)(pike_frame->current_storage + - pike_frame->context.prog->identifiers[d].func.offset); - dmalloc_touch_svalue(s); - gc_cycle_check_svalues(s, 1); - }else{ - union anything *u; - int rtt = pike_frame->context.prog->identifiers[d].run_time_type; - u=(union anything *)(pike_frame->current_storage + - pike_frame->context.prog->identifiers[d].func.offset); + int d=pike_frame->context.prog->variable_index[q]; + + if(pike_frame->context.prog->identifiers[d].run_time_type == T_MIXED) + { + struct svalue *s; + s=(struct svalue *)(pike_frame->current_storage + + pike_frame->context.prog->identifiers[d].func.offset); + dmalloc_touch_svalue(s); + gc_cycle_check_svalues(s, 1); + }else{ + union anything *u; + int rtt = pike_frame->context.prog->identifiers[d].run_time_type; + u=(union anything *)(pike_frame->current_storage + + pike_frame->context.prog->identifiers[d].func.offset); #ifdef DEBUG_MALLOC - if (rtt <= MAX_REF_TYPE) debug_malloc_touch(u->refs); + if (rtt <= MAX_REF_TYPE) debug_malloc_touch(u->refs); #endif - gc_cycle_check_short_svalue(u, rtt); + gc_cycle_check_short_svalue(u, rtt); + } } + LOW_UNSET_FRAME_CONTEXT(); } - LOW_UNSET_FRAME_CONTEXT(); - } - LOW_POP_FRAME(); - } -} - -void real_gc_cycle_check_object(struct object *o) -{ - GC_CYCLE_ENTER_OBJECT(o, 0) { - low_gc_cycle_check_object(o); - } GC_CYCLE_LEAVE; -} - -void real_gc_cycle_check_object_weak(struct object *o) -{ - GC_CYCLE_ENTER_OBJECT(o, 1) { - low_gc_cycle_check_object(o); - } GC_CYCLE_LEAVE; -} + LOW_POP_FRAME(); -void real_gc_cycle_check_object_strong(struct object *o) -{ - GC_CYCLE_ENTER_OBJECT(o, -1) { - low_gc_cycle_check_object(o); + /* This must be last. */ + if(o->parent) + gc_cycle_check_object(o->parent, -1); + } } GC_CYCLE_LEAVE; } @@ -1468,29 +1449,27 @@ void gc_cycle_check_all_objects(void) { struct object *o; for (o = gc_internal_object; o; o = o->next) { - real_gc_cycle_check_object(o); - run_lifo_queue(&gc_mark_queue); + real_gc_cycle_check_object(o, 0); + gc_cycle_run_queue(); } } +void gc_zap_ext_weak_refs_in_objects(void) +{ + gc_mark_object_pos = first_object; + while (gc_mark_object_pos != gc_internal_object && gc_ext_weak_refs) { + struct object *o = gc_mark_object_pos; + gc_mark_object_pos = o->next; + if (o->refs) + gc_mark_object_as_referenced(o); + } + discard_queue(&gc_mark_queue); +} + void gc_free_all_unreferenced_objects(void) { struct object *o,*next; - if (gc_ext_weak_refs) { - /* Have to go through all marked things if we got external weak - * references to otherwise unreferenced things, so the mark - * functions can free those references. */ - gc_mark_object_pos = first_object; - while (gc_mark_object_pos != gc_internal_object && gc_ext_weak_refs) { - struct object *o = gc_mark_object_pos; - gc_mark_object_pos = o->next; - if (o->refs) - gc_mark_object_as_referenced(o); - } - discard_queue(&gc_mark_queue); - } - for(o=gc_internal_object; o; o=next) { if(gc_do_free(o)) diff --git a/src/object.h b/src/object.h index 56ce772429e5d1ea20812d3faf2ac753e184eee5..ae0b8cfdae1d6b6bd0a96ebfc62d172696b1b83b 100644 --- a/src/object.h +++ b/src/object.h @@ -5,7 +5,7 @@ \*/ /* - * $Id: object.h,v 1.48 2000/06/24 00:48:13 hubbe Exp $ + * $Id: object.h,v 1.49 2000/07/18 05:48:20 mast Exp $ */ #ifndef OBJECT_H #define OBJECT_H @@ -100,10 +100,9 @@ void gc_mark_object_as_referenced(struct object *o); unsigned gc_touch_all_objects(void); void gc_check_all_objects(void); void gc_mark_all_objects(void); -void real_gc_cycle_check_object(struct object *o); -void real_gc_cycle_check_object_weak(struct object *o); -void real_gc_cycle_check_object_strong(struct object *o); +void real_gc_cycle_check_object(struct object *o, int weak); void gc_cycle_check_all_objects(void); +void gc_zap_ext_weak_refs_in_objects(void); void gc_free_all_unreferenced_objects(void); void count_memory_in_objects(INT32 *num_, INT32 *size_); struct magic_index_struct; @@ -129,12 +128,8 @@ void check_all_objects(void); #define master() debug_master() #endif -#define gc_cycle_check_object(X) \ - enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_object, (X)) -#define gc_cycle_check_object_weak(X) \ - enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_object_weak, (X)) -#define gc_cycle_check_object_strong(X) \ - enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_object_strong, (X)) +#define gc_cycle_check_object(X, WEAK) \ + gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_object, (X), (WEAK)) #endif /* OBJECT_H */ diff --git a/src/program.c b/src/program.c index cc64155704c4a49f0ba25eca6f93ce67b091a527..c56866e3af0b284d097e99ae0766381ff2106256 100644 --- a/src/program.c +++ b/src/program.c @@ -5,7 +5,7 @@ \*/ /**/ #include "global.h" -RCSID("$Id: program.c,v 1.250 2000/07/12 12:38:41 grubba Exp $"); +RCSID("$Id: program.c,v 1.251 2000/07/18 05:48:20 mast Exp $"); #include "program.h" #include "object.h" #include "dynamic_buffer.h" @@ -3586,40 +3586,28 @@ void gc_mark_program_as_referenced(struct program *p) } } -void low_gc_cycle_check_program(struct program *p) +void real_gc_cycle_check_program(struct program *p, int weak) { - int e; - - if (p->flags & PROGRAM_AVOID_CHECK) - /* Program is in an inconsistent state. - * don't look closer at it. - */ - return; - - for(e=0;e<p->num_constants;e++) - gc_cycle_check_svalues(& p->constants[e].sval, 1); + GC_CYCLE_ENTER(p, weak) { + int e; - for(e=0;e<p->num_inherits;e++) - { - if(p->inherits[e].parent) - gc_cycle_check_object(p->inherits[e].parent); + if (p->flags & PROGRAM_AVOID_CHECK) + /* Program is in an inconsistent state. + * don't look closer at it. + */ + return; - if(e && p->inherits[e].prog) - gc_cycle_check_program(p->inherits[e].prog); - } -} + for(e=0;e<p->num_constants;e++) + gc_cycle_check_svalues(& p->constants[e].sval, 1); -void real_gc_cycle_check_program(struct program *p) -{ - GC_CYCLE_ENTER(p, 0) { - low_gc_cycle_check_program(p); - } GC_CYCLE_LEAVE; -} + for(e=0;e<p->num_inherits;e++) + { + if(p->inherits[e].parent) + gc_cycle_check_object(p->inherits[e].parent, 0); -void real_gc_cycle_check_program_weak(struct program *p) -{ - GC_CYCLE_ENTER(p, 1) { - low_gc_cycle_check_program(p); + if(e && p->inherits[e].prog) + gc_cycle_check_program(p->inherits[e].prog, 0); + } } GC_CYCLE_LEAVE; } @@ -3723,28 +3711,26 @@ void gc_cycle_check_all_programs(void) { struct program *p; for (p = gc_internal_program; p; p = p->next) { - real_gc_cycle_check_program(p); - run_lifo_queue(&gc_mark_queue); + real_gc_cycle_check_program(p, 0); + gc_cycle_run_queue(); + } +} + +void gc_zap_ext_weak_refs_in_programs(void) +{ + gc_mark_program_pos = first_program; + while (gc_mark_program_pos != gc_internal_program && gc_ext_weak_refs) { + struct program *p = gc_mark_program_pos; + gc_mark_program_pos = p->next; + gc_mark_program_as_referenced(p); } + discard_queue(&gc_mark_queue); } void gc_free_all_unreferenced_programs(void) { struct program *p,*next; - if (gc_ext_weak_refs) { - /* Have to go through all marked things if we got external weak - * references to otherwise unreferenced things, so the mark - * functions can free those references. */ - gc_mark_program_pos = first_program; - while (gc_mark_program_pos != gc_internal_program && gc_ext_weak_refs) { - struct program *p = gc_mark_program_pos; - gc_mark_program_pos = p->next; - gc_mark_program_as_referenced(p); - } - discard_queue(&gc_mark_queue); - } - for(p=gc_internal_program;p;p=next) { debug_malloc_touch(p); diff --git a/src/program.h b/src/program.h index 11434bc6581293f439d805ce68fb13423a8807f2..c0b338d047d394eab3df9f80aedba15359f3d3cf 100644 --- a/src/program.h +++ b/src/program.h @@ -5,7 +5,7 @@ \*/ /* - * $Id: program.h,v 1.95 2000/07/12 16:09:36 grubba Exp $ + * $Id: program.h,v 1.96 2000/07/18 05:48:20 mast Exp $ */ #ifndef PROGRAM_H #define PROGRAM_H @@ -471,9 +471,9 @@ void gc_mark_program_as_referenced(struct program *p); unsigned gc_touch_all_programs(void); void gc_check_all_programs(void); void gc_mark_all_programs(void); -void real_gc_cycle_check_program(struct program *p); -void real_gc_cycle_check_program_weak(struct program *p); +void real_gc_cycle_check_program(struct program *p, int weak); void gc_cycle_check_all_programs(void); +void gc_zap_ext_weak_refs_in_programs(void); void gc_free_all_unreferenced_programs(void); void count_memory_in_programs(INT32 *num_, INT32 *size_); void push_compiler_frame(int lexical_scope); @@ -542,10 +542,8 @@ void *parent_storage(int depth); #define start_new_program() debug_start_new_program() #endif -#define gc_cycle_check_program(X) \ - enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_program, (X)) -#define gc_cycle_check_program_weak(X) \ - enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_program_weak, (X)) +#define gc_cycle_check_program(X, WEAK) \ + gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_program, (X), (WEAK)) /* This can be used for backwards compatibility * (if added to program.h in Pike 0.6 and Pike 7.0 diff --git a/src/svalue.c b/src/svalue.c index fc7757d9fa54ee084ac8777a1612cea708580434..0a401a80c1b5ad6586830f66d513816002ca72fa 100644 --- a/src/svalue.c +++ b/src/svalue.c @@ -24,7 +24,7 @@ #include "queue.h" #include "bignum.h" -RCSID("$Id: svalue.c,v 1.83 2000/07/11 03:45:10 mast Exp $"); +RCSID("$Id: svalue.c,v 1.84 2000/07/18 05:48:20 mast Exp $"); struct svalue dest_ob_zero = { T_INT, 0 }; @@ -1515,8 +1515,8 @@ int gc_mark_weak_short_svalue(union anything *u, TYPE_T type) #define DO_CYCLE_CHECK_STRING(U) -#define GC_DO_CYCLE_CHECK(U, TN) PIKE_CONCAT(gc_cycle_check_, TN)(U.TN) -#define GC_DO_CYCLE_CHECK_WEAK(U, TN) PIKE_CONCAT3(gc_cycle_check_, TN, _weak)(U.TN) +#define GC_DO_CYCLE_CHECK(U, TN) PIKE_CONCAT(gc_cycle_check_, TN)(U.TN, 0) +#define GC_DO_CYCLE_CHECK_WEAK(U, TN) PIKE_CONCAT(gc_cycle_check_, TN)(U.TN, 1) TYPE_FIELD real_gc_cycle_check_svalues(struct svalue *s, size_t num) { diff --git a/src/testsuite.in b/src/testsuite.in index d549826ad0f976364547089d9a574978d1f3c381..c721eb4290c84d18592cbfb5195d474b5e01832d 100644 --- a/src/testsuite.in +++ b/src/testsuite.in @@ -1,4 +1,4 @@ -test_true([["$Id: testsuite.in,v 1.313 2000/07/11 20:42:58 hubbe Exp $"]]); +test_true([["$Id: testsuite.in,v 1.314 2000/07/18 05:48:20 mast Exp $"]]); cond([[all_constants()->_verify_internals]], [[ @@ -1945,7 +1945,7 @@ ifefun(gc, string id; void create (int i) {id = sprintf (\"dead[%d]\", i);} mixed a = 1, b = 1; // Mustn't be zero at destruct time. - mixed x, y; + mixed x, y, z; array v = set_weak_flag (({1}), 1); // Mustn't be zero at destruct time. array w = set_weak_flag (({0, 0}), 1); function(object:mixed) checkfn; @@ -1962,6 +1962,7 @@ ifefun(gc, if (objectp (b) && !checked[b]) b->check_live (checked); if (objectp (x) && !checked[x]) x->check_live (checked); if (objectp (y) && !checked[y]) y->check_live (checked); + if (objectp (z) && !checked[z]) z->check_live (checked); if (objectp (v[0]) && !checked[v[0] ]) v[0]->check_live (checked); if (objectp (w[0]) && !checked[w[0] ]) w[0]->check_live (checked); if (objectp (w[1]) && !checked[w[1] ]) w[1]->check_live (checked); @@ -2476,6 +2477,34 @@ ifefun(gc, live_nested[1]->x = dead_nested[0]; dead_nested[0]->x = live_nested[0]; }}), + ({2, 0, 2, 0, lambda() { // 72 + live[0]->x = live[1]; + live[1]->x = live_nested[1]; + live_nested[1]->x = live[0]; + live_nested[0]->x = live[1]; + }}), + ({2, 0, 4, 0, lambda() { // 73 + live[0]->x = live[1], live[0]->y = live_nested[2], live[0]->z = live_nested[3]; + live[1]->x = live[0]; + live_nested[1]->x = live[0]; + }}), + ({2, 0, 4, 0, lambda() { // 74 + live[0]->y = live[1], live[0]->z = live_nested[2], live[0]->x = live_nested[3]; + live[1]->x = live[0]; + live_nested[1]->x = live[0]; + }}), + ({2, 0, 4, 0, lambda() { // 75 + live[0]->z = live[1], live[0]->x = live_nested[2], live[0]->y = live_nested[3]; + live[1]->x = live[0]; + live_nested[1]->x = live[0]; + }}), + ({2, 1, 2, 0, lambda() { // 76 + dead[0]->x = dead[0], dead[0]->a = live_nested[0]; + live_nested[0]->y = live_nested[1], live_nested[0]->x = live[1]; + live_nested[1]->x = live[0]; + live[0]->x = live_nested[0]; + live[1]->x = live[0]; + }}), // ({3, 0, 0, 0, lambda() { // Not possible without weak refs directly in objects. // live[0]->x = live[0], live[0]->v[0] = live[1]; // live[1]->x = live[1], live[1]->w[0] = live[2]; @@ -2541,8 +2570,8 @@ ifefun(gc, } } } - werror ("%60s\r", ""); if (test_failed) error ("GC destruct order test failed.\n"); + werror ("%60s\r", ""); }]]) test_any([[mapping q=([ "t":class {} ()]); gc(); if(!objectp(q->t)) return -1; set_weak_flag(q,1); gc(); if(objectp(q->t)) return -2; return 0;]],0);