pike.git / src / gc.c

version» Context lines:

pike.git/src/gc.c:7:   #include "global.h"      struct callback *gc_evaluator_callback=0;      #include "array.h"   #include "multiset.h"   #include "mapping.h"   #include "object.h"   #include "program.h"   #include "stralloc.h" - #include "stuff.h" +    #include "pike_error.h"   #include "pike_memory.h"   #include "pike_macros.h"   #include "pike_rusage.h"   #include "pike_types.h"   #include "time_stuff.h"   #include "constants.h"   #include "interpret.h"   #include "bignum.h"   #include "pike_threadlib.h"   #include "gc.h"   #include "main.h" -  + #include "builtin_functions.h"   #include "block_allocator.h"      #include <math.h>      #include "block_alloc.h"      int gc_enabled = 1;      /* These defaults are only guesses and hardly tested at all. Please improve. */   double gc_garbage_ratio_low = 0.2;
pike.git/src/gc.c:55:   struct svalue gc_post_cb;      /* Callback called for each object that is to be destructed explicitly    * by the gc().    */   struct svalue gc_destruct_cb;      /* Callback called when the gc() is about to exit. */   struct svalue gc_done_cb;    + /* The gc has the following passes: +  * +  * GC_PASS_PREPARE +  * +  * GC_PASS_PRETOUCH +  * Debug. +  * +  * GC_PASS_CHECK +  * Counts all internal references (strong, normal and weak) +  * by calling gc_check() or gc_check_weak() (and thus +  * real_gc_check() or real_gc_check_weak()). They increase +  * the reference counters in the marker. +  * +  * GC_PASS_MARK +  * Recursively mark objects that have more references than +  * internal references for keeping. Sets the flag +  * GC_MARKED and clears the flag GC_NOT_REFERENCED +  * in the marker. +  * +  * GC_PASS_CYCLE +  * Identify cycles in the unmarked objects. +  * +  * GC_PASS_ZAP_WEAK +  * Zap weak references to unmarked objects. +  * +  * GC_PASS_POSTTOUCH +  * Debug. +  * +  * GC_PASS_FREE +  * Free remaining unmarked objects. +  * +  * GC_PASS_KILL +  * Destruct remaining unmarked live (lfun::_destruct()) objects. +  * +  * GC_PASS_DESTRUCT +  * Destruct objects to destruct. +  * +  * And the following simulated passes: +  * +  * GC_PASS_LOCATE +  * +  * GC_PASS_DISABLED +  */ +    /* The gc will free all things with no external nonweak references    * that isn't referenced by live objects. An object is considered    * "live" if it contains code that must be executed when it is    * destructed; see gc_object_is_live for details. Live objects without    * external references are then destructed and garbage collected with    * normal refcount garbing (which might leave dead garbage around for    * the next gc). These live objects are destructed in an order that    * tries to be as well defined as possible using several rules:    *    * o If an object A references B single way, then A is destructed
pike.git/src/gc.c:98:    * to save them by adding external references to them. However, it's    * not possible for live objects to save themselves or other live    * objects; all live objects that didn't have external references at    * the start of the gc pass will be destructed regardless of added    * references.    *    * Things that have only weak external references at the start of the    * gc pass will be freed. That's done before the live object destruct    * pass. Internal weak references are however still intact.    * -  * Note: Keep the doc for lfun::destroy up-to-date with the above. +  * Note: Keep the doc for lfun::_destruct up-to-date with the above.    */      /* #define GC_DEBUG */   /* #define GC_VERBOSE */   /* #define GC_CYCLE_DEBUG */   /* #define GC_STACK_DEBUG */   /* #define GC_INTERVAL_DEBUG */      #if defined(GC_VERBOSE) && !defined(PIKE_DEBUG)   #undef GC_VERBOSE
pike.git/src/gc.c:121:   #define GC_VERBOSE_DO(X) X   #else   #define GC_VERBOSE_DO(X)   #endif      int num_objects = 2; /* Account for *_empty_array. */   int got_unlinked_things;   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; + unsigned INT16 gc_generation = 1;   time_t last_gc;   int gc_trace = 0, gc_debug = 0;   #ifdef DO_PIKE_CLEANUP   int gc_destruct_everything = 0;   #endif   size_t gc_ext_weak_refs;      ALLOC_COUNT_TYPE saved_alloc_threshold;   /* Used to backup alloc_threshold if the gc is disabled, so that it    * can be restored when it's enabled again. This is to not affect the
pike.git/src/gc.c:469:    } u;   };      static struct block_allocator ba_mixed_frame_allocator    = BA_INIT_PAGES(sizeof(struct ba_mixed_frame), 2);      void count_memory_in_ba_mixed_frames(size_t *num, size_t * size) {    ba_count_all(&ba_mixed_frame_allocator, num, size);   }    - static INLINE struct link_frame *alloc_link_frame() + static inline struct link_frame *alloc_link_frame()   {    struct ba_mixed_frame *f = ba_alloc(&ba_mixed_frame_allocator);    if (++link_frames > max_link_frames)    max_link_frames = link_frames;    return (struct link_frame *) f;   }    - static INLINE struct free_extra_frame *alloc_free_extra_frame() + static inline struct free_extra_frame *alloc_free_extra_frame()   {    struct ba_mixed_frame *f = ba_alloc(&ba_mixed_frame_allocator);    free_extra_frames++;    return (struct free_extra_frame *) f;   }    - static INLINE void really_free_link_frame (struct link_frame *f) + static inline void really_free_link_frame (struct link_frame *f)   {    link_frames--;    ba_free(&ba_mixed_frame_allocator, f);   }    - static INLINE void really_free_free_extra_frame (struct free_extra_frame *f) + static inline void really_free_free_extra_frame (struct free_extra_frame *f)   {    free_extra_frames--;    ba_free(&ba_mixed_frame_allocator, f);   }      /* These are only collected for the sake of gc_status. */   static double last_garbage_ratio = 0.0;   static enum {    GARBAGE_RATIO_LOW, GARBAGE_RATIO_HIGH, GARBAGE_MAX_INTERVAL   } last_garbage_strategy = GARBAGE_RATIO_LOW;
pike.git/src/gc.c:514:   /* These callbacks are run early in the check pass of the gc and when    * locate_references is called. They are typically used to mark    * external references (using gc_mark_external) for debug purposes. */   struct callback *debug_add_gc_callback(callback_func call,    void *arg,    callback_func free_func)   {    return add_to_callback(&gc_callbacks, call, arg, free_func);   }    - static void init_gc(void); +    static void gc_cycle_pop();    - #undef BLOCK_ALLOC_NEXT - #define BLOCK_ALLOC_NEXT next -  - #undef INIT_BLOCK - #ifdef PIKE_DEBUG - #define INIT_BLOCK(X) \ -  (X)->flags=(X)->refs=(X)->weak_refs=(X)->xrefs=0; \ -  (X)->saved_refs=-1; \ -  (X)->frame = 0; - #else - #define INIT_BLOCK(X) \ -  (X)->flags=(X)->refs=(X)->weak_refs=0; \ -  (X)->frame = 0; - #endif - #undef EXIT_BLOCK - #define EXIT_BLOCK(f) -  - #undef get_marker - #define get_marker debug_get_marker - #undef find_marker - #define find_marker debug_find_marker -  - PTR_HASH_ALLOC_FIXED_FILL_PAGES(marker,2) -  - #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))) -  - PMOD_EXPORT struct marker *pmod_get_marker (void *p) - { -  return debug_get_marker (p); - } -  - PMOD_EXPORT struct marker *pmod_find_marker (void *p) - { -  return debug_find_marker (p); - } -  +    #if defined (PIKE_DEBUG) || defined (GC_MARK_DEBUG)   PMOD_EXPORT void *gc_found_in = NULL;   PMOD_EXPORT int gc_found_in_type = PIKE_T_UNKNOWN;   PMOD_EXPORT const char *gc_found_place = NULL;   #endif      #ifdef DO_PIKE_CLEANUP   /* To keep the markers after the gc. Only used for the leak report at exit. */   int gc_keep_markers = 0;   PMOD_EXPORT int gc_external_refs_zapped = 0;
pike.git/src/gc.c:680: Inside #if defined(PIKE_DEBUG)
   for(mu=first_multiset;mu;mu=mu->next)    if(mu==(struct multiset *)something)    return T_MULTISET;    else if (mu->msd == (struct multiset_data *) something)    return T_MULTISET_DATA;       if(safe_debug_findstring((struct pike_string *)something))    return T_STRING;       if (pike_type_hash) -  for (i = 0; i < pike_type_hash_size; i++) +  for (i = 0; i <= pike_type_hash_size; i++)    for (t = pike_type_hash[i]; t; t = t->next)    if (t == (struct pike_type *) something)    return T_TYPE;       for (c = first_callable; c; c = c->next)    if (c == (struct callable *) something)    return T_STRUCT_CALLABLE;       return PIKE_T_UNKNOWN;   }
pike.git/src/gc.c:713:    * really be printed..    */   void describe_location(void *real_memblock,    int type,    void *location,    int indent,    int depth,    int flags)   {    struct program *p; -  void *memblock=0, *descblock, *inblock; +  void *memblock=0, *descblock, *inblock = NULL;    if(!location) return;   /* fprintf(stderr,"**Location of (short) svalue: %p\n",location); */       if(type!=-1 && real_memblock != (void *) -1) memblock=real_memblock;      #ifdef DEBUG_MALLOC    if(memblock == 0 || type == -1)    {    extern void *dmalloc_find_memblock_base(void *);    memblock=dmalloc_find_memblock_base(location);
pike.git/src/gc.c:890:    }    break;    }       case T_STORAGE:    fprintf(stderr, "%*s **In storage of object\n", indent, "");    break;       case T_MULTISET:    descblock = ((struct multiset *) memblock)->msd; -  /* FALL THROUGH */ +  /* FALLTHRU */       case T_MULTISET_DATA: {    struct multiset_data *msd = (struct multiset_data *) descblock;    union msnode *node = low_multiset_first (msd);    struct svalue ind; -  int indval = msd->flags & MULTISET_INDVAL; +     for (; node; node = low_multiset_next (node)) {    if (&node->i.ind == (struct svalue *) location) {    fprintf (stderr, "%*s **In index ", indent, "");    safe_print_svalue (stderr, low_use_multiset_index (node, ind));    fputc ('\n', stderr);    break;    } -  else if (indval && &node->iv.val == (struct svalue *) location) { -  fprintf(stderr, "%*s **In value with index ", indent, ""); -  safe_print_svalue (stderr, low_use_multiset_index (node, ind)); -  fputc('\n', stderr); -  break; +     } -  } +     break;    }       case T_ARRAY:    {    struct array *a=(struct array *)descblock;    struct svalue *s=(struct svalue *)location;       if(location == (void *)&a->next)    fprintf(stderr,"%*s **In a->next\n",indent,"");
pike.git/src/gc.c:933:    fprintf(stderr,"%*s **In a->prev\n",indent,"");       if( s-ITEM(a) > 0)    fprintf(stderr,"%*s **In index number %"PRINTPTRDIFFT"d\n",indent,"",    s-ITEM(a));    break;    }       case T_MAPPING:    descblock = ((struct mapping *) memblock)->data; -  /* FALL THROUGH */ +  /* FALLTHRU */    case T_MAPPING_DATA: {    INT32 e;    struct keypair *k;    NEW_MAPPING_LOOP((struct mapping_data *) descblock)    if (&k->ind == (struct svalue *) location) {    fprintf(stderr, "%*s **In index ", indent, "");    safe_print_svalue (stderr, &k->ind);    fputc('\n', stderr);    break;    }
pike.git/src/gc.c:986: 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   }    + #ifdef GC_STACK_DEBUG   static void describe_link_frame (struct link_frame *f)   {    fprintf (stderr, "data=%p prev=%p checkfn=%p weak=%d",    f->data, f->prev, f->checkfn, f->weak);   } -  + #endif      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, (long) m->gc_flags, m->gc_refs, m->weak_refs,    m->xrefs, m->saved_refs, m->frame);    if (m->frame) {    fputs(" [", stderr);    describe_rec_frame (m->frame);    putc(']', stderr);    }    putc('\n', stderr);    }    else    fprintf(stderr, "no marker\n");   }      #endif /* PIKE_DEBUG */      static void debug_gc_fatal_va (void *DEBUGUSED(a), int DEBUGUSED(type), int DEBUGUSED(flags),    const char *fmt, va_list args)   {    int orig_gc_pass = Pike_in_gc;    -  (void) VFPRINTF(stderr, fmt, args); +  (void) vfprintf(stderr, fmt, args);      #ifdef PIKE_DEBUG    if (a) { -  void *inblock; +  void *inblock = NULL;    /* Temporarily jumping out of gc to avoid being caught in debug    * checks in describe(). */    Pike_in_gc = 0;    if (type == PIKE_T_UNKNOWN)    type = attempt_to_identify (a, &inblock);    describe_something (a, type, 0, 0, 0, inblock);    if (flags & 1) locate_references(a);    Pike_in_gc = orig_gc_pass;    }   
pike.git/src/gc.c:1058:   }      void debug_gc_fatal_2 (void *a, int type, int flags, const char *fmt, ...)   {    va_list args;    va_start (args, fmt);    debug_gc_fatal_va (a, type, flags, fmt, args);    va_end (args);   }    + #ifdef PIKE_DEBUG +    static void dloc_gc_fatal (const char *file, INT_TYPE line,    void *a, int flags, const char *fmt, ...)   {    va_list args;    fprintf (stderr, "%s:%ld: GC fatal:\n", file, (long)line);    va_start (args, fmt);    debug_gc_fatal_va (a, PIKE_T_UNKNOWN, flags, fmt, args);    va_end (args);   }    - static void rec_stack_fatal (struct gc_rec_frame *err, const char *err_name, -  struct gc_rec_frame *p1, const char *p1n, -  struct gc_rec_frame *p2, const char *p2n, + static void rec_stack_fatal (struct gc_rec_frame *DEBUGUSED(err), +  const char *DEBUGUSED(err_name), +  struct gc_rec_frame *DEBUGUSED(p1), +  const char *DEBUGUSED(p1n), +  struct gc_rec_frame *DEBUGUSED(p2), +  const char *DEBUGUSED(p2n),    const char *file, INT_TYPE line,    const char *fmt, ...)   {    va_list args;    va_start (args, fmt);    fprintf (stderr, msg_fatal_error, file, line); -  (void) VFPRINTF (stderr, fmt, args); +  (void) vfprintf (stderr, fmt, args);   #if defined (PIKE_DEBUG) || defined (GC_CYCLE_DEBUG)    fputs ("Recursion stack:\n", stderr);    describe_rec_stack (err, err_name, p1, p1n, p2, p2n);    if (err) {    fprintf (stderr, "Describing frame %p: ", err);    describe_rec_frame (err);    fputc ('\n', stderr);    }   #endif    d_flag = 0; /* The instruction backlog is never of any use here. */    debug_fatal (NULL);    va_end (args);   }    - #ifdef PIKE_DEBUG -  - static void gdb_gc_stop_here(void *a, int weak) + static void gdb_gc_stop_here(void *UNUSED(a), int weak)   {    found_ref_count++;    fprintf(stderr,"***One %sref found%s. ",    weak ? "weak " : "",    gc_found_place ? gc_found_place : "");    if (gc_found_in) {    if (gc_svalue_location)    describe_location(gc_found_in , gc_found_in_type, gc_svalue_location,0,1,    DESCRIBE_SHORT);    else {
pike.git/src/gc.c:1126: Inside #if defined(PIKE_DEBUG)
   int indent,    int depth,    int flags,    void *inblock)   {    struct program *p=(struct program *)a;    struct marker *m;       if(depth<0) return;    -  if (marker_hash_table && (m = find_marker(a))) { +  if ((m = find_marker(a))) {    fprintf(stderr,"%*s**Got gc ",indent,"");    describe_marker(m);    }      again:    switch(t)    {    case T_STORAGE:    if (!inblock) attempt_to_identify (a, &a);    t = T_OBJECT;    goto again;       case T_FUNCTION:    if(attempt_to_identify(a, 0) != T_OBJECT)    {    fprintf(stderr,"%*s**Builtin function!\n",indent,"");    break;    } -  /* FALL THROUGH */ +  /* FALLTHRU */       case T_OBJECT:    p=((struct object *)a)->prog;    if(p && (p->flags & PROGRAM_USES_PARENT))    {    fprintf(stderr,"%*s**Parent identifier: %d\n",indent,"",PARENT_INFO( ((struct object *)a) )->parent_identifier);    }    fprintf(stderr,"%*s**Program id: %d\n",indent,"",((struct object *)a)->program_id);       if (((struct object *)a)->next == ((struct object *)a))
pike.git/src/gc.c:1647:    get_name_of_type(t),    *(INT32 *)a);       low_describe_something(a,t,indent,depth,flags,inblock);      #ifdef DEBUG_MALLOC    if(!(flags & DESCRIBE_NO_DMALLOC))    debug_malloc_dump_references(a,indent+2,depth-1,flags);   #endif    } + #if defined(USE_VALGRIND) && defined(VALGRIND_MONITOR_COMMAND) +  if (PIKE_MEM_CHECKER()) { +  char buf[40]; +  snprintf(buf, 40, "v.info location %p", a); +  fprintf(stderr, "**Valgrind info:\n"); +  VALGRIND_MONITOR_COMMAND(buf); +  } + #endif    -  +     fprintf(stderr,"%*s*******************\n",indent,"");    d_flag=tmp;   }      PMOD_EXPORT void describe(void *x)   {    void *inblock;    int type = attempt_to_identify(x, &inblock);    describe_something(x, type, 0, 0, 0, inblock);   }    - void debug_describe_svalue(struct svalue *s) + PMOD_EXPORT void debug_describe_svalue(struct svalue *s)   {    fprintf(stderr,"Svalue at %p is:\n",s);    switch(TYPEOF(*s))    {    case T_INT:    fprintf(stderr," %"PRINTPIKEINT"d (subtype %d)\n",s->u.integer,    SUBTYPEOF(*s));    return;       case T_FLOAT:
pike.git/src/gc.c:1697:    }    }else{    fprintf(stderr," Function name: %s\n",    ID_FROM_INT(s->u.object->prog, SUBTYPEOF(*s))->name->str);    }    }    }    describe_something(s->u.refs, TYPEOF(*s), 0, 1, 0, 0);   }    - void gc_watch(void *a) + PMOD_EXPORT void gc_watch(void *a)   {    struct marker *m; -  init_gc(); +     m = get_marker(a); -  if (!(m->flags & GC_WATCHED)) { -  m->flags |= GC_WATCHED; +  if (!(m->gc_flags & GC_WATCHED)) { +  m->gc_flags |= GC_WATCHED;    fprintf(stderr, "## Watching thing %p.\n", a);    gc_is_watching++;    }    else    fprintf(stderr, "## Already watching thing %p.\n", a);   }      static void gc_watched_found (struct marker *m, const char *found_in)   { -  fprintf(stderr, "## Watched thing %p with %d refs found in " -  "%s in pass %d.\n", m->data, *(INT32 *) m->data, found_in, Pike_in_gc); +  fprintf(stderr, "## Watched thing found in " +  "%s in pass %d.\n", found_in, Pike_in_gc);    describe_marker (m);   }      #endif /* PIKE_DEBUG */      #ifndef GC_MARK_DEBUG   struct pike_queue gc_mark_queue;   #define CHECK_MARK_QUEUE_EMPTY() assert (!gc_mark_queue.first)   #else /* GC_MARK_DEBUG */   
pike.git/src/gc.c:1749:   {    struct gc_queue_block *next;    int used;    struct gc_queue_entry entries[GC_QUEUE_ENTRIES];   };      struct gc_queue_block *gc_mark_first = NULL, *gc_mark_last = NULL;      #define CHECK_MARK_QUEUE_EMPTY() assert (!gc_mark_first)    - void gc_mark_run_queue() + void gc_mark_run_queue(void)   {    struct gc_queue_block *b;       while((b=gc_mark_first))    {    int e;    for(e=0;e<b->used;e++)    {    debug_malloc_touch(b->entries[e].data);    b->entries[e].call(b->entries[e].data);    }       gc_mark_first=b->next;    free((char *)b);    }    gc_mark_last=0;   }    - void gc_mark_discard_queue() + void gc_mark_discard_queue(void)   {    struct gc_queue_block *b = gc_mark_first;    while (b)    {    struct gc_queue_block *next = b->next;    free((char *) b);    b = next;    }    gc_mark_first = gc_mark_last = 0;   }
pike.git/src/gc.c:1791:   {    struct gc_queue_block *b;      #ifdef PIKE_DEBUG    if (Pike_in_gc != GC_PASS_MARK && Pike_in_gc != GC_PASS_ZAP_WEAK)    gc_fatal (data, 0, "gc_mark_enqueue() called in invalid gc pass.\n");    if (gc_found_in_type == PIKE_T_UNKNOWN || !gc_found_in)    gc_fatal (data, 0, "gc_mark_enqueue() called outside GC_ENTER.\n");    {    struct marker *m; -  if (gc_is_watching && (m = find_marker(data)) && m->flags & GC_WATCHED) { +  if (gc_is_watching && (m = find_marker(data)) && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_mark_enqueue()");    }    }   #endif       b=gc_mark_last;    if(!b || b->used >= GC_QUEUE_ENTRIES)    {    b = (struct gc_queue_block *) malloc (sizeof (struct gc_queue_block)); -  if (!b) fatal ("Out of memory in gc.\n"); +  if (!b) Pike_fatal ("Out of memory in gc.\n");    b->used=0;    b->next=0;    if(gc_mark_first)    gc_mark_last->next=b;    else    gc_mark_first=b;    gc_mark_last=b;    }       b->entries[b->used].call=call;
pike.git/src/gc.c:1827:    b->used++;   }      #endif /* GC_MARK_DEBUG */      void debug_gc_touch(void *a)   {    struct marker *m;      #ifdef PIKE_DEBUG -  if (gc_is_watching && (m = find_marker(a)) && m->flags & GC_WATCHED) { +  if (!a) Pike_fatal("Got null pointer.\n"); +  if (gc_is_watching && (m = find_marker(a)) && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_touch()");    }   #endif    -  if (!a) Pike_fatal("Got null pointer.\n"); -  +     switch (Pike_in_gc) {    case GC_PASS_PRETOUCH:    m = find_marker(a); -  + #ifdef PIKE_DEBUG    if (   #ifdef DO_PIKE_CLEANUP    !gc_keep_markers &&   #endif -  m && !(m->flags & (GC_PRETOUCHED - #ifdef PIKE_DEBUG -  |GC_WATCHED - #endif -  ))) +  m && !(m->gc_flags & (GC_PRETOUCHED|GC_WATCHED )))    gc_fatal(a, 1, "Thing got an existing but untouched marker.\n"); -  + #endif /* PIKE_DEBUG */    m = get_marker(a); -  m->flags |= GC_PRETOUCHED; +  m->gc_flags |= GC_PRETOUCHED;   #ifdef PIKE_DEBUG    m->saved_refs = *(INT32 *) a;   #endif    break;       case GC_PASS_POSTTOUCH: {   #ifdef PIKE_DEBUG    int extra_ref;   #endif    m = find_marker(a); -  + #ifdef PIKE_DEBUG    if (!m)    gc_fatal(a, 1, "Found a thing without marker.\n"); -  else if (!(m->flags & GC_PRETOUCHED)) +  else if (!(m->gc_flags & GC_PRETOUCHED))    gc_fatal(a, 1, "Thing got an existing but untouched marker.\n"); -  if (gc_destruct_everything && (m->flags & GC_MARKED)) +  if (gc_destruct_everything && (m->gc_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; +  extra_ref = (m->gc_flags & GC_GOT_EXTRA_REF) == GC_GOT_EXTRA_REF;    if (m->saved_refs + extra_ref < *(INT32 *) a) -  if (m->flags & GC_WEAK_FREED) +  if (m->gc_flags & GC_WEAK_FREED)    gc_fatal(a, 1, "Something failed to remove weak reference(s) to thing, "    "or it has gotten more references since gc start.\n");    else    gc_fatal(a, 1, "Thing has gotten more references since gc start.\n");    else    if (m->weak_refs > m->saved_refs)    gc_fatal(a, 0, "A thing got more weak references than references.\n");   #endif -  m->flags |= GC_POSTTOUCHED; +  m->gc_flags |= GC_POSTTOUCHED;    break;    }       default:    Pike_fatal("debug_gc_touch() used in invalid gc pass.\n");    }   }      #ifdef PIKE_DEBUG    - static INLINE struct marker *gc_check_debug(void *a, int weak) + static inline struct marker *gc_check_debug(void *a, int weak)   {    struct marker *m;       if (!a) Pike_fatal("Got null pointer.\n");    if(Pike_in_gc == GC_PASS_LOCATE)    {    if(check_for == a)    {    gdb_gc_stop_here(a, weak);    }
pike.git/src/gc.c:1919: Inside #if defined(PIKE_DEBUG)
   if (Pike_in_gc != GC_PASS_CHECK)    Pike_fatal("gc check attempted in invalid pass.\n");       m = get_marker(a);       if (!*(INT32 *)a)    gc_fatal(a, 1, "GC check on thing without refs.\n");    if (m->saved_refs == -1) m->saved_refs = *(INT32 *)a;    else if (m->saved_refs != *(INT32 *)a)    gc_fatal(a, 1, "Refs changed in gc check pass.\n"); -  if (m->refs + m->xrefs >= *(INT32 *) a) -  /* m->refs will be incremented by the caller. */ +  if (m->gc_refs + m->xrefs >= *(INT32 *) a) +  /* m->gc_refs will be incremented by the caller. */    gc_fatal (a, 1, "Thing is getting more internal refs (%d + %d) "    "than refs (%d).\n"    "(Could be an extra free somewhere, or "    "a pointer might have been checked more than once.)\n", -  m->refs, m->xrefs, *(INT32 *) a); +  m->gc_refs, m->xrefs, *(INT32 *) a);    checked++;       return m;   }      #endif /* PIKE_DEBUG */      PMOD_EXPORT INT32 real_gc_check(void *a)   {    struct marker *m;    INT32 ret;      #ifdef PIKE_DEBUG    if (gc_found_in_type == PIKE_T_UNKNOWN || !gc_found_in)    gc_fatal (a, 0, "gc_check() called outside GC_ENTER.\n"); -  if (gc_is_watching && (m = find_marker(a)) && m->flags & GC_WATCHED) { +  if (gc_is_watching && (m = find_marker(a)) && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_check()");    }    if (!(m = gc_check_debug(a, 0))) return 0;   #else    m = get_marker(a);   #endif    -  ret=m->refs; -  add_ref(m); -  if (m->refs == *(INT32 *) a) -  m->flags |= GC_NOT_REFERENCED; +  ret=m->gc_refs++; +  if (m->gc_refs == *(INT32 *) a) +  m->gc_flags |= GC_NOT_REFERENCED;    return ret;   }      PMOD_EXPORT INT32 real_gc_check_weak(void *a)   {    struct marker *m;    INT32 ret;      #ifdef PIKE_DEBUG    if (gc_found_in_type == PIKE_T_UNKNOWN || !gc_found_in)    gc_fatal (a, 0, "gc_check_weak() called outside GC_ENTER.\n"); -  if (gc_is_watching && (m = find_marker(a)) && m->flags & GC_WATCHED) { +  if (gc_is_watching && (m = find_marker(a)) && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_check_weak()");    }    if (!(m = gc_check_debug(a, 1))) return 0;    if (m->weak_refs < 0)    gc_fatal(a, 1, "Thing has already reached threshold for weak free.\n");    if (m->weak_refs >= *(INT32 *) a)    gc_fatal(a, 1, "Thing has gotten more weak refs than refs.\n"); -  if (m->weak_refs > m->refs + 1) +  if (m->weak_refs > m->gc_refs + 1)    gc_fatal(a, 1, "Thing has gotten more weak refs than internal refs.\n");   #else    m = get_marker(a);   #endif       m->weak_refs++;    gc_ext_weak_refs++;    if (m->weak_refs == *(INT32 *) a)    m->weak_refs = -1;    -  ret=m->refs; -  add_ref(m); -  if (m->refs == *(INT32 *) a) -  m->flags |= GC_NOT_REFERENCED; +  ret=m->gc_refs++; +  if (m->gc_refs == *(INT32 *) a) +  m->gc_flags |= GC_NOT_REFERENCED;    return ret;   }      static void cleanup_markers (void)   { - #ifdef DO_PIKE_CLEANUP -  size_t e=0; -  -  if (gc_keep_markers) { -  /* Carry over any GC_CLEANUP_LEAKED flags but reinitialize them -  * otherwise. */ -  for(e=0;e<marker_hash_table_size;e++) { -  struct marker *m; -  for (m = marker_hash_table[e]; m; m = m->next) { - #ifdef PIKE_DEBUG -  m->flags &= GC_CLEANUP_LEAKED; -  m->xrefs = 0; -  m->saved_refs = -1; - #else -  m->flags = 0; - #endif -  m->refs = m->weak_refs = 0; -  m->frame = 0; +    } -  } -  return; -  } +     -  for(e=0;e<marker_hash_table_size;e++) -  while(marker_hash_table[e]) -  remove_marker(marker_hash_table[e]->data); - #endif -  exit_marker_hash(); - } -  - static void init_gc(void) - { - #ifdef PIKE_DEBUG -  if (!gc_is_watching) { - #endif - #if defined (PIKE_DEBUG) || defined (DO_PIKE_CLEANUP) -  /* The marker hash table is left around after a previous gc if -  * gc_keep_markers is set. */ -  if (marker_hash_table) cleanup_markers(); -  if (!marker_hash_table) - #endif -  low_init_marker_hash(num_objects); - #ifdef PIKE_DEBUG -  } - #endif - } -  +    void exit_gc(void)   {    if (gc_evaluator_callback) {    remove_callback(gc_evaluator_callback);    gc_evaluator_callback = NULL;    }    if (!gc_keep_markers)    cleanup_markers();       ba_free_all(&gc_rec_frame_allocator);
pike.git/src/gc.c:2067: Inside #if defined(PIKE_DEBUG)
   gc_is_watching = 0;    }   #endif   }      #ifdef PIKE_DEBUG      PMOD_EXPORT void gc_check_zapped (void *a, TYPE_T type, const char *file, INT_TYPE line)   {    struct marker *m = find_marker (a); -  if (m && (m->flags & GC_CLEANUP_LEAKED)) +  if (m && (m->gc_flags & GC_CLEANUP_LEAKED))    fprintf (stderr, "Free of leaked %s %p from %s:%ld, %d refs remaining\n",    get_name_of_type (type), a, file, (long)line, *(INT32 *)a - 1);   }      /* This function marks some known externals. The rest are handled by    * callbacks added with add_gc_callback. */   static void mark_externals (void)   {    struct mapping *constants;    if (master_object)    gc_mark_external (master_object, " as master_object");    if ((constants = get_builtin_constants()))    gc_mark_external (constants, " as global constants mapping");   }    - void locate_references(void *a) + PMOD_EXPORT void locate_references(void *a)   {    int tmp, orig_in_gc = Pike_in_gc;    const char *orig_gc_found_place = gc_found_place; -  int i=0; -  if(!marker_hash_table) -  { -  i=1; -  init_gc(); -  } +     Pike_in_gc = GC_PASS_LOCATE;    gc_found_place = NULL;       /* Disable debug, this may help reduce recursion bugs */    tmp=d_flag;    d_flag=0;       fprintf(stderr,"**Looking for references to %p:\n", a);       check_for=a;
pike.git/src/gc.c:2132: Inside #if defined(PIKE_DEBUG) and #if defined(DEBUG_MALLOC)
   dmalloc_find_references_to(a);   #endif    }   #endif       fprintf(stderr,"**Done looking for references to %p, "    "found %"PRINTSIZET"u refs.\n", a, found_ref_count);       Pike_in_gc = orig_in_gc;    gc_found_place = orig_gc_found_place; -  if(i) exit_gc(); +     d_flag=tmp;   }      void debug_gc_add_extra_ref(void *a)   {    struct marker *m;    -  if (gc_is_watching && (m = find_marker(a)) && m->flags & GC_WATCHED) { +  if (gc_is_watching && (m = find_marker(a)) && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_add_extra_ref()");    }       if (gc_debug) {    m = find_marker(a); -  if ((!m || !(m->flags & GC_PRETOUCHED)) && +  if ((!m || !(m->gc_flags & GC_PRETOUCHED)) &&    !safe_debug_findstring((struct pike_string *) a))    gc_fatal(a, 0, "Doing gc_add_extra_ref() on invalid object.\n");    if (!m) m = get_marker(a);    }    else m = get_marker(a);    -  if (m->flags & GC_GOT_EXTRA_REF) +  if (m->gc_flags & GC_GOT_EXTRA_REF)    gc_fatal(a, 0, "Thing already got an extra gc ref.\n"); -  m->flags |= GC_GOT_EXTRA_REF; +  m->gc_flags |= GC_GOT_EXTRA_REF;    gc_extra_refs++;    add_ref( (struct ref_dummy *)a);   }      void debug_gc_free_extra_ref(void *a)   {    struct marker *m;    -  if (gc_is_watching && (m = find_marker(a)) && m->flags & GC_WATCHED) { +  if (gc_is_watching && (m = find_marker(a)) && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_free_extra_ref()");    }       if (gc_debug) {    m = find_marker(a); -  if ((!m || !(m->flags & GC_PRETOUCHED)) && +  if ((!m || !(m->gc_flags & GC_PRETOUCHED)) &&    !safe_debug_findstring((struct pike_string *) a))    gc_fatal(a, 0, "Doing gc_add_extra_ref() on invalid object.\n");    if (!m) m = get_marker(a);    }    else m = get_marker(a);    -  if (!(m->flags & GC_GOT_EXTRA_REF)) +  if (!(m->gc_flags & GC_GOT_EXTRA_REF))    gc_fatal(a, 0, "Thing haven't got an extra gc ref.\n"); -  m->flags &= ~GC_GOT_EXTRA_REF; +  m->gc_flags &= ~GC_GOT_EXTRA_REF;    gc_extra_refs--;   }         int debug_gc_is_referenced(void *a)   {    struct marker *m;    -  if (gc_is_watching && (m = find_marker(a)) && m->flags & GC_WATCHED) { +  if (gc_is_watching && (m = find_marker(a)) && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_is_referenced()");    }       if (!a) Pike_fatal("Got null pointer.\n");    if (Pike_in_gc != GC_PASS_MARK)    Pike_fatal("gc_is_referenced() called in invalid gc pass.\n");       if (gc_debug) {    m = find_marker(a); -  if ((!m || !(m->flags & GC_PRETOUCHED)) && +  if ((!m || !(m->gc_flags & GC_PRETOUCHED)) &&    !safe_debug_findstring((struct pike_string *) a))    gc_fatal(a, 0, "Doing gc_is_referenced() on invalid object.\n");    if (!m) m = get_marker(a);    }    else m = get_marker(a);    -  if (m->flags & GC_IS_REFERENCED) +  if (m->gc_flags & GC_IS_REFERENCED)    gc_fatal(a, 0, "gc_is_referenced() called twice for thing.\n"); -  m->flags |= GC_IS_REFERENCED; +  m->gc_flags |= GC_IS_REFERENCED;    -  return !(m->flags & GC_NOT_REFERENCED); +  return !(m->gc_flags & GC_NOT_REFERENCED);   }      int gc_mark_external (void *a, const char *place)   {    struct marker *m;    -  if (gc_is_watching && (m = find_marker(a)) && m->flags & GC_WATCHED) { +  if (gc_is_watching && (m = find_marker(a)) && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_mark_external()");    }       if (!a) Pike_fatal("Got null pointer.\n");       if(Pike_in_gc == GC_PASS_LOCATE)    {    if(a==check_for) {    const char *orig_gc_found_place = gc_found_place;
pike.git/src/gc.c:2251: Inside #if defined(PIKE_DEBUG) and #if defined(DEBUG_MALLOC)
   if (gc_external_refs_zapped) {    fprintf (stderr, "One external ref to %p found%s.\n",    a, place ? place : "");    if (gc_found_in) describe (gc_found_in);    return 0;    }   #endif       m=get_marker(a);    m->xrefs++; -  m->flags|=GC_XREFERENCED; +  m->gc_flags|=GC_XREFERENCED;    if(Pike_in_gc == GC_PASS_CHECK && -  (m->refs + m->xrefs > *(INT32 *)a || +  (m->gc_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;   }      #define LOW_CHECK_REC_FRAME(f, file, line) do { \    if (f->rf_flags & GC_FRAME_FREED) \    dloc_gc_fatal (file, line, f->data, 0, \    "Accessing freed gc_stack_frame %p.\n", f); \    if (f->cycle_id->rf_flags & GC_FRAME_FREED) { \
pike.git/src/gc.c:2425:   #define CHECK_CYCLE_PIECE_FRAME(f) do {} while (0)   #define CHECK_KILL_LIST_FRAME(f) do {} while (0)   #define CHECK_REC_STACK(p1, p1n, p2, p2n) do {} while (0)   #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) { +  if (gc_is_watching && (m = find_marker(a)) && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_do_weak_free()");    }    if (!a) Pike_fatal("Got null pointer.\n");    if (Pike_in_gc != GC_PASS_MARK && Pike_in_gc != GC_PASS_ZAP_WEAK)    Pike_fatal("gc_do_weak_free() called in invalid gc pass.\n");    if (gc_debug) {    if (!(m = find_marker(a)))    gc_fatal(a, 0, "gc_do_weak_free() got unknown object.\n");    }    else m = get_marker(a);    debug_malloc_touch(a);    -  if (m->weak_refs > m->refs) +  if (m->weak_refs > m->gc_refs)    gc_fatal(a, 0, "More weak references than internal references.\n");   #else    m = get_marker(a);   #endif       if (Pike_in_gc != GC_PASS_ZAP_WEAK) {    if (m->weak_refs < 0)    goto should_free;    }    else -  if (!(m->flags & GC_MARKED)) { +  if (!(m->gc_flags & GC_MARKED)) {   #ifdef PIKE_DEBUG    if (m->weak_refs <= 0)    gc_fatal(a, 0, "Too many weak refs cleared to thing with external "    "weak refs.\n");   #endif    m->weak_refs--;    goto should_free;    }    return 0;      should_free:    gc_ext_weak_refs--;   #ifdef PIKE_DEBUG    m->saved_refs--; -  m->flags |= GC_WEAK_FREED; +  m->gc_flags |= GC_WEAK_FREED;   #endif       if (*(INT32 *) a == 1) {    /* Make sure the thing doesn't run out of refs, since we can't    * handle cascading frees now. We'll do it in the free pass    * instead. */    gc_add_extra_ref(a); -  m->flags |= GC_GOT_DEAD_REF; +  m->gc_flags |= GC_GOT_DEAD_REF;   #ifdef PIKE_DEBUG    delayed_freed++;   #endif    }       return 1;   }      void gc_delayed_free(void *a, int type)   {    struct marker *m;      #ifdef PIKE_DEBUG -  if (gc_is_watching && (m = find_marker(a)) && m->flags & GC_WATCHED) { +  if (gc_is_watching && (m = find_marker(a)) && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_delayed_free()");    }    if (Pike_in_gc != GC_PASS_MARK && Pike_in_gc != GC_PASS_CYCLE &&    Pike_in_gc != GC_PASS_ZAP_WEAK)    Pike_fatal("gc_delayed_free() called in invalid gc pass.\n");    if (gc_debug) {    if (!(m = find_marker(a)))    gc_fatal(a, 0, "gc_delayed_free() got unknown object (missed by pretouch pass).\n");    }    else m = get_marker(a);    if (*(INT32 *) a != 1)    Pike_fatal("gc_delayed_free() called for thing that haven't got a single ref.\n");    debug_malloc_touch(a);    delayed_freed++;   #else    m = get_marker(a);   #endif    -  if (m->flags & GC_MARKED) { +  if (m->gc_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 free_extra_frame *l = alloc_free_extra_frame();    l->data = a;    l->type = type;    l->next = free_extra_list;    free_extra_list = l;    }       gc_add_extra_ref(a); -  m->flags |= GC_GOT_DEAD_REF; +  m->gc_flags |= GC_GOT_DEAD_REF;   }      int real_gc_mark(void *a DO_IF_DEBUG (COMMA int type))   {    struct marker *m;      #ifdef PIKE_DEBUG    if (Pike_in_gc == GC_PASS_ZAP_WEAK && !find_marker (a))    gc_fatal_2 (a, type, 0, "gc_mark() called for for thing without marker "    "in zap weak pass.\n");   #endif       m = get_marker (a);    -  /* Note: m->refs and m->xrefs are useless already here due to how +  /* Note: m->gc_refs and m->xrefs are useless already here due to how    * gc_free_(short_)svalue works. */      #ifdef PIKE_DEBUG -  if (gc_is_watching && m && m->flags & GC_WATCHED) { +  if (gc_is_watching && m && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_mark()");    }    if (!a) Pike_fatal("Got null pointer.\n");    if (Pike_in_gc != GC_PASS_MARK && Pike_in_gc != GC_PASS_ZAP_WEAK)    Pike_fatal("GC mark attempted in invalid pass.\n");    if (!*(INT32 *) a)    gc_fatal_2 (a, type, 1, "Marked a thing without refs.\n");    if (m->weak_refs < 0)    gc_fatal_2 (a, type, 1, "Marked a thing scheduled for weak free.\n");   #endif       if (Pike_in_gc == GC_PASS_ZAP_WEAK) {    /* Things are visited in the zap weak pass through the mark    * functions to free refs to internal things that only got weak    * external references. That happens only when a thing also have    * internal cyclic nonweak refs. */   #ifdef PIKE_DEBUG -  if (!(m->flags & GC_MARKED)) +  if (!(m->gc_flags & GC_MARKED))    gc_fatal_2 (a, type, 0, "gc_mark() called for thing in zap weak pass "    "that wasn't marked before.\n");   #endif -  if (m->flags & GC_FREE_VISITED) { +  if (m->gc_flags & GC_FREE_VISITED) {    debug_malloc_touch (a);    return 0;    }    else {    debug_malloc_touch (a); -  m->flags |= GC_FREE_VISITED; +  m->gc_flags |= GC_FREE_VISITED;    return 1;    }    }    -  else if (m->flags & GC_MARKED) { +  else if (m->gc_flags & GC_MARKED) {    debug_malloc_touch (a);   #ifdef PIKE_DEBUG    if (m->weak_refs != 0)    gc_fatal_2 (a, type, 0, "weak_refs changed in marker "    "already visited by gc_mark().\n");   #endif    return 0;    }       else {    debug_malloc_touch (a);    if (m->weak_refs) {    gc_ext_weak_refs -= m->weak_refs;    m->weak_refs = 0;    } -  m->flags = (m->flags & ~GC_NOT_REFERENCED) | GC_MARKED; +  m->gc_flags = (m->gc_flags & ~GC_NOT_REFERENCED) | GC_MARKED;    DO_IF_DEBUG(marked++);    return 1;    }   }      void gc_move_marker (void *old, void *new)   {    struct marker *m = find_marker (old);      #ifdef PIKE_DEBUG
pike.git/src/gc.c:2623:       move_marker (m, debug_malloc_pass (new));   }      PMOD_EXPORT void gc_cycle_enqueue(gc_cycle_check_cb *checkfn, void *data, int weak)   {    struct link_frame *l = alloc_link_frame();   #ifdef PIKE_DEBUG    {    struct marker *m; -  if (gc_is_watching && (m = find_marker(data)) && m->flags & GC_WATCHED) { +  if (gc_is_watching && (m = find_marker(data)) && m->gc_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");    if (stack_top == &sentinel_frame)    gc_fatal (data, 0, "No thing on rec stack to follow links from.\n");   #endif    l->data = data;
pike.git/src/gc.c:2703: Inside #if defined(GC_STACK_DEBUG)
   describe_rec_frame (stack_top);    fputc ('\n', stderr);   #endif    CHECK_REC_STACK_FRAME (stack_top);      #ifdef PIKE_DEBUG    {    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"); +  Pike_fatal ("gc_cycle_pop didn't pop the stack.\n");    }   #else    gc_cycle_pop();   #endif    }   }      #ifdef GC_CYCLE_DEBUG   static int gc_cycle_indent = 0;   #define CYCLE_DEBUG_MSG(REC, TXT) do { \
pike.git/src/gc.c:2768: Inside #if defined(GC_STACK_DEBUG)
  #ifdef GC_STACK_DEBUG    fprintf(stderr,"Stack before:\n");    describe_rec_stack (beg, "beg", pos, "pos", NULL, NULL);   #endif       /* Always keep chains of strong refs continuous, or else we risk    * breaking the order in a later rotation. */    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"); +  if (beg == &sentinel_frame) +  Pike_fatal ("Strong ref chain ended up off stack.\n");   #endif    CYCLE_DEBUG_MSG (beg, "> rotate_rec_stack, actual beg");       {    struct gc_rec_frame *new_stack_top = pos->prev;       beg->prev->next = pos;    pos->prev = beg->prev;       stack_top->next = beg;
pike.git/src/gc.c:2806:   #endif       return beg;   }      int gc_cycle_push(void *data, struct marker *m, int weak)   {    struct marker *pm;      #ifdef PIKE_DEBUG -  if (gc_is_watching && m && m->flags & GC_WATCHED) { +  if (gc_is_watching && m && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_cycle_push()");    }       debug_malloc_touch (data);       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)) +  if (gc_debug && !(m->gc_flags & GC_PRETOUCHED))    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) && +  if ((!(m->gc_flags & GC_NOT_REFERENCED) || m->gc_flags & GC_MARKED) &&    *(INT32 *) data)    gc_fatal (data, 1, "Got a referenced marker to gc_cycle_push.\n"); -  if (m->flags & GC_XREFERENCED) +  if (m->gc_flags & GC_XREFERENCED)    gc_fatal (data, 1, "Doing cycle check in externally referenced thing "    "missed in mark pass.\n");    }    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;    struct program *p;    struct mapping *m;
pike.git/src/gc.c:2854: Inside #if defined(PIKE_DEBUG)
   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 (stack_top->rf_flags & GC_MARK_LIVE) {    /* Only recurse through things already handled; we'll get to the    * other later in the normal recursion. */ -  if (m->flags & GC_CYCLE_CHECKED && !(m->flags & GC_LIVE)) { +  if (m->gc_flags & GC_CYCLE_CHECKED && !(m->gc_flags & GC_LIVE)) {    CYCLE_DEBUG_MSG (m->frame, "gc_cycle_push, mark live");    goto mark_live;    }    CYCLE_DEBUG_MSG (m->frame, "gc_cycle_push, no mark live");    return 0;    }       if (stack_top == &sentinel_frame)    pm = NULL;    else {
pike.git/src/gc.c:3048:    CYCLE_DEBUG_MSG (r, "> gc_cycle_push, mark cycle 1");    if (r == bottom) break;    }    }    CHECK_REC_STACK (cycle_frame, "cycle_frame", break_pos, "break_pos");    }    }    }       else -  if (!(m->flags & GC_CYCLE_CHECKED)) { +  if (!(m->gc_flags & GC_CYCLE_CHECKED)) {    struct gc_rec_frame *r;   #ifdef PIKE_DEBUG    cycle_checked++;    if (m->frame)    gc_fatal (data, 0, "Marker already got a frame.\n");   #endif    -  m->flags |= GC_CYCLE_CHECKED | (pm ? pm->flags & GC_LIVE : 0); +  m->gc_flags |= GC_CYCLE_CHECKED | (pm ? pm->gc_flags & GC_LIVE : 0);    m->frame = r = gc_cycle_enqueue_rec (data);    debug_malloc_touch (data);    if (weak) {    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
pike.git/src/gc.c:3078: Inside #if defined(GC_CYCLE_DEBUG)
   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    CHECK_REC_STACK (NULL, NULL, NULL, NULL);    return 1;    }       /* Should normally not recurse now, but got to do that anyway if we    * must propagate GC_LIVE flags. */ -  if (!pm || !(pm->flags & GC_LIVE) || m->flags & GC_LIVE) { +  if (!pm || !(pm->gc_flags & GC_LIVE) || m->gc_flags & GC_LIVE) {    CYCLE_DEBUG_MSG (m->frame ? m->frame : NULL, "gc_cycle_push, no recurse");    return 0;    }       /* 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) +  if (m->gc_flags & GC_LIVE)    Pike_fatal("Shouldn't mark live recurse when there's nothing to do.\n");   #endif -  m->flags |= GC_LIVE; +  m->gc_flags |= GC_LIVE;    debug_malloc_touch (data);    -  if (m->flags & GC_GOT_DEAD_REF) { +  if (m->gc_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 (data);    if (!sub_ref ((struct ref_dummy *) data)) {   #ifdef PIKE_DEBUG    gc_fatal (data, 0, "Thing got zero refs after removing the dead gc ref.\n");   #endif    }    }   
pike.git/src/gc.c:3142:    stack_top = r;    }       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) { +  if (gc_is_watching && m && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_cycle_pop()");    } -  if (!(m->flags & GC_CYCLE_CHECKED)) +  if (!(m->gc_flags & GC_CYCLE_CHECKED))    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) && +  if ((!(m->gc_flags & GC_NOT_REFERENCED) || m->gc_flags & GC_MARKED) &&    *(INT32 *) data)    gc_fatal (data, 1, "Got a referenced marker to gc_cycle_pop.\n"); -  if (m->flags & GC_XREFERENCED) +  if (m->gc_flags & GC_XREFERENCED)    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       stack_top = popped->prev;   #ifdef PIKE_DEBUG
pike.git/src/gc.c:3230: Inside #if defined(PIKE_DEBUG)
   CHECK_CYCLE_PIECE_FRAME (r);    }   #endif       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) { +  if (m->gc_flags & GC_LIVE_OBJ) {    /* Move to the kill list. */   #ifdef PIKE_DEBUG    popped->rf_flags &= ~GC_ON_CYCLE_PIECE_LIST;    popped->cycle_piece = popped->u.last_cycle_piece =    (struct gc_rec_frame *) (ptrdiff_t) -1;   #endif    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 (!(m->flags & GC_GOT_DEAD_REF)) +  if (!(m->gc_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 (!(m->flags & GC_LIVE)) { +  if (!(m->gc_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 (!(m->flags & GC_GOT_DEAD_REF)) { +  if (!(m->gc_flags & GC_GOT_DEAD_REF)) {    gc_add_extra_ref (popped->data); -  m->flags |= GC_GOT_DEAD_REF; +  m->gc_flags |= GC_GOT_DEAD_REF;    }    }   #ifdef PIKE_DEBUG    else -  if (m->flags & GC_GOT_DEAD_REF) +  if (m->gc_flags & GC_GOT_DEAD_REF)    gc_fatal (popped->data, 0, "Didn't expect a dead extra ref.\n");   #endif       CYCLE_DEBUG_MSG (popped, "> gc_cycle_pop, free");    m->frame = NULL;    really_free_gc_rec_frame (popped);    }       popped = next;    } while (popped);
pike.git/src/gc.c:3303:      void do_gc_recurse_short_svalue(union anything *u, int type)   {    gc_recurse_short_svalue(u, type);   }      int gc_do_free(void *a)   {    struct marker *m;   #ifdef PIKE_DEBUG -  if (gc_is_watching && (m = find_marker(a)) && m->flags & GC_WATCHED) { +  if (gc_is_watching && (m = find_marker(a)) && m->gc_flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    gc_watched_found (m, "gc_do_free()");    }    if (!a) Pike_fatal("Got null pointer.\n");    if (Pike_in_gc != GC_PASS_FREE)    Pike_fatal("gc free attempted in invalid pass.\n");   #endif       m=find_marker(debug_malloc_pass(a));    if (!m) return 0; /* Object created after cycle pass. */       if (gc_destruct_everything) {    /* We don't actually free much in this mode, just destruct    * objects. So when we normally would return nonzero we just    * remove the extra ref again. */ -  if (!(m->flags & GC_LIVE)) { +  if (!(m->gc_flags & GC_LIVE)) {    if (*(INT32 *) a == 1)    return 1;    else {    gc_free_extra_ref (a);    sub_ref ((struct ref_dummy *) a);    }    }    return 0;    }      #ifdef PIKE_DEBUG -  if (*(INT32 *) a > !!(m->flags & GC_GOT_EXTRA_REF)) { +  if (*(INT32 *) a > !!(m->gc_flags & GC_GOT_EXTRA_REF)) {    if (!gc_destruct_everything && -  (!(m->flags & GC_NOT_REFERENCED) || m->flags & GC_MARKED)) +  (!(m->gc_flags & GC_NOT_REFERENCED) || m->gc_flags & GC_MARKED))    gc_fatal(a, 0, "gc_do_free() called for referenced thing.\n");    if (gc_debug && -  (m->flags & (GC_PRETOUCHED|GC_MARKED|GC_IS_REFERENCED)) == GC_PRETOUCHED) +  (m->gc_flags & (GC_PRETOUCHED|GC_MARKED|GC_IS_REFERENCED)) == GC_PRETOUCHED)    gc_fatal(a, 0, "gc_do_free() called without prior call to "    "gc_mark() or gc_is_referenced().\n");    }    if(!gc_destruct_everything && -  (m->flags & (GC_MARKED|GC_XREFERENCED)) == GC_XREFERENCED) +  (m->gc_flags & (GC_MARKED|GC_XREFERENCED)) == GC_XREFERENCED)    gc_fatal(a, 1, "Thing with external reference missed in gc mark pass.\n"); -  if ((m->flags & (GC_DO_FREE|GC_LIVE)) == GC_LIVE) live_ref++; -  m->flags |= GC_DO_FREE; +  if ((m->gc_flags & (GC_DO_FREE|GC_LIVE)) == GC_LIVE) live_ref++; +  m->gc_flags |= GC_DO_FREE;   #endif    -  return !(m->flags & GC_LIVE); +  return !(m->gc_flags & GC_LIVE);   }    -  + #if 0   static void free_obj_arr(void *oa)   {    struct array *obj_arr = *((struct array **)oa);       if (obj_arr) free_array(obj_arr);    free(oa);   } -  + #endif      /*! @class MasterObject    */      /*! @decl void runtime_warning(string subsystem, string msg, mixed|void data)    *!    *! Called by the Pike runtime to warn about data inconsistencies.    *!    *! @param subsystem    *! Runtime subsystem where the warning was generated.
pike.git/src/gc.c:3399:   /*! @endclass    */      static void warn_bad_cycles(void)   {    /* The reason for the extra level of indirection, is that it might    * be clobbered by the longjump() in SET_ONERROR otherwise.    * (On some architectures longjump() might restore obj_arr's original    * value (eg if obj_arr is in a register)).    */ + #if 0    struct array **obj_arr_ = (struct array **)xalloc(sizeof(struct array *));    ONERROR tmp;       *obj_arr_ = NULL;       SET_ONERROR(tmp, free_obj_arr, obj_arr_);    - #if 0 +     {    struct gc_pop_frame *p;    unsigned cycle = 0;    *obj_arr_ = allocate_array(0);       for (p = kill_list; 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 = p->next;    if (p ? ((unsigned)(p->cycle != cycle)) : cycle) {    if ((*obj_arr_)->size >= 2) { -  push_constant_text("gc"); -  push_constant_text("bad_cycle"); +  push_static_text("gc"); +  push_static_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);    }    if (!p) break;    }    } - #endif +        CALL_AND_UNSET_ONERROR(tmp); -  + #endif   }      size_t do_gc(void *UNUSED(ignored), int explicit_call)   {    ALLOC_COUNT_TYPE start_allocs;    size_t start_num_objs, unreferenced;    cpu_time_t gc_start_time, gc_start_real_time;    ptrdiff_t objs, pre_kill_objs;   #if defined (PIKE_DEBUG) || defined (DO_PIKE_CLEANUP) -  unsigned destroy_count; +  unsigned destruct_count;   #endif   #ifdef PIKE_DEBUG    unsigned obj_count;    ONERROR uwp;   #endif       if(Pike_in_gc) return 0;       if (gc_enabled <= 0 && (gc_enabled < 0 || !explicit_call)) {    /* If this happens then the gc has been disabled for a very long
pike.git/src/gc.c:3479:    remove_callback (gc_evaluator_callback);    gc_evaluator_callback = NULL;    }    return 0;    }      #ifdef DEBUG_MALLOC    if(debug_options & GC_RESET_DMALLOC)    reset_debug_malloc();   #endif -  init_gc(); +     gc_generation++;    Pike_in_gc=GC_PASS_PREPARE;       if (!SAFE_IS_ZERO(&gc_pre_cb)) {    safe_apply_svalue(&gc_pre_cb, 0, 1);    pop_stack();    }       gc_start_time = get_cpu_time();    gc_start_real_time = get_real_time();
pike.git/src/gc.c:3523:    fprintf (stderr, "Destructing all objects... ");    else    fprintf(stderr,"Garbage collecting... ");    GC_VERBOSE_DO(fprintf(stderr, "\n"));    }   #ifdef PIKE_DEBUG    if(num_objects < 0)    Pike_fatal("Panic, less than zero objects!\n");   #endif    -  last_gc=TIME(0); +  last_gc=time(0);    start_num_objs = num_objects;    start_allocs = num_allocs;    num_allocs = 0;       /* Object alloc/free and any reference changes are disallowed now. */      #ifdef PIKE_DEBUG    delayed_freed = weak_freed = checked = marked = cycle_checked = live_ref = 0;    mark_live = frame_rot = link_search = 0;   #endif
pike.git/src/gc.c:3662:    "| cycle: %u internal things visited,\n"    "| %u weak references freed, %d more things to free,\n"    "| %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); +  if (link_frames) +  Pike_fatal ("Leaked %u link frames.\n", link_frames);   #endif    }       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;    CHECK_MARK_QUEUE_EMPTY();
pike.git/src/gc.c:3712: Inside #if defined(PIKE_DEBUG)
   n += gc_touch_all_objects();   #ifdef PIKE_DEBUG    gc_touch_all_strings();   #endif    if (n != (unsigned) num_objects && !got_unlinked_things)    Pike_fatal("Object count wrong in gc; expected %d, got %d.\n", num_objects, n);   #if 0 /* Temporarily disabled - Hubbe */   #ifdef PIKE_DEBUG   #ifdef DEBUG_MALLOC    PTR_HASH_LOOP(marker, i, m) -  if (!(m->flags & (GC_POSTTOUCHED|GC_WEAK_FREED)) && +  if (!(m->gc_flags & (GC_POSTTOUCHED|GC_WEAK_FREED)) &&    dmalloc_is_invalid_memory_block(m->data)) {    fprintf(stderr, "Found a stray marker after posttouch pass: ");    describe_marker(m);    fprintf(stderr, "Describing marker location(s):\n");    debug_malloc_dump_references(m, 2, 1, 0);    fprintf(stderr, "Describing thing for marker:\n");    Pike_in_gc = 0;    describe(m->data);    Pike_in_gc = GC_PASS_POSTTOUCH;    Pike_fatal("Fatal in garbage collector.\n");
pike.git/src/gc.c:3772:    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);    really_free_free_extra_frame (free_extra_list);    free_extra_list = next;    }      #ifdef PIKE_DEBUG -  if (free_extra_frames) fatal ("Leaked %u free extra frames.\n", free_extra_frames); +  if (free_extra_frames) +  Pike_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    gc_internal_array = (struct array *) (ptrdiff_t) -1;    gc_internal_multiset = (struct multiset *) (ptrdiff_t) -1;    gc_internal_mapping = (struct mapping *) (ptrdiff_t) -1;
pike.git/src/gc.c:3800:       /* Destruct the live objects in cycles, but first warn about any bad    * cycles. */    pre_kill_objs = num_objects;    if (Pike_interpreter.evaluator_stack && !gc_destruct_everything) {    objs -= num_objects;    warn_bad_cycles();    objs += num_objects;    }   #if defined (PIKE_DEBUG) || defined (DO_PIKE_CLEANUP) -  destroy_count = 0; +  destruct_count = 0;   #endif       if (!SAFE_IS_ZERO(&gc_post_cb)) {    safe_apply_svalue(&gc_post_cb, 0, 1);    pop_stack();    }       {    enum object_destruct_reason reason =   #ifdef DO_PIKE_CLEANUP
pike.git/src/gc.c:3844: Inside #if defined(PIKE_DEBUG)
   }    }    }   #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 ((get_marker(kill_list->data)->flags & (GC_LIVE|GC_LIVE_OBJ)) != +  if ((get_marker(kill_list->data)->gc_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 && -  get_marker(PARENT_INFO(o)->parent)->flags & GC_LIVE_OBJ) +  get_marker(PARENT_INFO(o)->parent)->gc_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) {    INT_TYPE line;    struct pike_string *file = get_program_line (o->prog, &line);    fprintf(stderr, ", prog %s:%d\n", file->str, line);    free_string(file);    }    else fputs(", is destructed\n", stderr);    );    if (!SAFE_IS_ZERO(&gc_destruct_cb)) {    ref_push_object(o); -  safe_apply_svalue(&gc_destruct_cb, 1, 1); +  push_int(reason); +  push_int(o->refs - 1); +  safe_apply_svalue(&gc_destruct_cb, 3, 1);    pop_stack();    }       destruct_object (o, reason);    free_object(o);    gc_free_extra_ref(o);   #if defined (PIKE_DEBUG) || defined (DO_PIKE_CLEANUP) -  destroy_count++; +  destruct_count++;   #endif    really_free_gc_rec_frame (kill_list);    kill_list = next;    }    }      #ifdef PIKE_DEBUG -  if (rec_frames) fatal ("Leaked %u rec frames.\n", rec_frames); +  if (rec_frames) +  Pike_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)); +  destruct_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);    destruct_objects_to_destruct();    GC_VERBOSE_DO(fprintf(stderr, "| destruct: %d things really freed\n",    obj_count - num_objects));      #ifdef PIKE_DEBUG    if (gc_extra_refs) {    size_t e;    fprintf (stderr, "Lost track of %d extra refs to things in gc.\n"    "Searching for marker(s) with extra refs:\n", gc_extra_refs); -  for (e = 0; e < marker_hash_table_size; e++) { -  struct marker *s = marker_hash_table[e], *m; -  for (m = s; m;) { -  if (m->flags & GC_GOT_EXTRA_REF) { +     fprintf (stderr, "========================================\n" -  "Found marker with extra ref: "); -  describe_marker (m); -  fprintf (stderr, "Describing the thing pointed to:\n"); -  describe (m->data); -  } -  m = m->next; -  /* The marker might be moved to the head of the chain via -  * describe() above, so do this to avoid infinite recursion. -  * Some entries in the chain might be missed, but I don't want -  * to bother. */ -  if (m == s) break; -  } -  } -  fprintf (stderr, "========================================\n" +     "Done searching for marker(s) with extra refs.\n");    Pike_fatal("Lost track of %d extra refs to things in gc.\n", gc_extra_refs);    }    if(fatal_after_gc) Pike_fatal("%s", fatal_after_gc);   #endif       /* Calculate the next alloc_threshold. */    {    double multiplier, new_threshold;    cpu_time_t last_non_gc_time, last_gc_time;
pike.git/src/gc.c:3993:    {    cpu_time_t gc_end_time = get_cpu_time();    if (gc_end_time > gc_start_time)    last_gc_time = gc_end_time - gc_start_time;    else    last_gc_time = (cpu_time_t) -1;    }       /* At this point, unreferenced contains the number of things that    * were without external references during the check and mark -  * passes. In the process of freeing them, destroy functions might +  * passes. In the process of freeing them, _destruct functions might    * have been called which means anything might have happened.    * Therefore we use that figure instead of the difference between    * the number of allocated things to measure the amount of    * garbage. */    last_garbage_ratio = (double) unreferenced / start_num_objs;       objects_alloced = objects_alloced * multiplier +    start_allocs * (1.0 - multiplier);    objects_freed = objects_freed * multiplier +    unreferenced * (1.0 - multiplier);
pike.git/src/gc.c:4112:    {    char timestr[40];    if (last_gc_time != (cpu_time_t) -1)    sprintf (timestr, ", %ld ms",    (long) (last_gc_time / (CPU_TIME_TICKS / 1000)));    else    timestr[0] = 0;   #ifdef DO_PIKE_CLEANUP    if (gc_destruct_everything)    fprintf(stderr, "done (%u %s destructed)%s\n", -  destroy_count, destroy_count == 1 ? "was" : "were", timestr); +  destruct_count, destruct_count == 1 ? "was" : "were", timestr);    else   #endif    fprintf(stderr, "done (%"PRINTSIZET"u of %"PRINTSIZET"u "    "%s unreferenced)%s\n",    unreferenced, start_num_objs,    unreferenced == 1 ? "was" : "were",    timestr);    }    }   
pike.git/src/gc.c:4144:    exit_gc();      #ifdef ALWAYS_GC    ADD_GC_CALLBACK();   #else    if(d_flag > 3) ADD_GC_CALLBACK();   #endif      #ifdef DO_PIKE_CLEANUP    if (gc_destruct_everything) -  return destroy_count; +  return destruct_count;   #endif       if (!SAFE_IS_ZERO(&gc_done_cb)) {    push_int(unreferenced);    safe_apply_svalue(&gc_done_cb, 1, 1);    pop_stack();    }       return unreferenced;   }
pike.git/src/gc.c:4215:    *!    *! @seealso    *! @[gc()], @[Pike.gc_parameters()], @[Pike.implicit_gc_real_time]    */   void f__gc_status(INT32 args)   {    int size = 0;       pop_n_elems(args);    -  push_constant_text("num_objects"); +  push_static_text("num_objects");    push_int(num_objects);    size++;    -  push_constant_text("num_allocs"); +  push_static_text("num_allocs");    push_int64(num_allocs);    size++;    -  push_constant_text("alloc_threshold"); +  push_static_text("alloc_threshold");    push_int64(alloc_threshold);    size++;    -  push_constant_text("projected_garbage"); -  push_float(DO_NOT_WARN((FLOAT_TYPE)(objects_freed * (double) num_allocs / -  (double) alloc_threshold))); +  push_static_text("projected_garbage"); +  push_float((FLOAT_TYPE)(objects_freed * (double) num_allocs / +  (double) alloc_threshold));    size++;    -  push_constant_text("objects_alloced"); -  push_int64(DO_NOT_WARN((INT64)objects_alloced)); +  push_static_text("objects_alloced"); +  push_int64((INT64)objects_alloced);    size++;    -  push_constant_text("objects_freed"); -  push_int64(DO_NOT_WARN((INT64)objects_freed)); +  push_static_text("objects_freed"); +  push_int64((INT64)objects_freed);    size++;    -  push_constant_text("last_garbage_ratio"); -  push_float(DO_NOT_WARN((FLOAT_TYPE) last_garbage_ratio)); +  push_static_text("last_garbage_ratio"); +  push_float((FLOAT_TYPE) last_garbage_ratio);    size++;    -  push_constant_text("non_gc_time"); -  push_int64(DO_NOT_WARN((INT64) non_gc_time)); +  push_static_text("non_gc_time"); +  push_int64((INT64) non_gc_time);    size++;    -  push_constant_text("gc_time"); -  push_int64(DO_NOT_WARN((INT64) gc_time)); +  push_static_text("gc_time"); +  push_int64((INT64) gc_time);    size++;    -  push_constant_text ("last_garbage_strategy"); +  push_static_text ("last_garbage_strategy");    switch (last_garbage_strategy) {    case GARBAGE_RATIO_LOW: -  push_constant_text ("garbage_ratio_low"); break; +  push_static_text ("garbage_ratio_low"); break;    case GARBAGE_RATIO_HIGH: -  push_constant_text ("garbage_ratio_high"); break; +  push_static_text ("garbage_ratio_high"); break;    case GARBAGE_MAX_INTERVAL: -  push_constant_text ("garbage_max_interval"); break; +  push_static_text ("garbage_max_interval"); break;   #ifdef PIKE_DEBUG    default:    Pike_fatal ("Unknown last_garbage_strategy %d\n", last_garbage_strategy);   #endif    }    size++;    -  push_constant_text("last_gc"); +  push_static_text("last_gc");    push_int64(last_gc);    size++;    -  push_constant_text ("total_gc_cpu_time"); +  push_static_text ("total_gc_cpu_time");    push_int64 (auto_gc_time);   #ifndef LONG_CPU_TIME    push_int (1000000000 / CPU_TIME_TICKS);    o_multiply();   #endif    size++;    -  push_constant_text ("total_gc_real_time"); +  push_static_text ("total_gc_real_time");    push_int64 (auto_gc_real_time);   #ifndef LONG_CPU_TIME    push_int (1000000000 / CPU_TIME_TICKS);    o_multiply();   #endif    size++;      #ifdef PIKE_DEBUG -  push_constant_text ("max_rec_frames"); -  push_int64 (DO_NOT_WARN ((INT64) tot_max_rec_frames)); +  push_static_text ("max_rec_frames"); +  push_int64 ((INT64) tot_max_rec_frames);    size++;    -  push_constant_text ("max_link_frames"); -  push_int64 (DO_NOT_WARN ((INT64) tot_max_link_frames)); +  push_static_text ("max_link_frames"); +  push_int64 ((INT64) tot_max_link_frames);    size++;    -  push_constant_text ("max_free_extra_frames"); -  push_int64 (DO_NOT_WARN ((INT64) tot_max_free_extra_frames)); +  push_static_text ("max_free_extra_frames"); +  push_int64 ((INT64) tot_max_free_extra_frames);    size++;   #endif       f_aggregate_mapping(size * 2);   }      /*! @decl int implicit_gc_real_time (void|int nsec)    *! @belongs Pike    *!    *! Returns the total amount of real time that has been spent in
pike.git/src/gc.c:4361:    fprintf(stderr,"Avg "CPU_TIME_UNIT" between gc : %f\n", non_gc_time);    fprintf(stderr,"Avg "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" :    last_garbage_strategy == GARBAGE_MAX_INTERVAL ?    "garbage_max_interval" : "???");    - #ifdef PIKE_DEBUG -  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", +  DWERR("Max used recursion frames : %u\n", tot_max_rec_frames); +  DWERR("Max used link frames : %u\n", tot_max_link_frames); +  DWERR("Max used free extra frames : %u\n", tot_max_free_extra_frames); +  DWERR("Marked live ratio : %g\n",    (double) tot_mark_live / tot_cycle_checked); -  fprintf(stderr,"Frame rotation ratio : %g\n", +  DWERR("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 */   }      /* Visit things API */      PMOD_EXPORT visit_ref_cb *visit_ref = NULL; -  + PMOD_EXPORT visit_enter_cb *visit_enter = NULL; + PMOD_EXPORT visit_leave_cb *visit_leave = NULL;      /* Be careful if extending this with internal types like    * T_MAPPING_DATA and T_MULTISET_DATA; there's code that assumes    * type_from_visit_fn only returns types that fit in a TYPE_FIELD. */   PMOD_EXPORT visit_thing_fn *const visit_fn_from_type[MAX_TYPE + 1] = {    (visit_thing_fn *) (ptrdiff_t) -1,    (visit_thing_fn *) (ptrdiff_t) -1,    (visit_thing_fn *) (ptrdiff_t) -1,    (visit_thing_fn *) (ptrdiff_t) -1,    (visit_thing_fn *) (ptrdiff_t) -1,    (visit_thing_fn *) (ptrdiff_t) -1,    (visit_thing_fn *) (ptrdiff_t) -1,    (visit_thing_fn *) (ptrdiff_t) -1, -  (visit_thing_fn *) &visit_array, -  (visit_thing_fn *) &visit_mapping, -  (visit_thing_fn *) &visit_multiset, -  (visit_thing_fn *) &visit_object, +  (visit_thing_fn *)&visit_array, +  (visit_thing_fn *)&visit_mapping, +  (visit_thing_fn *)&visit_multiset, +  (visit_thing_fn *)&visit_object,    /* visit_function must be called with a whole svalue, so it's not    * included here. */    (visit_thing_fn *) (ptrdiff_t) -1, -  (visit_thing_fn *) &visit_program, -  (visit_thing_fn *) &visit_string, -  (visit_thing_fn *) &visit_type, +  (visit_thing_fn *)&visit_program, +  (visit_thing_fn *)&visit_string, +  (visit_thing_fn *)&visit_type,   };      PMOD_EXPORT TYPE_T type_from_visit_fn (visit_thing_fn *fn)   {    /* Since the array to search is so small, linear search is probably    * fastest. */    unsigned t;    for (t = 0; t < NELEM (visit_fn_from_type); t++)    if (visit_fn_from_type[t] == fn)    return (TYPE_T) t;    return PIKE_T_UNKNOWN;   }      PMOD_EXPORT TYPE_FIELD real_visit_svalues (struct svalue *s, size_t num, -  int ref_type) +  int ref_type, void *extra)   {    for (; num; num--, s++) -  visit_svalue (s, ref_type); +  visit_svalue (s, ref_type, extra);    return 0;   }      /* Memory counting    *    * This mode is used by f_count_memory, and it's recognized by a    * nonzero value in mc_pass.    *    * The basic idea is to follow and count all refs from the starting    * point things given to f_count_memory. Whenever the counted refs add
pike.git/src/gc.c:4561:    * If there's anything left in the complete list then it's internal    * cyclic stuff. In that case we put those things into the work list,    * move the indirectly incomplete list back to complete and repeat    * MC_PASS_LOOKAHEAD. Otherwise we're done.    */      /* #define MEMORY_COUNT_DEBUG */      #define MC_WQ_START_SIZE 1024    + #ifdef PIKE_THREADS + static IMUTEX_T mc_mutex; + #endif +    PMOD_EXPORT int mc_pass;   PMOD_EXPORT size_t mc_counted_bytes;      static int mc_lookahead, mc_block_pike_cycle_depth;   static TYPE_FIELD mc_block_lookahead;   static TYPE_FIELD mc_block_lookahead_default = BIT_PROGRAM|BIT_STRING|BIT_TYPE;   /* Strings are blocked because they don't contain refs. Types are    * blocked because they are acyclic and don't contain refs to anything    * but strings and other types. */    -  + static inline int mc_lookahead_blocked(unsigned INT16 type) { +  if (type < sizeof(TYPE_FIELD)*8) { +  return !!(mc_block_lookahead & ((TYPE_FIELD)1 << type)); +  } +  +  return 0; + } +    static int mc_block_strings;      static int mc_enqueued_noninternal;   /* Set whenever something is enqueued in MC_PASS_LOOKAHEAD that isn't    * internal already. This is used to detect whether another    * MC_PASS_MARK_EXTERNAL is necessary. */      static unsigned mc_ext_toggle_bias = 0;      #define MC_PASS_LOOKAHEAD 1
pike.git/src/gc.c:4664:   #define BLOCK_ALLOC_NEXT hash_next   #undef PTR_HASH_ALLOC_DATA   #define PTR_HASH_ALLOC_DATA thing   #undef INIT_BLOCK   #define INIT_BLOCK(f)   #undef EXIT_BLOCK   #define EXIT_BLOCK(f)      PTR_HASH_ALLOC_FILL_PAGES (mc_marker, 2)    + static void start_mc(void) + { +  LOCK_IMUTEX(&mc_mutex); +  init_mc_marker_hash(); + } +  + static void stop_mc(void) + { +  exit_mc_marker_hash(); +  UNLOCK_IMUTEX(&mc_mutex); + } +    static struct mc_marker *my_make_mc_marker (void *thing,    visit_thing_fn *visit_fn,    void *extra)   {    struct mc_marker *m = make_mc_marker (thing);    assert (thing);    assert (visit_fn);    m->thing = thing;    m->visit_fn = visit_fn;    m->extra = extra;    m->int_refs = m->la_refs = m->flags = 0;    INIT_CLEARED_EXTERNAL (m);    m->queuepos = MAX_UINT32;   #ifdef PIKE_DEBUG    m->dl_prev = m->dl_next = (void *) (ptrdiff_t) -1;    m->la_count = ((unsigned INT16) -1) >> 1;   #endif    return m;   }    - #if defined (PIKE_DEBUG) || defined (MEMORY_COUNT_DEBUG) + #ifdef MEMORY_COUNT_DEBUG   static void describe_mc_marker (struct mc_marker *m)   {    fprintf (stderr, "%s %p: refs %d, int %d, la %d, cnt %d",    get_name_of_type (type_from_visit_fn (m->visit_fn)),    m->thing, *(INT32 *) m->thing, m->int_refs, m->la_refs, m->la_count);    if (m->queuepos != MAX_UINT32) fprintf (stderr, ", wq %u", m->queuepos);    if (m->flags & MC_FLAG_INTERNAL) fputs (", I", stderr);    if (m->flags & MC_FLAG_INT_VISITED) fputs (", IV", stderr);    if (m->flags & MC_FLAG_LA_VISITED) fputs (", LAV", stderr);    if (m->flags & MC_FLAG_CANDIDATE) fputs (", C", stderr);
pike.git/src/gc.c:4961:    move_svalue (&throw_value, --Pike_sp);    return -1;    }       return val.u.integer;   }      static void pass_lookahead_visit_ref (void *thing, int ref_type,    visit_thing_fn *visit_fn, void *extra)   { -  struct mc_marker *ref_to = find_mc_marker (thing); +  struct mc_marker *ref_to;    int ref_from_flags, ref_to_flags, old_la_count, ref_to_la_count;    int ref_added = 0, check_new_candidate = 0, la_count_handled = 0;       assert (mc_lookahead >= 0);    assert (mc_pass == MC_PASS_LOOKAHEAD);   #ifdef PIKE_DEBUG    assert (mc_ref_from != (void *) (ptrdiff_t) -1);    assert (mc_ref_from->la_count != ((unsigned INT16) -1) >> 1);   #endif       if (mc_block_strings > 0 &&    visit_fn == (visit_thing_fn *) &visit_string) { -  + #ifdef MEMORY_COUNT_DEBUG +  ref_to = find_mc_marker (thing); + #endif    MC_DEBUG_MSG (ref_to, "ignored string");    return;    }    -  +  ref_to = find_mc_marker (thing);    ref_from_flags = mc_ref_from->flags;       /* Create mc_marker if necessary. */       if (!ref_to) {    ref_to = my_make_mc_marker (thing, visit_fn, extra);    MC_DEBUG_MSG (ref_to, "visiting new thing");    assert (!(ref_from_flags & (MC_FLAG_INT_VISITED | MC_FLAG_LA_VISITED)));    ref_to_la_count = old_la_count = 0;    }
pike.git/src/gc.c:5118:    (ref_to_flags & MC_FLAG_CANDIDATE_REF) &&    ref_to->int_refs + ref_to->la_refs == *(INT32 *) thing) {    assert (!(ref_to_flags & MC_FLAG_CANDIDATE));    assert (ref_to->la_refs > 0);    ref_to_flags |= MC_FLAG_CANDIDATE;    MC_DEBUG_MSG (ref_to, "made candidate");       ref_to->flags = ref_to_flags;    ref_to->la_count = ref_to_la_count;    -  if (mc_block_lookahead & (1 << type_from_visit_fn (visit_fn))) { +  if (mc_lookahead_blocked(type_from_visit_fn (visit_fn))) {    MC_DEBUG_MSG (ref_to, "type is blocked - not enqueued");    return;    }       if (ref_to_la_count > 0) {    /* Always enqueue if the count allows it, even if it hasn't    * increased. That since MC_FLAG_CANDIDATE_REF must be propagated. */    if (ref_to->queuepos != MAX_UINT32 && old_la_count == ref_to_la_count)    MC_DEBUG_MSG (ref_to, "already in queue");    else {
pike.git/src/gc.c:5142:    mc_enqueued_noninternal = 1;    }    }    else    MC_DEBUG_MSG (ref_to, "candidate not enqueued due to zero count");    return;    }       /* Normal handling. */    -  if (mc_block_lookahead & (1 << type_from_visit_fn (visit_fn))) { +  if (mc_lookahead_blocked(type_from_visit_fn (visit_fn))) {    ref_to->flags = ref_to_flags;    ref_to->la_count = ref_to_la_count;    MC_DEBUG_MSG (ref_to, "type is blocked - not enqueued");    return;    }       if (!la_count_handled && !(ref_to_flags & MC_FLAG_LA_COUNT_FIXED)) {    int cycle_depth;    int count_from_source = ref_type & REF_TYPE_INTERNAL ?    mc_ref_from->la_count : mc_ref_from->la_count - 1;
pike.git/src/gc.c:5221:    "ignored internal" : "ignored fringe thing");    }   }      static void current_only_visit_ref (void *thing, int ref_type,    visit_thing_fn *visit_fn, void *extra)   /* This is used when count_memory has a negative lookahead. It only    * recurses through REF_TYPE_INTERNAL references. Note that most    * fields in mc_marker aren't used. */   { -  struct mc_marker *ref_to = find_mc_marker (thing); +     int ref_from_flags; -  +  struct mc_marker *ref_to;       assert (mc_pass);    assert (mc_lookahead < 0);   #ifdef PIKE_DEBUG    assert (mc_ref_from != (void *) (ptrdiff_t) -1);   #endif       ref_from_flags = mc_ref_from->flags;    assert (ref_from_flags & MC_FLAG_INTERNAL);    assert (!(ref_from_flags & MC_FLAG_INT_VISITED));    -  + #ifndef MEMORY_COUNT_DEBUG +  if (!(ref_type & REF_TYPE_INTERNAL)) { +  /* Return before lookup (or much worse, allocation) in the +  mc_marker hash table. The only reason to allocate a marker in +  this case is, AFAICS, to get the tracing right with +  MEMORY_COUNT_DEBUG enabled. That case is handled below. */ +  return; +  } + #endif +  +  ref_to = find_mc_marker (thing); +     if (!ref_to) {    ref_to = my_make_mc_marker (thing, visit_fn, extra);    MC_DEBUG_MSG (ref_to, "got new thing");    }    else if (ref_to->flags & MC_FLAG_INTERNAL) {    /* Ignore refs to the starting points. Can't treat them like other    * things anyway since the int_refs aren't valid. */    MC_DEBUG_MSG (ref_to, "ignored starting point");    return;    }    else    MC_DEBUG_MSG (ref_to, "got old thing");    -  + #ifdef MEMORY_COUNT_DEBUG    if (!(ref_type & REF_TYPE_INTERNAL)) {    MC_DEBUG_MSG (ref_to, "ignored non-internal ref");    return;    } -  + #endif       ref_to->int_refs++;    MC_DEBUG_MSG (ref_to, "added really internal ref");    assert (ref_to->int_refs <= *(INT32 *) thing);       if (ref_to->int_refs == *(INT32 *) thing) {    ref_to->flags |= MC_FLAG_INTERNAL;    mc_wq_enqueue (ref_to);    MC_DEBUG_MSG (ref_to, "enqueued internal");    }   }    -  + static void ignore_visit_enter(void *UNUSED(thing), int UNUSED(type), void *UNUSED(extra)) + { + } +  + static void ignore_visit_leave(void *UNUSED(thing), int UNUSED(type), void *UNUSED(extra)) + { + } +    PMOD_EXPORT int mc_count_bytes (void *thing)   {    if (mc_pass == MC_PASS_LOOKAHEAD) {    struct mc_marker *m = find_mc_marker (thing);   #ifdef PIKE_DEBUG    if (!m) Pike_fatal ("mc_marker not found for %p.\n", thing);   #endif    if ((m->flags & (MC_FLAG_INTERNAL|MC_FLAG_INT_VISITED)) == MC_FLAG_INTERNAL)    return 1;    }
pike.git/src/gc.c:5496:    *! string may be in use in some unrelated part of the program.    */   void f_count_memory (INT32 args)   {    struct svalue *collect_internal = NULL;    unsigned count_internal, count_cyclic, count_visited;    unsigned count_visits, count_revisits, count_rounds;    int collect_stats = 0, return_count = 0;       if (args < 1) -  SIMPLE_TOO_FEW_ARGS_ERROR ("count_memory", 1); +  SIMPLE_WRONG_NUM_ARGS_ERROR ("count_memory", 1);       mc_block_lookahead = mc_block_lookahead_default;    mc_block_pike_cycle_depth = 0;    mc_block_strings = 0;       if (TYPEOF(Pike_sp[-args]) == T_MAPPING) {    struct mapping *opts = Pike_sp[-args].u.mapping;    struct pike_string *ind;    struct svalue *val;   
pike.git/src/gc.c:5569:       else {    if (TYPEOF(Pike_sp[-args]) != T_INT)    SIMPLE_ARG_TYPE_ERROR ("count_memory", 1, "int|mapping(string:int)");    mc_lookahead =    Pike_sp[-args].u.integer > (unsigned INT16) -1 ? (unsigned INT16) -1 :    Pike_sp[-args].u.integer < 0 ? -1 :    Pike_sp[-args].u.integer;    }    -  init_mc_marker_hash(); +  start_mc();       if (TYPEOF(pike_cycle_depth_str) == PIKE_T_FREE) {    SET_SVAL_TYPE(pike_cycle_depth_str, T_STRING);    MAKE_CONST_STRING (pike_cycle_depth_str.u.string, "pike_cycle_depth");    }       assert (mc_work_queue == NULL);    mc_work_queue = malloc (MC_WQ_START_SIZE * sizeof (mc_work_queue[0]));    if (!mc_work_queue) { -  exit_mc_marker_hash(); +  stop_mc();    SIMPLE_OUT_OF_MEMORY_ERROR ("Pike.count_memory",    MC_WQ_START_SIZE * sizeof (mc_work_queue[0]));    }    mc_wq_size = MC_WQ_START_SIZE;    mc_work_queue--; /* Compensate for 1-based indexing. */    mc_wq_used = 1;       assert (!mc_pass); -  +  assert (visit_enter == NULL);    assert (visit_ref == NULL); -  +  assert (visit_leave == NULL);       free_svalue (&throw_value);    mark_free_svalue (&throw_value);       {    int i;    for (i = -args + 1; i < 0; i++) {    struct svalue *s = Pike_sp + i;       if (TYPEOF(*s) == T_INT)    continue;       else if (!REFCOUNTED_TYPE(TYPEOF(*s))) { -  exit_mc_marker_hash(); -  free (mc_work_queue + 1); +  mc_work_queue++; /* Compensate for 1-based indexing. */ +  free(mc_work_queue);    mc_work_queue = NULL; -  +  stop_mc();    SIMPLE_ARG_TYPE_ERROR (    "count_memory", i + args + 1,    "array|multiset|mapping|object|program|string|type|int");    }       else {    if (TYPEOF(*s) == T_FUNCTION) {    struct svalue s2;    if (!(s2.u.program = program_from_function (s))) { -  exit_mc_marker_hash(); -  free (mc_work_queue + 1); +  mc_work_queue++; /* Compensate for 1-based indexing. */ +  free(mc_work_queue);    mc_work_queue = NULL; -  +  stop_mc();    SIMPLE_ARG_TYPE_ERROR (    "count_memory", i + args + 1,    "array|multiset|mapping|object|program|string|type|int");    }    add_ref (s2.u.program);    SET_SVAL_TYPE(s2, T_PROGRAM);    free_svalue (s);    move_svalue (s, &s2);    }   
pike.git/src/gc.c:5638:    /* The user passed the same thing several times. Ignore it. */    }       else {    struct mc_marker *m =    my_make_mc_marker (s->u.ptr, visit_fn_from_type[TYPEOF(*s)], NULL);    m->flags |= MC_FLAG_INTERNAL;    if (!mc_block_pike_cycle_depth && TYPEOF(*s) == T_OBJECT) {    int cycle_depth = mc_cycle_depth_from_obj (s->u.object);    if (TYPEOF(throw_value) != PIKE_T_FREE) { -  exit_mc_marker_hash(); -  free (mc_work_queue + 1); +  mc_work_queue++; /* Compensate for 1-based indexing. */ +  free(mc_work_queue);    mc_work_queue = NULL; -  +  stop_mc();    throw_severity = THROW_ERROR;    pike_throw();    }    m->la_count = cycle_depth >= 0 ? cycle_depth : mc_lookahead;    }    else    m->la_count = mc_lookahead;    mc_wq_enqueue (m);    MC_DEBUG_MSG (m, "enqueued starting point");    }
pike.git/src/gc.c:5673:    assert (mc_complete.dl_prev == &mc_complete);    assert (mc_complete.dl_next == &mc_complete);   #ifdef PIKE_DEBUG    assert (mc_ref_from == (void *) (ptrdiff_t) -1);   #endif       mc_counted_bytes = 0;    count_internal = count_cyclic = count_visited = 0;    count_visits = count_revisits = count_rounds = 0;    +  visit_enter = ignore_visit_enter;    visit_ref = mc_lookahead < 0 ?    current_only_visit_ref : pass_lookahead_visit_ref; -  +  visit_leave = ignore_visit_leave;       do {    count_rounds++;    mc_enqueued_noninternal = 0;      #ifdef MEMORY_COUNT_DEBUG    fprintf (stderr, "[%d] MC_PASS_LOOKAHEAD\n", count_rounds);   #endif    mc_pass = MC_PASS_LOOKAHEAD;       while ((mc_ref_from = mc_wq_dequeue())) {    int action;       assert (!(mc_ref_from->flags & MC_FLAG_INT_VISITED));       if (mc_ref_from->flags & MC_FLAG_INTERNAL) {    action = VISIT_COUNT_BYTES; /* Memory count this. */    MC_DEBUG_MSG (NULL, "enter with byte counting"); -  +  if (mc_lookahead < 0) { +  MC_DEBUG_MSG (NULL, "VISIT_NO_REFS mode"); +  action |= VISIT_NO_REFS; +  }       mc_ref_from->visit_fn (mc_ref_from->thing, action, mc_ref_from->extra);    count_visits++;       if (mc_ref_from->flags & MC_FLAG_LA_VISITED) {    count_revisits++;    DL_REMOVE (mc_ref_from);    MC_DEBUG_MSG (NULL, "leave - removed from list");    }    else {
pike.git/src/gc.c:5761:    MC_DEBUG_MSG (NULL, "leave - added to incomplete list");    }    else {    DL_ADD_LAST (mc_complete, mc_ref_from);    MC_DEBUG_MSG (NULL, "leave - added to complete list");    }    }    }       if (TYPEOF(throw_value) != PIKE_T_FREE) { -  exit_mc_marker_hash(); -  free (mc_work_queue + 1); +  mc_work_queue++; /* Compensate for 1-based indexing. */ +  free(mc_work_queue);    mc_work_queue = NULL; -  +  stop_mc();    throw_severity = THROW_ERROR;    pike_throw();    }    }   #if defined (PIKE_DEBUG) || defined (MEMORY_COUNT_DEBUG)    mc_ref_from = (void *) (ptrdiff_t) -1;   #endif       /* If no things that might be indirectly incomplete have been    * enqueued then there's no need to do another mark external pass. */
pike.git/src/gc.c:5813:    list = &mc_incomplete;    while (1) {    /* First go through the incomplete list to visit externals,    * then the indirectly incomplete list where all the new    * indirect externals appear. */    for (m = list->dl_next; m != list; m = m->dl_next) {    TYPE_T type = type_from_visit_fn (m->visit_fn);    assert (!(m->flags & MC_FLAG_INTERNAL));    assert (m->flags & MC_FLAG_LA_VISITED);    assert (list != &mc_incomplete || !(m->flags & MC_FLAG_CANDIDATE)); -  if (mc_block_lookahead & (1 << type)) +  if (mc_lookahead_blocked(type))    MC_DEBUG_MSG (m, "type is blocked - not visiting");    else {   #ifdef MEMORY_COUNT_DEBUG    mc_ref_from = m;    MC_DEBUG_MSG (NULL, "visiting external");   #endif    count_visits++;    count_revisits++;    m->visit_fn (m->thing, VISIT_NORMAL, m->extra);    }
pike.git/src/gc.c:5856:       {    /* We've found some internal cyclic stuff. Put it in the work    * list for the next round. */    struct mc_marker *m = mc_complete.dl_next;    assert (m != &mc_complete);    do {    assert (!(m->flags & (MC_FLAG_INTERNAL | MC_FLAG_INT_VISITED)));    m->flags |= MC_FLAG_INTERNAL;    assert (m->flags & (MC_FLAG_CANDIDATE | MC_FLAG_LA_VISITED)); -  assert (!(mc_block_lookahead & -  (1 << type_from_visit_fn (m->visit_fn)))); +  assert (!(mc_lookahead_blocked(type_from_visit_fn (m->visit_fn))));    /* The following assertion implies that the lookahead count    * already has been raised as it should. */    assert (m->flags & MC_FLAG_CANDIDATE_REF);    mc_wq_enqueue (m);    if (collect_stats && type_from_visit_fn (m->visit_fn) <= MAX_TYPE)    count_cyclic++;    MC_DEBUG_MSG (m, "enqueued cyclic internal");    m = m->dl_next;    } while (m != &mc_complete);    }
pike.git/src/gc.c:5995:    DO_AGGREGATE_ARRAY (120);    }    }    } END_AGGREGATE_ARRAY;    args++;    mapping_string_insert (opts, ind, Pike_sp - 1);    }    }       mc_pass = 0; +  visit_enter = NULL;    visit_ref = NULL; -  +  visit_leave = NULL;       DL_MAKE_EMPTY (mc_incomplete);    DL_MAKE_EMPTY (mc_indirect);   #ifdef DO_PIKE_CLEANUP -  { -  size_t e; -  for (e = 0; e < mc_marker_hash_table_size; e++) -  while (mc_marker_hash_table[e]) -  remove_mc_marker (mc_marker_hash_table[e]->thing); -  } +    #endif -  exit_mc_marker_hash(); +        assert (mc_wq_used == 1); -  free (mc_work_queue + 1); +  mc_work_queue++; /* Compensate for 1-based indexing. */ +  free(mc_work_queue);    mc_work_queue = NULL; -  +  stop_mc();       pop_n_elems (args);    push_ulongest (return_count ? count_internal : mc_counted_bytes);   } -  +  + static struct mapping *identify_loop_reverse = NULL; +  + void identify_loop_visit_enter(void *thing, int type, void *UNUSED(extra)) + { +  if (type < T_VOID) { +  /* Valid svalue type. */ +  SET_SVAL(*Pike_sp, type, 0, refs, thing); +  add_ref(((struct array *)thing)); +  Pike_sp++; +  } + } +  + void identify_loop_visit_ref(void *dst, int UNUSED(ref_type), +  visit_thing_fn *visit_dst, +  void *extra) + { +  int type = type_from_visit_fn(visit_dst); +  struct mc_marker *ref_to = find_mc_marker(dst); +  if (ref_to) { +  /* Already visited or queued for visiting. */ +  return; +  } +  +  ref_to = my_make_mc_marker(dst, visit_dst, extra); +  +  if (type != PIKE_T_UNKNOWN) { +  /* NB: low_mapping_insert() for object indices may throw errors +  * if eg lfun::`==() throws an error. We therefore instead +  * use the raw pointers as indices instead. +  */ +  struct svalue s; +  SET_SVAL(s, PIKE_T_INT, NUMBER_NUMBER, integer, (INT_TYPE)(ptrdiff_t)dst); +  mc_wq_enqueue(ref_to); +  low_mapping_insert(identify_loop_reverse, &s, Pike_sp-1, 0); +  } else { +  /* Not a valid svalue type. +  * +  * Probably T_MAPPING_DATA or T_MULTISET_DATA or similar. +  * +  * Recurse directly while we have the containing thing on the stack. +  */ +  ref_to->flags |= MC_FLAG_INT_VISITED; +  visit_dst(dst, VISIT_COMPLEX_ONLY, extra); +  } + } +  + void identify_loop_visit_leave(void *UNUSED(thing), int type, void *UNUSED(extra)) + { +  if (type < T_VOID) { +  /* Valid svalue type. */ +  pop_stack(); +  } + } +  + /*! @decl array(mixed) identify_cycle(mixed x) +  *! @belongs Pike +  *! +  *! Identify reference cycles in Pike datastructures. +  *! +  *! This function is typically used to identify why certain +  *! datastructures need the @[gc] to run to be freed. +  *! +  *! @param x +  *! Value that is believed to be involved in a reference cycle. +  *! +  *! @returns +  *! @mixed +  *! @type zero +  *! Returns @expr{UNDEFINED@} if @[x] is not member of a reference cycle. +  *! @type array(mixed) +  *! Otherwise returns an array identifying a cycle with @[x] as the first +  *! element, and where the elements refer to each other in order, and the +  *! last element refers to the first. +  *! @endmixed +  */ + void f_identify_cycle(INT32 args) + { +  struct svalue *s; +  struct mc_marker *m; +  struct svalue *k; +  +  if (args != 1) +  SIMPLE_WRONG_NUM_ARGS_ERROR("identify_loops", 1); +  +  s = Pike_sp - 1; +  +  if (!REFCOUNTED_TYPE(TYPEOF(*s))) { +  SIMPLE_ARG_TYPE_ERROR("identify_loops", 1, +  "array|multiset|mapping|object|program|string|type"); +  } +  if (TYPEOF(*s) == T_FUNCTION) { +  if (SUBTYPEOF(*s) == FUNCTION_BUILTIN) { +  SIMPLE_ARG_TYPE_ERROR("identify_loops", 1, +  "array|multiset|mapping|object|program|string|type"); +  } +  SET_SVAL_TYPE(*s, T_OBJECT); +  } +  +  start_mc(); +  +  if (TYPEOF(pike_cycle_depth_str) == PIKE_T_FREE) { +  SET_SVAL_TYPE(pike_cycle_depth_str, T_STRING); +  MAKE_CONST_STRING (pike_cycle_depth_str.u.string, "pike_cycle_depth"); +  } +  +  assert (mc_work_queue == NULL); +  mc_work_queue = malloc (MC_WQ_START_SIZE * sizeof (mc_work_queue[0])); +  if (!mc_work_queue) { +  stop_mc(); +  SIMPLE_OUT_OF_MEMORY_ERROR ("Pike.count_memory", +  MC_WQ_START_SIZE * sizeof (mc_work_queue[0])); +  } +  /* NB: 1-based indexing in mc_work_queue. */ +  mc_work_queue--; +  mc_wq_size = MC_WQ_START_SIZE; +  mc_wq_used = 1; +  mc_lookahead = -1; +  +  assert (!mc_pass); +  assert (visit_enter == NULL); +  assert (visit_ref == NULL); +  assert (visit_leave == NULL); +  +  /* There's a fair chance of there being lots of stuff being referenced, +  * so preallocate a reasonable initial size. +  */ +  identify_loop_reverse = allocate_mapping(1024); +  +  visit_enter = identify_loop_visit_enter; +  visit_ref = identify_loop_visit_ref; +  visit_leave = identify_loop_visit_leave; +  +  /* NB: This initial call will botstrap the wq_queue. */ +  visit_fn_from_type[TYPEOF(*s)](s->u.ptr, VISIT_COMPLEX_ONLY, NULL); +  + #ifdef PIKE_DEBUG +  assert (mc_ref_from == (void *) (ptrdiff_t) -1); + #endif +  +  while ((mc_ref_from = mc_wq_dequeue())) { +  if (mc_ref_from->flags & MC_FLAG_INT_VISITED) continue; +  +  mc_ref_from->flags |= MC_FLAG_INT_VISITED; +  mc_ref_from->visit_fn(mc_ref_from->thing, VISIT_COMPLEX_ONLY, NULL); +  } +  + #if defined (PIKE_DEBUG) || defined (MEMORY_COUNT_DEBUG) +  mc_ref_from = (void *) (ptrdiff_t) -1; + #endif +  +  mc_work_queue++; /* Compensate for 1-based indexing. */ +  free(mc_work_queue); +  mc_work_queue = NULL; +  +  visit_enter = NULL; +  visit_ref = NULL; +  visit_leave = NULL; +  + #ifdef PIKE_DEBUG +  if (s != Pike_sp-1) { +  Pike_fatal("Stack error in identify_loops.\n"); +  } + #endif +  +  /* NB: low_mapping_lookup() for object indices may throw errors +  * if eg lfun::`==() throws an error. We therefore instead +  * use the raw pointers as indices instead. +  */ +  push_int((INT_TYPE)(ptrdiff_t)s->u.refs); +  while ((k = low_mapping_lookup(identify_loop_reverse, Pike_sp-1))) { +  /* NB: Since we entered this loop, we know that there's a +  * reference loop involving s, as s otherwise wouldn't +  * have been in the mapping. +  */ +  pop_stack(); +  push_svalue(k); +  push_int((INT_TYPE)(ptrdiff_t)k->u.refs); +  if (k->u.refs == s->u.refs) { +  /* Found! */ +  pop_stack(); +  break; +  } +  } +  +  free_mapping(identify_loop_reverse); +  +  stop_mc(); +  +  if (!k) { +  push_undefined(); +  } else { +  /* NB: We push s an extra time last above, to simplify the +  * reversing below. +  */ +  f_aggregate(Pike_sp - (s + 1)); +  f_reverse(1); +  } + } +  + void init_mc(void) + { +  init_interleave_mutex(&mc_mutex); + } +  + void exit_mc(void) + { +  exit_interleave_mutex(&mc_mutex); + }