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.265 2005/04/09 10:52:52 grubba Exp $ + || $Id: gc.c,v 1.266 2005/04/14 16:48:00 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:103:   ALLOC_COUNT_TYPE num_allocs =0;   ALLOC_COUNT_TYPE alloc_threshold = GC_MIN_ALLOC_THRESHOLD;   PMOD_EXPORT int Pike_in_gc = 0;   int gc_generation = 0;   time_t last_gc;   int gc_trace = 0, gc_debug = 0;   #ifdef DO_PIKE_CLEANUP   int gc_destruct_everything = 0;   #endif    - struct gc_frame + /* Shared fields between link and pop frames. */ + #define GC_STACK_COMMON_FIELDS \ +  void *data; \ +  struct gc_stack_frame *s_prev; /* Previous stack frame. */ \ +  unsigned INT16 frameflags +  + struct gc_stack_frame   { -  struct gc_frame *back; /* Previous stack frame. */ +  GC_STACK_COMMON_FIELDS; + }; +  + struct gc_link_frame + { +  GC_STACK_COMMON_FIELDS; +  INT16 weak; /* Weak flag to checkfn. */ +  gc_cycle_check_cb *checkfn; + }; +  + struct gc_pop_frame + { +  GC_STACK_COMMON_FIELDS; +  unsigned INT16 cycle; /* Cycle id number. */ +  struct gc_pop_frame *prev; /* Previous frame in rec_list. */ +  struct gc_pop_frame *next; /* Next pointer in rec_list and kill_list. */ + }; +  + struct gc_free_extra_frame /* Used on free_extra_list. See gc_delayed_free. */ + {    void *data; -  +  struct gc_free_extra_frame *fe_next; /* Next pointer. */ +  int type; /* The type of the thing. */ + }; +  + struct gc_frame + {    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; -  int free_extra_type; /* Used on free_extra_list. The type of the thing. */ +  struct gc_link_frame link; +  struct gc_pop_frame pop; +  struct gc_free_extra_frame free_extra; +  struct gc_frame *next; /* For block_alloc. */    } u; -  unsigned INT16 frameflags; +    };    -  + /* frameflags bits. */   #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 0x20   #define GC_FOLLOWED_NONSTRONG 0x40   #endif      #undef BLOCK_ALLOC_NEXT - #define BLOCK_ALLOC_NEXT back + #define BLOCK_ALLOC_NEXT u.next      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) + static INLINE struct gc_pop_frame *alloc_pop_frame() + { +  struct gc_frame *f = alloc_gc_frame(); +  f->u.pop.frameflags = GC_POP_FRAME; +  return (struct gc_pop_frame *) f; + }    -  + static INLINE struct gc_link_frame *alloc_link_frame() + { +  struct gc_frame *f = alloc_gc_frame(); +  f->u.link.frameflags = 0; +  return (struct gc_link_frame *) f; + } +  + static INLINE struct gc_free_extra_frame *alloc_free_extra_frame() + { +  return (struct gc_free_extra_frame *) alloc_gc_frame(); + } +    #ifdef PIKE_DEBUG -  +  + static unsigned num_gc_stack_frames = 0; +  + void debug_free_gc_stack_frame (struct gc_stack_frame *f) + { +  if (f->frameflags & GC_LINK_FREED) +  gc_fatal (f->data, 0, "Freeing freed gc_stack_frame.\n"); +  f->frameflags |= GC_LINK_FREED; +  f->s_prev = (struct gc_stack_frame *) (ptrdiff_t) -1; +  if (f->frameflags |= GC_POP_FRAME) { +  struct gc_pop_frame *p = (struct gc_pop_frame *) f; +  p->prev = p->next = (struct gc_pop_frame *)(ptrdiff_t) -1; +  } +  really_free_gc_frame ((struct gc_frame *) f); + #ifdef GC_VERBOSE +  num_gc_stack_frames--; + #endif + } +  + static INLINE void FREE_POP_FRAME (struct gc_pop_frame *f) {debug_free_gc_stack_frame ((struct gc_stack_frame *) f);} + static INLINE void FREE_LINK_FRAME (struct gc_link_frame *f) {debug_free_gc_stack_frame ((struct gc_stack_frame *) f);} + static INLINE void FREE_FREE_EXTRA_FRAME (struct gc_free_extra_frame *f) {really_free_gc_frame ((struct gc_frame *) f);} + static INLINE struct gc_stack_frame *POP2STACK (struct gc_pop_frame *f) {return (struct gc_stack_frame *) f;} + static INLINE struct gc_stack_frame *LINK2STACK (struct gc_link_frame *f) {return (struct gc_stack_frame *) f;} + static INLINE struct gc_pop_frame *STACK2POP (struct gc_stack_frame *f) + { +  if (!(f->frameflags & GC_POP_FRAME)) fatal ("Pop frame expected.\n"); +  return (struct gc_pop_frame *) f; + } + static INLINE struct gc_link_frame *STACK2LINK (struct gc_stack_frame *f) + { +  if (f->frameflags & GC_POP_FRAME) fatal ("Link frame expected.\n"); +  return (struct gc_link_frame *) f; + } +  + #else +  + #define FREE_POP_FRAME(f) really_free_gc_frame ((struct gc_frame *) f) + #define FREE_LINK_FRAME(f) really_free_gc_frame ((struct gc_frame *) f) + #define FREE_FREE_EXTRA_FRAME(f) really_free_gc_frame ((struct gc_frame *) f) + #define POP2STACK(f) ((struct gc_stack_frame *) f) + #define LINK2STACK(f) ((struct gc_stack_frame *) f) + #define STACK2POP(f) ((struct gc_pop_frame *) f) + #define STACK2LINK(f) ((struct gc_link_frame *) f) +  + #endif +  + #define gc_frame gc_foo_frame +  + #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"); \ +  struct gc_pop_frame *f_ = (frame); \ +  if (f_->frameflags & GC_LINK_FREED) \ +  gc_fatal (f_->data, 0, "Accessing freed gc_stack_frame.\n"); \ +  if (!(f_->frameflags & GC_POP_FRAME)) \ +  gc_fatal(f_->data, 0, #frame " is not a pop frame.\n"); \ +  if (f_->prev->next != f_) \ +  gc_fatal(f_->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 struct gc_frame *free_extra_list = 0; /* See note in gc_delayed_free. */ + static struct gc_pop_frame rec_list = {NULL, NULL, GC_POP_FRAME, 0, NULL, NULL}; + static struct gc_pop_frame *gc_rec_last = &rec_list; + static struct gc_stack_frame *gc_rec_top = NULL; + static struct gc_pop_frame *kill_list = NULL; + static struct gc_free_extra_frame *free_extra_list = NULL; /* See note in gc_delayed_free. */      static unsigned last_cycle;   size_t gc_ext_weak_refs;      /* 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.    *
pike.git/src/gc.c:263: Inside #if defined(PIKE_DEBUG)
     #ifdef PIKE_DEBUG      #undef get_marker   #define get_marker(X) ((struct marker *) debug_malloc_pass(debug_get_marker(X)))   #undef find_marker   #define find_marker(X) ((struct marker *) debug_malloc_pass(debug_find_marker(X)))      int gc_in_cycle_check = 0;   static unsigned delayed_freed, weak_freed, checked, marked, cycle_checked, live_ref; - static unsigned max_gc_frames, num_gc_frames = 0, live_rec, frame_rot; + static unsigned max_gc_stack_frames, live_rec, frame_rot;   static unsigned gc_extra_refs = 0;    - static unsigned max_tot_gc_frames = 0; + static unsigned max_tot_gc_stack_frames = 0;   static unsigned tot_cycle_checked = 0, tot_live_rec = 0, tot_frame_rot = 0;      static int gc_is_watching = 0;      int attempt_to_identify(void *something, void **inblock)   {    size_t i;    struct array *a;    struct object *o;    struct program *p;
pike.git/src/gc.c:626: Inside #if defined(DEBUG_MALLOC)
   /* FIXME: Is the following call correct?    * Shouldn't the second argument be an offset?    */    /* dmalloc_describe_location(descblock, location, indent); */    /* My attempt to fix it, although I'm not really sure: /mast */    if (memblock)    dmalloc_describe_location(memblock, (char *) location - (char *) memblock, indent);   #endif   }    - static void describe_gc_frame(struct gc_frame *l) + static void describe_gc_stack_frame (struct gc_stack_frame *f)   { -  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); +  if (f->frameflags & GC_POP_FRAME) { +  struct gc_pop_frame *p = STACK2POP (f); +  fprintf (stderr, "s_prev=%p, data=%p, flags=0x%02x, prev=%p, next=%p, cycle=%u", +  p->s_prev, p->data, p->frameflags, p->prev, p->next, p->cycle);    } -  +  else { +  struct gc_link_frame *l = STACK2LINK (f); +  fprintf (stderr, "s_prev=%p, data=%p, flags=0x%02x, weak=%d", +  l->s_prev, l->data, l->frameflags, l->weak); +  } + }      static void describe_marker(struct marker *m)   {    if (m) {    fprintf(stderr, "marker at %p: flags=0x%05lx, refs=%d, weak=%d, "    "xrefs=%d, saved=%d, frame=%p",    m, (long) m->flags, m->refs, m->weak_refs,    m->xrefs, m->saved_refs, m->frame);   #ifdef PIKE_DEBUG    if (m->frame) {    fputs(" [", stderr); -  describe_gc_frame(m->frame); +  describe_gc_stack_frame (POP2STACK (m->frame));    putc(']', stderr);    }   #endif    putc('\n', stderr);    }    else    fprintf(stderr, "no marker\n");   }      #endif /* PIKE_DEBUG */
pike.git/src/gc.c:1591:    if (gc_evaluator_callback) {    remove_callback(gc_evaluator_callback);    gc_evaluator_callback = NULL;    }    if (!gc_keep_markers)    cleanup_markers();       free_all_gc_frame_blocks();      #ifdef GC_VERBOSE -  num_gc_frames = 0; +  num_gc_stack_frames = 0;   #endif      #ifdef PIKE_DEBUG    if (gc_is_watching) {    fprintf(stderr, "## Exiting gc and resetting watches for %d things.\n",    gc_is_watching);    gc_is_watching = 0;    }   #endif   }
pike.git/src/gc.c:1800: Inside #if defined(PIKE_DEBUG)
   m=get_marker(a);    m->xrefs++;    m->flags|=GC_XREFERENCED;    if(Pike_in_gc == GC_PASS_CHECK &&    (m->refs + m->xrefs > *(INT32 *)a ||    (m->saved_refs != -1 && m->saved_refs != *(INT32 *)a)))    gc_fatal(a, 1, "Ref counts are wrong.\n");    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 *)(ptrdiff_t) -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_do_weak_free(void *a)   {    struct marker *m;      #ifdef PIKE_DEBUG    if (gc_is_watching && (m = find_marker(a)) && m->flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_do_weak_free()");
pike.git/src/gc.c:1915:    if (m->flags & GC_MARKED) {    /* Note that we can get marked things here, e.g. if the index in a    * mapping with weak indices is removed in the zap weak pass, the    * value will be zapped too, but it will still have a mark from    * the mark pass. This means that the stuff referenced by the    * value will only be refcount garbed, which can leave cyclic    * garbage for the next gc round.    *    * Since the value has been marked we won't find it in the free    * pass, so we have to keep special track of it. :P */ -  struct gc_frame *l = alloc_gc_frame(); +  struct gc_free_extra_frame *l = alloc_free_extra_frame();    l->data = a; -  l->u.free_extra_type = type; -  l->back = free_extra_list; -  l->frameflags = 0; +  l->type = type; +  l->fe_next = free_extra_list;    free_extra_list = l;    }       gc_add_extra_ref(a);    m->flags |= GC_GOT_DEAD_REF;   }      int gc_mark(void *a)   {    struct marker *m = get_marker(debug_malloc_pass(a));
pike.git/src/gc.c:1983:    m->weak_refs = 0;    }    m->flags = (m->flags & ~GC_NOT_REFERENCED) | GC_MARKED;    DO_IF_DEBUG(marked++);    return 1;    }   }      PMOD_EXPORT void gc_cycle_enqueue(gc_cycle_check_cb *checkfn, void *data, int weak)   { -  struct gc_frame *l = alloc_gc_frame(); +  struct gc_link_frame *l = alloc_link_frame();   #ifdef PIKE_DEBUG    {    struct marker *m;    if (gc_is_watching && (m = find_marker(data)) && m->flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_cycle_enqueue()");    }    }    if (Pike_in_gc != GC_PASS_CYCLE)    gc_fatal(data, 0, "Use of the gc frame stack outside the cycle check pass.\n");   #endif   #ifdef GC_VERBOSE -  if (++num_gc_frames > max_gc_frames) max_gc_frames = num_gc_frames; +  if (++num_gc_stack_frames > max_gc_stack_frames) +  max_gc_stack_frames = num_gc_stack_frames;   #endif    l->data = data; -  l->u.link.checkfn = checkfn; -  l->u.link.weak = weak; -  l->frameflags = 0; -  l->back = gc_rec_top; +  l->checkfn = checkfn; +  l->weak = weak; +  l->s_prev = gc_rec_top;   #ifdef GC_STACK_DEBUG    fprintf(stderr, "enqueue %p [%p]: ", l, gc_rec_top); -  describe_gc_frame(l); +  describe_gc_stack_frame (LINK2STACK (l));    fputc('\n', stderr);   #endif -  gc_rec_top = l; +  gc_rec_top = LINK2STACK (l);   }    - static struct gc_frame *gc_cycle_enqueue_pop(void *data) + static struct gc_pop_frame *gc_cycle_enqueue_pop(void *data)   { -  struct gc_frame *l = alloc_gc_frame(); +  struct gc_pop_frame *p = alloc_pop_frame();   #ifdef PIKE_DEBUG    if (Pike_in_gc != GC_PASS_CYCLE)    gc_fatal(data, 0, "Use of the gc frame stack outside the cycle check pass.\n");   #endif   #ifdef GC_VERBOSE -  if (++num_gc_frames > max_gc_frames) max_gc_frames = num_gc_frames; +  if (++num_gc_stack_frames > max_gc_stack_frames) +  max_gc_stack_frames = num_gc_stack_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; +  p->data = data; +  p->prev = gc_rec_last; +  p->next = 0; +  p->cycle = 0; +  p->s_prev = gc_rec_top;   #ifdef GC_STACK_DEBUG -  fprintf(stderr, "enqueue %p [%p]: ", l, gc_rec_top); -  describe_gc_frame(l); +  fprintf(stderr, "enqueue %p [%p]: ", p, gc_rec_top); +  describe_gc_stack_frame (POP2STACK (p));    fputc('\n', stderr);   #endif -  gc_rec_top = l; -  return l; +  gc_rec_top = POP2STACK (p); +  return p;   }      void gc_cycle_run_queue()   {   #ifdef PIKE_DEBUG    if (Pike_in_gc != GC_PASS_CYCLE)    Pike_fatal("Use of the gc frame stack outside the cycle check pass.\n");   #endif    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); +  fprintf(stderr, "dequeue %p [%p]: ", gc_rec_top, gc_rec_top->s_prev); +  describe_gc_stack_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; +  struct gc_stack_frame *f = gc_rec_top->s_prev; +  gc_cycle_pop (gc_rec_top->data); +  gc_rec_top = f;    } else { -  struct gc_frame l = *gc_rec_top; +  struct gc_link_frame l = *STACK2LINK (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); +  FREE_LINK_FRAME (STACK2LINK (gc_rec_top)); +  gc_rec_top = l.s_prev; +  l.checkfn (l.data, l.weak);    }    }   }      #ifdef GC_CYCLE_DEBUG   static int gc_cycle_indent = 0;   #define CYCLE_DEBUG_MSG(M, TXT) do { \    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) + static void rotate_rec_list (struct gc_pop_frame *beg, struct gc_pop_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. The beg    * pos might be moved further down the stack to avoid mixing cycles or    * breaking strong link sequences. */   {   #ifdef GC_STACK_DEBUG -  struct gc_frame *l; +  struct gc_stack_frame *l;   #endif    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)) +  if (gc_rec_last->next)    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) { +  for (l = gc_rec_top; l; l = l->s_prev) {    fprintf(stderr, " %p ", l); -  describe_gc_frame(l); +  describe_gc_stack_frame(l);    fputc('\n', stderr);    }   #endif      #if 0 -  if (CYCLE(beg)) { -  for (l = beg; CYCLE(PREV(l)) == CYCLE(beg); l = PREV(l)) +  if (beg->cycle) { +  for (l = beg; l->prev->cycle == beg->cycle; l = l->prev)    CHECK_POP_FRAME(l); -  if (CYCLE(l) == CYCLE(pos)) { +  if (l->cycle == pos->cycle) {    /* Breaking something previously marked as a cycle. Clear it    * since we're no longer sure it's an unambiguous cycle. */ -  unsigned cycle = CYCLE(l); -  for (; l && CYCLE(l) == cycle; l = NEXT(l)) { +  unsigned cycle = l->cycle; +  for (; l && l->cycle == cycle; l = l->next) {    CHECK_POP_FRAME(l);   #ifdef GC_CYCLE_DEBUG -  if (CYCLE(l)) +  if (l->cycle)    CYCLE_DEBUG_MSG(find_marker(l->data), "> rotate_rec_list, clear cycle");   #endif -  CYCLE(l) = 0; +  l->cycle = 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)) {} +  for (; beg->frameflags & GC_STRONG_REF; beg = beg->prev) {}       CYCLE_DEBUG_MSG(find_marker(beg->data), "> rotate_rec_list, begin at");       { -  struct gc_frame *b = beg, *p = pos, *old_rec_top; +  struct gc_pop_frame *b = beg, *p = pos; +  struct gc_stack_frame *old_rec_top;    while (b->frameflags & GC_OFF_STACK) { -  if ((b = NEXT(b)) == pos) goto done; +  if ((b = b->next) == pos) goto done;    CHECK_POP_FRAME(b);    DO_IF_DEBUG(frame_rot++);    }    while (p->frameflags & GC_OFF_STACK) { -  if (!(p = NEXT(p))) goto done; +  if (!(p = p->next)) goto done;    CHECK_POP_FRAME(p);    DO_IF_DEBUG(frame_rot++);    }    old_rec_top = gc_rec_top; -  gc_rec_top = p->back; -  p->back = b->back; -  b->back = old_rec_top; +  gc_rec_top = p->s_prev; +  p->s_prev = b->s_prev; +  b->s_prev = old_rec_top;    }   done:    DO_IF_DEBUG(frame_rot++);       { -  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; +  struct gc_pop_frame *new_rec_last = pos->prev; +  beg->prev->next = pos; +  pos->prev = beg->prev; +  gc_rec_last->next = beg; +  beg->prev = gc_rec_last;    gc_rec_last = new_rec_last; -  NEXT(gc_rec_last) = 0; +  gc_rec_last->next = 0;    }       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");    }      #ifdef GC_STACK_DEBUG    fprintf(stderr,"Stack after:\n"); -  for (l = gc_rec_top; l; l = l->back) { +  for (l = gc_rec_top; l; l = l->s_prev) {    fprintf(stderr, " %p ", l); -  describe_gc_frame(l); +  describe_gc_stack_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
pike.git/src/gc.c:2255:    }    CYCLE_DEBUG_MSG(m, "gc_cycle_push, no live recurse");    }       else {    /* We'll get here eventually in the normal recursion. Pop off    * the remaining live recurse frames for the last thing. */    CYCLE_DEBUG_MSG(m, "gc_cycle_push, no live recurse");    last->flags &= ~GC_LIVE_RECURSE;    while (1) { -  struct gc_frame *l = gc_rec_top; +  struct gc_stack_frame *l = gc_rec_top;   #ifdef PIKE_DEBUG    if (!gc_rec_top)    Pike_fatal("Expected a gc_cycle_pop entry in gc_rec_top.\n");   #endif -  gc_rec_top = l->back; +  gc_rec_top = l->s_prev;    if (l->frameflags & GC_POP_FRAME) { -  gc_rec_last = PREV(l); -  debug_really_free_gc_frame(l); +  gc_rec_last = STACK2POP (l)->prev; +  FREE_POP_FRAME (STACK2POP (l));    break;    } -  debug_really_free_gc_frame(l); +  FREE_LINK_FRAME (STACK2LINK (l));    }   #ifdef GC_CYCLE_DEBUG    gc_cycle_indent -= 2;    CYCLE_DEBUG_MSG(m, "> gc_cycle_push, unwound live rec");   #endif    }       return 0;    }   
pike.git/src/gc.c:2293:    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; +  struct gc_pop_frame *p, *weak_ref = 0, *nonstrong_ref = 0;    if (!weak) { -  struct gc_frame *q; +  struct gc_pop_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)) { +  for (q = m->frame, p = q->next;; q = p, p = p->next) {    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)) { +  for (p = m->frame->next;; p = p->next) {    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)) { +  for (p = m->frame;; p = p->next) {    describe(p->data);    locate_references(p->data);    if (p == gc_rec_last) break;    fprintf(stderr, "========= next =========\n");    }    gc_fatal(0, 0, "Only strong links in cycle.\n");    }   #endif    }       else { -  struct gc_frame *q; +  struct gc_pop_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)) { +  for (q = m->frame, p = q->next;; q = p, p = p->next) {    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    * or more weak links in the cycle. Let's break it at the
pike.git/src/gc.c:2361:    "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. */    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; +  nonstrong_ref->next->frameflags = +  (nonstrong_ref->next->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);    }    }       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)) +  unsigned cycle = m->frame->cycle ? m->frame->cycle : ++last_cycle; +  if (cycle == gc_rec_last->cycle)    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; +  for (p = m->frame;; p = p->next) { +  p->cycle = 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 gc_frame *l; +  struct gc_pop_frame *l;   #ifdef PIKE_DEBUG    cycle_checked++;    if (m->frame)    gc_fatal(x, 0, "Marker already got a frame.\n"); -  if (NEXT(gc_rec_last)) +  if (gc_rec_last->next)    gc_fatal(gc_rec_last->data, 0, "Not at end of list.\n");   #endif    -  NEXT(gc_rec_last) = m->frame = l = gc_cycle_enqueue_pop(x); +  gc_rec_last->next = m->frame = l = gc_cycle_enqueue_pop(x);    m->flags |= GC_CYCLE_CHECKED | (last->flags & GC_LIVE);    debug_malloc_touch(x);    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");    else if (weak < 0) CYCLE_DEBUG_MSG(m, "gc_cycle_push, recurse strong");
pike.git/src/gc.c:2448:    gc_free_extra_ref(x);    if (!sub_ref ((struct ref_dummy *) x)) {   #ifdef PIKE_DEBUG    gc_fatal(x, 0, "Thing got zero refs after removing the dead gc ref.\n");   #endif    }    }       {    /* Recurse without linking onto rec_list. */ -  struct gc_frame *l = gc_cycle_enqueue_pop(x); +  struct gc_pop_frame *l = gc_cycle_enqueue_pop(x);   #ifdef GC_CYCLE_DEBUG    CYCLE_DEBUG_MSG(m, "gc_cycle_push, live recurse");    gc_cycle_indent += 2;   #endif    gc_rec_last = l;    }      #ifdef PIKE_DEBUG    live_rec++;   #endif    return 1;   }      static void gc_cycle_pop(void *a)   {    struct marker *m = find_marker(a); -  struct gc_frame *here, *base, *p; +  struct gc_pop_frame *here, *base, *p;      #ifdef PIKE_DEBUG    if (gc_is_watching && m && m->flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_cycle_pop()");    }    if (!a) Pike_fatal("Got null pointer.\n");    if (Pike_in_gc != GC_PASS_CYCLE)    Pike_fatal("GC cycle pop attempted in invalid pass.\n");    if (!(m->flags & GC_CYCLE_CHECKED))
pike.git/src/gc.c:2493: Inside #if defined(PIKE_DEBUG)
   "missed in mark pass.\n");    }   #endif   #ifdef GC_CYCLE_DEBUG    gc_cycle_indent -= 2;   #endif       if (m->flags & GC_LIVE_RECURSE) {    m->flags &= ~GC_LIVE_RECURSE;    CYCLE_DEBUG_MSG(m, "gc_cycle_pop_live"); -  gc_rec_last = PREV(gc_rec_top); -  debug_really_free_gc_frame(gc_rec_top); +  gc_rec_last = STACK2POP (gc_rec_top)->prev; +  FREE_POP_FRAME (STACK2POP (gc_rec_top));    return;    }       here = m->frame;   #ifdef PIKE_DEBUG    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"); -  here->back = (struct gc_frame *)(ptrdiff_t) -1; +  here->s_prev = (struct gc_stack_frame *)(ptrdiff_t) -1;   #endif    here->frameflags |= GC_OFF_STACK;    -  for (base = PREV(here), p = here;; base = p, p = NEXT(p)) { +  for (base = here->prev, p = here;; base = p, p = p->next) {    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 *)(ptrdiff_t) -1); +  DO_IF_DEBUG(m->frame->s_prev = (struct gc_stack_frame *)(ptrdiff_t) -1);    CYCLE_DEBUG_MSG(m, "gc_cycle_pop, keep cycle");    return;    }    CHECK_POP_FRAME(p); -  if (!(CYCLE(p) && CYCLE(p) == CYCLE(base))) +  if (!(p->cycle && p->cycle == base->cycle))    break;    }       gc_rec_last = base; -  while ((p = NEXT(base))) { +  while ((p = base->next)) {    struct marker *pm = find_marker(p->data);   #ifdef 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); +  DO_IF_DEBUG(p->prev = (struct gc_pop_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)) {    gc_add_extra_ref(pm->data);    pm->flags |= GC_GOT_DEAD_REF;    }    }   #ifdef PIKE_DEBUG    else    if (pm->flags & GC_GOT_DEAD_REF)    gc_fatal(p->data, 0, "Didn't expect a dead extra ref.\n");   #endif -  NEXT(base) = NEXT(p); +  base->next = p->next;    CYCLE_DEBUG_MSG(pm, "gc_cycle_pop, pop off");    pm->frame = 0; -  debug_really_free_gc_frame(p); +  FREE_POP_FRAME (p);    }    }       if (base != gc_rec_last) { -  NEXT(base) = kill_list; -  kill_list = NEXT(gc_rec_last); -  NEXT(gc_rec_last) = 0; +  base->next = kill_list; +  kill_list = gc_rec_last->next; +  gc_rec_last->next = 0;    }   }      void do_gc_recurse_svalues(struct svalue *s, int num)   {    gc_recurse_svalues(s, num);   }      void do_gc_recurse_short_svalue(union anything *u, int type)   {
pike.git/src/gc.c:2689:    * value (eg if obj_arr is in a register)).    */    struct array **obj_arr_ = (struct array **)xalloc(sizeof(struct array *));    ONERROR tmp;       *obj_arr_ = NULL;       SET_ONERROR(tmp, free_obj_arr, obj_arr_);       { -  struct gc_frame *p; +  struct gc_pop_frame *p;    unsigned cycle = 0;    *obj_arr_ = allocate_array(0);       for (p = kill_list; p;) { -  if ((cycle = CYCLE(p))) { +  if ((cycle = p->cycle)) {    push_object((struct object *) p->data);    dmalloc_touch_svalue(Pike_sp-1);    *obj_arr_ = append_array(*obj_arr_, --Pike_sp);    } -  p = NEXT(p); -  if (p ? ((unsigned)(CYCLE(p) != cycle)) : cycle) { +  p = p->next; +  if (p ? ((unsigned)(p->cycle != cycle)) : cycle) {    if ((*obj_arr_)->size >= 2) {    push_constant_text("gc");    push_constant_text("bad_cycle");    push_array(*obj_arr_);    *obj_arr_ = 0;    SAFE_APPLY_MASTER("runtime_warning", 3);    pop_stack();    *obj_arr_ = allocate_array(0);    }    else *obj_arr_ = resize_array(*obj_arr_, 0);
pike.git/src/gc.c:2877:    "| mark: %u markers referenced, %u weak references freed,\n"    "| %d things to free, "    "got %"PRINTSIZET"u tricky weak refs\n",    marked, weak_freed, delayed_freed, gc_ext_weak_refs));    }       {   #ifdef PIKE_DEBUG    size_t orig_ext_weak_refs = gc_ext_weak_refs;    obj_count = delayed_freed; -  max_gc_frames = 0; +  max_gc_stack_frames = 0;   #endif    Pike_in_gc=GC_PASS_CYCLE;       /* Now find all cycles in the internal structures. Note that we can    * follow the same reference several times, just like in the mark    * pass. */    /* Note: The order between types here is normally not significant,    * but the permuting destruct order tests in the testsuite won't be    * really effective unless objects are handled first. :P */    gc_cycle_check_all_objects();    gc_cycle_check_all_arrays();    gc_cycle_check_all_multisets();    gc_cycle_check_all_mappings();    gc_cycle_check_all_programs();      #ifdef PIKE_DEBUG    if (gc_rec_top)    Pike_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) +  if (rec_list.next || gc_rec_last != &rec_list || gc_rec_top)    Pike_fatal("Recurse list not empty or inconsistent after cycle check pass.\n");    if (gc_ext_weak_refs != orig_ext_weak_refs)    Pike_fatal("gc_ext_weak_refs changed from %"PRINTSIZET"u "    "to %"PRINTSIZET"u in cycle check pass.\n",    orig_ext_weak_refs, gc_ext_weak_refs);   #endif       GC_VERBOSE_DO(fprintf(stderr,    "| cycle: %u internal things visited, %u cycle ids used,\n"    "| %u weak references freed, %d more things to free,\n"    "| %u live recursed frames, %u frame rotations,\n"    "| space for %u gc frames used\n",    cycle_checked, last_cycle, weak_freed,    delayed_freed - obj_count, -  live_rec, frame_rot, max_gc_frames)); +  live_rec, frame_rot, max_gc_stack_frames));    }       if (gc_ext_weak_refs) {    size_t to_free = gc_ext_weak_refs;   #ifdef PIKE_DEBUG    obj_count = delayed_freed;   #endif    Pike_in_gc = GC_PASS_ZAP_WEAK;    /* Zap weak references from external to internal things. That    * occurs when something has both external weak refs and nonweak
pike.git/src/gc.c:3006:    unreferenced += gc_free_all_unreferenced_objects();    /* Note: gc_free_all_unreferenced_objects needs to have the programs    * around to handle the free (even when they aren't live). So it's    * necessary to free the objects before the programs. */    if (gc_internal_program)    unreferenced += gc_free_all_unreferenced_programs();       /* We might occasionally get things to gc_delayed_free that the free    * calls above won't find. They're tracked in this list. */    while (free_extra_list) { -  struct gc_frame *next = free_extra_list->back; +  struct gc_free_extra_frame *next = free_extra_list->fe_next;    union anything u;    u.refs = (INT32 *) free_extra_list->data; -  gc_free_extra_ref(u.refs); -  free_short_svalue(&u, free_extra_list->u.free_extra_type); -  debug_really_free_gc_frame(free_extra_list); +  gc_free_extra_ref (u.refs); +  free_short_svalue (&u, free_extra_list->type); +  FREE_FREE_EXTRA_FRAME (free_extra_list);    free_extra_list = next;    }       GC_VERBOSE_DO(fprintf(stderr, "| free: %d unreferenced, %d really freed, "    "%u left with live references\n",    unreferenced, obj_count - num_objects, live_ref));      #ifdef PIKE_DEBUG    gc_internal_array = (struct array *) (ptrdiff_t) -1;    gc_internal_multiset = (struct multiset *) (ptrdiff_t) -1;
pike.git/src/gc.c:3049: Inside #if defined (PIKE_DEBUG) || defined (DO_PIKE_CLEANUP)
  #if defined (PIKE_DEBUG) || defined (DO_PIKE_CLEANUP)    destroy_count = 0;   #endif    {    enum object_destruct_reason reason =   #ifdef DO_PIKE_CLEANUP    gc_destruct_everything ? DESTRUCT_CLEANUP :   #endif    DESTRUCT_GC;    while (kill_list) { -  struct gc_frame *next = NEXT(kill_list); +  struct gc_pop_frame *next = kill_list->next;    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 object on kill list.\n");    if (o->prog && (o->prog->flags & PROGRAM_USES_PARENT) &&    PARENT_INFO(o)->parent &&    !PARENT_INFO(o)->parent->prog &&
pike.git/src/gc.c:3079:    free_string(file);    }    else fputs(", is destructed\n", stderr);    );    destruct_object (o, reason);    free_object(o);    gc_free_extra_ref(o);   #if defined (PIKE_DEBUG) || defined (DO_PIKE_CLEANUP)    destroy_count++;   #endif -  debug_really_free_gc_frame(kill_list); +  FREE_POP_FRAME (kill_list);    kill_list = next;    }    }       GC_VERBOSE_DO(fprintf(stderr, "| kill: %u objects killed, %d things really freed\n",    destroy_count, pre_kill_objs - num_objects));       Pike_in_gc=GC_PASS_DESTRUCT;    /* Destruct objects on the destruct queue. */    GC_VERBOSE_DO(obj_count = num_objects);
pike.git/src/gc.c:3249:    else   #endif    fprintf(stderr, "done (%"PRINTSIZET"d of %"PRINTSIZET"d "    "was unreferenced)%s\n",    unreferenced, start_num_objs, timestr);    }    }      #ifdef PIKE_DEBUG    UNSET_ONERROR (uwp); -  if (max_gc_frames > max_tot_gc_frames) max_tot_gc_frames = max_gc_frames; +  if (max_gc_stack_frames > max_tot_gc_stack_frames) +  max_tot_gc_stack_frames = max_gc_stack_frames;    tot_cycle_checked += cycle_checked;    tot_live_rec += live_rec, tot_frame_rot += frame_rot;   #endif      #ifdef ALWAYS_GC    ADD_GC_CALLBACK();   #else    if(d_flag > 3) ADD_GC_CALLBACK();   #endif   
pike.git/src/gc.c:3395:    fprintf(stderr,"Avg cpu "CPU_TIME_UNIT" between gc : %f\n", non_gc_time);    fprintf(stderr,"Avg cpu "CPU_TIME_UNIT" in gc : %f\n", gc_time);    fprintf(stderr,"Avg time ratio in gc : %f\n", gc_time / non_gc_time);       fprintf(stderr,"Garbage strategy in last gc: %s\n",    last_garbage_strategy == GARBAGE_RATIO_LOW ? "garbage_ratio_low" :    last_garbage_strategy == GARBAGE_RATIO_HIGH ? "garbage_ratio_high" :    "???");      #ifdef PIKE_DEBUG -  fprintf(stderr,"Max used gc frames : %u\n", max_tot_gc_frames); +  fprintf(stderr,"Max used gc frames : %u\n", max_tot_gc_stack_frames);    fprintf(stderr,"Live recursed ratio : %g\n",    (double) tot_live_rec / tot_cycle_checked);    fprintf(stderr,"Frame rotation ratio : %g\n",    (double) tot_frame_rot / tot_cycle_checked);   #endif       fprintf(stderr,"in_gc : %d\n", Pike_in_gc);   }      void cleanup_gc(void)   {   #ifdef PIKE_DEBUG    if (gc_evaluator_callback) {    remove_callback(gc_evaluator_callback);    gc_evaluator_callback = NULL;    }   #endif /* PIKE_DEBUG */   }