pike.git / src / gc.c

version» Context lines:

pike.git/src/gc.c:1:   /*   || This file is part of Pike. For copyright information see COPYRIGHT.   || Pike is distributed under GPL, LGPL and MPL. See the file COPYING   || for more information. - || $Id: gc.c,v 1.194 2003/07/16 14:10:12 mast Exp $ + || $Id: gc.c,v 1.195 2003/07/16 14:43:25 mast Exp $   */      #include "global.h"      struct callback *gc_evaluator_callback=0;      #include "array.h"   #include "multiset.h"   #include "mapping.h"   #include "object.h"
pike.git/src/gc.c:24:   #include "constants.h"   #include "interpret.h"   #include "bignum.h"      #include "gc.h"   #include "main.h"   #include <math.h>      #include "block_alloc.h"    - RCSID("$Id: gc.c,v 1.194 2003/07/16 14:10:12 mast Exp $"); + RCSID("$Id: gc.c,v 1.195 2003/07/16 14:43:25 mast Exp $");      /* Run garbage collect approximately every time    * 20 percent of all arrays, objects and programs is    * garbage.    */      #define GC_CONST 20   #define MIN_ALLOC_THRESHOLD 1000   #define MAX_ALLOC_THRESHOLD 2000000000   #define MULTIPLIER 0.9
pike.git/src/gc.c:120:    } link;    int free_extra_type; /* Used on free_extra_list. The type of the thing. */    } u;    unsigned INT16 frameflags;   };      #define GC_POP_FRAME 0x01   #define GC_WEAK_REF 0x02   #define GC_STRONG_REF 0x04   #define GC_OFF_STACK 0x08 + #define GC_ON_KILL_LIST 0x10   #ifdef PIKE_DEBUG - #define GC_LINK_FREED 0x10 - #define GC_FOLLOWED_NONSTRONG 0x20 + #define GC_LINK_FREED 0x20 + #define GC_FOLLOWED_NONSTRONG 0x40   #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)
pike.git/src/gc.c:167:    * 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.    *    * 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. +  * rec_list might contain frames that are no longer on the stack.    * -  * A range of frames which always ends at the end of the list, may be +  * 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    * 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.
pike.git/src/gc.c:1834:    fprintf(stderr, "%*s%-35s %p [%p] ", gc_cycle_indent, "", \    (TXT), (M) ? (M)->data : 0, gc_rec_last->data); \    describe_marker(M); \   } while (0)   #else   #define CYCLE_DEBUG_MSG(M, TXT) do {} while (0)   #endif      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. */ +  * down to the end gets before the bit from beg down to pos. The beg +  * pos might be moved further down the stack to avoid mixing cycles or +  * breaking strong link sequences. */   {    struct gc_frame *l;    -  +  CYCLE_DEBUG_MSG(find_marker(beg->data), "> rotate_rec_list, asked to begin at"); +    #ifdef PIKE_DEBUG    if (Pike_in_gc != GC_PASS_CYCLE)    Pike_fatal("Use of the gc frame stack outside the cycle check pass.\n");    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      #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 0    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. */ +  * since we're no longer sure it's an unambiguous cycle. */    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. */    } -  + #endif    -  +  /* Always keep chains of strong refs continuous, or else we risk +  * breaking the order in a later rotation. */ +  for (; beg->frameflags & GC_STRONG_REF; beg = PREV(beg)) {} +     CYCLE_DEBUG_MSG(find_marker(beg->data), "> rotate_rec_list, begin at");       {    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);    DO_IF_DEBUG(frame_rot++);    }    while (p->frameflags & GC_OFF_STACK) {
pike.git/src/gc.c:1951: Inside #if defined(PIKE_DEBUG)
   if (Pike_in_gc != GC_PASS_CYCLE)    Pike_fatal("GC cycle push attempted in invalid pass.\n");    if (gc_debug && !(m->flags & GC_PRETOUCHED))    gc_fatal(x, 0, "gc_cycle_push() called for untouched thing.\n");    if ((!(m->flags & GC_NOT_REFERENCED) || m->flags & GC_MARKED) &&    *(INT32 *) x)    gc_fatal(x, 1, "Got a referenced marker to gc_cycle_push.\n");    if (m->flags & GC_XREFERENCED)    gc_fatal(x, 1, "Doing cycle check in externally referenced thing "    "missed in mark pass.\n"); -  if (gc_debug) { +  if (weak && gc_rec_last == &rec_list) +  gc_fatal(x, 1, "weak is %d when on top of stack.\n", weak); +  if (gc_debug > 1) {    struct array *a;    struct object *o;    struct program *p;    struct mapping *m;    struct multiset *l;    for(a = gc_internal_array; a != &empty_array; a = a->next)    if(a == (struct array *) x) goto on_gc_internal_lists;    for(o = gc_internal_object; o; o = o->next)    if(o == (struct object *) x) goto on_gc_internal_lists;    for(p = gc_internal_program; p; p = p->next)
pike.git/src/gc.c:2026:       return 0;    }      #ifdef PIKE_DEBUG    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 (m->frame && !(m->frame->frameflags & GC_OFF_STACK)) { +  if (m->frame && !(m->frame->frameflags & GC_ON_KILL_LIST)) {    /* A cyclic reference is found. */   #ifdef PIKE_DEBUG    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 != last) {    struct gc_frame *p, *weak_ref = 0, *nonstrong_ref = 0;    if (!weak) {    struct gc_frame *q;    CYCLE_DEBUG_MSG(m, "gc_cycle_push, search normal"); -  +  /* Find the last weakly linked thing and the one before the +  * first strongly linked thing. */    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;    }    }       else if (weak < 0) {    CYCLE_DEBUG_MSG(m, "gc_cycle_push, search strong"); -  +  /* Find the last weakly linked thing and the last one which +  * isn't strongly linked. */    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    if (p == gc_rec_last && !nonstrong_ref) {    fprintf(stderr, "Only strong links in cycle:\n");    for (p = m->frame;; p = NEXT(p)) {
pike.git/src/gc.c:2075: Inside #if defined(PIKE_DEBUG)
   fprintf(stderr, "========= next =========\n");    }    gc_fatal(0, 0, "Only strong links in cycle.\n");    }   #endif    }       else {    struct gc_frame *q;    CYCLE_DEBUG_MSG(m, "gc_cycle_push, search weak"); +  /* Find the thing before the first strongly linked one. */    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 (weak_ref) {    /* The backward link is normal or strong and there are one
pike.git/src/gc.c:2096:    * 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 (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); -  +  NEXT(nonstrong_ref)->frameflags = +  (NEXT(nonstrong_ref)->frameflags & ~GC_WEAK_REF) | GC_STRONG_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->frame) {    CYCLE_DEBUG_MSG(find_marker(nonstrong_ref->data),    "gc_cycle_push, weaker break");    rotate_rec_list(m->frame, nonstrong_ref);
pike.git/src/gc.c:2278: Inside #if defined(PIKE_DEBUG)
   if (pm->frame != p)    gc_fatal(p->data, 0, "Bogus marker for thing being popped.\n");   #endif    p->frameflags &= ~(GC_WEAK_REF|GC_STRONG_REF);    if (pm->flags & GC_LIVE_OBJ) {    /* This extra ref is taken away in the kill pass. Don't add one    * if it got an extra ref already due to weak free. */    if (!(pm->flags & GC_GOT_DEAD_REF))    gc_add_extra_ref(p->data);    base = p; +  p->frameflags |= GC_ON_KILL_LIST;    DO_IF_DEBUG(PREV(p) = (struct gc_frame *)(ptrdiff_t) -1);    CYCLE_DEBUG_MSG(pm, "gc_cycle_pop, put on kill list");    }    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. */    if (!(pm->flags & GC_GOT_DEAD_REF)) {
pike.git/src/gc.c:2745:    warn_bad_cycles();    objs += num_objects;    }   #ifdef PIKE_DEBUG    destroy_count = 0;   #endif    while (kill_list) {    struct gc_frame *next = NEXT(kill_list);    struct object *o = (struct object *) kill_list->data;   #ifdef PIKE_DEBUG +  if (!(kill_list->frameflags & GC_ON_KILL_LIST)) +  gc_fatal(o, 0, "Kill list element hasn't got the proper flag.\n");    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"); +  gc_fatal(o, 0, "Invalid object on kill list.\n");    if (o->prog && (o->prog->flags & PROGRAM_USES_PARENT) &&    PARENT_INFO(o)->parent &&    !PARENT_INFO(o)->parent->prog &&    get_marker(PARENT_INFO(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", o, o->refs);    if (o->prog) {    INT32 line;