pike.git / src / gc.c

version» Context lines:

pike.git/src/gc.c:20:   #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 "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;   double gc_time_ratio = 0.05;   double gc_garbage_ratio_high = 0.5;   double gc_min_time_ratio = 1.0/10000.0; /* Martys constant. */      /* This slowness factor approximately corresponds to the average over    * the last ten gc rounds. (0.9 == 1 - 1/10) */   double gc_average_slowness = 0.9;    -  + /* High-level callbacks. +  * NB: These are initialized from builtin.cmod. +  */ + /* Callback called when gc() starts. */ + struct svalue gc_pre_cb; +  + /* Callback called when the mark and sweep phase of the gc() is done. */ + 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 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:413: Inside #if defined(PIKE_DEBUG)
  static unsigned mark_live, frame_rot, link_search;   static unsigned gc_extra_refs = 0;   static unsigned tot_cycle_checked = 0, tot_mark_live = 0, tot_frame_rot = 0;   static unsigned gc_rec_frame_seq_max;   #endif      static unsigned rec_frames, link_frames, free_extra_frames;   static unsigned max_rec_frames, max_link_frames;   static unsigned tot_max_rec_frames = 0, tot_max_link_frames = 0, tot_max_free_extra_frames = 0;    - #undef INIT_BLOCK - #define INIT_BLOCK(f) do { \ -  if (++rec_frames > max_rec_frames) \ -  max_rec_frames = rec_frames; \ -  } while (0) - #undef EXIT_BLOCK - #define EXIT_BLOCK(f) do { \ -  DO_IF_DEBUG ({ \ -  if (f->rf_flags & GC_FRAME_FREED) \ -  gc_fatal (f->data, 0, "Freeing gc_rec_frame twice.\n"); \ -  f->rf_flags |= GC_FRAME_FREED; \ -  f->u.link_top = (struct link_frame *) (ptrdiff_t) -1; \ -  f->prev = f->next = f->cycle_id = f->cycle_piece = \ -  (struct gc_rec_frame *) (ptrdiff_t) -1; \ -  }); \ -  rec_frames--; \ -  } while (0) + struct block_allocator gc_rec_frame_allocator = +  BA_INIT_PAGES(sizeof(struct gc_rec_frame), 2);    - BLOCK_ALLOC_FILL_PAGES (gc_rec_frame, 2) + static void really_free_gc_rec_frame(struct gc_rec_frame * f) { + #ifdef PIKE_DEBUG +  if (f->rf_flags & GC_FRAME_FREED) +  gc_fatal (f->data, 0, "Freeing gc_rec_frame twice.\n"); +  f->rf_flags |= GC_FRAME_FREED; +  f->u.link_top = (struct link_frame *) (ptrdiff_t) -1; +  f->prev = f->next = f->cycle_id = f->cycle_piece = +  (struct gc_rec_frame *) (ptrdiff_t) -1; + #endif +  rec_frames--; +  ba_free(&gc_rec_frame_allocator, f); + }    -  + void count_memory_in_gc_rec_frames(size_t *num, size_t * size) { +  ba_count_all(&gc_rec_frame_allocator, num, size); + } +    /* Link and free_extra frames are approximately the same size, so let    * them share block_alloc area. */   struct ba_mixed_frame   {    union {    struct link_frame link;    struct free_extra_frame free_extra;    struct ba_mixed_frame *next; /* For block_alloc internals. */    } u;   };    - #undef BLOCK_ALLOC_NEXT - #define BLOCK_ALLOC_NEXT u.next - #undef INIT_BLOCK - #define INIT_BLOCK(f) - #undef EXIT_BLOCK - #define EXIT_BLOCK(f) + static struct block_allocator ba_mixed_frame_allocator +  = BA_INIT_PAGES(sizeof(struct ba_mixed_frame), 2);    - BLOCK_ALLOC_FILL_PAGES (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()   { -  struct ba_mixed_frame *f = alloc_ba_mixed_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()   { -  struct ba_mixed_frame *f = alloc_ba_mixed_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)   {    link_frames--; -  really_free_ba_mixed_frame ((struct ba_mixed_frame *) f); +  ba_free(&ba_mixed_frame_allocator, f);   }      static INLINE void really_free_free_extra_frame (struct free_extra_frame *f)   {    free_extra_frames--; -  really_free_ba_mixed_frame ((struct ba_mixed_frame *) f); +  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;      struct callback_list gc_callbacks;   
pike.git/src/gc.c:515:   #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)))
pike.git/src/gc.c:539:   {    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) - void *gc_found_in = NULL; - int gc_found_in_type = PIKE_T_UNKNOWN; - const char *gc_found_place = NULL; + 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;   #endif      #if defined (PIKE_DEBUG) || defined (GC_CYCLE_DEBUG)   
pike.git/src/gc.c:567: Inside #if defined (PIKE_DEBUG) || defined (GC_CYCLE_DEBUG)
   f->cycle_id, f->cycle_piece, f->u.link_top);   }      /* If p* isn't NULL then p*_name will be written out next to the    * matching frame in the stack, if any is found. */   static void describe_rec_stack (struct gc_rec_frame *p1, const char *p1_name,    struct gc_rec_frame *p2, const char *p2_name,    struct gc_rec_frame *p3, const char *p3_name)   {    struct gc_rec_frame *l, *cp; -  size_t longest; +  size_t longest = 0;       if (p1) longest = strlen (p1_name);    if (p2) {size_t l = strlen (p2_name); if (l > longest) longest = l;}    if (p3) {size_t l = strlen (p3_name); if (l > longest) longest = l;}    longest++;       /* Note: Stack is listed from top to bottom, but cycle piece lists    * are lists from first to last, i.e. reverse order. */       for (l = stack_top; l != &sentinel_frame; l = l->prev) {
pike.git/src/gc.c:994:    putc(']', stderr);    }    putc('\n', stderr);    }    else    fprintf(stderr, "no marker\n");   }      #endif /* PIKE_DEBUG */    - static void debug_gc_fatal_va (void *a, int type, int flags, + 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);      #ifdef PIKE_DEBUG    if (a) {    void *inblock;    /* Temporarily jumping out of gc to avoid being caught in debug
pike.git/src/gc.c:2032:      void exit_gc(void)   {    if (gc_evaluator_callback) {    remove_callback(gc_evaluator_callback);    gc_evaluator_callback = NULL;    }    if (!gc_keep_markers)    cleanup_markers();    -  free_all_gc_rec_frame_blocks(); -  free_all_ba_mixed_frame_blocks(); +  ba_free_all(&gc_rec_frame_allocator); +  ba_free_all(&ba_mixed_frame_allocator);      #ifdef PIKE_DEBUG    if (gc_is_watching) {    fprintf(stderr, "## Exiting gc and resetting watches for %d things.\n",    gc_is_watching);    gc_is_watching = 0;    }   #endif   }   
pike.git/src/gc.c:2070: Inside #if defined(PIKE_DEBUG)
   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)   {    int tmp, orig_in_gc = Pike_in_gc;    const char *orig_gc_found_place = gc_found_place;    int i=0; -  if(!marker_blocks) +  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;
pike.git/src/gc.c:2340:   #define CHECK_KILL_LIST_FRAME(f) \    do check_kill_list_frame ((f), __FILE__, __LINE__); while (0)      static void check_rec_stack (struct gc_rec_frame *p1, const char *p1n,    struct gc_rec_frame *p2, const char *p2n,    const char *file, int line)   {    /* This debug check is disabled during the final cleanup since this    * is O(n^2) on the stack size, and the stack gets a lot larger then. */    if (gc_debug && !gc_destruct_everything) { -  struct gc_rec_frame *l, *last_cycle_id; +  struct gc_rec_frame *l, *last_cycle_id = NULL;    for (l = &sentinel_frame; l != stack_top;) {    l = l->next;    check_rec_stack_frame (l, p1, p1n, p2, p2n, file, line);    if (l->cycle_id == l)    last_cycle_id = l;    else if (l->cycle_id != last_cycle_id)    rec_stack_fatal (l, "err", p1, p1n, p2, p2n, file, line,    "Unexpected cycle id for frame %p.\n", l);    else if (l->rf_flags & GC_PREV_WEAK)    rec_stack_fatal (l, "err", p1, p1n, p2, p2n, file, line,
pike.git/src/gc.c:2629: Inside #if defined(GC_STACK_DEBUG)
  #ifdef GC_STACK_DEBUG    fprintf (stderr, "push link %p [%p in %p]: ", l, stack_top->u.link_top, stack_top);    describe_link_frame (l);    fputc('\n', stderr);   #endif    stack_top->u.link_top = l;   }      static struct gc_rec_frame *gc_cycle_enqueue_rec (void *data)   { -  struct gc_rec_frame *r = alloc_gc_rec_frame(); +  struct gc_rec_frame *r = +  (struct gc_rec_frame*)ba_alloc(&gc_rec_frame_allocator); +  if (++rec_frames > max_rec_frames) max_rec_frames = rec_frames;   #ifdef PIKE_DEBUG    if (Pike_in_gc != GC_PASS_CYCLE)    gc_fatal(data, 0, "Use of the gc frame stack outside the cycle check pass.\n");    r->next = (struct gc_rec_frame *) (ptrdiff_t) -1;   #endif    r->data = data;    r->u.link_top = NULL;    r->prev = stack_top;    r->cycle_id = r;    r->cycle_piece = NULL;
pike.git/src/gc.c:3419: Inside #if 0
   else *obj_arr_ = resize_array(*obj_arr_, 0);    }    if (!p) break;    }    }   #endif       CALL_AND_UNSET_ONERROR(tmp);   }    - size_t do_gc(void *ignored, int explicit_call) + 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;   #endif   #ifdef PIKE_DEBUG    unsigned obj_count;
pike.git/src/gc.c:3462:    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();   #ifdef GC_DEBUG    gc_debug = (GC_DEBUG + 0) || 1;   #else    gc_debug = d_flag;   #endif   #ifdef PIKE_DEBUG    SET_ONERROR(uwp, fatal_on_error, "Shouldn't get an exception inside the gc.\n");    if (gc_is_watching)
pike.git/src/gc.c:3777:    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;   #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    gc_destruct_everything ? DESTRUCT_CLEANUP :   #endif    DESTRUCT_GC;      #ifdef PIKE_DEBUG    {    struct gc_rec_frame *r;
pike.git/src/gc.c:3833:    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); +  pop_stack(); +  }       destruct_object (o, reason);    free_object(o);    gc_free_extra_ref(o);   #if defined (PIKE_DEBUG) || defined (DO_PIKE_CLEANUP)    destroy_count++;   #endif    really_free_gc_rec_frame (kill_list);    kill_list = next;    }
pike.git/src/gc.c:4110: Inside #if defined(ALWAYS_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;   #endif +  +  if (!SAFE_IS_ZERO(&gc_done_cb)) { +  push_int(unreferenced); +  safe_apply_svalue(&gc_done_cb, 1, 1); +  pop_stack(); +  } +     return unreferenced;   }      /*! @decl mapping(string:int|float) gc_status()    *! @belongs Debug    *!    *! Get statistics from the garbage collector.    *!    *! @returns    *! A mapping with the following content will be returned:
pike.git/src/gc.c:4348:   #endif /* PIKE_DEBUG */   }      /* Visit things API */      PMOD_EXPORT visit_ref_cb *visit_ref = 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_REF_TYPE + 1] = { + 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_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,
pike.git/src/gc.c:5135:    assert (ref_to->la_count >= old_la_count);    if (ref_to->la_count > old_la_count) {    mc_wq_enqueue (ref_to);    MC_DEBUG_MSG (ref_to, "enqueued");    mc_enqueued_noninternal = 1;    }    else    MC_DEBUG_MSG (ref_to, "not enqueued");   }    - static void pass_mark_external_visit_ref (void *thing, int ref_type, -  visit_thing_fn *visit_fn, void *extra) + static void pass_mark_external_visit_ref (void *thing, int UNUSED(ref_type), +  visit_thing_fn *UNUSED(visit_fn), void *UNUSED(extra))   {    struct mc_marker *ref_to = find_mc_marker (thing);       assert (mc_pass == MC_PASS_MARK_EXTERNAL);       if (ref_to) {    if ((ref_to->flags & (MC_FLAG_INT_VISITED | MC_FLAG_LA_VISITED)) ==    MC_FLAG_LA_VISITED) {    /* Only interested in existing lookahead things, except those on    * the "fringe" that haven't been visited. */
pike.git/src/gc.c:5550:    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 (TYPEOF(*s) > MAX_REF_TYPE) { +  else if (!REFCOUNTED_TYPE(TYPEOF(*s))) {    exit_mc_marker_hash();    free (mc_work_queue + 1);    mc_work_queue = NULL;    SIMPLE_ARG_TYPE_ERROR (    "count_memory", i + args + 1,    "array|multiset|mapping|object|program|string|type|int");    }       else {    if (TYPEOF(*s) == T_FUNCTION) {