Branch: Tag:

2007-05-13

2007-05-13 14:55:26 by Martin Stjernholm <mast@lysator.liu.se>

Reworked the cycle handling in the gc and documented it in detail, to work
out some kinks and to make it more convincing that the end result really is
what the gc claims it to be.

Rev: src/gc.c:1.281
Rev: src/pike_embed.c:1.11
Rev: src/program.c:1.614

2:   || 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.280 2007/04/25 21:58:33 mast Exp $ + || $Id: gc.c,v 1.281 2007/05/13 14:55:26 mast Exp $   */      #include "global.h"
44:    * the last ten gc rounds. (0.9 == 1 - 1/10) */   double gc_average_slowness = 0.9;    - #define GC_LINK_CHUNK_SIZE 64 -  +    /* The gc will free all things with no external references that isn't    * referenced by undestructed objects with destroy() lfuns (known as    * "live" objects). Live objects without external references are then
58:    * before B.    * o If A and B are in a cycle, and there is a reference somewhere    * from B to A that is weaker than any reference from A to B, then -  * A is destructed before B. +  * the cycle is resolved by disregarding the weaker reference, and +  * A is therefore destructed before B. +  * o If a cycle is resolved through disregarding a weaker reference +  * according to the preceding rule, and there is another cycle +  * without weak references which also gets resolved through +  * disregarding the same reference, then the other cycle won't be +  * resolved by disregarding some other reference.    * o Weak references are considered weaker than normal ones, and both    * are considered weaker than strong references.    * o Strong references are used in special cases like parent object
109: Inside #if defined(DO_PIKE_CLEANUP)
  #ifdef DO_PIKE_CLEANUP   int gc_destruct_everything = 0;   #endif + size_t gc_ext_weak_refs;    - /* 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 + static double objects_alloced = 0.0; + static double objects_freed = 0.0; + static double gc_time = 0.0, non_gc_time = 0.0; + static cpu_time_t last_gc_end_time = 0; + #if CPU_TIME_IS_THREAD_LOCAL == PIKE_NO + cpu_time_t auto_gc_time = 0; + #endif    - struct gc_stack_frame + struct link_frame /* See cycle checking blurb below. */   { -  GC_STACK_COMMON_FIELDS; +  void *data; +  struct link_frame *prev; /* Previous frame in the link stack. */ +  gc_cycle_check_cb *checkfn; /* Function to call to recurse the thing. */ +  int weak; /* Weak flag to checkfn. */   };    - struct gc_link_frame + struct gc_rec_frame /* See cycle checking blurb below. */   { -  GC_STACK_COMMON_FIELDS; -  INT16 weak; /* Weak flag to checkfn. */ -  gc_cycle_check_cb *checkfn; +  void *data; +  int rf_flags; +  struct gc_rec_frame *prev; /* The previous frame in the recursion +  * stack. NULL for frames not in the stack. */ +  struct gc_rec_frame *next; /* The next frame in the recursion +  * stack or the kill list. */ +  struct gc_rec_frame *cycle_id;/* The cycle identifier frame. +  * Initially points to self. */ +  struct gc_rec_frame *cycle_piece;/* The start of the cycle piece list +  * for frames on the recursion stack, +  * or the next frame in the list for +  * frames in cycle piece lists. */ +  union { +  struct link_frame *link_top;/* The top of the link stack for +  * frames on the recursion stack. */ +  struct gc_rec_frame *last_cycle_piece;/* In the first frame on a +  * cycle piece list, this is used to +  * point to the last frame in the list. */ +  } u;   };    - struct gc_pop_frame - { -  GC_STACK_COMMON_FIELDS; -  unsigned INT16 cycle; /* Cycle id number. */ -  struct gc_pop_frame *prev; /* Previous frame in the frame list. */ -  struct gc_pop_frame *next; /* Next pointer in the frame list and in kill_list. */ + /* rf_flags bits. */ + #define GC_PREV_WEAK 0x01 + #define GC_PREV_STRONG 0x02 + #define GC_PREV_BROKEN 0x04 + #define GC_MARK_LIVE 0x08 + #define GC_ON_KILL_LIST 0x10 + #ifdef PIKE_DEBUG + #define GC_ON_CYCLE_PIECE_LIST 0x20 + #define GC_FRAME_FREED 0x40 + #define GC_FOLLOWED_NONSTRONG 0x80 + #endif +  + static struct gc_rec_frame sentinel_frame = { +  (void *) (ptrdiff_t) -1, +  0, +  (struct gc_rec_frame *) (ptrdiff_t) -1, +  (struct gc_rec_frame *) (ptrdiff_t) -1, +  &sentinel_frame, /* Recognize as cycle id frame. */ +  (struct gc_rec_frame *) (ptrdiff_t) -1, +  {(struct link_frame *) (ptrdiff_t) -1}   }; -  + static struct gc_rec_frame *stack_top = &sentinel_frame; + static struct gc_rec_frame *kill_list = &sentinel_frame;    - struct gc_free_extra_frame /* Used on free_extra_list. See gc_delayed_free. */ + /* Cycle checking +  * +  * When a thing is recursed into, a gc_rec_frame is pushed onto the +  * recursion stack whose top pointer is stack_top. After that the +  * links emanating from that thing are collected through the +  * gc_cycle_check_* function and pushed as link_frames onto a link +  * stack that is specific to the rec frame. gc_rec_frame.u.link_top is +  * the top pointer of that stack. The link frames are then popped off +  * again one by one. If the thing that the link points to hasn't been +  * visited already then it's recursed, which means that the link frame +  * is popped off the link stack and a new rec frame is pushed onto the +  * main stack instead. +  * +  * When a reference is followed to a thing which has a rec frame +  * (either on the stack or on a cycle piece list - see below), we have +  * a cycle. However, if that reference is weak (or becomes weak after +  * rotation - see below), it's still not regarded as a cycle since +  * weak refs always are eligible to be broken to resolve cycles. +  * +  * A cycle is marked by setting gc_rec_frame.cycle_id in the rec +  * frames on the stack that are part of the cycle to the first +  * (deepest) one of those frames. That frame is called the "cycle +  * identifier frame" since all frames in the same cycle will end up +  * there if the cycle pointers are followed transitively. The cycle_id +  * pointer in the cycle identifier frame points to itself. Every frame +  * is initially treated as a cycle containing only itself. +  * +  * When the recursion leaves a thing, the rec frame is popped off the +  * stack. If the frame is part of a cycle that isn't finished at that +  * point, it's not freed but instead linked onto the cycle piece list +  * in gc_rec_frame.cycle_piece of the parent rec frame (which +  * necessarily is part of the same cycle). That is done to detect +  * cyclic refs that end up at the popped frame later on. +  * +  * The cycle_id pointers for frames on cycle piece lists point back +  * towards the rec frame that still is on the stack, but not past it +  * to the cycle id frame (which might be further back in the stack). +  * Whenever cycle_id pointer chains are traversed to find the root of +  * a cycle piece list, they are compacted to avoid O(n) complexity. +  * +  * The current tentative destruct order is described by the order on +  * the stack and the attached cycle piece lists: The thing that's +  * deepest in the stack is destructed first and the cycle piece list +  * has precedence over the next frame on the recursion stack. To +  * illustrate: +  * ,- stack_top +  * v +  * t1 <=> t4 <=> ... <=> t7 +  * | | `-> t8 -> ... -> t9 +  * | `-> t5 -> ... -> t6 +  * `-> t2 -> ... -> t3 +  * +  * Here <=> represents links on the recursion stack and -> links in +  * the cycle piece lists. The tentative destruct order for these +  * things is the same as the numbering above. +  * +  * Since we strive to keep the refs intact during destruction, the +  * above means that the refs which have priority to be kept intact +  * should point towards the top of the stack and towards the end of +  * the cycle piece lists. +  * +  * To allow rotations, the recursion stack is a double linked list +  * using gc_rec_frame.prev and gc_rec_frame.next. Rotations are the +  * operation used to manipulate the order to avoid getting a +  * prioritized link pointing in the wrong direction: +  * ,- stack_top +  * weak v +  * t1 <=> ... <=> t2 <=> ... <=> t3 <-> t4 <=> ... <=> t5 +  * +  * If a non-weak backward pointer from t5 to t2 is encountered here, +  * we should prefer to break(*) the weak ref between t3 and t4. The +  * stack is therefore rotated to become: +  * ,- stack_top +  * broken v +  * t1 <=> ... <#> t4 <=> ... <=> t5 <=> t2 <=> ... <=> t3 +  * +  * The section to rotate always ends at the top of the stack. +  * +  * The strength of the refs along the stack links are represented as +  * follows: +  * +  * o Things with a strong ref between them are kept next to each +  * other, and the second (the one being referenced by the strong +  * ref) has the GC_PREV_STRONG bit set. A rotation never breaks the +  * list inside a sequence of strong refs. +  * +  * o The GC_PREV_WEAK bit is set in the next frame for every link on +  * the stack where no preceding frame reference any following frame +  * with anything but weak refs. +  * +  * o GC_PREV_BROKEN is set in frames that are rotated back, i.e. t4 +  * in the example above. This is used to break later cycles in the +  * same position when they can't be broken at a weak link. +  * +  * Several separate cycles may be present on the stack simultaneously. +  * That happens when a subcycle which is referenced one way from an +  * earlier cycle is encountered. E.g. +  * +  * L--. L--. +  * t1 t2 -> t3 t4 +  * `--7 `--7 +  * +  * where the visit order is t1, t2, t3 and then t4. Because of the +  * stack which causes a subcycle to always be added to the top, it can +  * be handled independently of the earlier cycles, and those can also +  * be extended later on when the subcycle has been popped off. If a +  * ref from the subcycle to an earlier cycle is found, that means that +  * both are really the same cycle, and the frames in the former +  * subcycle will instead become a cycle piece list on a frame in the +  * former preceding cycle. +  * +  * Since the link frames are kept in substacks attached to the rec +  * frames, they get rotated with the rec frames. This has the effect +  * that the links from the top rec frame on the stack always are +  * tested first. That is necessary to avoid clobbering weak ref +  * partitions. Example: +  * +  * weak weak +  * t1 <=> t2 <-> t3 <=> t4 <-> t5 +  * +  * A nonweak ref is found from t5 to t2. We get this after rotation: +  * +  * broken weak +  * t1 <#> t5 <=> t2 <-> t3 <=> t4 +  * +  * Now, if we would continue to follow the links from t5 and encounter +  * a new thing t7, we'd have to add it to the top. If that ref isn't +  * weak we'd have to blank out the weak flag which could be used in +  * other rotations above t5 (e.g. if a normal ref from t4 to t2 is +  * encountered). To avoid this we do the t4 links instead and continue +  * with t5 when t4, t3 and t2 are done. +  * +  * As said earlier, rec frames are moved to cycle piece lists when +  * they are popped off while being part of unfinished cycles. Since +  * there are no more outgoing refs at that point, there can be no more +  * rotations that affect the order between the rec frame and its +  * predecessor. Therefore the order on a cycle piece list is optimal +  * (in as far as the gc destruct order policy goes). Any further +  * rotations can move the predecessor around, but it can always be +  * treated as one unit together with its cycle piece list. Remaining +  * weak refs inside the cycle piece list are no longer relevant since +  * they don't apply to the links that remain to be followed for the +  * predecessor (i.e. the root of the cycle piece list, still on the +  * rec frame stack). +  * +  * If the preceding frame already has a cycle piece list when a rec +  * frame should be added to it, the rec frame (and its attached cycle +  * piece list) is linked in before that list. That since the rec frame +  * might have refs to the earlier cycle piece list, but the opposite +  * can't happen. +  * +  * When a cycle identifier frame is popped off the stack, the frame +  * together with its cycle piece list represent the complete cycle, +  * and the list holds an optimal order for destructing it. The frames +  * are freed at that point, except for the ones which correspond to +  * live objects, which instead are linked in order into the beginning +  * of the kill list. That list, whose beginning is pointed to by +  * kill_list, holds the final destruct order for all live objects. +  * +  * Note that the complete cycle has to be added to the kill list at +  * once since all live objects that are referenced single way from the +  * cycle should be destructed later and must therefore be put on the +  * kill list before the cycle. +  * +  * 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. In this mode a single dummy rec frame with the +  * GC_MARK_LIVE bit is pushed on the recursion stack, and all link +  * frames are stacked in it, regardless of the things they originate +  * from. +  * +  * *) Here "breaking" a ref doesn't mean that it actually gets +  * zeroed out. It's only disregarded to resolve the cycle to +  * produce an optimal destruct order. I.e. it will still be intact +  * when the first object in the cycle is destructed, and it will +  * only be zeroed when the thing it points to has been destructed. +  */ +  + /* The free extra list. See note in gc_delayed_free. */ + struct free_extra_frame   {    void *data; -  struct gc_free_extra_frame *fe_next; /* Next pointer. */ +  struct free_extra_frame *next; /* Next pointer. */    int type; /* The type of the thing. */   }; -  + static struct free_extra_frame *free_extra_list = NULL;    - struct gc_frame + #ifdef PIKE_DEBUG + static unsigned delayed_freed, weak_freed, checked, marked, cycle_checked, live_ref; + static unsigned mark_live, frame_rot, link_search; + static unsigned gc_extra_refs = 0; + static unsigned tot_cycle_checked = 0, tot_mark_live = 0, tot_frame_rot = 0; + static unsigned gc_rec_frame_seq_max; + #endif +  + static unsigned rec_frames, link_frames, free_extra_frames; + static unsigned max_rec_frames, max_link_frames; + static unsigned tot_max_rec_frames = 0, tot_max_link_frames = 0, tot_max_free_extra_frames = 0; +  + #undef INIT_BLOCK + #define INIT_BLOCK(f) do { \ +  if (++rec_frames > max_rec_frames) \ +  max_rec_frames = rec_frames; \ +  } while (0) + #undef EXIT_BLOCK + #define EXIT_BLOCK(f) do { \ +  DO_IF_DEBUG ({ \ +  if (f->rf_flags & GC_FRAME_FREED) \ +  gc_fatal (f->data, 0, "Freeing gc_rec_frame twice.\n"); \ +  f->rf_flags |= GC_FRAME_FREED; \ +  f->u.link_top = (struct link_frame *) (ptrdiff_t) -1; \ +  f->prev = f->next = f->cycle_id = f->cycle_piece = \ +  (struct gc_rec_frame *) (ptrdiff_t) -1; \ +  }); \ +  rec_frames--; \ +  } while (0) +  + BLOCK_ALLOC_FILL_PAGES (gc_rec_frame, 2) +  + #ifdef PIKE_DEBUG + #define LOW_CHECK_REC_FRAME(f, extra) do { \ +  struct gc_rec_frame *f_ = (f); \ +  if (f_->rf_flags & GC_FRAME_FREED) \ +  gc_fatal (f_->data, 0, "Accessing freed gc_stack_frame %p.\n", f_); \ +  if (f_->cycle_id->rf_flags & GC_FRAME_FREED) { \ +  fprintf (stderr, "Cycle id frame is freed. It is: "); \ +  describe_rec_frame (f_->cycle_id); \ +  fputc ('\n', stderr); \ +  gc_fatal (f_->data, 0, "Cycle id frame is freed.\n"); \ +  } \ +  {extra;} \ +  } while (0) + #define CHECK_REC_STACK_FRAME(f) LOW_CHECK_REC_FRAME (f, { \ +  if (f_->rf_flags & (GC_ON_CYCLE_PIECE_LIST|GC_ON_KILL_LIST)) \ +  gc_fatal (f_->data, 0, "Frame is not on the rec stack.\n"); \ +  if (!f_->prev) \ +  gc_fatal (f_->data, 0, "Prev pointer not set for rec stack frame.\n"); \ +  if (f_->prev->next != f_) \ +  gc_fatal (f_->data, 0, "Rec stack pointers are inconsistent.\n"); \ +  if (f_->cycle_id && \ +  f_->cycle_id->rf_flags & (GC_ON_CYCLE_PIECE_LIST|GC_ON_KILL_LIST)) \ +  gc_fatal (f_->data, 0, "Cycle id frame not on the rec stack.\n"); \ +  if (f_->cycle_piece && \ +  (!f_->cycle_piece->u.last_cycle_piece || \ +  f_->cycle_piece->u.last_cycle_piece->cycle_piece)) \ +  gc_fatal (f_->data, 0, "Bogus last_cycle_piece %p is %p in %p.\n", \ +  f_->cycle_piece->u.last_cycle_piece, \ +  f_->cycle_piece->u.last_cycle_piece ? \ +  f_->cycle_piece->u.last_cycle_piece->cycle_piece : NULL, \ +  f_->cycle_piece); \ +  if ((f_->rf_flags & GC_PREV_STRONG) && \ +  (f_->rf_flags & (GC_PREV_WEAK|GC_PREV_BROKEN))) \ +  gc_fatal (f_->data, 0, "GC_PREV_STRONG set together with " \ +  "GC_PREV_WEAK or GC_PREV_BROKEN.\n"); \ +  }) + #define CHECK_CYCLE_PIECE_FRAME(f) LOW_CHECK_REC_FRAME (f, { \ +  if ((f_->rf_flags & (GC_ON_CYCLE_PIECE_LIST|GC_ON_KILL_LIST)) != \ +  GC_ON_CYCLE_PIECE_LIST) \ +  gc_fatal (f_->data, 0, "Frame is not on a cycle piece list.\n"); \ +  if (f_->prev) \ +  gc_fatal (f_->data, 0, "Prev pointer set for frame on cycle piece list.\n"); \ +  }) + #define CHECK_KILL_LIST_FRAME(f) LOW_CHECK_REC_FRAME (f, { \ +  if ((f_->rf_flags & (GC_ON_CYCLE_PIECE_LIST|GC_ON_KILL_LIST)) != \ +  GC_ON_KILL_LIST) \ +  gc_fatal (f_->data, 0, "Frame is not on kill list.\n"); \ +  if (f_->prev) \ +  gc_fatal (f_->data, 0, "Prev pointer set for frame on kill list.\n"); \ +  }) + #else + #define CHECK_REC_STACK_FRAME(f) do {} while (0) + #define CHECK_CYCLE_PIECE_FRAME(f) do {} while (0) + #define CHECK_KILL_LIST_FRAME(f) do {} while (0) + #endif +  + /* Link and free_extra frames are approximately the same size, so let +  * them share block_alloc area. */ + struct ba_mixed_frame   {    union { -  struct gc_link_frame link; -  struct gc_pop_frame pop; -  struct gc_free_extra_frame free_extra; -  struct gc_frame *next; /* For block_alloc. */ +  struct link_frame link; +  struct free_extra_frame free_extra; +  struct ba_mixed_frame *next; /* For block_alloc internals. */    } u;   };    - /* frameflags bits. */ - #define GC_POP_FRAME 0x01 - #define GC_PREV_WEAK 0x02 /* Only applicable in pop frames. */ - #define GC_PREV_STRONG 0x04 /* Only applicable in pop frames. */ - #define GC_OFF_STACK 0x08 /* Only applicable in pop frames. */ - #define GC_ON_KILL_LIST 0x10 /* Only applicable in pop frames. */ - #ifdef PIKE_DEBUG - #define GC_FRAME_FREED 0x20 - #define GC_FOLLOWED_NONSTRONG 0x40 - #endif -  +    #undef BLOCK_ALLOC_NEXT   #define BLOCK_ALLOC_NEXT u.next -  + #undef INIT_BLOCK + #define INIT_BLOCK(f) + #undef EXIT_BLOCK + #define EXIT_BLOCK(f)    - BLOCK_ALLOC(gc_frame,GC_LINK_CHUNK_SIZE) + BLOCK_ALLOC_FILL_PAGES (ba_mixed_frame, 2)    - static INLINE struct gc_pop_frame *alloc_pop_frame() + static INLINE struct link_frame *alloc_link_frame()   { -  struct gc_frame *f = alloc_gc_frame(); -  f->u.pop.frameflags = GC_POP_FRAME; -  return (struct gc_pop_frame *) f; +  struct ba_mixed_frame *f = alloc_ba_mixed_frame(); +  if (++link_frames > max_link_frames) +  max_link_frames = link_frames; +  return (struct link_frame *) f;   }    - static INLINE struct gc_link_frame *alloc_link_frame() + static INLINE struct free_extra_frame *alloc_free_extra_frame()   { -  struct gc_frame *f = alloc_gc_frame(); -  f->u.link.frameflags = 0; -  return (struct gc_link_frame *) f; +  struct ba_mixed_frame *f = alloc_ba_mixed_frame(); +  free_extra_frames++; +  return (struct free_extra_frame *) f;   }    - static INLINE struct gc_free_extra_frame *alloc_free_extra_frame() + static INLINE void really_free_link_frame (struct link_frame *f)   { -  return (struct gc_free_extra_frame *) alloc_gc_frame(); +  link_frames--; +  really_free_ba_mixed_frame ((struct ba_mixed_frame *) f);   }    - #ifdef PIKE_DEBUG -  - static unsigned num_gc_stack_frames = 0; -  - void debug_free_gc_stack_frame (struct gc_stack_frame *f) + static INLINE void really_free_free_extra_frame (struct free_extra_frame *f)   { -  if (f->frameflags & GC_FRAME_FREED) -  gc_fatal (f->data, 0, "Freeing freed gc_stack_frame.\n"); -  f->frameflags |= GC_FRAME_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; +  free_extra_frames--; +  really_free_ba_mixed_frame ((struct ba_mixed_frame *) f);   } -  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 { \ -  struct gc_pop_frame *f_ = (frame); \ -  if (f_->frameflags & GC_FRAME_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_pop_frame sentinel_frame = {NULL, NULL, GC_POP_FRAME, 0, NULL, NULL}; - static struct gc_pop_frame *list_end = &sentinel_frame; - static struct gc_stack_frame *stack_top = (struct gc_stack_frame *) &sentinel_frame; - static struct gc_pop_frame *stack_top_pop = &sentinel_frame; - 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. stack_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. -  * list_end 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, -  * i.e. it's either popped whole or not at all. That means that -  * 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 -  * 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. The frame list is not touched at all in this extra round. -  */ -  - static double objects_alloced = 0.0; - static double objects_freed = 0.0; - static double gc_time = 0.0, non_gc_time = 0.0; - static cpu_time_t last_gc_end_time = 0; - #if CPU_TIME_IS_THREAD_LOCAL == PIKE_NO - cpu_time_t auto_gc_time = 0; - #endif -  +    /* These are only collected for the sake of gc_status. */   static double last_garbage_ratio = 0.0;   static enum {
315:   }      static void init_gc(void); - static void gc_cycle_pop(void *a); + static void gc_cycle_pop();      #undef BLOCK_ALLOC_NEXT   #define BLOCK_ALLOC_NEXT next
366:   PMOD_EXPORT int gc_external_refs_zapped = 0;   #endif    + #if defined (PIKE_DEBUG) || defined (GC_CYCLE_DEBUG) + static void describe_rec_frame (struct gc_rec_frame *f) + { +  fprintf (stderr, "data=%p rf_flags=0x%02x prev=%p next=%p " +  "cycle_id=%p cycle_piece=%p link_top/last_cycle_piece=%p", +  f->data, f->rf_flags, f->prev, f->next, +  f->cycle_id, f->cycle_piece, f->u.link_top); + } + #endif +    #ifdef PIKE_DEBUG      int gc_in_cycle_check = 0; - static unsigned delayed_freed, weak_freed, checked, marked, cycle_checked, live_ref; - static unsigned max_gc_stack_frames, live_rec, frame_rot, link_search; - static unsigned gc_extra_refs = 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)
732:   #endif   }    - static void describe_gc_stack_frame (struct gc_stack_frame *f) + static void describe_link_frame (struct link_frame *f)   { -  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); +  fprintf (stderr, "data=%p prev=%p checkfn=%p weak=%d", +  f->data, f->prev, f->checkfn, f->weak);   } -  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", +  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_stack_frame (POP2STACK (m->frame)); +  describe_rec_frame (m->frame);    putc(']', stderr);    } - #endif +     putc('\n', stderr);    }    else
1481:    gc_fatal(a, 1, "Found a thing without marker.\n");    else if (!(m->flags & GC_PRETOUCHED))    gc_fatal(a, 1, "Thing got an existing but untouched marker.\n"); +  if (gc_destruct_everything && (m->flags & GC_MARKED)) +  gc_fatal (a, 1, "Thing got marked in gc_destruct_everything mode.\n");   #ifdef PIKE_DEBUG    extra_ref = (m->flags & GC_GOT_EXTRA_REF) == GC_GOT_EXTRA_REF;    if (m->saved_refs + extra_ref < *(INT32 *) a)
1508: Inside #if 0
   if (!(m->flags & (GC_PRETOUCHED|GC_MIDDLETOUCHED)))    gc_fatal(a, 2, "An existing but untouched marker found "    "for object in linked lists.\n"); -  else if (m->flags & GC_LIVE_RECURSE || -  (m->frame && m->frame->frameflags & (GC_PREV_WEAK|GC_PREV_STRONG))) -  gc_fatal(a, 2, "Thing still got flag from recurse list.\n"); +    #ifdef PIKE_DEBUG    else if (m->flags & GC_MARKED)    return;
1695:    if (!marker_hash_table)   #endif    low_init_marker_hash(num_objects); -  get_marker(sentinel_frame.data); /* Used to simplify fencepost conditions. */ +    #ifdef PIKE_DEBUG    }   #endif
1710:    if (!gc_keep_markers)    cleanup_markers();    -  free_all_gc_frame_blocks(); +  free_all_gc_rec_frame_blocks(); +  free_all_ba_mixed_frame_blocks();    - #ifdef GC_VERBOSE -  num_gc_stack_frames = 0; - #endif -  +    #ifdef PIKE_DEBUG    if (gc_is_watching) {    fprintf(stderr, "## Exiting gc and resetting watches for %d things.\n",
2024:    *    * 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_free_extra_frame *l = alloc_free_extra_frame(); +  struct free_extra_frame *l = alloc_free_extra_frame();    l->data = a;    l->type = type; -  l->fe_next = free_extra_list; +  l->next = free_extra_list;    free_extra_list = l;    }   
2121:      PMOD_EXPORT void gc_cycle_enqueue(gc_cycle_check_cb *checkfn, void *data, int weak)   { -  struct gc_link_frame *l = alloc_link_frame(); +  struct link_frame *l = alloc_link_frame();   #ifdef PIKE_DEBUG    {    struct marker *m;
2132: Inside #if defined(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"); +  if (stack_top == &sentinel_frame) +  gc_fatal (data, 0, "No thing on rec stack to follow links from.\n");   #endif - #ifdef GC_VERBOSE -  if (++num_gc_stack_frames > max_gc_stack_frames) -  max_gc_stack_frames = num_gc_stack_frames; - #endif +     l->data = data;    l->checkfn = checkfn;    l->weak = weak; -  l->s_prev = stack_top; +  l->prev = stack_top->u.link_top;   #ifdef GC_STACK_DEBUG -  fprintf(stderr, "enqueue %p [%p]: ", l, stack_top); -  describe_gc_stack_frame (LINK2STACK (l)); +  fprintf (stderr, "push link %p [%p in %p]: ", l, stack_top->u.link_top, stack_top); +  describe_link_frame (l);    fputc('\n', stderr);   #endif -  stack_top = LINK2STACK (l); +  stack_top->u.link_top = l;   }    - static struct gc_pop_frame *gc_cycle_enqueue_pop(void *data) + static struct gc_rec_frame *gc_cycle_enqueue_rec (void *data)   { -  struct gc_pop_frame *p = alloc_pop_frame(); +  struct gc_rec_frame *r = alloc_gc_rec_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"); -  +  r->next = (struct gc_rec_frame *) (ptrdiff_t) -1;   #endif - #ifdef GC_VERBOSE -  if (++num_gc_stack_frames > max_gc_stack_frames) -  max_gc_stack_frames = num_gc_stack_frames; - #endif -  p->data = data; - #ifdef PIKE_DEBUG -  p->prev = p->next = (struct gc_pop_frame *) (ptrdiff_t) -1; - #endif -  p->cycle = 0; -  p->s_prev = stack_top; +  r->data = data; +  r->u.link_top = NULL; +  r->prev = stack_top; +  r->cycle_id = r; +  r->cycle_piece = NULL;   #ifdef GC_STACK_DEBUG -  fprintf(stderr, "enqueue %p [%p]: ", p, stack_top); -  describe_gc_stack_frame (POP2STACK (p)); +  fprintf (stderr, "push rec %p [%p]: ", r, stack_top); +  describe_rec_frame (r);    fputc('\n', stderr);   #endif -  stack_top = POP2STACK (p); -  stack_top_pop = p; -  return p; +  stack_top->next = r; +  stack_top = r; +  return r;   }      void gc_cycle_run_queue()
2183:    Pike_fatal("Use of the gc frame stack outside the cycle check pass.\n");   #endif    -  while (stack_top != POP2STACK (&sentinel_frame)) { +  while (stack_top != &sentinel_frame) { +  while (stack_top->u.link_top) { +  struct link_frame l = *stack_top->u.link_top;   #ifdef GC_STACK_DEBUG -  fprintf(stderr, "dequeue %p [%p]: ", stack_top, stack_top->s_prev); -  describe_gc_stack_frame(stack_top); -  fputc('\n', stderr); +  fprintf (stderr, "pop link %p [%p in %p]: ", +  stack_top->u.link_top, l.prev, stack_top); +  describe_link_frame (stack_top->u.link_top); +  fputc ('\n', stderr);   #endif -  -  if (stack_top->frameflags & GC_POP_FRAME) { -  struct gc_stack_frame *f = stack_top->s_prev; -  gc_cycle_pop (stack_top->data); -  stack_top = f; -  -  /* Use separate pointer chain instead? */ -  for (; !(f->frameflags & GC_POP_FRAME); f = f->s_prev) {} -  stack_top_pop = STACK2POP (f); +  really_free_link_frame (stack_top->u.link_top); +  stack_top->u.link_top = l.prev; +  l.checkfn (l.data, l.weak); /* Might change stack_top. */    }    -  else { -  struct gc_link_frame l = *STACK2LINK (stack_top); + #ifdef GC_STACK_DEBUG +  fprintf (stderr, "pop rec %p [%p]: ", stack_top, stack_top->prev); +  describe_rec_frame (stack_top); +  fputc ('\n', stderr); + #endif +  CHECK_REC_STACK_FRAME (stack_top); +    #ifdef PIKE_DEBUG -  if (l.frameflags & GC_FRAME_FREED) -  gc_fatal(l.data, 0, "Accessing freed gc_frame.\n"); +  { +  struct gc_rec_frame *old_stack_top = stack_top; +  gc_cycle_pop(); +  if (stack_top == old_stack_top) +  fatal ("gc_cycle_pop didn't pop the stack.\n"); +  } + #else +  gc_cycle_pop();   #endif -  FREE_LINK_FRAME (STACK2LINK (stack_top)); -  stack_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 [stack:%p, list:%p] ", gc_cycle_indent, "", \ -  (TXT), (M) ? (M)->data : 0, stack_top->data, list_end->data); \ -  describe_marker(M); \ + #define CYCLE_DEBUG_MSG(REC, TXT) do { \ +  struct gc_rec_frame *r_ = (REC); \ +  fprintf (stderr, "%*s%-35s %p [%p] ", gc_cycle_indent, "", \ +  (TXT), r_ ? r_->data : NULL, stack_top->data); \ +  if (r_) describe_rec_frame (r_); \ +  putc ('\n', stderr); \    } while (0)   #else - #define CYCLE_DEBUG_MSG(M, TXT) do {} while (0) + #define CYCLE_DEBUG_MSG(REC, TXT) do {} while (0)   #endif    - static void rotate_frame_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. */ + static struct gc_rec_frame *rotate_rec_stack (struct gc_rec_frame *beg, +  struct gc_rec_frame *pos) + /* Performs a rotation of the recursion stack so the part from pos +  * down to the end gets before the part from beg down to pos. The beg +  * pos might be moved further back the list to avoid breaking strong +  * link sequences. Returns the actual beg pos. Example: +  * +  * strong +  * a1 <=> ... <=> a2 <=> b1 <*> b2 <=> ... <=> b3 <=> c1 <=> ... <=> c2 +  * ^ beg ^ pos +  * +  * becomes +  * +  * broken strong +  * a1 <=> ... <=> a2 <#> c1 <=> ... <=> c2 <=> b1 <*> b2 <=> ... <=> b3 +  * ^ pos ^ ^ beg +  * returned +  * +  * Note: The part from pos down to the end is assumed to not contain +  * any weak refs. (If it does they must be cleared, unless the link +  * before beg is weak.) +  */   { - #ifdef GC_STACK_DEBUG -  struct gc_stack_frame *l; - #endif -  CYCLE_DEBUG_MSG(find_marker(beg->data), "> rotate_frame_list, asked to begin at"); +  CYCLE_DEBUG_MSG (beg, "> rotate_rec_stack, requested beg");      #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); +  CHECK_REC_STACK_FRAME (beg); +  CHECK_REC_STACK_FRAME (pos);    if (beg == pos) -  gc_fatal(beg->data, 0, "Cycle already broken at requested position.\n"); -  if (list_end->next) -  gc_fatal(list_end->data, 0, "list_end not at end.\n"); +  gc_fatal (beg->data, 0, "Cycle already broken at requested position.\n");   #endif      #ifdef GC_STACK_DEBUG    fprintf(stderr,"Stack before:\n"); -  for (l = stack_top; l != &sentinel_frame; l = l->s_prev) { -  fprintf(stderr, " %p ", l); -  describe_gc_stack_frame(l); -  fputc('\n', stderr); +  { +  struct gc_rec_frame *l; +  for (l = stack_top; l != &sentinel_frame; l = l->prev) { +  fprintf (stderr, " %p%s ", l, +  l == beg ? " (beg)" : l == pos ? " (pos)" : ""); +  describe_rec_frame (l); +  fputc ('\n', stderr);    } -  +  }   #endif       /* Always keep chains of strong refs continuous, or else we risk    * breaking the order in a later rotation. */ -  for (; beg->frameflags & GC_PREV_STRONG; beg = beg->prev) {} +  for (; beg->rf_flags & GC_PREV_STRONG; beg = beg->prev) +  CYCLE_DEBUG_MSG (beg, "> rotate_rec_stack, skipping strong"); + #ifdef PIKE_DEBUG +  if (beg == &sentinel_frame) fatal ("Strong ref chain ended up off stack.\n"); + #endif +  CYCLE_DEBUG_MSG (beg, "> rotate_rec_stack, actual beg");    -  CYCLE_DEBUG_MSG(find_marker(beg->data), "> rotate_frame_list, begin at"); -  +     { -  struct gc_pop_frame *b = beg, *p = pos; -  struct gc_stack_frame *old_stack_top; -  while (b->frameflags & GC_OFF_STACK) { -  if ((b = b->next) == pos) goto done; -  CHECK_POP_FRAME(b); -  DO_IF_DEBUG(frame_rot++); -  } -  while (p->frameflags & GC_OFF_STACK) { -  if (!(p = p->next)) goto done; -  CHECK_POP_FRAME(p); -  DO_IF_DEBUG(frame_rot++); -  } -  old_stack_top = stack_top; -  stack_top = p->s_prev; -  p->s_prev = b->s_prev; -  b->s_prev = old_stack_top; -  } - done: -  DO_IF_DEBUG(frame_rot++); +  struct gc_rec_frame *new_stack_top = pos->prev;    -  { -  struct gc_pop_frame *new_list_end = pos->prev; +     beg->prev->next = pos;    pos->prev = beg->prev; -  list_end->next = beg; -  beg->prev = list_end; -  list_end = new_list_end; -  list_end->next = 0; +  +  stack_top->next = beg; +  beg->prev = stack_top; +  +  stack_top = new_stack_top; + #ifdef PIKE_DEBUG +  stack_top->next = (struct gc_rec_frame *) (ptrdiff_t) -1; + #endif    }    -  + #ifdef PIKE_DEBUG +  frame_rot++; + #endif +  +  pos->rf_flags |= GC_PREV_BROKEN; +    #ifdef GC_STACK_DEBUG    fprintf(stderr,"Stack after:\n"); -  for (l = stack_top; l != sentinel_frame; l = l->s_prev) { -  fprintf(stderr, " %p ", l); -  describe_gc_stack_frame(l); -  fputc('\n', stderr); +  { +  struct gc_rec_frame *l; +  for (l = stack_top; l != &sentinel_frame; l = l->prev) { +  fprintf (stderr, " %p%s ", l, +  l == beg ? " (beg)" : l == pos ? " (pos)" : ""); +  describe_rec_frame (l); +  fputc ('\n', stderr);    } -  +  }   #endif -  +  +  return beg;   }    - int gc_cycle_push(void *x, struct marker *m, int weak) + int gc_cycle_push(void *data, struct marker *m, int weak)   { -  struct marker *top = find_marker (stack_top_pop->data); +  struct marker *pm;    -  /* FIXME: Should use top instead? */ -  struct marker *last = find_marker (list_end->data); -  +    #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_push()");    }    -  debug_malloc_touch(x); +  debug_malloc_touch (data);    -  if (!x) Pike_fatal("Got null pointer.\n"); -  if (m->data != x) Pike_fatal("Got wrong marker.\n"); +  if (!data) Pike_fatal ("Got null pointer.\n"); +  if (m->data != data) Pike_fatal ("Got wrong marker.\n");    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"); +  gc_fatal (data, 0, "gc_cycle_push() called for untouched thing.\n");    if (!gc_destruct_everything) {    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"); +  *(INT32 *) data) +  gc_fatal (data, 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 " +  gc_fatal (data, 1, "Doing cycle check in externally referenced thing "    "missed in mark pass.\n");    } -  if (weak && list_end == &sentinel_frame) -  gc_fatal(x, 1, "weak is %d when on top of stack.\n", weak); +  if (weak && stack_top == &sentinel_frame) +  gc_fatal (data, 1, "weak is %d when stack is empty.\n", weak);    if (gc_debug > 1) {    struct array *a;    struct object *o;
2340: Inside #if defined(PIKE_DEBUG)
   struct mapping *m;    struct multiset *l;    for(a = gc_internal_array; a; a = a->next) -  if(a == (struct array *) x) goto on_gc_internal_lists; +  if(a == (struct array *) data) goto on_gc_internal_lists;    for(o = gc_internal_object; o; o = o->next) -  if(o == (struct object *) x) goto on_gc_internal_lists; +  if(o == (struct object *) data) goto on_gc_internal_lists;    for(p = gc_internal_program; p; p = p->next) -  if(p == (struct program *) x) goto on_gc_internal_lists; +  if(p == (struct program *) data) goto on_gc_internal_lists;    for(m = gc_internal_mapping; m; m = m->next) -  if(m == (struct mapping *) x) goto on_gc_internal_lists; +  if(m == (struct mapping *) data) goto on_gc_internal_lists;    for(l = gc_internal_multiset; l; l = l->next) -  if(l == (struct multiset *) x) goto on_gc_internal_lists; -  gc_fatal(x, 0, "gc_cycle_check() called for thing not on gc_internal lists.\n"); +  if(l == (struct multiset *) data) goto on_gc_internal_lists; +  gc_fatal (data, 0, "gc_cycle_check() called for thing not on gc_internal lists.\n");    on_gc_internal_lists:    ; /* We must have a least one expression after a label! - Hubbe */    }   #endif    -  if (top->flags & GC_LIVE_RECURSE) { - #ifdef PIKE_DEBUG -  if (!(top->flags & GC_LIVE)) -  gc_fatal(x, 0, "Doing live recursion from a dead thing.\n"); - #endif -  -  if (m->flags & GC_CYCLE_CHECKED) { -  if (!(m->flags & GC_LIVE)) { +  if (stack_top->rf_flags & GC_MARK_LIVE) {    /* Only recurse through things already handled; we'll get to the    * other later in the normal recursion. */ - #ifdef PIKE_DEBUG -  if (m->flags & GC_LIVE_RECURSE) -  gc_fatal(x, 0, "Mark live recursion attempted twice into thing.\n"); - #endif -  goto live_recurse; +  if (m->flags & GC_CYCLE_CHECKED && !(m->flags & GC_LIVE)) { +  CYCLE_DEBUG_MSG (m->frame, "gc_cycle_push, mark live"); +  goto mark_live;    } -  CYCLE_DEBUG_MSG(m, "gc_cycle_push, no live recurse"); +  CYCLE_DEBUG_MSG (m->frame, "gc_cycle_push, no mark live"); +  return 0;    }    -  +  if (stack_top == &sentinel_frame) +  pm = NULL;    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"); -  top->flags &= ~GC_LIVE_RECURSE; -  while (1) { -  struct gc_stack_frame *l = stack_top, *f; +  pm = find_marker (stack_top->data);   #ifdef PIKE_DEBUG -  if (stack_top == POP2STACK (&sentinel_frame)) -  Pike_fatal ("Stack shouldn't be empty here.\n"); +  if (!pm) +  gc_fatal (stack_top->data, 0, "No marker for thing on top of the stack.\n");   #endif -  stack_top = l->s_prev; -  -  /* Use separate pointer chain instead? */ -  for (f = stack_top; !(f->frameflags & GC_POP_FRAME); f = f->s_prev) {} -  stack_top_pop = STACK2POP (f); -  -  if (l->frameflags & GC_POP_FRAME) { -  FREE_POP_FRAME (STACK2POP (l)); -  break; +     } -  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; -  } -  +    #ifdef PIKE_DEBUG -  if (weak < 0 && stack_top->frameflags & GC_FOLLOWED_NONSTRONG) -  gc_fatal(x, 0, "Followed strong link too late.\n"); -  if (weak >= 0) stack_top->frameflags |= GC_FOLLOWED_NONSTRONG; +  if (weak < 0 && stack_top->rf_flags & GC_FOLLOWED_NONSTRONG) +  gc_fatal (data, 0, "Followed strong link too late.\n"); +  if (weak >= 0) stack_top->rf_flags |= GC_FOLLOWED_NONSTRONG;   #endif    -  if (m->frame && !(m->frame->frameflags & GC_ON_KILL_LIST)) { -  /* A cyclic reference is found. */ +  if (m->frame) { +  /* A cyclic ref or a ref to something on the kill list is found. */ +  struct gc_rec_frame *cycle_frame = m->frame; +  +  if (cycle_frame->rf_flags & GC_ON_KILL_LIST) +  CYCLE_DEBUG_MSG (cycle_frame, "gc_cycle_push, ref to kill list"); +  else if (cycle_frame == stack_top) +  CYCLE_DEBUG_MSG (cycle_frame, "gc_cycle_push, self-ref"); +  else if (weak > 0) +  /* Ignore weak refs since they always are eligible to be broken anyway. */ +  CYCLE_DEBUG_MSG (cycle_frame, "gc_cycle_push, weak cyclic ref"); +  +  else { +  struct gc_rec_frame *weakly_refd = NULL; +  struct gc_rec_frame *brokenly_refd = NULL; +  struct gc_rec_frame *nonstrongly_refd = NULL;   #ifdef PIKE_DEBUG -  if (list_end == &sentinel_frame) -  gc_fatal(x, 0, "Cyclic ref involves dummy sentinel_frame marker.\n"); -  CHECK_POP_FRAME(list_end); -  CHECK_POP_FRAME(m->frame); +  if (stack_top == &sentinel_frame) +  gc_fatal (data, 0, "Cyclic ref involves dummy sentinel frame.\n"); +  CHECK_REC_STACK_FRAME (stack_top);   #endif    -  if (m != last) { -  struct gc_pop_frame *p, *weak_ref = 0, *nonstrong_ref = 0; +  CYCLE_DEBUG_MSG (cycle_frame, "gc_cycle_push, cyclic ref"); +  +  /* Find the corresponding frame still on the stack and compress +  * indirect cycle_id links. */ +  { +  struct gc_rec_frame *r; +  for (r = cycle_frame; !r->prev; r = r->cycle_id) +  CHECK_CYCLE_PIECE_FRAME (r); +  while (cycle_frame != r) { +  struct gc_rec_frame *next = cycle_frame->cycle_id; +  cycle_frame->cycle_id = r; +  cycle_frame = next; +  } +  CHECK_REC_STACK_FRAME (cycle_frame); +  } +     if (!weak) { -  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 = q->next;; q = p, p = p->next) { -  CYCLE_DEBUG_MSG (find_marker (p->data), "> gc_cycle_push, search"); -  CHECK_POP_FRAME(p); +  struct gc_rec_frame *r; +  CYCLE_DEBUG_MSG (cycle_frame, "gc_cycle_push, search normal"); +  /* Find the last weakly linked thing and the last thing whose +  * normal ref already has been broken. */ +  for (r = stack_top; r != cycle_frame; r = r->prev) { +  CHECK_REC_STACK_FRAME (r);    DO_IF_DEBUG (link_search++); -  if (p->frameflags & (GC_PREV_WEAK|GC_PREV_STRONG)) { -  if (p->frameflags & GC_PREV_WEAK) weak_ref = p; -  else if (!nonstrong_ref) nonstrong_ref = q; +  if (r->rf_flags & GC_PREV_WEAK) { +  CYCLE_DEBUG_MSG (r, "> gc_cycle_push, found weak"); +  weakly_refd = r; +  break;    } -  if (p == list_end) break; +  if (!brokenly_refd && (r->rf_flags & GC_PREV_BROKEN)) { +  CYCLE_DEBUG_MSG (r, "> gc_cycle_push, found broken"); +  brokenly_refd = r;    } -  +  else +  CYCLE_DEBUG_MSG (r, "> gc_cycle_push, search");    } -  +  }    -  else if (weak < 0) { -  CYCLE_DEBUG_MSG(m, "gc_cycle_push, search strong"); +  else { /* weak < 0 */ +  struct gc_rec_frame *r; +  CYCLE_DEBUG_MSG (cycle_frame, "gc_cycle_push, search strong");    /* Find the last weakly linked thing and the last one which    * isn't strongly linked. */ -  for (p = m->frame->next;; p = p->next) { -  CYCLE_DEBUG_MSG (find_marker (p->data), "> gc_cycle_push, search"); -  CHECK_POP_FRAME(p); +  for (r = stack_top; r != cycle_frame; r = r->prev) { +  CHECK_REC_STACK_FRAME (r);    DO_IF_DEBUG (link_search++); -  if (p->frameflags & GC_PREV_WEAK) weak_ref = p; -  if (!(p->frameflags & GC_PREV_STRONG)) nonstrong_ref = p; -  if (p == list_end) break; +  if (r->rf_flags & GC_PREV_WEAK) { +  CYCLE_DEBUG_MSG (r, "> gc_cycle_push, found weak"); +  weakly_refd = r; +  break;    } -  +  if (!nonstrongly_refd && !(r->rf_flags & GC_PREV_STRONG)) { +  nonstrongly_refd = r; +  CYCLE_DEBUG_MSG (r, "> gc_cycle_push, found nonstrong"); +  } +  if (!brokenly_refd && (r->rf_flags & GC_PREV_BROKEN)) { +  CYCLE_DEBUG_MSG (r, "> gc_cycle_push, found broken"); +  brokenly_refd = r; +  } + #ifdef GC_CYCLE_DEBUG +  else if (r != nonstrongly_refd) +  CYCLE_DEBUG_MSG (r, "> gc_cycle_push, search"); + #endif +  }   #ifdef PIKE_DEBUG -  if (p == list_end && !nonstrong_ref) { +  if (weak && r == cycle_frame && !nonstrongly_refd) {    fprintf(stderr, "Only strong links in cycle:\n"); -  for (p = m->frame;; p = p->next) { -  describe(p->data); -  locate_references(p->data); -  if (p == list_end) break; +  for (r = cycle_frame;; r = r->next) { +  describe (r->data); +  locate_references (r->data); +  if (r == stack_top) break;    fprintf(stderr, "========= next =========\n");    }    gc_fatal(0, 0, "Only strong links in cycle.\n");
2466:   #endif    }    -  else { -  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 = q->next;; q = p, p = p->next) { -  CYCLE_DEBUG_MSG (find_marker (p->data), "> gc_cycle_push, search"); -  CHECK_POP_FRAME(p); -  DO_IF_DEBUG (link_search++); -  if (!(p->frameflags & GC_PREV_WEAK) && !nonstrong_ref) -  nonstrong_ref = q; -  if (p == list_end) break; +  if (weakly_refd) { +  /* 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 avoid having to clobber the others after the +  * rotation). */ +  CYCLE_DEBUG_MSG (weakly_refd, "gc_cycle_push, weak break"); +  rotate_rec_stack (cycle_frame, weakly_refd);    } -  } +     -  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_frame_list (m->frame, weak_ref); -  } +  else { +  struct gc_rec_frame *cycle_id = cycle_frame->cycle_id; +  struct gc_rec_frame *break_pos;    -  else if (weak < 0) { +  if (brokenly_refd) { +  /* Found a link that already has been broken once, so we +  * prefer to break at it again. */ +  CYCLE_DEBUG_MSG (brokenly_refd, "gc_cycle_push, break at broken"); +  break_pos = brokenly_refd; +  } +  else if (!weak) { +  CYCLE_DEBUG_MSG (cycle_frame, "gc_cycle_push, no break spot found"); +  break_pos = NULL; +  } +  else { /* 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_frame_list (m->frame, nonstrong_ref); -  nonstrong_ref->next->frameflags = -  (nonstrong_ref->next->frameflags & ~GC_PREV_WEAK) | GC_PREV_STRONG; +  CYCLE_DEBUG_MSG (nonstrongly_refd, "gc_cycle_push, nonstrong break"); +  break_pos = nonstrongly_refd;    }    -  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_frame_list (m->frame, nonstrong_ref); +  if (break_pos) { +  struct gc_rec_frame *rot_beg; +  rot_beg = rotate_rec_stack (cycle_frame, break_pos); +  cycle_frame->rf_flags = +  (cycle_frame->rf_flags & ~(GC_PREV_WEAK|GC_PREV_BROKEN)) | GC_PREV_STRONG; +  +  if (rot_beg->cycle_id != break_pos->prev->cycle_id) +  /* Ensure that the cycle id frame is kept deepest in the +  * stack: Since there's no already marked cycle that +  * continues past the beginning of the rotated portion +  * (rot_beg and break_pos->prev were previously next to +  * each other), break_pos is now the deepest frame in the +  * cycle. */ +  cycle_id = break_pos;    } -  } +     -  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->frame->cycle ? m->frame->cycle : ++last_cycle; -  if (cycle == list_end->cycle) -  CYCLE_DEBUG_MSG(m, "gc_cycle_push, old cycle"); -  else { -  CYCLE_DEBUG_MSG(m, "gc_cycle_push, 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 == list_end) break; +  /* Mark the cycle. NB: This causes O(n^2) complexity for some +  * kinds of data structures. */ +  CHECK_REC_STACK_FRAME (cycle_id); +  { +  struct gc_rec_frame *r, *bottom = break_pos ? break_pos : cycle_frame; +  CYCLE_DEBUG_MSG (cycle_id, "gc_cycle_push, cycle"); +  for (r = stack_top;; r = r->prev) { +  CHECK_REC_STACK_FRAME (r); +  r->cycle_id = cycle_id; +  CYCLE_DEBUG_MSG (r, "> gc_cycle_push, mark cycle 1"); +  if (r == bottom) break;    }}}}} /* Mmm.. lisp ;) */       else    if (!(m->flags & GC_CYCLE_CHECKED)) { -  struct gc_pop_frame *l; +  struct gc_rec_frame *r;   #ifdef PIKE_DEBUG    cycle_checked++;    if (m->frame) -  gc_fatal(x, 0, "Marker already got a frame.\n"); -  if (list_end->next) -  gc_fatal(list_end->data, 0, "Not at end of list.\n"); +  gc_fatal (data, 0, "Marker already got a frame.\n");   #endif    -  m->frame = l = gc_cycle_enqueue_pop(x); -  l->next = NULL; -  l->prev = list_end; -  list_end->next = l; -  m->flags |= GC_CYCLE_CHECKED | (last->flags & GC_LIVE); -  debug_malloc_touch(x); +  m->flags |= GC_CYCLE_CHECKED | (pm ? pm->flags & GC_LIVE : 0); +  m->frame = r = gc_cycle_enqueue_rec (data); +  debug_malloc_touch (data);    if (weak) { -  if (weak > 0) l->frameflags |= GC_PREV_WEAK; -  else l->frameflags |= GC_PREV_STRONG; +  if (weak > 0) r->rf_flags = GC_PREV_WEAK; +  else r->rf_flags = GC_PREV_STRONG;    } -  +  else +  r->rf_flags = 0;      #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"); -  else CYCLE_DEBUG_MSG(m, "gc_cycle_push, recurse"); +  if (weak > 0) CYCLE_DEBUG_MSG (r, "gc_cycle_push, recurse weak"); +  else if (weak < 0) CYCLE_DEBUG_MSG (r, "gc_cycle_push, recurse strong"); +  else CYCLE_DEBUG_MSG (r, "gc_cycle_push, recurse");    gc_cycle_indent += 2;   #endif -  list_end = l; +     return 1;    }       /* Should normally not recurse now, but got to do that anyway if we -  * must mark live things. */ -  if (!(last->flags & GC_LIVE) || m->flags & GC_LIVE) { -  CYCLE_DEBUG_MSG(m, "gc_cycle_push, no recurse"); +  * must propagate GC_LIVE flags. */ +  if (!pm || !(pm->flags & GC_LIVE) || m->flags & GC_LIVE) { +  CYCLE_DEBUG_MSG (m->frame ? m->frame : NULL, "gc_cycle_push, no recurse");    return 0;    }    - live_recurse: +  /* Initialize mark live recursion. */ +  gc_cycle_enqueue_rec (NULL)->rf_flags = GC_MARK_LIVE; + #ifdef GC_CYCLE_DEBUG +  CYCLE_DEBUG_MSG (m->frame ? m->frame : NULL, "gc_cycle_push, mark live begins"); +  gc_cycle_indent += 2; + #endif +  + mark_live:   #ifdef PIKE_DEBUG    if (m->flags & GC_LIVE) -  Pike_fatal("Shouldn't live recurse when there's nothing to do.\n"); +  Pike_fatal("Shouldn't mark live recurse when there's nothing to do.\n");   #endif -  m->flags |= GC_LIVE|GC_LIVE_RECURSE; -  debug_malloc_touch(x); +  m->flags |= GC_LIVE; +  debug_malloc_touch (data);       if (m->flags & GC_GOT_DEAD_REF) {    /* A thing previously popped as dead is now being marked live.    * Have to remove the extra ref added by gc_cycle_pop(). */ -  gc_free_extra_ref(x); -  if (!sub_ref ((struct ref_dummy *) x)) { +  gc_free_extra_ref (data); +  if (!sub_ref ((struct ref_dummy *) data)) {   #ifdef PIKE_DEBUG -  gc_fatal(x, 0, "Thing got zero refs after removing the dead gc ref.\n"); +  gc_fatal (data, 0, "Thing got zero refs after removing the dead gc ref.\n");   #endif    }    }    -  { -  /* Recurse without linking onto the frame list. */ -  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; +  /* Visit links without pushing a rec frame. */ + #ifdef PIKE_DEBUG +  mark_live++;   #endif -  +  return 1;   }    -  + static void gc_cycle_pop() + {   #ifdef PIKE_DEBUG -  live_rec++; +  if (Pike_in_gc != GC_PASS_CYCLE) +  Pike_fatal("GC cycle pop attempted in invalid pass.\n"); +  if (stack_top->u.link_top) +  gc_fatal (stack_top->data, 0, "Link list not empty for popped rec frame.\n");   #endif -  return 1; + #ifdef GC_CYCLE_DEBUG +  gc_cycle_indent -= 2; + #endif +  +  if (stack_top->rf_flags & GC_MARK_LIVE) { +  struct gc_rec_frame *r = stack_top->prev; +  CYCLE_DEBUG_MSG (stack_top, "gc_cycle_pop, mark live ends"); +  really_free_gc_rec_frame (stack_top); +  stack_top = r;    }    - static void gc_cycle_pop(void *a) - { -  struct marker *m = find_marker(a); -  struct gc_pop_frame *here, *base, *p; +  else { +  struct gc_rec_frame *popped = stack_top;      #ifdef PIKE_DEBUG -  +  { +  void *data = popped->data; +  struct marker *m = find_marker (data);    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)) -  gc_fatal(a, 0, "Marker being popped doesn't have GC_CYCLE_CHECKED.\n"); +  gc_fatal (data, 0, "Marker being popped doesn't have GC_CYCLE_CHECKED.\n");    if (!gc_destruct_everything) {    if ((!(m->flags & GC_NOT_REFERENCED) || m->flags & GC_MARKED) && -  *(INT32 *) a) -  gc_fatal(a, 1, "Got a referenced marker to gc_cycle_pop.\n"); +  *(INT32 *) data) +  gc_fatal (data, 1, "Got a referenced marker to gc_cycle_pop.\n");    if (m->flags & GC_XREFERENCED) -  gc_fatal(a, 1, "Doing cycle check in externally referenced thing " +  gc_fatal (data, 1, "Doing cycle check in externally referenced thing "    "missed in mark pass.\n");    } -  +  if (popped->next != (struct gc_rec_frame *) (ptrdiff_t) -1) +  gc_fatal (data, 0, "Popped rec frame got stuff in the next pointer.\n"); +  }   #endif - #ifdef GC_CYCLE_DEBUG -  gc_cycle_indent -= 2; +  +  stack_top = popped->prev; + #ifdef PIKE_DEBUG +  if (stack_top != &sentinel_frame) CHECK_REC_STACK_FRAME (stack_top); +  CHECK_REC_STACK_FRAME (popped); +  popped->prev = NULL;   #endif    -  if (m->flags & GC_LIVE_RECURSE) { -  m->flags &= ~GC_LIVE_RECURSE; -  CYCLE_DEBUG_MSG(m, "gc_cycle_pop_live"); -  FREE_POP_FRAME (STACK2POP (stack_top)); -  /* stack_top and stack_top_pop updated by caller. */ -  return; -  } +  if (popped->cycle_id != popped) { +  /* Part of a cycle that extends further back - move to the cycle +  * piece list of the previous frame. */ +  struct gc_rec_frame *this_list_last = +  popped->cycle_piece ? popped->cycle_piece->u.last_cycle_piece : popped; + #ifdef PIKE_DEBUG +  if (this_list_last->cycle_piece) +  gc_fatal (this_list_last->data, 0, +  "This frame should be last on the cycle piece list.\n"); +  popped->rf_flags |= GC_ON_CYCLE_PIECE_LIST; +  CHECK_CYCLE_PIECE_FRAME (this_list_last); + #endif +  CYCLE_DEBUG_MSG (popped, "gc_cycle_pop, keep cycle piece");    -  here = m->frame; +  if (!stack_top->cycle_piece) +  popped->u.last_cycle_piece = this_list_last; +  else { +  /* Link in the popped frame and its cycle piece list before +  * the one that the previous frame has. */ +  struct gc_rec_frame *up_list_first = stack_top->cycle_piece; +  struct gc_rec_frame *up_list_last = up_list_first->u.last_cycle_piece;   #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(list_end); -  if (here->frameflags & GC_OFF_STACK) -  gc_fatal(a, 0, "Marker being popped isn't on stack.\n"); -  here->s_prev = (struct gc_stack_frame *)(ptrdiff_t) -1; +  CHECK_CYCLE_PIECE_FRAME (up_list_last); +  if (up_list_last->cycle_piece) +  gc_fatal (up_list_last->data, 0, +  "This frame should be last on the cycle piece list.\n");   #endif -  here->frameflags |= GC_OFF_STACK; +  CYCLE_DEBUG_MSG (up_list_first, "> gc_cycle_pop, inserted before"); +  this_list_last->cycle_piece = up_list_first; +  popped->u.last_cycle_piece = up_list_last; +  } +  stack_top->cycle_piece = popped; +  popped->cycle_id = stack_top;    -  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 the frame list. */ -  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_CYCLE_PIECE_FRAME (popped); +  CHECK_REC_STACK_FRAME (stack_top);    } -  CHECK_POP_FRAME(p); -  if (!(p->cycle && p->cycle == base->cycle)) -  break; +  +  else { +  /* Free or move to the kill list the popped frame and its cycle +  * piece list. */ +  struct gc_rec_frame **kill_list_ptr = &kill_list; +  struct gc_rec_frame *cycle_id = NULL; +  + #ifdef PIKE_DEBUG +  { +  struct gc_rec_frame *r; +  for (r = popped->cycle_piece; r; r = r->cycle_piece) +  /* Can't do this while the list is being freed below. */ +  CHECK_CYCLE_PIECE_FRAME (r);    } -  + #endif    -  list_end = base; -  while ((p = base->next)) { -  struct marker *pm = find_marker(p->data); +  CYCLE_DEBUG_MSG (popped, "gc_cycle_pop, popping cycle"); +  +  do { +  struct marker *m = find_marker (popped->data); +  struct gc_rec_frame *next = popped->cycle_piece; +  +  if (m->flags & GC_LIVE_OBJ) { +  /* Move to the kill list. */   #ifdef PIKE_DEBUG -  if (pm->frame != p) -  gc_fatal(p->data, 0, "Bogus marker for thing being popped.\n"); +  popped->rf_flags &= ~GC_ON_CYCLE_PIECE_LIST; +  popped->cycle_piece = popped->u.last_cycle_piece = +  (struct gc_rec_frame *) (ptrdiff_t) -1;   #endif -  p->frameflags &= ~(GC_PREV_WEAK|GC_PREV_STRONG); -  if (pm->flags & GC_LIVE_OBJ) { +  popped->next = *kill_list_ptr; +  *kill_list_ptr = popped; +  kill_list_ptr = &popped->next; +  popped->rf_flags |= GC_ON_KILL_LIST; +  +  /* Ensure that the frames on the kill list have a valid +  * cycle id frame and that every frame is linked directly to +  * it. This is only for the sake of warn_bad_cycles. */ +  if (!cycle_id) cycle_id = popped; +  popped->cycle_id = cycle_id; +     /* 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(p->prev = (struct gc_pop_frame *)(ptrdiff_t) -1); -  CYCLE_DEBUG_MSG(pm, "gc_cycle_pop, put on kill list"); +  if (!(m->flags & GC_GOT_DEAD_REF)) +  gc_add_extra_ref (popped->data); +  +  CHECK_KILL_LIST_FRAME (popped); +  CYCLE_DEBUG_MSG (popped, "> gc_cycle_pop, move to kill list");    } -  +     else { -  if (!(pm->flags & GC_LIVE)) { +  if (!(m->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; +  if (!(m->flags & GC_GOT_DEAD_REF)) { +  gc_add_extra_ref (popped->data); +  m->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"); +  if (m->flags & GC_GOT_DEAD_REF) +  gc_fatal (popped->data, 0, "Didn't expect a dead extra ref.\n");   #endif -  base->next = p->next; -  CYCLE_DEBUG_MSG(pm, "gc_cycle_pop, pop off"); -  pm->frame = 0; -  FREE_POP_FRAME (p); +  +  CYCLE_DEBUG_MSG (popped, "> gc_cycle_pop, free"); +  m->frame = NULL; +  really_free_gc_rec_frame (popped);    } -  } +     -  if (base != list_end) { -  base->next = kill_list; -  kill_list = list_end->next; -  list_end->next = 0; +  popped = next; +  } while (popped);    } -  +  }    -  /* stack_top and stack_top_pop updated by caller. */ + #ifdef PIKE_DEBUG +  stack_top->next = (struct gc_rec_frame *) (ptrdiff_t) -1; + #endif   }      void do_gc_recurse_svalues(struct svalue *s, int num)
2827:       SET_ONERROR(tmp, free_obj_arr, obj_arr_);    + #if 0    {    struct gc_pop_frame *p;    unsigned cycle = 0;
2854:    if (!p) break;    }    } + #endif       CALL_AND_UNSET_ONERROR(tmp);   }
2908:    }       objs=num_objects; -  last_cycle = 0; +        if(GC_VERBOSE_DO(1 ||) gc_trace) {    if (gc_destruct_everything)
2931: Inside #if defined(PIKE_DEBUG)
     #ifdef PIKE_DEBUG    delayed_freed = weak_freed = checked = marked = cycle_checked = live_ref = 0; -  live_rec = frame_rot = link_search = 0; +  mark_live = frame_rot = link_search = 0;   #endif -  +  rec_frames = link_frames = free_extra_frames = 0; +  max_rec_frames = max_link_frames = 0;    if (gc_debug) {    unsigned n;    Pike_in_gc = GC_PASS_PRETOUCH;
3024: Inside #if defined(PIKE_DEBUG)
  #ifdef PIKE_DEBUG    size_t orig_ext_weak_refs = gc_ext_weak_refs;    obj_count = delayed_freed; -  max_gc_stack_frames = 0; +    #endif    Pike_in_gc=GC_PASS_CYCLE;   
3046:   #endif      #ifdef PIKE_DEBUG -  if (stack_top != POP2STACK (&sentinel_frame)) +  if (stack_top != &sentinel_frame)    Pike_fatal("Frame stack not empty at end of cycle check pass.\n"); -  if (sentinel_frame.next || list_end != &sentinel_frame) -  Pike_fatal("Frame 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",
3057:   #endif       GC_VERBOSE_DO(fprintf(stderr, -  "| cycle: %u internal things visited, %u cycle ids used,\n" +  "| cycle: %u internal things visited,\n"    "| %u weak references freed, %d more things to free,\n" -  "| %u live recursed frames, %u frame rotations,\n" -  "| %u links searched, space for %u gc frames used\n", -  cycle_checked, last_cycle, weak_freed, -  delayed_freed - obj_count, -  live_rec, frame_rot, link_search, max_gc_stack_frames)); +  "| %u mark live visits, %u frame rotations,\n" +  "| %u links searched, used max %u link frames,\n" +  "| %u rec frames and %u free extra frames\n", +  cycle_checked, weak_freed, delayed_freed - obj_count, +  mark_live, frame_rot, link_search, max_link_frames, +  max_rec_frames, free_extra_frames)); +  + #ifdef PIKE_DEBUG +  if (link_frames) fatal ("Leaked %u link frames.\n", link_frames); + #endif    }       if (gc_ext_weak_refs) {
3109:   #endif    if (n != (unsigned) num_objects)    Pike_fatal("Object count wrong in gc; expected %d, got %d.\n", num_objects, n); -  get_marker(sentinel_frame.data)->flags |= GC_MIDDLETOUCHED; +    #if 0 /* Temporarily disabled - Hubbe */   #ifdef PIKE_DEBUG   #ifdef DEBUG_MALLOC
3158:    if (gc_internal_program)    unreferenced += gc_free_all_unreferenced_programs();    +  if (free_extra_frames > tot_max_free_extra_frames) +  tot_max_free_extra_frames = free_extra_frames; +    #if 0   #ifdef DEBUG_MALLOC    unreferenced += gc_free_all_unreferenced_types();
3167:    /* 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_free_extra_frame *next = free_extra_list->fe_next; +  struct free_extra_frame *next = free_extra_list->next;    union anything u;    u.refs = (INT32 *) free_extra_list->data;    gc_free_extra_ref (u.refs);    free_short_svalue (&u, free_extra_list->type); -  FREE_FREE_EXTRA_FRAME (free_extra_list); +  really_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", + #ifdef PIKE_DEBUG +  if (free_extra_frames) fatal ("Leaked %u free extra frames.\n", free_extra_frames); + #endif +  +  GC_VERBOSE_DO(fprintf(stderr, "| free: %"PRINTSIZET"u unreferenced, " +  "%d really freed, %u left with live references\n",    unreferenced, obj_count - num_objects, live_ref));      #ifdef PIKE_DEBUG
3191:   #endif       Pike_in_gc=GC_PASS_KILL; +     /* Destruct the live objects in cycles, but first warn about any bad    * cycles. */    pre_kill_objs = num_objects; -  if (last_cycle && Pike_interpreter.evaluator_stack && -  !gc_destruct_everything) { +  if (Pike_interpreter.evaluator_stack && !gc_destruct_everything) {    objs -= num_objects;    warn_bad_cycles();    objs += num_objects;
3203: 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_pop_frame *next = kill_list->next; +  + #ifdef PIKE_DEBUG +  { +  struct gc_rec_frame *r; +  for (r = kill_list; r != &sentinel_frame; r = r->next) +  /* Can't do this while the list is being freed below. */ +  CHECK_KILL_LIST_FRAME (r); +  } + #endif +  +  while (kill_list != &sentinel_frame) { +  struct gc_rec_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");
3224: Inside #if defined(PIKE_DEBUG)
   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) {
3234:    }    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 -  FREE_POP_FRAME (kill_list); +  really_free_gc_rec_frame (kill_list);    kill_list = next;    }    }    -  GC_VERBOSE_DO(fprintf(stderr, "| kill: %u objects killed, %d things really freed\n", + #ifdef PIKE_DEBUG +  if (rec_frames) fatal ("Leaked %u rec frames.\n", rec_frames); + #endif +  +  GC_VERBOSE_DO(fprintf(stderr, "| kill: %u objects killed, " +  "%"PRINTSIZET"u things really freed\n",    destroy_count, pre_kill_objs - num_objects));       Pike_in_gc=GC_PASS_DESTRUCT;
3418: Inside #if defined(PIKE_DEBUG)
     #ifdef PIKE_DEBUG    UNSET_ONERROR (uwp); -  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; +  tot_mark_live += mark_live, tot_frame_rot += frame_rot;   #endif -  +  if (max_rec_frames > tot_max_rec_frames) +  tot_max_rec_frames = max_rec_frames; +  if (max_link_frames > tot_max_link_frames) +  tot_max_link_frames = max_link_frames;      #ifdef ALWAYS_GC    ADD_GC_CALLBACK();
3538:    push_int64(last_gc);    size++;    + #ifdef PIKE_DEBUG +  push_constant_text ("max_rec_frames"); +  push_int64 (DO_NOT_WARN ((INT64) tot_max_rec_frames)); +  +  push_constant_text ("max_link_frames"); +  push_int64 (DO_NOT_WARN ((INT64) tot_max_link_frames)); +  +  push_constant_text ("max_free_extra_frames"); +  push_int64 (DO_NOT_WARN ((INT64) tot_max_free_extra_frames)); + #endif +     f_aggregate_mapping(size * 2);   }   
3565:    "???");      #ifdef PIKE_DEBUG -  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,"Max used recursion frames : %u\n", tot_max_rec_frames); +  fprintf(stderr,"Max used link frames : %u\n", tot_max_link_frames); +  fprintf(stderr,"Max used free extra frames : %u\n", tot_max_free_extra_frames); +  fprintf(stderr,"Marked live ratio : %g\n", +  (double) tot_mark_live / tot_cycle_checked);    fprintf(stderr,"Frame rotation ratio : %g\n",    (double) tot_frame_rot / tot_cycle_checked);   #endif