gc.c

Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   gc.c -
00004 
00005   $Author: nagachika $
00006   created at: Tue Oct  5 09:44:46 JST 1993
00007 
00008   Copyright (C) 1993-2007 Yukihiro Matsumoto
00009   Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
00010   Copyright (C) 2000  Information-technology Promotion Agency, Japan
00011 
00012 **********************************************************************/
00013 
00014 #include "ruby/ruby.h"
00015 #include "ruby/st.h"
00016 #include "ruby/re.h"
00017 #include "ruby/io.h"
00018 #include "ruby/thread.h"
00019 #include "ruby/util.h"
00020 #include "ruby/debug.h"
00021 #include "eval_intern.h"
00022 #include "vm_core.h"
00023 #include "internal.h"
00024 #include "gc.h"
00025 #include "constant.h"
00026 #include "ruby_atomic.h"
00027 #include "probes.h"
00028 #include <stdio.h>
00029 #include <stdarg.h>
00030 #include <setjmp.h>
00031 #include <sys/types.h>
00032 #include <assert.h>
00033 
00034 #ifndef __has_feature
00035 # define __has_feature(x) 0
00036 #endif
00037 
00038 #ifndef HAVE_MALLOC_USABLE_SIZE
00039 # ifdef _WIN32
00040 #   define HAVE_MALLOC_USABLE_SIZE
00041 #   define malloc_usable_size(a) _msize(a)
00042 # elif defined HAVE_MALLOC_SIZE
00043 #   define HAVE_MALLOC_USABLE_SIZE
00044 #   define malloc_usable_size(a) malloc_size(a)
00045 # endif
00046 #endif
00047 #ifdef HAVE_MALLOC_USABLE_SIZE
00048 # ifdef HAVE_MALLOC_H
00049 #  include <malloc.h>
00050 # elif defined(HAVE_MALLOC_NP_H)
00051 #  include <malloc_np.h>
00052 # elif defined(HAVE_MALLOC_MALLOC_H)
00053 #  include <malloc/malloc.h>
00054 # endif
00055 #endif
00056 
00057 #if /* is ASAN enabled? */ \
00058     __has_feature(address_sanitizer) /* Clang */ || \
00059     defined(__SANITIZE_ADDRESS__)  /* GCC 4.8.x */
00060   #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \
00061         __attribute__((no_address_safety_analysis)) \
00062         __attribute__((noinline))
00063 #else
00064   #define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
00065 #endif
00066 
00067 #ifdef HAVE_SYS_TIME_H
00068 #include <sys/time.h>
00069 #endif
00070 
00071 #ifdef HAVE_SYS_RESOURCE_H
00072 #include <sys/resource.h>
00073 #endif
00074 #if defined(__native_client__) && defined(NACL_NEWLIB)
00075 # include "nacl/resource.h"
00076 # undef HAVE_POSIX_MEMALIGN
00077 # undef HAVE_MEMALIGN
00078 
00079 #endif
00080 
00081 #if defined _WIN32 || defined __CYGWIN__
00082 #include <windows.h>
00083 #elif defined(HAVE_POSIX_MEMALIGN)
00084 #elif defined(HAVE_MEMALIGN)
00085 #include <malloc.h>
00086 #endif
00087 
00088 #define rb_setjmp(env) RUBY_SETJMP(env)
00089 #define rb_jmp_buf rb_jmpbuf_t
00090 
00091 #if defined(HAVE_RB_GC_GUARDED_PTR) && HAVE_RB_GC_GUARDED_PTR
00092 volatile VALUE *
00093 rb_gc_guarded_ptr(volatile VALUE *ptr)
00094 {
00095     return ptr;
00096 }
00097 #endif
00098 
00099 #ifndef GC_HEAP_FREE_SLOTS
00100 #define GC_HEAP_FREE_SLOTS  4096
00101 #endif
00102 #ifndef GC_HEAP_INIT_SLOTS
00103 #define GC_HEAP_INIT_SLOTS 10000
00104 #endif
00105 #ifndef GC_HEAP_GROWTH_FACTOR
00106 #define GC_HEAP_GROWTH_FACTOR 1.8
00107 #endif
00108 #ifndef GC_HEAP_GROWTH_MAX_SLOTS
00109 #define GC_HEAP_GROWTH_MAX_SLOTS 0 /* 0 is disable */
00110 #endif
00111 #ifndef GC_HEAP_OLDOBJECT_LIMIT_FACTOR
00112 #define GC_HEAP_OLDOBJECT_LIMIT_FACTOR 2.0
00113 #endif
00114 
00115 #ifndef GC_MALLOC_LIMIT_MIN
00116 #define GC_MALLOC_LIMIT_MIN (16 * 1024 * 1024 /* 16MB */)
00117 #endif
00118 #ifndef GC_MALLOC_LIMIT_MAX
00119 #define GC_MALLOC_LIMIT_MAX (32 * 1024 * 1024 /* 32MB */)
00120 #endif
00121 #ifndef GC_MALLOC_LIMIT_GROWTH_FACTOR
00122 #define GC_MALLOC_LIMIT_GROWTH_FACTOR 1.4
00123 #endif
00124 
00125 #ifndef GC_OLDMALLOC_LIMIT_MIN
00126 #define GC_OLDMALLOC_LIMIT_MIN (16 * 1024 * 1024 /* 16MB */)
00127 #endif
00128 #ifndef GC_OLDMALLOC_LIMIT_GROWTH_FACTOR
00129 #define GC_OLDMALLOC_LIMIT_GROWTH_FACTOR 1.2
00130 #endif
00131 #ifndef GC_OLDMALLOC_LIMIT_MAX
00132 #define GC_OLDMALLOC_LIMIT_MAX (128 * 1024 * 1024 /* 128MB */)
00133 #endif
00134 
00135 typedef struct {
00136     unsigned int heap_init_slots;
00137     unsigned int heap_free_slots;
00138     double growth_factor;
00139     unsigned int growth_max_slots;
00140     double oldobject_limit_factor;
00141     unsigned int malloc_limit_min;
00142     unsigned int malloc_limit_max;
00143     double malloc_limit_growth_factor;
00144     unsigned int oldmalloc_limit_min;
00145     unsigned int oldmalloc_limit_max;
00146     double oldmalloc_limit_growth_factor;
00147 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00148     VALUE gc_stress;
00149 #endif
00150 } ruby_gc_params_t;
00151 
00152 static ruby_gc_params_t gc_params = {
00153     GC_HEAP_INIT_SLOTS,
00154     GC_HEAP_FREE_SLOTS,
00155     GC_HEAP_GROWTH_FACTOR,
00156     GC_HEAP_GROWTH_MAX_SLOTS,
00157     GC_HEAP_OLDOBJECT_LIMIT_FACTOR,
00158     GC_MALLOC_LIMIT_MIN,
00159     GC_MALLOC_LIMIT_MAX,
00160     GC_MALLOC_LIMIT_GROWTH_FACTOR,
00161     GC_OLDMALLOC_LIMIT_MIN,
00162     GC_OLDMALLOC_LIMIT_MAX,
00163     GC_OLDMALLOC_LIMIT_GROWTH_FACTOR,
00164 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00165     FALSE,
00166 #endif
00167 };
00168 
00169 /* GC_DEBUG:
00170  *  enable to embed GC debugging information.
00171  */
00172 #ifndef GC_DEBUG
00173 #define GC_DEBUG 0
00174 #endif
00175 
00176 #if USE_RGENGC
00177 /* RGENGC_DEBUG:
00178  * 1: basic information
00179  * 2: remember set operation
00180  * 3: mark
00181  * 4:
00182  * 5: sweep
00183  */
00184 #ifndef RGENGC_DEBUG
00185 #define RGENGC_DEBUG       0
00186 #endif
00187 
00188 /* RGENGC_CHECK_MODE
00189  * 0: disable all assertions
00190  * 1: enable assertions (to debug RGenGC)
00191  * 2: enable generational bits check (for debugging)
00192  * 3: enable livness check
00193  * 4: show all references
00194  */
00195 #ifndef RGENGC_CHECK_MODE
00196 #define RGENGC_CHECK_MODE  0
00197 #endif
00198 
00199 /* RGENGC_PROFILE
00200  * 0: disable RGenGC profiling
00201  * 1: enable profiling for basic information
00202  * 2: enable profiling for each types
00203  */
00204 #ifndef RGENGC_PROFILE
00205 #define RGENGC_PROFILE     0
00206 #endif
00207 
00208 /* RGENGC_THREEGEN
00209  * Enable/disable three gen GC.
00210  * 0: Infant gen -> Old gen
00211  * 1: Infant gen -> Young -> Old gen
00212  */
00213 #ifndef RGENGC_THREEGEN
00214 #define RGENGC_THREEGEN    0
00215 #endif
00216 
00217 /* RGENGC_ESTIMATE_OLDMALLOC
00218  * Enable/disable to estimate increase size of malloc'ed size by old objects.
00219  * If estimation exceeds threashold, then will invoke full GC.
00220  * 0: disable estimation.
00221  * 1: enable estimation.
00222  */
00223 #ifndef RGENGC_ESTIMATE_OLDMALLOC
00224 #define RGENGC_ESTIMATE_OLDMALLOC 1
00225 #endif
00226 
00227 #else /* USE_RGENGC */
00228 
00229 #define RGENGC_DEBUG       0
00230 #define RGENGC_CHECK_MODE  0
00231 #define RGENGC_PROFILE     0
00232 #define RGENGC_THREEGEN    0
00233 #define RGENGC_ESTIMATE_OLDMALLOC 0
00234 
00235 #endif /* USE_RGENGC */
00236 
00237 #ifndef GC_PROFILE_MORE_DETAIL
00238 #define GC_PROFILE_MORE_DETAIL 0
00239 #endif
00240 #ifndef GC_PROFILE_DETAIL_MEMORY
00241 #define GC_PROFILE_DETAIL_MEMORY 0
00242 #endif
00243 #ifndef GC_ENABLE_LAZY_SWEEP
00244 #define GC_ENABLE_LAZY_SWEEP   1
00245 #endif
00246 #ifndef CALC_EXACT_MALLOC_SIZE
00247 #define CALC_EXACT_MALLOC_SIZE 0
00248 #endif
00249 #if defined(HAVE_MALLOC_USABLE_SIZE) || CALC_EXACT_MALLOC_SIZE > 0
00250 #ifndef MALLOC_ALLOCATED_SIZE
00251 #define MALLOC_ALLOCATED_SIZE 0
00252 #endif
00253 #else
00254 #define MALLOC_ALLOCATED_SIZE 0
00255 #endif
00256 #ifndef MALLOC_ALLOCATED_SIZE_CHECK
00257 #define MALLOC_ALLOCATED_SIZE_CHECK 0
00258 #endif
00259 
00260 typedef enum {
00261     GPR_FLAG_NONE               = 0x000,
00262     /* major reason */
00263     GPR_FLAG_MAJOR_BY_NOFREE    = 0x001,
00264     GPR_FLAG_MAJOR_BY_OLDGEN    = 0x002,
00265     GPR_FLAG_MAJOR_BY_SHADY     = 0x004,
00266     GPR_FLAG_MAJOR_BY_RESCAN    = 0x008,
00267     GPR_FLAG_MAJOR_BY_STRESS    = 0x010,
00268 #if RGENGC_ESTIMATE_OLDMALLOC
00269     GPR_FLAG_MAJOR_BY_OLDMALLOC = 0x020,
00270 #endif
00271     GPR_FLAG_MAJOR_MASK         = 0x0ff,
00272 
00273     /* gc reason */
00274     GPR_FLAG_NEWOBJ             = 0x100,
00275     GPR_FLAG_MALLOC             = 0x200,
00276     GPR_FLAG_METHOD             = 0x400,
00277     GPR_FLAG_CAPI               = 0x800,
00278     GPR_FLAG_STRESS            = 0x1000,
00279 
00280     /* others */
00281     GPR_FLAG_IMMEDIATE_SWEEP   = 0x2000,
00282     GPR_FLAG_HAVE_FINALIZE     = 0x4000
00283 } gc_profile_record_flag;
00284 
00285 typedef struct gc_profile_record {
00286     int flags;
00287 
00288     double gc_time;
00289     double gc_invoke_time;
00290 
00291     size_t heap_total_objects;
00292     size_t heap_use_size;
00293     size_t heap_total_size;
00294 
00295 #if GC_PROFILE_MORE_DETAIL
00296     double gc_mark_time;
00297     double gc_sweep_time;
00298 
00299     size_t heap_use_pages;
00300     size_t heap_live_objects;
00301     size_t heap_free_objects;
00302 
00303     size_t allocate_increase;
00304     size_t allocate_limit;
00305 
00306     double prepare_time;
00307     size_t removing_objects;
00308     size_t empty_objects;
00309 #if GC_PROFILE_DETAIL_MEMORY
00310     long maxrss;
00311     long minflt;
00312     long majflt;
00313 #endif
00314 #endif
00315 #if MALLOC_ALLOCATED_SIZE
00316     size_t allocated_size;
00317 #endif
00318 
00319 #if RGENGC_PROFILE > 0
00320     size_t old_objects;
00321     size_t remembered_normal_objects;
00322     size_t remembered_shady_objects;
00323 #endif
00324 } gc_profile_record;
00325 
00326 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
00327 #pragma pack(push, 1) /* magic for reducing sizeof(RVALUE): 24 -> 20 */
00328 #endif
00329 
00330 typedef struct RVALUE {
00331     union {
00332         struct {
00333             VALUE flags;                /* always 0 for freed obj */
00334             struct RVALUE *next;
00335         } free;
00336         struct RBasic  basic;
00337         struct RObject object;
00338         struct RClass  klass;
00339         struct RFloat  flonum;
00340         struct RString string;
00341         struct RArray  array;
00342         struct RRegexp regexp;
00343         struct RHash   hash;
00344         struct RData   data;
00345         struct RTypedData   typeddata;
00346         struct RStruct rstruct;
00347         struct RBignum bignum;
00348         struct RFile   file;
00349         struct RNode   node;
00350         struct RMatch  match;
00351         struct RRational rational;
00352         struct RComplex complex;
00353         struct {
00354             struct RBasic basic;
00355             VALUE v1;
00356             VALUE v2;
00357             VALUE v3;
00358         } values;
00359     } as;
00360 #if GC_DEBUG
00361     const char *file;
00362     VALUE line;
00363 #endif
00364 } RVALUE;
00365 
00366 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
00367 #pragma pack(pop)
00368 #endif
00369 
00370 typedef uintptr_t bits_t;
00371 enum {
00372     BITS_SIZE = sizeof(bits_t),
00373     BITS_BITLENGTH = ( BITS_SIZE * CHAR_BIT )
00374 };
00375 
00376 struct heap_page_header {
00377     struct heap_page *page;
00378 };
00379 
00380 struct heap_page_body {
00381     struct heap_page_header header;
00382     /* char gap[];      */
00383     /* RVALUE values[]; */
00384 };
00385 
00386 struct gc_list {
00387     VALUE *varptr;
00388     struct gc_list *next;
00389 };
00390 
00391 #define STACK_CHUNK_SIZE 500
00392 
00393 typedef struct stack_chunk {
00394     VALUE data[STACK_CHUNK_SIZE];
00395     struct stack_chunk *next;
00396 } stack_chunk_t;
00397 
00398 typedef struct mark_stack {
00399     stack_chunk_t *chunk;
00400     stack_chunk_t *cache;
00401     size_t index;
00402     size_t limit;
00403     size_t cache_size;
00404     size_t unused_cache_size;
00405 } mark_stack_t;
00406 
00407 typedef struct rb_heap_struct {
00408     struct heap_page *pages;
00409     struct heap_page *free_pages;
00410     struct heap_page *using_page;
00411     struct heap_page *sweep_pages;
00412     RVALUE *freelist;
00413     size_t page_length;      /* total page count in a heap */
00414     size_t total_slots;      /* total slot count (page_length * HEAP_OBJ_LIMIT) */
00415 } rb_heap_t;
00416 
00417 typedef struct rb_objspace {
00418     struct {
00419         size_t limit;
00420         size_t increase;
00421 #if MALLOC_ALLOCATED_SIZE
00422         size_t allocated_size;
00423         size_t allocations;
00424 #endif
00425     } malloc_params;
00426 
00427     rb_heap_t eden_heap;
00428     rb_heap_t tomb_heap; /* heap for zombies and ghosts */
00429 
00430     struct {
00431         struct heap_page **sorted;
00432         size_t used;
00433         size_t length;
00434         RVALUE *range[2];
00435 
00436         size_t limit;
00437         size_t increment;
00438 
00439         size_t swept_slots;
00440         size_t min_free_slots;
00441         size_t max_free_slots;
00442 
00443         /* final */
00444         size_t final_slots;
00445         RVALUE *deferred_final;
00446     } heap_pages;
00447 
00448     struct {
00449         int dont_gc;
00450         int dont_lazy_sweep;
00451         int during_gc;
00452         rb_atomic_t finalizing;
00453     } flags;
00454     st_table *finalizer_table;
00455     mark_stack_t mark_stack;
00456     struct {
00457         int run;
00458         gc_profile_record *records;
00459         gc_profile_record *current_record;
00460         size_t next_index;
00461         size_t size;
00462 
00463 #if GC_PROFILE_MORE_DETAIL
00464         double prepare_time;
00465 #endif
00466         double invoke_time;
00467 
00468 #if USE_RGENGC
00469         size_t minor_gc_count;
00470         size_t major_gc_count;
00471 #if RGENGC_PROFILE > 0
00472         size_t generated_normal_object_count;
00473         size_t generated_shady_object_count;
00474         size_t shade_operation_count;
00475         size_t promote_infant_count;
00476 #if RGENGC_THREEGEN
00477         size_t promote_young_count;
00478 #endif
00479         size_t remembered_normal_object_count;
00480         size_t remembered_shady_object_count;
00481 
00482 #if RGENGC_PROFILE >= 2
00483         size_t generated_normal_object_count_types[RUBY_T_MASK];
00484         size_t generated_shady_object_count_types[RUBY_T_MASK];
00485         size_t shade_operation_count_types[RUBY_T_MASK];
00486         size_t promote_infant_types[RUBY_T_MASK];
00487 #if RGENGC_THREEGEN
00488         size_t promote_young_types[RUBY_T_MASK];
00489 #endif
00490         size_t remembered_normal_object_count_types[RUBY_T_MASK];
00491         size_t remembered_shady_object_count_types[RUBY_T_MASK];
00492 #endif
00493 #endif /* RGENGC_PROFILE */
00494 #endif /* USE_RGENGC */
00495 
00496         /* temporary profiling space */
00497         double gc_sweep_start_time;
00498         size_t total_allocated_object_num_at_gc_start;
00499         size_t heap_used_at_gc_start;
00500 
00501         /* basic statistics */
00502         size_t count;
00503         size_t total_allocated_object_num;
00504         size_t total_freed_object_num;
00505         int latest_gc_info;
00506     } profile;
00507     struct gc_list *global_list;
00508     rb_event_flag_t hook_events; /* this place may be affinity with memory cache */
00509     VALUE gc_stress;
00510 
00511     struct mark_func_data_struct {
00512         void *data;
00513         void (*mark_func)(VALUE v, void *data);
00514     } *mark_func_data;
00515 
00516 #if USE_RGENGC
00517     struct {
00518         int during_minor_gc;
00519         int parent_object_is_old;
00520 
00521         int need_major_gc;
00522 
00523         size_t last_major_gc;
00524 
00525         size_t remembered_shady_object_count;
00526         size_t remembered_shady_object_limit;
00527         size_t old_object_count;
00528         size_t old_object_limit;
00529 #if RGENGC_THREEGEN
00530         size_t young_object_count;
00531 #endif
00532 
00533 #if RGENGC_ESTIMATE_OLDMALLOC
00534         size_t oldmalloc_increase;
00535         size_t oldmalloc_increase_limit;
00536 #endif
00537 
00538 #if RGENGC_CHECK_MODE >= 2
00539         struct st_table *allrefs_table;
00540         size_t error_count;
00541 #endif
00542     } rgengc;
00543 #endif /* USE_RGENGC */
00544 } rb_objspace_t;
00545 
00546 
00547 #ifndef HEAP_ALIGN_LOG
00548 /* default tiny heap size: 16KB */
00549 #define HEAP_ALIGN_LOG 14
00550 #endif
00551 #define CEILDIV(i, mod) (((i) + (mod) - 1)/(mod))
00552 enum {
00553     HEAP_ALIGN = (1UL << HEAP_ALIGN_LOG),
00554     HEAP_ALIGN_MASK = (~(~0UL << HEAP_ALIGN_LOG)),
00555     REQUIRED_SIZE_BY_MALLOC = (sizeof(size_t) * 5),
00556     HEAP_SIZE = (HEAP_ALIGN - REQUIRED_SIZE_BY_MALLOC),
00557     HEAP_OBJ_LIMIT = (unsigned int)((HEAP_SIZE - sizeof(struct heap_page_header))/sizeof(struct RVALUE)),
00558     HEAP_BITMAP_LIMIT = CEILDIV(CEILDIV(HEAP_SIZE, sizeof(struct RVALUE)), BITS_BITLENGTH),
00559     HEAP_BITMAP_SIZE = ( BITS_SIZE * HEAP_BITMAP_LIMIT),
00560     HEAP_BITMAP_PLANES = USE_RGENGC ? 3 : 1 /* RGENGC: mark bits, rememberset bits and oldgen bits */
00561 };
00562 
00563 struct heap_page {
00564     struct heap_page_body *body;
00565     RVALUE *freelist;
00566     RVALUE *start;
00567     size_t final_slots;
00568     size_t limit;
00569     struct heap_page *next;
00570     struct heap_page *prev;
00571     struct heap_page *free_next;
00572     rb_heap_t *heap;
00573     int before_sweep;
00574 
00575     bits_t mark_bits[HEAP_BITMAP_LIMIT];
00576 #if USE_RGENGC
00577     bits_t rememberset_bits[HEAP_BITMAP_LIMIT];
00578     bits_t oldgen_bits[HEAP_BITMAP_LIMIT];
00579 #endif
00580 };
00581 
00582 #define GET_PAGE_BODY(x)             ((struct heap_page_body *)((bits_t)(x) & ~(HEAP_ALIGN_MASK)))
00583 #define GET_PAGE_HEADER(x)           (&GET_PAGE_BODY(x)->header)
00584 #define GET_HEAP_PAGE(x)             (GET_PAGE_HEADER(x)->page)
00585 #define GET_HEAP_MARK_BITS(x)        (&GET_HEAP_PAGE(x)->mark_bits[0])
00586 #define GET_HEAP_REMEMBERSET_BITS(x) (&GET_HEAP_PAGE(x)->rememberset_bits[0])
00587 #define GET_HEAP_OLDGEN_BITS(x)      (&GET_HEAP_PAGE(x)->oldgen_bits[0])
00588 #define NUM_IN_PAGE(p)               (((bits_t)(p) & HEAP_ALIGN_MASK)/sizeof(RVALUE))
00589 #define BITMAP_INDEX(p)              (NUM_IN_PAGE(p) / BITS_BITLENGTH )
00590 #define BITMAP_OFFSET(p)             (NUM_IN_PAGE(p) & (BITS_BITLENGTH-1))
00591 #define BITMAP_BIT(p)                ((bits_t)1 << BITMAP_OFFSET(p))
00592 /* Bitmap Operations */
00593 #define MARKED_IN_BITMAP(bits, p)    ((bits)[BITMAP_INDEX(p)] & BITMAP_BIT(p))
00594 #define MARK_IN_BITMAP(bits, p)      ((bits)[BITMAP_INDEX(p)] = (bits)[BITMAP_INDEX(p)] | BITMAP_BIT(p))
00595 #define CLEAR_IN_BITMAP(bits, p)     ((bits)[BITMAP_INDEX(p)] = (bits)[BITMAP_INDEX(p)] & ~BITMAP_BIT(p))
00596 
00597 /* Aliases */
00598 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00599 #define rb_objspace (*GET_VM()->objspace)
00600 #define ruby_initial_gc_stress  gc_params.gc_stress
00601 VALUE *ruby_initial_gc_stress_ptr = &ruby_initial_gc_stress;
00602 #else
00603 static rb_objspace_t rb_objspace = {{GC_MALLOC_LIMIT_MIN}};
00604 VALUE *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress;
00605 #endif
00606 
00607 #define malloc_limit            objspace->malloc_params.limit
00608 #define malloc_increase         objspace->malloc_params.increase
00609 #define malloc_allocated_size   objspace->malloc_params.allocated_size
00610 #define heap_pages_sorted       objspace->heap_pages.sorted
00611 #define heap_pages_used         objspace->heap_pages.used
00612 #define heap_pages_length       objspace->heap_pages.length
00613 #define heap_pages_lomem        objspace->heap_pages.range[0]
00614 #define heap_pages_himem        objspace->heap_pages.range[1]
00615 #define heap_pages_swept_slots  objspace->heap_pages.swept_slots
00616 #define heap_pages_increment    objspace->heap_pages.increment
00617 #define heap_pages_min_free_slots       objspace->heap_pages.min_free_slots
00618 #define heap_pages_max_free_slots       objspace->heap_pages.max_free_slots
00619 #define heap_pages_final_slots          objspace->heap_pages.final_slots
00620 #define heap_pages_deferred_final       objspace->heap_pages.deferred_final
00621 #define heap_eden               (&objspace->eden_heap)
00622 #define heap_tomb               (&objspace->tomb_heap)
00623 #define dont_gc                 objspace->flags.dont_gc
00624 #define during_gc               objspace->flags.during_gc
00625 #define finalizing              objspace->flags.finalizing
00626 #define finalizer_table         objspace->finalizer_table
00627 #define global_List             objspace->global_list
00628 #define ruby_gc_stress          objspace->gc_stress
00629 #define monitor_level           objspace->rgengc.monitor_level
00630 #define monitored_object_table  objspace->rgengc.monitored_object_table
00631 
00632 #define is_lazy_sweeping(heap) ((heap)->sweep_pages != 0)
00633 #if SIZEOF_LONG == SIZEOF_VOIDP
00634 # define nonspecial_obj_id(obj) (VALUE)((SIGNED_VALUE)(obj)|FIXNUM_FLAG)
00635 # define obj_id_to_ref(objid) ((objid) ^ FIXNUM_FLAG) /* unset FIXNUM_FLAG */
00636 #elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
00637 # define nonspecial_obj_id(obj) LL2NUM((SIGNED_VALUE)(obj) / 2)
00638 # define obj_id_to_ref(objid) (FIXNUM_P(objid) ? \
00639    ((objid) ^ FIXNUM_FLAG) : (NUM2PTR(objid) << 1))
00640 #else
00641 # error not supported
00642 #endif
00643 
00644 #define RANY(o) ((RVALUE*)(o))
00645 
00646 #define nomem_error GET_VM()->special_exceptions[ruby_error_nomemory]
00647 
00648 int ruby_gc_debug_indent = 0;
00649 VALUE rb_mGC;
00650 int ruby_disable_gc_stress = 0;
00651 
00652 void rb_gcdebug_print_obj_condition(VALUE obj);
00653 
00654 static void rb_objspace_call_finalizer(rb_objspace_t *objspace);
00655 static VALUE define_final0(VALUE obj, VALUE block);
00656 
00657 static void negative_size_allocation_error(const char *);
00658 static void *aligned_malloc(size_t, size_t);
00659 static void aligned_free(void *);
00660 
00661 static void init_mark_stack(mark_stack_t *stack);
00662 
00663 static VALUE lazy_sweep_enable(void);
00664 static int ready_to_gc(rb_objspace_t *objspace);
00665 static int heap_ready_to_gc(rb_objspace_t *objspace, rb_heap_t *heap);
00666 static int garbage_collect(rb_objspace_t *, int full_mark, int immediate_sweep, int reason);
00667 static int garbage_collect_body(rb_objspace_t *, int full_mark, int immediate_sweep, int reason);
00668 static int gc_heap_lazy_sweep(rb_objspace_t *objspace, rb_heap_t *heap);
00669 static void gc_rest_sweep(rb_objspace_t *objspace);
00670 static void gc_heap_rest_sweep(rb_objspace_t *objspace, rb_heap_t *heap);
00671 
00672 static void gc_mark_stacked_objects(rb_objspace_t *);
00673 static void gc_mark(rb_objspace_t *objspace, VALUE ptr);
00674 static void gc_mark_maybe(rb_objspace_t *objspace, VALUE ptr);
00675 static void gc_mark_children(rb_objspace_t *objspace, VALUE ptr);
00676 
00677 static size_t obj_memsize_of(VALUE obj, int use_tdata);
00678 
00679 static double getrusage_time(void);
00680 static inline void gc_prof_setup_new_record(rb_objspace_t *objspace, int reason);
00681 static inline void gc_prof_timer_start(rb_objspace_t *);
00682 static inline void gc_prof_timer_stop(rb_objspace_t *);
00683 static inline void gc_prof_mark_timer_start(rb_objspace_t *);
00684 static inline void gc_prof_mark_timer_stop(rb_objspace_t *);
00685 static inline void gc_prof_sweep_timer_start(rb_objspace_t *);
00686 static inline void gc_prof_sweep_timer_stop(rb_objspace_t *);
00687 static inline void gc_prof_set_malloc_info(rb_objspace_t *);
00688 static inline void gc_prof_set_heap_info(rb_objspace_t *);
00689 
00690 #define gc_prof_record(objspace) (objspace)->profile.current_record
00691 #define gc_prof_enabled(objspace) ((objspace)->profile.run && (objspace)->profile.current_record)
00692 
00693 #define rgengc_report if (RGENGC_DEBUG) rgengc_report_body
00694 static void rgengc_report_body(int level, rb_objspace_t *objspace, const char *fmt, ...);
00695 static const char * type_name(int type, VALUE obj);
00696 static const char *obj_type_name(VALUE obj);
00697 
00698 #if USE_RGENGC
00699 static int rgengc_remembered(rb_objspace_t *objspace, VALUE obj);
00700 static int rgengc_remember(rb_objspace_t *objspace, VALUE obj);
00701 static void rgengc_mark_and_rememberset_clear(rb_objspace_t *objspace, rb_heap_t *heap);
00702 static void rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap);
00703 
00704 #define FL_TEST2(x,f)         ((RGENGC_CHECK_MODE && SPECIAL_CONST_P(x)) ? (rb_bug("FL_TEST2: SPECIAL_CONST"), 0) : FL_TEST_RAW((x),(f)) != 0)
00705 #define FL_SET2(x,f)          do {if (RGENGC_CHECK_MODE && SPECIAL_CONST_P(x)) rb_bug("FL_SET2: SPECIAL_CONST");   RBASIC(x)->flags |= (f);} while (0)
00706 #define FL_UNSET2(x,f)        do {if (RGENGC_CHECK_MODE && SPECIAL_CONST_P(x)) rb_bug("FL_UNSET2: SPECIAL_CONST"); RBASIC(x)->flags &= ~(f);} while (0)
00707 
00708 #define RVALUE_WB_PROTECTED_RAW(obj)     FL_TEST2((obj), FL_WB_PROTECTED)
00709 #define RVALUE_WB_PROTECTED(obj)         RVALUE_WB_PROTECTED_RAW(check_gen_consistency((VALUE)obj))
00710 
00711 #define RVALUE_OLDGEN_BITMAP(obj) MARKED_IN_BITMAP(GET_HEAP_OLDGEN_BITS(obj), (obj))
00712 
00713 static inline int is_pointer_to_heap(rb_objspace_t *objspace, void *ptr);
00714 static inline int gc_marked(rb_objspace_t *objspace, VALUE ptr);
00715 
00716 static inline VALUE
00717 check_gen_consistency(VALUE obj)
00718 {
00719     if (RGENGC_CHECK_MODE > 0) {
00720         int old_flag = RVALUE_OLDGEN_BITMAP(obj) != 0;
00721         int promoted_flag = FL_TEST2(obj, FL_PROMOTED);
00722         rb_objspace_t *objspace = &rb_objspace;
00723 
00724         obj_memsize_of((VALUE)obj, FALSE);
00725 
00726         if (!is_pointer_to_heap(objspace, (void *)obj)) {
00727             rb_bug("check_gen_consistency: %p (%s) is not Ruby object.", (void *)obj, obj_type_name(obj));
00728         }
00729 
00730         if (promoted_flag) {
00731             if (!RVALUE_WB_PROTECTED_RAW(obj)) {
00732                 const char *type = old_flag ? "old" : "young";
00733                 rb_bug("check_gen_consistency: %p (%s) is not WB protected, but %s object.", (void *)obj, obj_type_name(obj), type);
00734             }
00735 
00736 #if !RGENGC_THREEGEN
00737             if (!old_flag) {
00738                 rb_bug("check_gen_consistency: %p (%s) is not infant, but is not old (on 2gen).", (void *)obj, obj_type_name(obj));
00739             }
00740 #endif
00741 
00742             if (old_flag && objspace->rgengc.during_minor_gc && !gc_marked(objspace, obj)) {
00743                 rb_bug("check_gen_consistency: %p (%s) is old, but is not marked while minor marking.", (void *)obj, obj_type_name(obj));
00744             }
00745         }
00746         else {
00747             if (old_flag) {
00748                 rb_bug("check_gen_consistency: %p (%s) is not infant, but is old.", (void *)obj, obj_type_name(obj));
00749             }
00750         }
00751     }
00752     return obj;
00753 }
00754 
00755 static inline VALUE
00756 RVALUE_INFANT_P(VALUE obj)
00757 {
00758     check_gen_consistency(obj);
00759     return !FL_TEST2(obj, FL_PROMOTED);
00760 }
00761 
00762 static inline VALUE
00763 RVALUE_OLD_BITMAP_P(VALUE obj)
00764 {
00765     check_gen_consistency(obj);
00766     return (RVALUE_OLDGEN_BITMAP(obj) != 0);
00767 }
00768 
00769 static inline VALUE
00770 RVALUE_OLD_P(VALUE obj)
00771 {
00772     check_gen_consistency(obj);
00773 #if RGENGC_THREEGEN
00774     return FL_TEST2(obj, FL_PROMOTED) && RVALUE_OLD_BITMAP_P(obj);
00775 #else
00776     return FL_TEST2(obj, FL_PROMOTED);
00777 #endif
00778 }
00779 
00780 static inline VALUE
00781 RVALUE_PROMOTED_P(VALUE obj)
00782 {
00783     check_gen_consistency(obj);
00784     return FL_TEST2(obj, FL_PROMOTED);
00785 }
00786 
00787 static inline void
00788 RVALUE_PROMOTE_INFANT(VALUE obj)
00789 {
00790     check_gen_consistency(obj);
00791     if (RGENGC_CHECK_MODE && !RVALUE_INFANT_P(obj)) rb_bug("RVALUE_PROMOTE_INFANT: %p (%s) is not infant object.", (void *)obj, obj_type_name(obj));
00792     FL_SET2(obj, FL_PROMOTED);
00793 #if !RGENGC_THREEGEN
00794     MARK_IN_BITMAP(GET_HEAP_OLDGEN_BITS(obj), obj);
00795 #endif
00796     check_gen_consistency(obj);
00797 
00798 #if RGENGC_PROFILE >= 1
00799     {
00800         rb_objspace_t *objspace = &rb_objspace;
00801         objspace->profile.promote_infant_count++;
00802 
00803 #if RGENGC_PROFILE >= 2
00804         objspace->profile.promote_infant_types[BUILTIN_TYPE(obj)]++;
00805 #endif
00806     }
00807 #endif
00808 }
00809 
00810 #if RGENGC_THREEGEN
00811 /*
00812  * Two gen: Infant -> Old.
00813  * Three gen: Infant -> Young -> Old.
00814  */
00815 static inline VALUE
00816 RVALUE_YOUNG_P(VALUE obj)
00817 {
00818     check_gen_consistency(obj);
00819     return FL_TEST2(obj, FL_PROMOTED) && (RVALUE_OLDGEN_BITMAP(obj) == 0);
00820 }
00821 
00822 static inline void
00823 RVALUE_PROMOTE_YOUNG(VALUE obj)
00824 {
00825     check_gen_consistency(obj);
00826     if (RGENGC_CHECK_MODE && !RVALUE_YOUNG_P(obj)) rb_bug("RVALUE_PROMOTE_YOUNG: %p (%s) is not young object.", (void *)obj, obj_type_name(obj));
00827     MARK_IN_BITMAP(GET_HEAP_OLDGEN_BITS(obj), obj);
00828     check_gen_consistency(obj);
00829 
00830 #if RGENGC_PROFILE >= 1
00831     {
00832         rb_objspace_t *objspace = &rb_objspace;
00833         objspace->profile.promote_young_count++;
00834 #if RGENGC_PROFILE >= 2
00835         objspace->profile.promote_young_types[BUILTIN_TYPE(obj)]++;
00836 #endif
00837     }
00838 #endif
00839 }
00840 
00841 static inline void
00842 RVALUE_DEMOTE_FROM_YOUNG(VALUE obj)
00843 {
00844     if (RGENGC_CHECK_MODE && !RVALUE_YOUNG_P(obj))
00845       rb_bug("RVALUE_DEMOTE_FROM_YOUNG: %p (%s) is not young object.", (void *)obj, obj_type_name(obj));
00846 
00847     check_gen_consistency(obj);
00848     FL_UNSET2(obj, FL_PROMOTED);
00849     check_gen_consistency(obj);
00850 }
00851 #endif
00852 
00853 static inline void
00854 RVALUE_DEMOTE_FROM_OLD(VALUE obj)
00855 {
00856     if (RGENGC_CHECK_MODE && !RVALUE_OLD_P(obj))
00857       rb_bug("RVALUE_DEMOTE_FROM_OLD: %p (%s) is not old object.", (void *)obj, obj_type_name(obj));
00858 
00859     check_gen_consistency(obj);
00860     FL_UNSET2(obj, FL_PROMOTED);
00861     CLEAR_IN_BITMAP(GET_HEAP_OLDGEN_BITS(obj), obj);
00862     check_gen_consistency(obj);
00863 }
00864 
00865 #endif /* USE_RGENGC */
00866 
00867 /*
00868   --------------------------- ObjectSpace -----------------------------
00869 */
00870 
00871 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00872 rb_objspace_t *
00873 rb_objspace_alloc(void)
00874 {
00875     rb_objspace_t *objspace = malloc(sizeof(rb_objspace_t));
00876     memset(objspace, 0, sizeof(*objspace));
00877     ruby_gc_stress = ruby_initial_gc_stress;
00878 
00879     malloc_limit = gc_params.malloc_limit_min;
00880 
00881     return objspace;
00882 }
00883 #endif
00884 
00885 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00886 static void free_stack_chunks(mark_stack_t *);
00887 static void heap_page_free(rb_objspace_t *objspace, struct heap_page *page);
00888 
00889 void
00890 rb_objspace_free(rb_objspace_t *objspace)
00891 {
00892     gc_rest_sweep(objspace);
00893 
00894     if (objspace->profile.records) {
00895         free(objspace->profile.records);
00896         objspace->profile.records = 0;
00897     }
00898 
00899     if (global_List) {
00900         struct gc_list *list, *next;
00901         for (list = global_List; list; list = next) {
00902             next = list->next;
00903             xfree(list);
00904         }
00905     }
00906     if (heap_pages_sorted) {
00907         size_t i;
00908         for (i = 0; i < heap_pages_used; ++i) {
00909             heap_page_free(objspace, heap_pages_sorted[i]);
00910         }
00911         free(heap_pages_sorted);
00912         heap_pages_used = 0;
00913         heap_pages_length = 0;
00914         heap_pages_lomem = 0;
00915         heap_pages_himem = 0;
00916 
00917         objspace->eden_heap.page_length = 0;
00918         objspace->eden_heap.total_slots = 0;
00919         objspace->eden_heap.pages = NULL;
00920     }
00921     free_stack_chunks(&objspace->mark_stack);
00922     free(objspace);
00923 }
00924 #endif
00925 
00926 static void
00927 heap_pages_expand_sorted(rb_objspace_t *objspace)
00928 {
00929     size_t next_length = heap_pages_increment;
00930     next_length += heap_eden->page_length;
00931     next_length += heap_tomb->page_length;
00932 
00933     if (next_length > heap_pages_length) {
00934         struct heap_page **sorted;
00935         size_t size = next_length * sizeof(struct heap_page *);
00936 
00937         rgengc_report(3, objspace, "heap_pages_expand_sorted: next_length: %d, size: %d\n", (int)next_length, (int)size);
00938 
00939         if (heap_pages_length > 0) {
00940             sorted = (struct heap_page **)realloc(heap_pages_sorted, size);
00941             if (sorted) heap_pages_sorted = sorted;
00942         }
00943         else {
00944             sorted = heap_pages_sorted = (struct heap_page **)malloc(size);
00945         }
00946 
00947         if (sorted == 0) {
00948             during_gc = 0;
00949             rb_memerror();
00950         }
00951 
00952         heap_pages_length = next_length;
00953     }
00954 }
00955 
00956 static inline void
00957 heap_page_add_freeobj(rb_objspace_t *objspace, struct heap_page *page, VALUE obj)
00958 {
00959     RVALUE *p = (RVALUE *)obj;
00960     p->as.free.flags = 0;
00961     p->as.free.next = page->freelist;
00962     page->freelist = p;
00963     rgengc_report(3, objspace, "heap_page_add_freeobj: %p (%s) is added to freelist\n", p, obj_type_name(obj));
00964 }
00965 
00966 static inline void
00967 heap_add_freepage(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *page)
00968 {
00969     if (page->freelist) {
00970         page->free_next = heap->free_pages;
00971         heap->free_pages = page;
00972     }
00973 }
00974 
00975 static void
00976 heap_unlink_page(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *page)
00977 {
00978     if (page->prev) page->prev->next = page->next;
00979     if (page->next) page->next->prev = page->prev;
00980     if (heap->pages == page) heap->pages = page->next;
00981     page->prev = NULL;
00982     page->next = NULL;
00983     page->heap = NULL;
00984     heap->page_length--;
00985     heap->total_slots -= page->limit;
00986 }
00987 
00988 static void
00989 heap_page_free(rb_objspace_t *objspace, struct heap_page *page)
00990 {
00991     heap_pages_used--;
00992     aligned_free(page->body);
00993     free(page);
00994 }
00995 
00996 static void
00997 heap_pages_free_unused_pages(rb_objspace_t *objspace)
00998 {
00999     size_t i, j;
01000 
01001     for (i = j = 1; j < heap_pages_used; i++) {
01002         struct heap_page *page = heap_pages_sorted[i];
01003 
01004         if (page->heap == heap_tomb && page->final_slots == 0) {
01005             if (heap_pages_swept_slots - page->limit > heap_pages_max_free_slots) {
01006                 if (0) fprintf(stderr, "heap_pages_free_unused_pages: %d free page %p, heap_pages_swept_slots: %d, heap_pages_max_free_slots: %d\n",
01007                                (int)i, page, (int)heap_pages_swept_slots, (int)heap_pages_max_free_slots);
01008                 heap_pages_swept_slots -= page->limit;
01009                 heap_unlink_page(objspace, heap_tomb, page);
01010                 heap_page_free(objspace, page);
01011                 continue;
01012             }
01013             else {
01014                 /* fprintf(stderr, "heap_pages_free_unused_pages: remain!!\n"); */
01015             }
01016         }
01017         if (i != j) {
01018             heap_pages_sorted[j] = page;
01019         }
01020         j++;
01021     }
01022     assert(j == heap_pages_used);
01023 }
01024 
01025 static struct heap_page *
01026 heap_page_allocate(rb_objspace_t *objspace)
01027 {
01028     RVALUE *start, *end, *p;
01029     struct heap_page *page;
01030     struct heap_page_body *page_body = 0;
01031     size_t hi, lo, mid;
01032     size_t limit = HEAP_OBJ_LIMIT;
01033 
01034     /* assign heap_page body (contains heap_page_header and RVALUEs) */
01035     page_body = (struct heap_page_body *)aligned_malloc(HEAP_ALIGN, HEAP_SIZE);
01036     if (page_body == 0) {
01037         during_gc = 0;
01038         rb_memerror();
01039     }
01040 
01041     /* assign heap_page entry */
01042     page = (struct heap_page *)malloc(sizeof(struct heap_page));
01043     if (page == 0) {
01044         aligned_free(page_body);
01045         during_gc = 0;
01046         rb_memerror();
01047     }
01048     MEMZERO((void*)page, struct heap_page, 1);
01049 
01050     page->body = page_body;
01051 
01052     /* setup heap_pages_sorted */
01053     lo = 0;
01054     hi = heap_pages_used;
01055     while (lo < hi) {
01056         struct heap_page *mid_page;
01057 
01058         mid = (lo + hi) / 2;
01059         mid_page = heap_pages_sorted[mid];
01060         if (mid_page->body < page_body) {
01061             lo = mid + 1;
01062         }
01063         else if (mid_page->body > page_body) {
01064             hi = mid;
01065         }
01066         else {
01067             rb_bug("same heap page is allocated: %p at %"PRIuVALUE, (void *)page_body, (VALUE)mid);
01068         }
01069     }
01070     if (hi < heap_pages_used) {
01071         MEMMOVE(&heap_pages_sorted[hi+1], &heap_pages_sorted[hi], struct heap_page_header*, heap_pages_used - hi);
01072     }
01073 
01074     heap_pages_sorted[hi] = page;
01075 
01076     heap_pages_used++;
01077     assert(heap_pages_used <= heap_pages_length);
01078 
01079     /* adjust obj_limit (object number available in this page) */
01080     start = (RVALUE*)((VALUE)page_body + sizeof(struct heap_page_header));
01081     if ((VALUE)start % sizeof(RVALUE) != 0) {
01082         int delta = (int)(sizeof(RVALUE) - ((VALUE)start % sizeof(RVALUE)));
01083         start = (RVALUE*)((VALUE)start + delta);
01084         limit = (HEAP_SIZE - (size_t)((VALUE)start - (VALUE)page_body))/sizeof(RVALUE);
01085     }
01086     end = start + limit;
01087 
01088     if (heap_pages_lomem == 0 || heap_pages_lomem > start) heap_pages_lomem = start;
01089     if (heap_pages_himem < end) heap_pages_himem = end;
01090 
01091     page->start = start;
01092     page->limit = limit;
01093     page_body->header.page = page;
01094 
01095     for (p = start; p != end; p++) {
01096         rgengc_report(3, objspace, "assign_heap_page: %p is added to freelist\n");
01097         heap_page_add_freeobj(objspace, page, (VALUE)p);
01098     }
01099 
01100     return page;
01101 }
01102 
01103 static struct heap_page *
01104 heap_page_resurrect(rb_objspace_t *objspace)
01105 {
01106     struct heap_page *page;
01107 
01108     if ((page = heap_tomb->pages) != NULL) {
01109         heap_unlink_page(objspace, heap_tomb, page);
01110         return page;
01111     }
01112     return NULL;
01113 }
01114 
01115 static struct heap_page *
01116 heap_page_create(rb_objspace_t *objspace)
01117 {
01118     struct heap_page *page = heap_page_resurrect(objspace);
01119     const char *method = "recycle";
01120     if (page == NULL) {
01121         page = heap_page_allocate(objspace);
01122         method = "allocate";
01123     }
01124     if (0) fprintf(stderr, "heap_page_create: %s - %p, heap_pages_used: %d, heap_pages_used: %d, tomb->page_length: %d\n",
01125                    method, page, (int)heap_pages_length, (int)heap_pages_used, (int)heap_tomb->page_length);
01126     return page;
01127 }
01128 
01129 static void
01130 heap_add_page(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *page)
01131 {
01132     page->heap = heap;
01133     page->next = heap->pages;
01134     if (heap->pages) heap->pages->prev = page;
01135     heap->pages = page;
01136     heap->page_length++;
01137     heap->total_slots += page->limit;
01138 }
01139 
01140 static void
01141 heap_assign_page(rb_objspace_t *objspace, rb_heap_t *heap)
01142 {
01143     struct heap_page *page = heap_page_create(objspace);
01144     heap_add_page(objspace, heap, page);
01145     heap_add_freepage(objspace, heap, page);
01146 }
01147 
01148 static void
01149 heap_add_pages(rb_objspace_t *objspace, rb_heap_t *heap, size_t add)
01150 {
01151     size_t i;
01152 
01153     heap_pages_increment = add;
01154     heap_pages_expand_sorted(objspace);
01155     for (i = 0; i < add; i++) {
01156         heap_assign_page(objspace, heap);
01157     }
01158     heap_pages_increment = 0;
01159 }
01160 
01161 static void
01162 heap_set_increment(rb_objspace_t *objspace, size_t minimum_limit)
01163 {
01164     size_t used = heap_pages_used - heap_tomb->page_length;
01165     size_t next_used_limit = (size_t)(used * gc_params.growth_factor);
01166     if (gc_params.growth_max_slots > 0) {
01167         size_t max_used_limit = (size_t)(used + gc_params.growth_max_slots/HEAP_OBJ_LIMIT);
01168         if (next_used_limit > max_used_limit) next_used_limit = max_used_limit;
01169     }
01170     if (next_used_limit == heap_pages_used) next_used_limit++;
01171 
01172     if (next_used_limit < minimum_limit) {
01173         next_used_limit = minimum_limit;
01174     }
01175 
01176     heap_pages_increment = next_used_limit - used;
01177     heap_pages_expand_sorted(objspace);
01178 
01179     if (0) fprintf(stderr, "heap_set_increment: heap_pages_length: %d, heap_pages_used: %d, heap_pages_increment: %d, next_used_limit: %d\n",
01180                    (int)heap_pages_length, (int)heap_pages_used, (int)heap_pages_increment, (int)next_used_limit);
01181 }
01182 
01183 static int
01184 heap_increment(rb_objspace_t *objspace, rb_heap_t *heap)
01185 {
01186     rgengc_report(5, objspace, "heap_increment: heap_pages_length: %d, heap_pages_inc: %d, heap->page_length: %d\n",
01187                   (int)heap_pages_length, (int)heap_pages_increment, (int)heap->page_length);
01188 
01189     if (heap_pages_increment > 0) {
01190         heap_pages_increment--;
01191         heap_assign_page(objspace, heap);
01192         return TRUE;
01193     }
01194     return FALSE;
01195 }
01196 
01197 static struct heap_page *
01198 heap_prepare_freepage(rb_objspace_t *objspace, rb_heap_t *heap)
01199 {
01200     if (!GC_ENABLE_LAZY_SWEEP && objspace->flags.dont_lazy_sweep) {
01201         if (heap_increment(objspace, heap) == 0 &&
01202             garbage_collect(objspace, FALSE, TRUE, GPR_FLAG_NEWOBJ) == 0) {
01203             goto err;
01204         }
01205         goto ok;
01206     }
01207 
01208     if (!heap_ready_to_gc(objspace, heap)) return heap->free_pages;
01209 
01210     during_gc++;
01211 
01212     if ((is_lazy_sweeping(heap) && gc_heap_lazy_sweep(objspace, heap)) || heap_increment(objspace, heap)) {
01213         goto ok;
01214     }
01215 
01216 #if GC_PROFILE_MORE_DETAIL
01217     objspace->profile.prepare_time = 0;
01218 #endif
01219     if (garbage_collect_body(objspace, 0, 0, GPR_FLAG_NEWOBJ) == 0) {
01220       err:
01221         during_gc = 0;
01222         rb_memerror();
01223     }
01224   ok:
01225     during_gc = 0;
01226     return heap->free_pages;
01227 }
01228 
01229 static RVALUE *
01230 heap_get_freeobj_from_next_freepage(rb_objspace_t *objspace, rb_heap_t *heap)
01231 {
01232     struct heap_page *page;
01233     RVALUE *p;
01234 
01235     page = heap->free_pages;
01236     while (page == NULL) {
01237         page = heap_prepare_freepage(objspace, heap);
01238     }
01239     heap->free_pages = page->free_next;
01240     heap->using_page = page;
01241 
01242     p = page->freelist;
01243     page->freelist = NULL;
01244 
01245     return p;
01246 }
01247 
01248 static inline VALUE
01249 heap_get_freeobj(rb_objspace_t *objspace, rb_heap_t *heap)
01250 {
01251     RVALUE *p = heap->freelist;
01252 
01253     while (1) {
01254         if (p) {
01255             heap->freelist = p->as.free.next;
01256             return (VALUE)p;
01257         }
01258         else {
01259             p = heap_get_freeobj_from_next_freepage(objspace, heap);
01260         }
01261     }
01262 }
01263 
01264 void
01265 rb_objspace_set_event_hook(const rb_event_flag_t event)
01266 {
01267     rb_objspace_t *objspace = &rb_objspace;
01268     objspace->hook_events = event & RUBY_INTERNAL_EVENT_OBJSPACE_MASK;
01269 }
01270 
01271 static void
01272 gc_event_hook_body(rb_objspace_t *objspace, const rb_event_flag_t event, VALUE data)
01273 {
01274     rb_thread_t *th = GET_THREAD();
01275     EXEC_EVENT_HOOK(th, event, th->cfp->self, 0, 0, data);
01276 }
01277 
01278 #define gc_event_hook(objspace, event, data) do { \
01279     if (UNLIKELY((objspace)->hook_events & (event))) { \
01280         gc_event_hook_body((objspace), (event), (data)); \
01281     } \
01282 } while (0)
01283 
01284 static VALUE
01285 newobj_of(VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3)
01286 {
01287     rb_objspace_t *objspace = &rb_objspace;
01288     VALUE obj;
01289 
01290     if (UNLIKELY(during_gc)) {
01291         dont_gc = 1;
01292         during_gc = 0;
01293         rb_bug("object allocation during garbage collection phase");
01294     }
01295 
01296     if (UNLIKELY(ruby_gc_stress && !ruby_disable_gc_stress)) {
01297         if (!garbage_collect(objspace, FALSE, FALSE, GPR_FLAG_NEWOBJ)) {
01298             during_gc = 0;
01299             rb_memerror();
01300         }
01301     }
01302 
01303     obj = heap_get_freeobj(objspace, heap_eden);
01304 
01305     /* OBJSETUP */
01306     RBASIC(obj)->flags = flags;
01307     RBASIC_SET_CLASS_RAW(obj, klass);
01308     if (rb_safe_level() >= 3) FL_SET((obj), FL_TAINT);
01309     RANY(obj)->as.values.v1 = v1;
01310     RANY(obj)->as.values.v2 = v2;
01311     RANY(obj)->as.values.v3 = v3;
01312 
01313 #if GC_DEBUG
01314     RANY(obj)->file = rb_sourcefile();
01315     RANY(obj)->line = rb_sourceline();
01316     assert(!SPECIAL_CONST_P(obj)); /* check alignment */
01317 #endif
01318 
01319 #if RGENGC_PROFILE
01320     if (flags & FL_WB_PROTECTED) {
01321         objspace->profile.generated_normal_object_count++;
01322 #if RGENGC_PROFILE >= 2
01323         objspace->profile.generated_normal_object_count_types[BUILTIN_TYPE(obj)]++;
01324 #endif
01325     }
01326     else {
01327         objspace->profile.generated_shady_object_count++;
01328 #if RGENGC_PROFILE >= 2
01329         objspace->profile.generated_shady_object_count_types[BUILTIN_TYPE(obj)]++;
01330 #endif
01331     }
01332 #endif
01333 
01334     rgengc_report(5, objspace, "newobj: %p (%s)\n", (void *)obj, obj_type_name(obj));
01335 
01336 #if USE_RGENGC && RGENGC_CHECK_MODE
01337     if (RVALUE_PROMOTED_P(obj)) rb_bug("newobj: %p (%s) is promoted.\n", (void *)obj, obj_type_name(obj));
01338     if (rgengc_remembered(objspace, (VALUE)obj)) rb_bug("newobj: %p (%s) is remembered.\n", (void *)obj, obj_type_name(obj));
01339 #endif
01340 
01341     objspace->profile.total_allocated_object_num++;
01342     gc_event_hook(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj);
01343 
01344     return obj;
01345 }
01346 
01347 VALUE
01348 rb_newobj(void)
01349 {
01350     return newobj_of(0, T_NONE, 0, 0, 0);
01351 }
01352 
01353 VALUE
01354 rb_newobj_of(VALUE klass, VALUE flags)
01355 {
01356     return newobj_of(klass, flags, 0, 0, 0);
01357 }
01358 
01359 NODE*
01360 rb_node_newnode(enum node_type type, VALUE a0, VALUE a1, VALUE a2)
01361 {
01362     VALUE flags = (RGENGC_WB_PROTECTED_NODE_CREF && type == NODE_CREF ? FL_WB_PROTECTED : 0);
01363     NODE *n = (NODE *)newobj_of(0, T_NODE | flags, a0, a1, a2);
01364     nd_set_type(n, type);
01365     return n;
01366 }
01367 
01368 VALUE
01369 rb_data_object_alloc(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
01370 {
01371     if (klass) Check_Type(klass, T_CLASS);
01372     return newobj_of(klass, T_DATA, (VALUE)dmark, (VALUE)dfree, (VALUE)datap);
01373 }
01374 
01375 VALUE
01376 rb_data_typed_object_alloc(VALUE klass, void *datap, const rb_data_type_t *type)
01377 {
01378     if (klass) Check_Type(klass, T_CLASS);
01379     return newobj_of(klass, T_DATA | (type->flags & ~T_MASK), (VALUE)type, (VALUE)1, (VALUE)datap);
01380 }
01381 
01382 size_t
01383 rb_objspace_data_type_memsize(VALUE obj)
01384 {
01385     if (RTYPEDDATA_P(obj) && RTYPEDDATA_TYPE(obj)->function.dsize) {
01386         return RTYPEDDATA_TYPE(obj)->function.dsize(RTYPEDDATA_DATA(obj));
01387     }
01388     else {
01389         return 0;
01390     }
01391 }
01392 
01393 const char *
01394 rb_objspace_data_type_name(VALUE obj)
01395 {
01396     if (RTYPEDDATA_P(obj)) {
01397         return RTYPEDDATA_TYPE(obj)->wrap_struct_name;
01398     }
01399     else {
01400         return 0;
01401     }
01402 }
01403 
01404 static inline int
01405 is_pointer_to_heap(rb_objspace_t *objspace, void *ptr)
01406 {
01407     register RVALUE *p = RANY(ptr);
01408     register struct heap_page *page;
01409     register size_t hi, lo, mid;
01410 
01411     if (p < heap_pages_lomem || p > heap_pages_himem) return FALSE;
01412     if ((VALUE)p % sizeof(RVALUE) != 0) return FALSE;
01413 
01414     /* check if p looks like a pointer using bsearch*/
01415     lo = 0;
01416     hi = heap_pages_used;
01417     while (lo < hi) {
01418         mid = (lo + hi) / 2;
01419         page = heap_pages_sorted[mid];
01420         if (page->start <= p) {
01421             if (p < page->start + page->limit) {
01422                 return TRUE;
01423             }
01424             lo = mid + 1;
01425         }
01426         else {
01427             hi = mid;
01428         }
01429     }
01430     return FALSE;
01431 }
01432 
01433 static int
01434 free_method_entry_i(ID key, rb_method_entry_t *me, st_data_t data)
01435 {
01436     if (!me->mark) {
01437         rb_free_method_entry(me);
01438     }
01439     return ST_CONTINUE;
01440 }
01441 
01442 void
01443 rb_free_m_tbl(st_table *tbl)
01444 {
01445     st_foreach(tbl, free_method_entry_i, 0);
01446     st_free_table(tbl);
01447 }
01448 
01449 void
01450 rb_free_m_tbl_wrapper(struct method_table_wrapper *wrapper)
01451 {
01452     if (wrapper->tbl) {
01453         rb_free_m_tbl(wrapper->tbl);
01454     }
01455     xfree(wrapper);
01456 }
01457 
01458 static int
01459 free_const_entry_i(ID key, rb_const_entry_t *ce, st_data_t data)
01460 {
01461     xfree(ce);
01462     return ST_CONTINUE;
01463 }
01464 
01465 void
01466 rb_free_const_table(st_table *tbl)
01467 {
01468     st_foreach(tbl, free_const_entry_i, 0);
01469     st_free_table(tbl);
01470 }
01471 
01472 static inline void
01473 make_deferred(rb_objspace_t *objspace,RVALUE *p)
01474 {
01475     p->as.basic.flags = T_ZOMBIE;
01476     p->as.free.next = heap_pages_deferred_final;
01477     heap_pages_deferred_final = p;
01478 }
01479 
01480 static inline void
01481 make_io_deferred(rb_objspace_t *objspace,RVALUE *p)
01482 {
01483     rb_io_t *fptr = p->as.file.fptr;
01484     make_deferred(objspace, p);
01485     p->as.data.dfree = (void (*)(void*))rb_io_fptr_finalize;
01486     p->as.data.data = fptr;
01487 }
01488 
01489 static int
01490 obj_free(rb_objspace_t *objspace, VALUE obj)
01491 {
01492     gc_event_hook(objspace, RUBY_INTERNAL_EVENT_FREEOBJ, obj);
01493 
01494     switch (BUILTIN_TYPE(obj)) {
01495       case T_NIL:
01496       case T_FIXNUM:
01497       case T_TRUE:
01498       case T_FALSE:
01499         rb_bug("obj_free() called for broken object");
01500         break;
01501     }
01502 
01503     if (FL_TEST(obj, FL_EXIVAR)) {
01504         rb_free_generic_ivar((VALUE)obj);
01505         FL_UNSET(obj, FL_EXIVAR);
01506     }
01507 
01508 #if USE_RGENGC
01509     if (MARKED_IN_BITMAP(GET_HEAP_OLDGEN_BITS(obj),obj))
01510         CLEAR_IN_BITMAP(GET_HEAP_OLDGEN_BITS(obj),obj);
01511 #endif
01512 
01513     switch (BUILTIN_TYPE(obj)) {
01514       case T_OBJECT:
01515         if (!(RANY(obj)->as.basic.flags & ROBJECT_EMBED) &&
01516             RANY(obj)->as.object.as.heap.ivptr) {
01517             xfree(RANY(obj)->as.object.as.heap.ivptr);
01518         }
01519         break;
01520       case T_MODULE:
01521       case T_CLASS:
01522         if (RCLASS_M_TBL_WRAPPER(obj)) {
01523             rb_free_m_tbl_wrapper(RCLASS_M_TBL_WRAPPER(obj));
01524         }
01525         if (RCLASS_IV_TBL(obj)) {
01526             st_free_table(RCLASS_IV_TBL(obj));
01527         }
01528         if (RCLASS_CONST_TBL(obj)) {
01529             rb_free_const_table(RCLASS_CONST_TBL(obj));
01530         }
01531         if (RCLASS_IV_INDEX_TBL(obj)) {
01532             st_free_table(RCLASS_IV_INDEX_TBL(obj));
01533         }
01534         if (RCLASS_EXT(obj)->subclasses) {
01535             if (BUILTIN_TYPE(obj) == T_MODULE) {
01536                 rb_class_detach_module_subclasses(obj);
01537             }
01538             else {
01539                 rb_class_detach_subclasses(obj);
01540             }
01541             RCLASS_EXT(obj)->subclasses = NULL;
01542         }
01543         rb_class_remove_from_module_subclasses(obj);
01544         rb_class_remove_from_super_subclasses(obj);
01545         if (RANY(obj)->as.klass.ptr)
01546             xfree(RANY(obj)->as.klass.ptr);
01547         RANY(obj)->as.klass.ptr = NULL;
01548         break;
01549       case T_STRING:
01550         rb_str_free(obj);
01551         break;
01552       case T_ARRAY:
01553         rb_ary_free(obj);
01554         break;
01555       case T_HASH:
01556         if (RANY(obj)->as.hash.ntbl) {
01557             st_free_table(RANY(obj)->as.hash.ntbl);
01558         }
01559         break;
01560       case T_REGEXP:
01561         if (RANY(obj)->as.regexp.ptr) {
01562             onig_free(RANY(obj)->as.regexp.ptr);
01563         }
01564         break;
01565       case T_DATA:
01566         if (DATA_PTR(obj)) {
01567             int free_immediately = FALSE;
01568 
01569             if (RTYPEDDATA_P(obj)) {
01570                 free_immediately = (RANY(obj)->as.typeddata.type->flags & RUBY_TYPED_FREE_IMMEDIATELY) != 0;
01571                 RDATA(obj)->dfree = RANY(obj)->as.typeddata.type->function.dfree;
01572                 if (0 && free_immediately == 0) /* to expose non-free-immediate T_DATA */
01573                     fprintf(stderr, "not immediate -> %s\n", RANY(obj)->as.typeddata.type->wrap_struct_name);
01574             }
01575             if (RANY(obj)->as.data.dfree == RUBY_DEFAULT_FREE) {
01576                 xfree(DATA_PTR(obj));
01577             }
01578             else if (RANY(obj)->as.data.dfree) {
01579                 if (free_immediately) {
01580                     (RDATA(obj)->dfree)(DATA_PTR(obj));
01581                 }
01582                 else {
01583                     make_deferred(objspace, RANY(obj));
01584                     return 1;
01585                 }
01586             }
01587         }
01588         break;
01589       case T_MATCH:
01590         if (RANY(obj)->as.match.rmatch) {
01591             struct rmatch *rm = RANY(obj)->as.match.rmatch;
01592             onig_region_free(&rm->regs, 0);
01593             if (rm->char_offset)
01594                 xfree(rm->char_offset);
01595             xfree(rm);
01596         }
01597         break;
01598       case T_FILE:
01599         if (RANY(obj)->as.file.fptr) {
01600             make_io_deferred(objspace, RANY(obj));
01601             return 1;
01602         }
01603         break;
01604       case T_RATIONAL:
01605       case T_COMPLEX:
01606         break;
01607       case T_ICLASS:
01608         /* iClass shares table with the module */
01609         if (RCLASS_EXT(obj)->subclasses) {
01610             rb_class_detach_subclasses(obj);
01611             RCLASS_EXT(obj)->subclasses = NULL;
01612         }
01613         rb_class_remove_from_module_subclasses(obj);
01614         rb_class_remove_from_super_subclasses(obj);
01615         xfree(RANY(obj)->as.klass.ptr);
01616         RANY(obj)->as.klass.ptr = NULL;
01617         break;
01618 
01619       case T_FLOAT:
01620         break;
01621 
01622       case T_BIGNUM:
01623         if (!(RBASIC(obj)->flags & RBIGNUM_EMBED_FLAG) && RBIGNUM_DIGITS(obj)) {
01624             xfree(RBIGNUM_DIGITS(obj));
01625         }
01626         break;
01627       case T_NODE:
01628         switch (nd_type(obj)) {
01629           case NODE_SCOPE:
01630             if (RANY(obj)->as.node.u1.tbl) {
01631                 xfree(RANY(obj)->as.node.u1.tbl);
01632             }
01633             break;
01634           case NODE_ARGS:
01635             if (RANY(obj)->as.node.u3.args) {
01636                 xfree(RANY(obj)->as.node.u3.args);
01637             }
01638             break;
01639           case NODE_ALLOCA:
01640             xfree(RANY(obj)->as.node.u1.node);
01641             break;
01642         }
01643         break;                  /* no need to free iv_tbl */
01644 
01645       case T_STRUCT:
01646         if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 &&
01647             RANY(obj)->as.rstruct.as.heap.ptr) {
01648             xfree((void *)RANY(obj)->as.rstruct.as.heap.ptr);
01649         }
01650         break;
01651 
01652       default:
01653         rb_bug("gc_sweep(): unknown data type 0x%x(%p) 0x%"PRIxVALUE,
01654                BUILTIN_TYPE(obj), (void*)obj, RBASIC(obj)->flags);
01655     }
01656 
01657     return 0;
01658 }
01659 
01660 void
01661 Init_heap(void)
01662 {
01663     rb_objspace_t *objspace = &rb_objspace;
01664 
01665 #if RGENGC_ESTIMATE_OLDMALLOC
01666     objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min;
01667 #endif
01668 
01669     heap_add_pages(objspace, heap_eden, gc_params.heap_init_slots / HEAP_OBJ_LIMIT);
01670 
01671     init_mark_stack(&objspace->mark_stack);
01672 
01673 #ifdef USE_SIGALTSTACK
01674     {
01675         /* altstack of another threads are allocated in another place */
01676         rb_thread_t *th = GET_THREAD();
01677         void *tmp = th->altstack;
01678         th->altstack = malloc(rb_sigaltstack_size());
01679         free(tmp); /* free previously allocated area */
01680     }
01681 #endif
01682 
01683     objspace->profile.invoke_time = getrusage_time();
01684     finalizer_table = st_init_numtable();
01685 }
01686 
01687 typedef int each_obj_callback(void *, void *, size_t, void *);
01688 
01689 struct each_obj_args {
01690     each_obj_callback *callback;
01691     void *data;
01692 };
01693 
01694 static VALUE
01695 objspace_each_objects(VALUE arg)
01696 {
01697     size_t i;
01698     struct heap_page_body *last_body = 0;
01699     struct heap_page *page;
01700     RVALUE *pstart, *pend;
01701     rb_objspace_t *objspace = &rb_objspace;
01702     struct each_obj_args *args = (struct each_obj_args *)arg;
01703 
01704     i = 0;
01705     while (i < heap_pages_used) {
01706         while (0 < i && last_body < heap_pages_sorted[i-1]->body)              i--;
01707         while (i < heap_pages_used && heap_pages_sorted[i]->body <= last_body) i++;
01708         if (heap_pages_used <= i) break;
01709 
01710         page = heap_pages_sorted[i];
01711         last_body = page->body;
01712 
01713         pstart = page->start;
01714         pend = pstart + page->limit;
01715 
01716         if ((*args->callback)(pstart, pend, sizeof(RVALUE), args->data)) {
01717             break;
01718         }
01719     }
01720 
01721     return Qnil;
01722 }
01723 
01724 /*
01725  * rb_objspace_each_objects() is special C API to walk through
01726  * Ruby object space.  This C API is too difficult to use it.
01727  * To be frank, you should not use it. Or you need to read the
01728  * source code of this function and understand what this function does.
01729  *
01730  * 'callback' will be called several times (the number of heap page,
01731  * at current implementation) with:
01732  *   vstart: a pointer to the first living object of the heap_page.
01733  *   vend: a pointer to next to the valid heap_page area.
01734  *   stride: a distance to next VALUE.
01735  *
01736  * If callback() returns non-zero, the iteration will be stopped.
01737  *
01738  * This is a sample callback code to iterate liveness objects:
01739  *
01740  *   int
01741  *   sample_callback(void *vstart, void *vend, int stride, void *data) {
01742  *     VALUE v = (VALUE)vstart;
01743  *     for (; v != (VALUE)vend; v += stride) {
01744  *       if (RBASIC(v)->flags) { // liveness check
01745  *       // do something with live object 'v'
01746  *     }
01747  *     return 0; // continue to iteration
01748  *   }
01749  *
01750  * Note: 'vstart' is not a top of heap_page.  This point the first
01751  *       living object to grasp at least one object to avoid GC issue.
01752  *       This means that you can not walk through all Ruby object page
01753  *       including freed object page.
01754  *
01755  * Note: On this implementation, 'stride' is same as sizeof(RVALUE).
01756  *       However, there are possibilities to pass variable values with
01757  *       'stride' with some reasons.  You must use stride instead of
01758  *       use some constant value in the iteration.
01759  */
01760 void
01761 rb_objspace_each_objects(each_obj_callback *callback, void *data)
01762 {
01763     struct each_obj_args args;
01764     rb_objspace_t *objspace = &rb_objspace;
01765     int prev_dont_lazy_sweep = objspace->flags.dont_lazy_sweep;
01766 
01767     gc_rest_sweep(objspace);
01768     objspace->flags.dont_lazy_sweep = TRUE;
01769 
01770     args.callback = callback;
01771     args.data = data;
01772 
01773     if (prev_dont_lazy_sweep) {
01774         objspace_each_objects((VALUE)&args);
01775     }
01776     else {
01777         rb_ensure(objspace_each_objects, (VALUE)&args, lazy_sweep_enable, Qnil);
01778     }
01779 }
01780 
01781 struct os_each_struct {
01782     size_t num;
01783     VALUE of;
01784 };
01785 
01786 static int
01787 internal_object_p(VALUE obj)
01788 {
01789     RVALUE *p = (RVALUE *)obj;
01790 
01791     if (p->as.basic.flags) {
01792         switch (BUILTIN_TYPE(p)) {
01793           case T_NONE:
01794           case T_ICLASS:
01795           case T_NODE:
01796           case T_ZOMBIE:
01797             break;
01798           case T_CLASS:
01799             if (FL_TEST(p, FL_SINGLETON))
01800               break;
01801           default:
01802             if (!p->as.basic.klass) break;
01803             return 0;
01804         }
01805     }
01806     return 1;
01807 }
01808 
01809 int
01810 rb_objspace_internal_object_p(VALUE obj)
01811 {
01812     return internal_object_p(obj);
01813 }
01814 
01815 static int
01816 os_obj_of_i(void *vstart, void *vend, size_t stride, void *data)
01817 {
01818     struct os_each_struct *oes = (struct os_each_struct *)data;
01819     RVALUE *p = (RVALUE *)vstart, *pend = (RVALUE *)vend;
01820 
01821     for (; p != pend; p++) {
01822         volatile VALUE v = (VALUE)p;
01823         if (!internal_object_p(v)) {
01824             if (!oes->of || rb_obj_is_kind_of(v, oes->of)) {
01825                 rb_yield(v);
01826                 oes->num++;
01827             }
01828         }
01829     }
01830 
01831     return 0;
01832 }
01833 
01834 static VALUE
01835 os_obj_of(VALUE of)
01836 {
01837     struct os_each_struct oes;
01838 
01839     oes.num = 0;
01840     oes.of = of;
01841     rb_objspace_each_objects(os_obj_of_i, &oes);
01842     return SIZET2NUM(oes.num);
01843 }
01844 
01845 /*
01846  *  call-seq:
01847  *     ObjectSpace.each_object([module]) {|obj| ... } -> fixnum
01848  *     ObjectSpace.each_object([module])              -> an_enumerator
01849  *
01850  *  Calls the block once for each living, nonimmediate object in this
01851  *  Ruby process. If <i>module</i> is specified, calls the block
01852  *  for only those classes or modules that match (or are a subclass of)
01853  *  <i>module</i>. Returns the number of objects found. Immediate
01854  *  objects (<code>Fixnum</code>s, <code>Symbol</code>s
01855  *  <code>true</code>, <code>false</code>, and <code>nil</code>) are
01856  *  never returned. In the example below, <code>each_object</code>
01857  *  returns both the numbers we defined and several constants defined in
01858  *  the <code>Math</code> module.
01859  *
01860  *  If no block is given, an enumerator is returned instead.
01861  *
01862  *     a = 102.7
01863  *     b = 95       # Won't be returned
01864  *     c = 12345678987654321
01865  *     count = ObjectSpace.each_object(Numeric) {|x| p x }
01866  *     puts "Total count: #{count}"
01867  *
01868  *  <em>produces:</em>
01869  *
01870  *     12345678987654321
01871  *     102.7
01872  *     2.71828182845905
01873  *     3.14159265358979
01874  *     2.22044604925031e-16
01875  *     1.7976931348623157e+308
01876  *     2.2250738585072e-308
01877  *     Total count: 7
01878  *
01879  */
01880 
01881 static VALUE
01882 os_each_obj(int argc, VALUE *argv, VALUE os)
01883 {
01884     VALUE of;
01885 
01886     if (argc == 0) {
01887         of = 0;
01888     }
01889     else {
01890         rb_scan_args(argc, argv, "01", &of);
01891     }
01892     RETURN_ENUMERATOR(os, 1, &of);
01893     return os_obj_of(of);
01894 }
01895 
01896 /*
01897  *  call-seq:
01898  *     ObjectSpace.undefine_finalizer(obj)
01899  *
01900  *  Removes all finalizers for <i>obj</i>.
01901  *
01902  */
01903 
01904 static VALUE
01905 undefine_final(VALUE os, VALUE obj)
01906 {
01907     return rb_undefine_finalizer(obj);
01908 }
01909 
01910 VALUE
01911 rb_undefine_finalizer(VALUE obj)
01912 {
01913     rb_objspace_t *objspace = &rb_objspace;
01914     st_data_t data = obj;
01915     rb_check_frozen(obj);
01916     st_delete(finalizer_table, &data, 0);
01917     FL_UNSET(obj, FL_FINALIZE);
01918     return obj;
01919 }
01920 
01921 static void
01922 should_be_callable(VALUE block)
01923 {
01924     if (!rb_obj_respond_to(block, rb_intern("call"), TRUE)) {
01925         rb_raise(rb_eArgError, "wrong type argument %s (should be callable)",
01926                  rb_obj_classname(block));
01927     }
01928 }
01929 static void
01930 should_be_finalizable(VALUE obj)
01931 {
01932     rb_check_frozen(obj);
01933     if (!FL_ABLE(obj)) {
01934         rb_raise(rb_eArgError, "cannot define finalizer for %s",
01935                  rb_obj_classname(obj));
01936     }
01937 }
01938 
01939 /*
01940  *  call-seq:
01941  *     ObjectSpace.define_finalizer(obj, aProc=proc())
01942  *
01943  *  Adds <i>aProc</i> as a finalizer, to be called after <i>obj</i>
01944  *  was destroyed.
01945  *
01946  */
01947 
01948 static VALUE
01949 define_final(int argc, VALUE *argv, VALUE os)
01950 {
01951     VALUE obj, block;
01952 
01953     rb_scan_args(argc, argv, "11", &obj, &block);
01954     should_be_finalizable(obj);
01955     if (argc == 1) {
01956         block = rb_block_proc();
01957     }
01958     else {
01959         should_be_callable(block);
01960     }
01961 
01962     return define_final0(obj, block);
01963 }
01964 
01965 static VALUE
01966 define_final0(VALUE obj, VALUE block)
01967 {
01968     rb_objspace_t *objspace = &rb_objspace;
01969     VALUE table;
01970     st_data_t data;
01971 
01972     RBASIC(obj)->flags |= FL_FINALIZE;
01973 
01974     block = rb_ary_new3(2, INT2FIX(rb_safe_level()), block);
01975     OBJ_FREEZE(block);
01976 
01977     if (st_lookup(finalizer_table, obj, &data)) {
01978         table = (VALUE)data;
01979         rb_ary_push(table, block);
01980     }
01981     else {
01982         table = rb_ary_new3(1, block);
01983         RBASIC_CLEAR_CLASS(table);
01984         st_add_direct(finalizer_table, obj, table);
01985     }
01986     return block;
01987 }
01988 
01989 VALUE
01990 rb_define_finalizer(VALUE obj, VALUE block)
01991 {
01992     should_be_finalizable(obj);
01993     should_be_callable(block);
01994     return define_final0(obj, block);
01995 }
01996 
01997 void
01998 rb_gc_copy_finalizer(VALUE dest, VALUE obj)
01999 {
02000     rb_objspace_t *objspace = &rb_objspace;
02001     VALUE table;
02002     st_data_t data;
02003 
02004     if (!FL_TEST(obj, FL_FINALIZE)) return;
02005     if (st_lookup(finalizer_table, obj, &data)) {
02006         table = (VALUE)data;
02007         st_insert(finalizer_table, dest, table);
02008     }
02009     FL_SET(dest, FL_FINALIZE);
02010 }
02011 
02012 static VALUE
02013 run_single_final(VALUE arg)
02014 {
02015     VALUE *args = (VALUE *)arg;
02016     rb_eval_cmd(args[0], args[1], (int)args[2]);
02017     return Qnil;
02018 }
02019 
02020 static void
02021 run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table)
02022 {
02023     long i;
02024     int status;
02025     VALUE args[3];
02026     VALUE objid = nonspecial_obj_id(obj);
02027 
02028     if (RARRAY_LEN(table) > 0) {
02029         args[1] = rb_obj_freeze(rb_ary_new3(1, objid));
02030     }
02031     else {
02032         args[1] = 0;
02033     }
02034 
02035     args[2] = (VALUE)rb_safe_level();
02036     for (i=0; i<RARRAY_LEN(table); i++) {
02037         VALUE final = RARRAY_AREF(table, i);
02038         args[0] = RARRAY_AREF(final, 1);
02039         args[2] = FIX2INT(RARRAY_AREF(final, 0));
02040         status = 0;
02041         rb_protect(run_single_final, (VALUE)args, &status);
02042         if (status)
02043             rb_set_errinfo(Qnil);
02044     }
02045 }
02046 
02047 static void
02048 run_final(rb_objspace_t *objspace, VALUE obj)
02049 {
02050     RUBY_DATA_FUNC free_func = 0;
02051     st_data_t key, table;
02052 
02053     heap_pages_final_slots--;
02054 
02055     RBASIC_CLEAR_CLASS(obj);
02056 
02057     if (RTYPEDDATA_P(obj)) {
02058         free_func = RTYPEDDATA_TYPE(obj)->function.dfree;
02059     }
02060     else {
02061         free_func = RDATA(obj)->dfree;
02062     }
02063     if (free_func) {
02064         (*free_func)(DATA_PTR(obj));
02065     }
02066 
02067     key = (st_data_t)obj;
02068     if (st_delete(finalizer_table, &key, &table)) {
02069         run_finalizer(objspace, obj, (VALUE)table);
02070     }
02071 }
02072 
02073 static void
02074 finalize_list(rb_objspace_t *objspace, RVALUE *p)
02075 {
02076     while (p) {
02077         RVALUE *tmp = p->as.free.next;
02078         struct heap_page *page = GET_HEAP_PAGE(p);
02079 
02080         run_final(objspace, (VALUE)p);
02081         objspace->profile.total_freed_object_num++;
02082 
02083         page->final_slots--;
02084         heap_page_add_freeobj(objspace, GET_HEAP_PAGE(p), (VALUE)p);
02085         heap_pages_swept_slots++;
02086 
02087         p = tmp;
02088     }
02089 }
02090 
02091 static void
02092 finalize_deferred(rb_objspace_t *objspace)
02093 {
02094     RVALUE *p;
02095 
02096     while ((p = ATOMIC_PTR_EXCHANGE(heap_pages_deferred_final, 0)) != 0) {
02097         finalize_list(objspace, p);
02098     }
02099 }
02100 
02101 static void
02102 gc_finalize_deferred(void *dmy)
02103 {
02104     rb_objspace_t *objspace = &rb_objspace;
02105     if (ATOMIC_EXCHANGE(finalizing, 1)) return;
02106     finalize_deferred(objspace);
02107     ATOMIC_SET(finalizing, 0);
02108 }
02109 
02110 /* TODO: to keep compatibility, maybe unused. */
02111 void
02112 rb_gc_finalize_deferred(void)
02113 {
02114     gc_finalize_deferred(0);
02115 }
02116 
02117 static void
02118 gc_finalize_deferred_register(void)
02119 {
02120     if (rb_postponed_job_register_one(0, gc_finalize_deferred, 0) == 0) {
02121         rb_bug("gc_finalize_deferred_register: can't register finalizer.");
02122     }
02123 }
02124 
02125 struct force_finalize_list {
02126     VALUE obj;
02127     VALUE table;
02128     struct force_finalize_list *next;
02129 };
02130 
02131 static int
02132 force_chain_object(st_data_t key, st_data_t val, st_data_t arg)
02133 {
02134     struct force_finalize_list **prev = (struct force_finalize_list **)arg;
02135     struct force_finalize_list *curr = ALLOC(struct force_finalize_list);
02136     curr->obj = key;
02137     curr->table = val;
02138     curr->next = *prev;
02139     *prev = curr;
02140     return ST_CONTINUE;
02141 }
02142 
02143 void
02144 rb_gc_call_finalizer_at_exit(void)
02145 {
02146     rb_objspace_call_finalizer(&rb_objspace);
02147 }
02148 
02149 static void
02150 rb_objspace_call_finalizer(rb_objspace_t *objspace)
02151 {
02152     RVALUE *p, *pend;
02153     size_t i;
02154 
02155     gc_rest_sweep(objspace);
02156 
02157     if (ATOMIC_EXCHANGE(finalizing, 1)) return;
02158 
02159     /* run finalizers */
02160     finalize_deferred(objspace);
02161     assert(heap_pages_deferred_final == 0);
02162 
02163     /* force to run finalizer */
02164     while (finalizer_table->num_entries) {
02165         struct force_finalize_list *list = 0;
02166         st_foreach(finalizer_table, force_chain_object, (st_data_t)&list);
02167         while (list) {
02168             struct force_finalize_list *curr = list;
02169             st_data_t obj = (st_data_t)curr->obj;
02170             run_finalizer(objspace, curr->obj, curr->table);
02171             st_delete(finalizer_table, &obj, 0);
02172             list = curr->next;
02173             xfree(curr);
02174         }
02175     }
02176 
02177     /* finalizers are part of garbage collection */
02178     during_gc++;
02179 
02180     /* run data object's finalizers */
02181     for (i = 0; i < heap_pages_used; i++) {
02182         p = heap_pages_sorted[i]->start; pend = p + heap_pages_sorted[i]->limit;
02183         while (p < pend) {
02184             switch (BUILTIN_TYPE(p)) {
02185               case T_DATA:
02186                 if (!DATA_PTR(p) || !RANY(p)->as.data.dfree) break;
02187                 if (rb_obj_is_thread((VALUE)p)) break;
02188                 if (rb_obj_is_mutex((VALUE)p)) break;
02189                 if (rb_obj_is_fiber((VALUE)p)) break;
02190                 p->as.free.flags = 0;
02191                 if (RTYPEDDATA_P(p)) {
02192                     RDATA(p)->dfree = RANY(p)->as.typeddata.type->function.dfree;
02193                 }
02194                 if (RANY(p)->as.data.dfree == (RUBY_DATA_FUNC)-1) {
02195                     xfree(DATA_PTR(p));
02196                 }
02197                 else if (RANY(p)->as.data.dfree) {
02198                     make_deferred(objspace, RANY(p));
02199                 }
02200                 break;
02201               case T_FILE:
02202                 if (RANY(p)->as.file.fptr) {
02203                     make_io_deferred(objspace, RANY(p));
02204                 }
02205                 break;
02206             }
02207             p++;
02208         }
02209     }
02210     during_gc = 0;
02211     if (heap_pages_deferred_final) {
02212         finalize_list(objspace, heap_pages_deferred_final);
02213     }
02214 
02215     st_free_table(finalizer_table);
02216     finalizer_table = 0;
02217     ATOMIC_SET(finalizing, 0);
02218 }
02219 
02220 static inline int
02221 is_id_value(rb_objspace_t *objspace, VALUE ptr)
02222 {
02223     if (!is_pointer_to_heap(objspace, (void *)ptr)) return FALSE;
02224     if (BUILTIN_TYPE(ptr) > T_FIXNUM) return FALSE;
02225     if (BUILTIN_TYPE(ptr) == T_ICLASS) return FALSE;
02226     return TRUE;
02227 }
02228 
02229 static inline int
02230 heap_is_swept_object(rb_objspace_t *objspace, rb_heap_t *heap, VALUE ptr)
02231 {
02232     struct heap_page *page = GET_HEAP_PAGE(ptr);
02233     return page->before_sweep ? FALSE : TRUE;
02234 }
02235 
02236 static inline int
02237 is_swept_object(rb_objspace_t *objspace, VALUE ptr)
02238 {
02239     if (heap_is_swept_object(objspace, heap_eden, ptr)) {
02240         return TRUE;
02241     }
02242     else {
02243         return FALSE;
02244     }
02245 }
02246 
02247 static inline int
02248 is_dead_object(rb_objspace_t *objspace, VALUE ptr)
02249 {
02250     if (!is_lazy_sweeping(heap_eden) || MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(ptr), ptr)) return FALSE;
02251     if (!is_swept_object(objspace, ptr)) return TRUE;
02252     return FALSE;
02253 }
02254 
02255 static inline int
02256 is_live_object(rb_objspace_t *objspace, VALUE ptr)
02257 {
02258     switch (BUILTIN_TYPE(ptr)) {
02259       case 0: case T_ZOMBIE:
02260         return FALSE;
02261     }
02262     if (is_dead_object(objspace, ptr)) return FALSE;
02263     return TRUE;
02264 }
02265 
02266 static inline int
02267 is_markable_object(rb_objspace_t *objspace, VALUE obj)
02268 {
02269     if (rb_special_const_p(obj)) return 0; /* special const is not markable */
02270 
02271     if (RGENGC_CHECK_MODE) {
02272         if (!is_pointer_to_heap(objspace, (void *)obj)) rb_bug("is_markable_object: %p is not pointer to heap", (void *)obj);
02273         if (BUILTIN_TYPE(obj) == T_NONE)   rb_bug("is_markable_object: %p is T_NONE", (void *)obj);
02274         if (BUILTIN_TYPE(obj) == T_ZOMBIE) rb_bug("is_markable_object: %p is T_ZOMBIE", (void *)obj);
02275     }
02276 
02277     return 1;
02278 }
02279 
02280 int
02281 rb_objspace_markable_object_p(VALUE obj)
02282 {
02283     rb_objspace_t *objspace = &rb_objspace;
02284     return is_markable_object(objspace, obj) && is_live_object(objspace, obj);
02285 }
02286 
02287 /*
02288  *  call-seq:
02289  *     ObjectSpace._id2ref(object_id) -> an_object
02290  *
02291  *  Converts an object id to a reference to the object. May not be
02292  *  called on an object id passed as a parameter to a finalizer.
02293  *
02294  *     s = "I am a string"                    #=> "I am a string"
02295  *     r = ObjectSpace._id2ref(s.object_id)   #=> "I am a string"
02296  *     r == s                                 #=> true
02297  *
02298  */
02299 
02300 static VALUE
02301 id2ref(VALUE obj, VALUE objid)
02302 {
02303 #if SIZEOF_LONG == SIZEOF_VOIDP
02304 #define NUM2PTR(x) NUM2ULONG(x)
02305 #elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
02306 #define NUM2PTR(x) NUM2ULL(x)
02307 #endif
02308     rb_objspace_t *objspace = &rb_objspace;
02309     VALUE ptr;
02310     void *p0;
02311 
02312     ptr = NUM2PTR(objid);
02313     p0 = (void *)ptr;
02314 
02315     if (ptr == Qtrue) return Qtrue;
02316     if (ptr == Qfalse) return Qfalse;
02317     if (ptr == Qnil) return Qnil;
02318     if (FIXNUM_P(ptr)) return (VALUE)ptr;
02319     if (FLONUM_P(ptr)) return (VALUE)ptr;
02320     ptr = obj_id_to_ref(objid);
02321 
02322     if ((ptr % sizeof(RVALUE)) == (4 << 2)) {
02323         ID symid = ptr / sizeof(RVALUE);
02324         if (rb_id2name(symid) == 0)
02325             rb_raise(rb_eRangeError, "%p is not symbol id value", p0);
02326         return ID2SYM(symid);
02327     }
02328 
02329     if (!is_id_value(objspace, ptr)) {
02330         rb_raise(rb_eRangeError, "%p is not id value", p0);
02331     }
02332     if (!is_live_object(objspace, ptr)) {
02333         rb_raise(rb_eRangeError, "%p is recycled object", p0);
02334     }
02335     return (VALUE)ptr;
02336 }
02337 
02338 /*
02339  *  Document-method: __id__
02340  *  Document-method: object_id
02341  *
02342  *  call-seq:
02343  *     obj.__id__       -> integer
02344  *     obj.object_id    -> integer
02345  *
02346  *  Returns an integer identifier for +obj+.
02347  *
02348  *  The same number will be returned on all calls to +id+ for a given object,
02349  *  and no two active objects will share an id.
02350  *
02351  *  Object#object_id is a different concept from the +:name+ notation, which
02352  *  returns the symbol id of +name+.
02353  *
02354  *  Replaces the deprecated Object#id.
02355  */
02356 
02357 /*
02358  *  call-seq:
02359  *     obj.hash    -> fixnum
02360  *
02361  *  Generates a Fixnum hash value for this object.
02362  *
02363  *  This function must have the property that <code>a.eql?(b)</code> implies
02364  *  <code>a.hash == b.hash</code>.
02365  *
02366  *  The hash value is used by Hash class.
02367  *
02368  *  Any hash value that exceeds the capacity of a Fixnum will be truncated
02369  *  before being used.
02370  */
02371 
02372 VALUE
02373 rb_obj_id(VALUE obj)
02374 {
02375     /*
02376      *                32-bit VALUE space
02377      *          MSB ------------------------ LSB
02378      *  false   00000000000000000000000000000000
02379      *  true    00000000000000000000000000000010
02380      *  nil     00000000000000000000000000000100
02381      *  undef   00000000000000000000000000000110
02382      *  symbol  ssssssssssssssssssssssss00001110
02383      *  object  oooooooooooooooooooooooooooooo00        = 0 (mod sizeof(RVALUE))
02384      *  fixnum  fffffffffffffffffffffffffffffff1
02385      *
02386      *                    object_id space
02387      *                                       LSB
02388      *  false   00000000000000000000000000000000
02389      *  true    00000000000000000000000000000010
02390      *  nil     00000000000000000000000000000100
02391      *  undef   00000000000000000000000000000110
02392      *  symbol   000SSSSSSSSSSSSSSSSSSSSSSSSSSS0        S...S % A = 4 (S...S = s...s * A + 4)
02393      *  object   oooooooooooooooooooooooooooooo0        o...o % A = 0
02394      *  fixnum  fffffffffffffffffffffffffffffff1        bignum if required
02395      *
02396      *  where A = sizeof(RVALUE)/4
02397      *
02398      *  sizeof(RVALUE) is
02399      *  20 if 32-bit, double is 4-byte aligned
02400      *  24 if 32-bit, double is 8-byte aligned
02401      *  40 if 64-bit
02402      */
02403     if (SYMBOL_P(obj)) {
02404         return (SYM2ID(obj) * sizeof(RVALUE) + (4 << 2)) | FIXNUM_FLAG;
02405     }
02406     else if (FLONUM_P(obj)) {
02407 #if SIZEOF_LONG == SIZEOF_VOIDP
02408         return LONG2NUM((SIGNED_VALUE)obj);
02409 #else
02410         return LL2NUM((SIGNED_VALUE)obj);
02411 #endif
02412     }
02413     else if (SPECIAL_CONST_P(obj)) {
02414         return LONG2NUM((SIGNED_VALUE)obj);
02415     }
02416     return nonspecial_obj_id(obj);
02417 }
02418 
02419 size_t rb_str_memsize(VALUE);
02420 size_t rb_ary_memsize(VALUE);
02421 size_t rb_io_memsize(const rb_io_t *);
02422 size_t rb_generic_ivar_memsize(VALUE);
02423 #include "regint.h"
02424 
02425 static size_t
02426 obj_memsize_of(VALUE obj, int use_tdata)
02427 {
02428     size_t size = 0;
02429 
02430     if (SPECIAL_CONST_P(obj)) {
02431         return 0;
02432     }
02433 
02434     if (FL_TEST(obj, FL_EXIVAR)) {
02435         size += rb_generic_ivar_memsize(obj);
02436     }
02437 
02438     switch (BUILTIN_TYPE(obj)) {
02439       case T_OBJECT:
02440         if (!(RBASIC(obj)->flags & ROBJECT_EMBED) &&
02441             ROBJECT(obj)->as.heap.ivptr) {
02442             size += ROBJECT(obj)->as.heap.numiv * sizeof(VALUE);
02443         }
02444         break;
02445       case T_MODULE:
02446       case T_CLASS:
02447         if (RCLASS_M_TBL_WRAPPER(obj)) {
02448             size += sizeof(struct method_table_wrapper);
02449         }
02450         if (RCLASS_M_TBL(obj)) {
02451             size += st_memsize(RCLASS_M_TBL(obj));
02452         }
02453         if (RCLASS_EXT(obj)) {
02454             if (RCLASS_IV_TBL(obj)) {
02455                 size += st_memsize(RCLASS_IV_TBL(obj));
02456             }
02457             if (RCLASS_IV_INDEX_TBL(obj)) {
02458                 size += st_memsize(RCLASS_IV_INDEX_TBL(obj));
02459             }
02460             if (RCLASS(obj)->ptr->iv_tbl) {
02461                 size += st_memsize(RCLASS(obj)->ptr->iv_tbl);
02462             }
02463             if (RCLASS(obj)->ptr->const_tbl) {
02464                 size += st_memsize(RCLASS(obj)->ptr->const_tbl);
02465             }
02466             size += sizeof(rb_classext_t);
02467         }
02468         break;
02469       case T_STRING:
02470         size += rb_str_memsize(obj);
02471         break;
02472       case T_ARRAY:
02473         size += rb_ary_memsize(obj);
02474         break;
02475       case T_HASH:
02476         if (RHASH(obj)->ntbl) {
02477             size += st_memsize(RHASH(obj)->ntbl);
02478         }
02479         break;
02480       case T_REGEXP:
02481         if (RREGEXP(obj)->ptr) {
02482             size += onig_memsize(RREGEXP(obj)->ptr);
02483         }
02484         break;
02485       case T_DATA:
02486         if (use_tdata) size += rb_objspace_data_type_memsize(obj);
02487         break;
02488       case T_MATCH:
02489         if (RMATCH(obj)->rmatch) {
02490             struct rmatch *rm = RMATCH(obj)->rmatch;
02491             size += onig_region_memsize(&rm->regs);
02492             size += sizeof(struct rmatch_offset) * rm->char_offset_num_allocated;
02493             size += sizeof(struct rmatch);
02494         }
02495         break;
02496       case T_FILE:
02497         if (RFILE(obj)->fptr) {
02498             size += rb_io_memsize(RFILE(obj)->fptr);
02499         }
02500         break;
02501       case T_RATIONAL:
02502       case T_COMPLEX:
02503         break;
02504       case T_ICLASS:
02505         /* iClass shares table with the module */
02506         break;
02507 
02508       case T_FLOAT:
02509         break;
02510 
02511       case T_BIGNUM:
02512         if (!(RBASIC(obj)->flags & RBIGNUM_EMBED_FLAG) && RBIGNUM_DIGITS(obj)) {
02513             size += RBIGNUM_LEN(obj) * sizeof(BDIGIT);
02514         }
02515         break;
02516       case T_NODE:
02517         switch (nd_type(obj)) {
02518           case NODE_SCOPE:
02519             if (RNODE(obj)->u1.tbl) {
02520                 /* TODO: xfree(RANY(obj)->as.node.u1.tbl); */
02521             }
02522             break;
02523           case NODE_ALLOCA:
02524             /* TODO: xfree(RANY(obj)->as.node.u1.node); */
02525             ;
02526         }
02527         break;                  /* no need to free iv_tbl */
02528 
02529       case T_STRUCT:
02530         if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 &&
02531             RSTRUCT(obj)->as.heap.ptr) {
02532             size += sizeof(VALUE) * RSTRUCT_LEN(obj);
02533         }
02534         break;
02535 
02536       case T_ZOMBIE:
02537         break;
02538 
02539       default:
02540         rb_bug("objspace/memsize_of(): unknown data type 0x%x(%p)",
02541                BUILTIN_TYPE(obj), (void*)obj);
02542     }
02543 
02544     return size;
02545 }
02546 
02547 size_t
02548 rb_obj_memsize_of(VALUE obj)
02549 {
02550     return obj_memsize_of(obj, TRUE);
02551 }
02552 
02553 static int
02554 set_zero(st_data_t key, st_data_t val, st_data_t arg)
02555 {
02556     VALUE k = (VALUE)key;
02557     VALUE hash = (VALUE)arg;
02558     rb_hash_aset(hash, k, INT2FIX(0));
02559     return ST_CONTINUE;
02560 }
02561 
02562 /*
02563  *  call-seq:
02564  *     ObjectSpace.count_objects([result_hash]) -> hash
02565  *
02566  *  Counts objects for each type.
02567  *
02568  *  It returns a hash, such as:
02569  *      {
02570  *        :TOTAL=>10000,
02571  *        :FREE=>3011,
02572  *        :T_OBJECT=>6,
02573  *        :T_CLASS=>404,
02574  *        # ...
02575  *      }
02576  *
02577  *  The contents of the returned hash are implementation specific.
02578  *  It may be changed in future.
02579  *
02580  *  If the optional argument +result_hash+ is given,
02581  *  it is overwritten and returned. This is intended to avoid probe effect.
02582  *
02583  *  This method is only expected to work on C Ruby.
02584  *
02585  */
02586 
02587 static VALUE
02588 count_objects(int argc, VALUE *argv, VALUE os)
02589 {
02590     rb_objspace_t *objspace = &rb_objspace;
02591     size_t counts[T_MASK+1];
02592     size_t freed = 0;
02593     size_t total = 0;
02594     size_t i;
02595     VALUE hash;
02596 
02597     if (rb_scan_args(argc, argv, "01", &hash) == 1) {
02598         if (!RB_TYPE_P(hash, T_HASH))
02599             rb_raise(rb_eTypeError, "non-hash given");
02600     }
02601 
02602     for (i = 0; i <= T_MASK; i++) {
02603         counts[i] = 0;
02604     }
02605 
02606     for (i = 0; i < heap_pages_used; i++) {
02607         struct heap_page *page = heap_pages_sorted[i];
02608         RVALUE *p, *pend;
02609 
02610         p = page->start; pend = p + page->limit;
02611         for (;p < pend; p++) {
02612             if (p->as.basic.flags) {
02613                 counts[BUILTIN_TYPE(p)]++;
02614             }
02615             else {
02616                 freed++;
02617             }
02618         }
02619         total += page->limit;
02620     }
02621 
02622     if (hash == Qnil) {
02623         hash = rb_hash_new();
02624     }
02625     else if (!RHASH_EMPTY_P(hash)) {
02626         st_foreach(RHASH_TBL_RAW(hash), set_zero, hash);
02627     }
02628     rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
02629     rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(freed));
02630 
02631     for (i = 0; i <= T_MASK; i++) {
02632         VALUE type;
02633         switch (i) {
02634 #define COUNT_TYPE(t) case (t): type = ID2SYM(rb_intern(#t)); break;
02635             COUNT_TYPE(T_NONE);
02636             COUNT_TYPE(T_OBJECT);
02637             COUNT_TYPE(T_CLASS);
02638             COUNT_TYPE(T_MODULE);
02639             COUNT_TYPE(T_FLOAT);
02640             COUNT_TYPE(T_STRING);
02641             COUNT_TYPE(T_REGEXP);
02642             COUNT_TYPE(T_ARRAY);
02643             COUNT_TYPE(T_HASH);
02644             COUNT_TYPE(T_STRUCT);
02645             COUNT_TYPE(T_BIGNUM);
02646             COUNT_TYPE(T_FILE);
02647             COUNT_TYPE(T_DATA);
02648             COUNT_TYPE(T_MATCH);
02649             COUNT_TYPE(T_COMPLEX);
02650             COUNT_TYPE(T_RATIONAL);
02651             COUNT_TYPE(T_NIL);
02652             COUNT_TYPE(T_TRUE);
02653             COUNT_TYPE(T_FALSE);
02654             COUNT_TYPE(T_SYMBOL);
02655             COUNT_TYPE(T_FIXNUM);
02656             COUNT_TYPE(T_UNDEF);
02657             COUNT_TYPE(T_NODE);
02658             COUNT_TYPE(T_ICLASS);
02659             COUNT_TYPE(T_ZOMBIE);
02660 #undef COUNT_TYPE
02661           default:              type = INT2NUM(i); break;
02662         }
02663         if (counts[i])
02664             rb_hash_aset(hash, type, SIZET2NUM(counts[i]));
02665     }
02666 
02667     return hash;
02668 }
02669 
02670 /*
02671   ------------------------ Garbage Collection ------------------------
02672 */
02673 
02674 /* Sweeping */
02675 
02676 static VALUE
02677 lazy_sweep_enable(void)
02678 {
02679     rb_objspace_t *objspace = &rb_objspace;
02680 
02681     objspace->flags.dont_lazy_sweep = FALSE;
02682     return Qnil;
02683 }
02684 
02685 static size_t
02686 objspace_live_slot(rb_objspace_t *objspace)
02687 {
02688     return objspace->profile.total_allocated_object_num - objspace->profile.total_freed_object_num;
02689 }
02690 
02691 static size_t
02692 objspace_total_slot(rb_objspace_t *objspace)
02693 {
02694     return heap_eden->total_slots + heap_tomb->total_slots;
02695 }
02696 
02697 static size_t
02698 objspace_free_slot(rb_objspace_t *objspace)
02699 {
02700     return objspace_total_slot(objspace) - (objspace_live_slot(objspace) - heap_pages_final_slots);
02701 }
02702 
02703 static void
02704 gc_setup_mark_bits(struct heap_page *page)
02705 {
02706 #if USE_RGENGC
02707     /* copy oldgen bitmap to mark bitmap */
02708     memcpy(&page->mark_bits[0], &page->oldgen_bits[0], HEAP_BITMAP_SIZE);
02709 #else
02710     /* clear mark bitmap */
02711     memset(&page->mark_bits[0], 0, HEAP_BITMAP_SIZE);
02712 #endif
02713 }
02714 
02715 static inline void
02716 gc_page_sweep(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *sweep_page)
02717 {
02718     int i;
02719     size_t empty_slots = 0, freed_slots = 0, final_slots = 0;
02720     RVALUE *p, *pend,*offset;
02721     bits_t *bits, bitset;
02722 
02723     rgengc_report(1, objspace, "page_sweep: start.\n");
02724 
02725     sweep_page->before_sweep = 0;
02726 
02727     p = sweep_page->start; pend = p + sweep_page->limit;
02728     offset = p - NUM_IN_PAGE(p);
02729     bits = sweep_page->mark_bits;
02730 
02731     /* create guard : fill 1 out-of-range */
02732     bits[BITMAP_INDEX(p)] |= BITMAP_BIT(p)-1;
02733     bits[BITMAP_INDEX(pend)] |= ~(BITMAP_BIT(pend) - 1);
02734 
02735     for (i=0; i < HEAP_BITMAP_LIMIT; i++) {
02736         bitset = ~bits[i];
02737         if (bitset) {
02738             p = offset  + i * BITS_BITLENGTH;
02739             do {
02740                 if ((bitset & 1) && BUILTIN_TYPE(p) != T_ZOMBIE) {
02741                     if (p->as.basic.flags) {
02742                         rgengc_report(3, objspace, "page_sweep: free %p (%s)\n", p, obj_type_name((VALUE)p));
02743 #if USE_RGENGC && RGENGC_CHECK_MODE
02744                         if (objspace->rgengc.during_minor_gc && RVALUE_OLD_P((VALUE)p)) rb_bug("page_sweep: %p (%s) is old while minor GC.\n", p, obj_type_name((VALUE)p));
02745                         if (rgengc_remembered(objspace, (VALUE)p)) rb_bug("page_sweep: %p (%s) is remembered.\n", p, obj_type_name((VALUE)p));
02746 #endif
02747                         if (obj_free(objspace, (VALUE)p)) {
02748                             final_slots++;
02749                         }
02750                         else if (FL_TEST(p, FL_FINALIZE)) {
02751                             RDATA(p)->dfree = 0;
02752                             make_deferred(objspace,p);
02753                             final_slots++;
02754                         }
02755                         else {
02756                             (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)p, sizeof(RVALUE));
02757                             heap_page_add_freeobj(objspace, sweep_page, (VALUE)p);
02758                             rgengc_report(3, objspace, "page_sweep: %p (%s) is added to freelist\n", p, obj_type_name((VALUE)p));
02759                             freed_slots++;
02760                         }
02761                     }
02762                     else {
02763                         empty_slots++;
02764                     }
02765                 }
02766                 p++;
02767                 bitset >>= 1;
02768             } while (bitset);
02769         }
02770     }
02771 
02772     gc_setup_mark_bits(sweep_page);
02773 
02774 #if GC_PROFILE_MORE_DETAIL
02775     if (gc_prof_enabled(objspace)) {
02776         gc_profile_record *record = gc_prof_record(objspace);
02777         record->removing_objects += final_slots + freed_slots;
02778         record->empty_objects += empty_slots;
02779     }
02780 #endif
02781 
02782     if (final_slots + freed_slots + empty_slots == sweep_page->limit) {
02783         /* there are no living objects -> move this page to tomb heap */
02784         heap_unlink_page(objspace, heap, sweep_page);
02785         heap_add_page(objspace, heap_tomb, sweep_page);
02786     }
02787     else {
02788         if (freed_slots + empty_slots > 0) {
02789             heap_add_freepage(objspace, heap, sweep_page);
02790         }
02791         else {
02792             sweep_page->free_next = NULL;
02793         }
02794     }
02795     heap_pages_swept_slots += freed_slots + empty_slots;
02796     objspace->profile.total_freed_object_num += freed_slots;
02797     heap_pages_final_slots += final_slots;
02798     sweep_page->final_slots = final_slots;
02799 
02800     if (0) fprintf(stderr, "gc_page_sweep(%d): freed?: %d, limt: %d, freed_slots: %d, empty_slots: %d, final_slots: %d\n",
02801                    (int)rb_gc_count(),
02802                    final_slots + freed_slots + empty_slots == sweep_page->limit,
02803                    (int)sweep_page->limit, (int)freed_slots, (int)empty_slots, (int)final_slots);
02804 
02805     if (heap_pages_deferred_final && !finalizing) {
02806         rb_thread_t *th = GET_THREAD();
02807         if (th) {
02808             gc_finalize_deferred_register();
02809         }
02810     }
02811 
02812     rgengc_report(1, objspace, "page_sweep: end.\n");
02813 }
02814 
02815 /* allocate additional minimum page to work */
02816 static void
02817 gc_heap_prepare_minimum_pages(rb_objspace_t *objspace, rb_heap_t *heap)
02818 {
02819     if (!heap->free_pages) {
02820         /* there is no free after page_sweep() */
02821         heap_set_increment(objspace, 0);
02822         if (!heap_increment(objspace, heap)) { /* can't allocate additional free objects */
02823             during_gc = 0;
02824             rb_memerror();
02825         }
02826     }
02827 }
02828 
02829 static void
02830 gc_before_heap_sweep(rb_objspace_t *objspace, rb_heap_t *heap)
02831 {
02832     heap->sweep_pages = heap->pages;
02833     heap->free_pages = NULL;
02834 
02835     if (heap->using_page) {
02836         RVALUE **p = &heap->using_page->freelist;
02837         while (*p) {
02838             p = &(*p)->as.free.next;
02839         }
02840         *p = heap->freelist;
02841         heap->using_page = NULL;
02842     }
02843     heap->freelist = NULL;
02844 }
02845 
02846 #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4
02847 __attribute__((noinline))
02848 #endif
02849 static void
02850 gc_before_sweep(rb_objspace_t *objspace)
02851 {
02852     rb_heap_t *heap;
02853     size_t total_limit_slot;
02854 
02855     rgengc_report(1, objspace, "gc_before_sweep\n");
02856 
02857     /* sweep unlinked method entries */
02858     if (GET_VM()->unlinked_method_entry_list) {
02859         rb_sweep_method_entry(GET_VM());
02860     }
02861 
02862     heap_pages_swept_slots = 0;
02863     total_limit_slot = objspace_total_slot(objspace);
02864 
02865     heap_pages_min_free_slots = (size_t)(total_limit_slot * 0.30);
02866     if (heap_pages_min_free_slots < gc_params.heap_free_slots) {
02867         heap_pages_min_free_slots = gc_params.heap_free_slots;
02868     }
02869     heap_pages_max_free_slots = (size_t)(total_limit_slot * 0.80);
02870     if (heap_pages_max_free_slots < gc_params.heap_init_slots) {
02871         heap_pages_max_free_slots = gc_params.heap_init_slots;
02872     }
02873     if (0) fprintf(stderr, "heap_pages_min_free_slots: %d, heap_pages_max_free_slots: %d\n",
02874                    (int)heap_pages_min_free_slots, (int)heap_pages_max_free_slots);
02875 
02876     heap = heap_eden;
02877     gc_before_heap_sweep(objspace, heap);
02878 
02879     gc_prof_set_malloc_info(objspace);
02880 
02881     /* reset malloc info */
02882     if (0) fprintf(stderr, "%d\t%d\t%d\n", (int)rb_gc_count(), (int)malloc_increase, (int)malloc_limit);
02883 
02884     {
02885         size_t inc = ATOMIC_SIZE_EXCHANGE(malloc_increase, 0);
02886         size_t old_limit = malloc_limit;
02887 
02888         if (inc > malloc_limit) {
02889             malloc_limit = (size_t)(inc * gc_params.malloc_limit_growth_factor);
02890             if (gc_params.malloc_limit_max > 0 && /* ignore max-check if 0 */
02891                 malloc_limit > gc_params.malloc_limit_max) {
02892                 malloc_limit = gc_params.malloc_limit_max;
02893             }
02894         }
02895         else {
02896             malloc_limit = (size_t)(malloc_limit * 0.98); /* magic number */
02897             if (malloc_limit < gc_params.malloc_limit_min) {
02898                 malloc_limit = gc_params.malloc_limit_min;
02899             }
02900         }
02901 
02902         if (0) {
02903             if (old_limit != malloc_limit) {
02904                 fprintf(stderr, "[%"PRIuSIZE"] malloc_limit: %"PRIuSIZE" -> %"PRIuSIZE"\n",
02905                         rb_gc_count(), old_limit, malloc_limit);
02906             }
02907             else {
02908                 fprintf(stderr, "[%"PRIuSIZE"] malloc_limit: not changed (%"PRIuSIZE")\n",
02909                         rb_gc_count(), malloc_limit);
02910             }
02911         }
02912     }
02913 
02914     /* reset oldmalloc info */
02915 #if RGENGC_ESTIMATE_OLDMALLOC
02916     if (objspace->rgengc.during_minor_gc) {
02917         if (objspace->rgengc.oldmalloc_increase > objspace->rgengc.oldmalloc_increase_limit) {
02918             objspace->rgengc.need_major_gc = GPR_FLAG_MAJOR_BY_OLDMALLOC;;
02919             objspace->rgengc.oldmalloc_increase_limit =
02920               (size_t)(objspace->rgengc.oldmalloc_increase_limit * gc_params.oldmalloc_limit_growth_factor);
02921 
02922             if (objspace->rgengc.oldmalloc_increase_limit > gc_params.oldmalloc_limit_max) {
02923                 objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_max;
02924             }
02925         }
02926 
02927         if (0) fprintf(stderr, "%d\t%d\t%u\t%u\t%d\n",
02928                        (int)rb_gc_count(),
02929                        (int)objspace->rgengc.need_major_gc,
02930                        (unsigned int)objspace->rgengc.oldmalloc_increase,
02931                        (unsigned int)objspace->rgengc.oldmalloc_increase_limit,
02932                        (unsigned int)gc_params.oldmalloc_limit_max);
02933     }
02934     else {
02935         /* major GC */
02936         objspace->rgengc.oldmalloc_increase = 0;
02937 
02938         if ((objspace->profile.latest_gc_info & GPR_FLAG_MAJOR_BY_OLDMALLOC) == 0) {
02939             objspace->rgengc.oldmalloc_increase_limit =
02940               (size_t)(objspace->rgengc.oldmalloc_increase_limit / ((gc_params.oldmalloc_limit_growth_factor - 1)/10 + 1));
02941             if (objspace->rgengc.oldmalloc_increase_limit < gc_params.oldmalloc_limit_min) {
02942                 objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min;
02943             }
02944         }
02945     }
02946 
02947 #endif
02948 
02949 }
02950 
02951 static void
02952 gc_after_sweep(rb_objspace_t *objspace)
02953 {
02954     rb_heap_t *heap = heap_eden;
02955 
02956     rgengc_report(1, objspace, "after_gc_sweep: heap->total_slots: %d, heap->swept_slots: %d, min_free_slots: %d\n",
02957                   (int)heap->total_slots, (int)heap_pages_swept_slots, (int)heap_pages_min_free_slots);
02958 
02959     if (heap_pages_swept_slots < heap_pages_min_free_slots) {
02960 #if USE_RGENGC
02961         if (objspace->rgengc.during_minor_gc && objspace->profile.count - objspace->rgengc.last_major_gc > 2 /* magic number */) {
02962             objspace->rgengc.need_major_gc = GPR_FLAG_MAJOR_BY_NOFREE;
02963         }
02964         else {
02965             heap_set_increment(objspace, (heap_pages_min_free_slots - heap_pages_swept_slots) / HEAP_OBJ_LIMIT);
02966             heap_increment(objspace, heap);
02967         }
02968 #else
02969         heap_set_increment(objspace, (heap_pages_min_free_slots - heap_pages_swept_slots) / HEAP_OBJ_LIMIT);
02970         heap_increment(objspace, heap);
02971 #endif
02972     }
02973 
02974     gc_prof_set_heap_info(objspace);
02975 
02976     heap_pages_free_unused_pages(objspace);
02977 
02978     /* if heap_pages has unused pages, then assign them to increment */
02979     if (heap_pages_increment < heap_tomb->page_length) {
02980         heap_pages_increment = heap_tomb->page_length;
02981     }
02982 
02983 #if RGENGC_PROFILE > 0
02984     if (0) {
02985         fprintf(stderr, "%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
02986                 (int)rb_gc_count(),
02987                 (int)objspace->profile.major_gc_count,
02988                 (int)objspace->profile.minor_gc_count,
02989                 (int)objspace->profile.promote_infant_count,
02990 #if RGENGC_THREEGEN
02991                 (int)objspace->profile.promote_young_count,
02992 #else
02993                 0,
02994 #endif
02995                 (int)objspace->profile.remembered_normal_object_count,
02996                 (int)objspace->rgengc.remembered_shady_object_count);
02997     }
02998 #endif
02999 
03000     gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_END_SWEEP, 0);
03001 }
03002 
03003 static int
03004 gc_heap_lazy_sweep(rb_objspace_t *objspace, rb_heap_t *heap)
03005 {
03006     struct heap_page *page = heap->sweep_pages, *next;
03007     int result = FALSE;
03008 
03009     if (page == NULL) return FALSE;
03010 
03011 #if GC_ENABLE_LAZY_SWEEP
03012     gc_prof_sweep_timer_start(objspace);
03013 #endif
03014 
03015     while (page) {
03016         heap->sweep_pages = next = page->next;
03017 
03018         gc_page_sweep(objspace, heap, page);
03019 
03020         if (!next) gc_after_sweep(objspace);
03021 
03022         if (heap->free_pages) {
03023             result = TRUE;
03024             break;
03025         }
03026 
03027         page = next;
03028     }
03029 
03030 #if GC_ENABLE_LAZY_SWEEP
03031     gc_prof_sweep_timer_stop(objspace);
03032 #endif
03033 
03034     return result;
03035 }
03036 
03037 static void
03038 gc_heap_rest_sweep(rb_objspace_t *objspace, rb_heap_t *heap)
03039 {
03040     if (is_lazy_sweeping(heap)) {
03041         during_gc++;
03042         while (is_lazy_sweeping(heap)) {
03043             gc_heap_lazy_sweep(objspace, heap);
03044         }
03045         during_gc = 0;
03046     }
03047 }
03048 
03049 static void
03050 gc_rest_sweep(rb_objspace_t *objspace)
03051 {
03052     rb_heap_t *heap = heap_eden; /* lazy sweep only for eden */
03053     gc_heap_rest_sweep(objspace, heap);
03054 }
03055 
03056 static void
03057 gc_sweep(rb_objspace_t *objspace, int immediate_sweep)
03058 {
03059     if (immediate_sweep) {
03060 #if !GC_ENABLE_LAZY_SWEEP
03061         gc_prof_sweep_timer_start(objspace);
03062 #endif
03063         gc_before_sweep(objspace);
03064         gc_heap_rest_sweep(objspace, heap_eden);
03065 #if !GC_ENABLE_LAZY_SWEEP
03066         gc_prof_sweep_timer_stop(objspace);
03067 #endif
03068     }
03069     else {
03070         struct heap_page *page;
03071         gc_before_sweep(objspace);
03072         page = heap_eden->sweep_pages;
03073         while (page) {
03074             page->before_sweep = 1;
03075             page = page->next;
03076         }
03077         gc_heap_lazy_sweep(objspace, heap_eden);
03078     }
03079 
03080     gc_heap_prepare_minimum_pages(objspace, heap_eden);
03081 }
03082 
03083 /* Marking - Marking stack */
03084 
03085 static void push_mark_stack(mark_stack_t *, VALUE);
03086 static int pop_mark_stack(mark_stack_t *, VALUE *);
03087 static void shrink_stack_chunk_cache(mark_stack_t *stack);
03088 
03089 static stack_chunk_t *
03090 stack_chunk_alloc(void)
03091 {
03092     stack_chunk_t *res;
03093 
03094     res = malloc(sizeof(stack_chunk_t));
03095     if (!res)
03096         rb_memerror();
03097 
03098     return res;
03099 }
03100 
03101 static inline int
03102 is_mark_stack_empty(mark_stack_t *stack)
03103 {
03104     return stack->chunk == NULL;
03105 }
03106 
03107 static void
03108 add_stack_chunk_cache(mark_stack_t *stack, stack_chunk_t *chunk)
03109 {
03110     chunk->next = stack->cache;
03111     stack->cache = chunk;
03112     stack->cache_size++;
03113 }
03114 
03115 static void
03116 shrink_stack_chunk_cache(mark_stack_t *stack)
03117 {
03118     stack_chunk_t *chunk;
03119 
03120     if (stack->unused_cache_size > (stack->cache_size/2)) {
03121         chunk = stack->cache;
03122         stack->cache = stack->cache->next;
03123         stack->cache_size--;
03124         free(chunk);
03125     }
03126     stack->unused_cache_size = stack->cache_size;
03127 }
03128 
03129 static void
03130 push_mark_stack_chunk(mark_stack_t *stack)
03131 {
03132     stack_chunk_t *next;
03133 
03134     assert(stack->index == stack->limit);
03135     if (stack->cache_size > 0) {
03136         next = stack->cache;
03137         stack->cache = stack->cache->next;
03138         stack->cache_size--;
03139         if (stack->unused_cache_size > stack->cache_size)
03140             stack->unused_cache_size = stack->cache_size;
03141     }
03142     else {
03143         next = stack_chunk_alloc();
03144     }
03145     next->next = stack->chunk;
03146     stack->chunk = next;
03147     stack->index = 0;
03148 }
03149 
03150 static void
03151 pop_mark_stack_chunk(mark_stack_t *stack)
03152 {
03153     stack_chunk_t *prev;
03154 
03155     prev = stack->chunk->next;
03156     assert(stack->index == 0);
03157     add_stack_chunk_cache(stack, stack->chunk);
03158     stack->chunk = prev;
03159     stack->index = stack->limit;
03160 }
03161 
03162 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
03163 static void
03164 free_stack_chunks(mark_stack_t *stack)
03165 {
03166     stack_chunk_t *chunk = stack->chunk;
03167     stack_chunk_t *next = NULL;
03168 
03169     while (chunk != NULL) {
03170         next = chunk->next;
03171         free(chunk);
03172         chunk = next;
03173     }
03174 }
03175 #endif
03176 
03177 static void
03178 push_mark_stack(mark_stack_t *stack, VALUE data)
03179 {
03180     if (stack->index == stack->limit) {
03181         push_mark_stack_chunk(stack);
03182     }
03183     stack->chunk->data[stack->index++] = data;
03184 }
03185 
03186 static int
03187 pop_mark_stack(mark_stack_t *stack, VALUE *data)
03188 {
03189     if (is_mark_stack_empty(stack)) {
03190         return FALSE;
03191     }
03192     if (stack->index == 1) {
03193         *data = stack->chunk->data[--stack->index];
03194         pop_mark_stack_chunk(stack);
03195     }
03196     else {
03197         *data = stack->chunk->data[--stack->index];
03198     }
03199     return TRUE;
03200 }
03201 
03202 static void
03203 init_mark_stack(mark_stack_t *stack)
03204 {
03205     int i;
03206 
03207     if (0) push_mark_stack_chunk(stack);
03208     stack->index = stack->limit = STACK_CHUNK_SIZE;
03209 
03210     for (i=0; i < 4; i++) {
03211         add_stack_chunk_cache(stack, stack_chunk_alloc());
03212     }
03213     stack->unused_cache_size = stack->cache_size;
03214 }
03215 
03216 /* Marking */
03217 
03218 #ifdef __ia64
03219 #define SET_STACK_END (SET_MACHINE_STACK_END(&th->machine.stack_end), th->machine.register_stack_end = rb_ia64_bsp())
03220 #else
03221 #define SET_STACK_END SET_MACHINE_STACK_END(&th->machine.stack_end)
03222 #endif
03223 
03224 #define STACK_START (th->machine.stack_start)
03225 #define STACK_END (th->machine.stack_end)
03226 #define STACK_LEVEL_MAX (th->machine.stack_maxsize/sizeof(VALUE))
03227 
03228 #if STACK_GROW_DIRECTION < 0
03229 # define STACK_LENGTH  (size_t)(STACK_START - STACK_END)
03230 #elif STACK_GROW_DIRECTION > 0
03231 # define STACK_LENGTH  (size_t)(STACK_END - STACK_START + 1)
03232 #else
03233 # define STACK_LENGTH  ((STACK_END < STACK_START) ? (size_t)(STACK_START - STACK_END) \
03234                         : (size_t)(STACK_END - STACK_START + 1))
03235 #endif
03236 #if !STACK_GROW_DIRECTION
03237 int ruby_stack_grow_direction;
03238 int
03239 ruby_get_stack_grow_direction(volatile VALUE *addr)
03240 {
03241     VALUE *end;
03242     SET_MACHINE_STACK_END(&end);
03243 
03244     if (end > addr) return ruby_stack_grow_direction = 1;
03245     return ruby_stack_grow_direction = -1;
03246 }
03247 #endif
03248 
03249 size_t
03250 ruby_stack_length(VALUE **p)
03251 {
03252     rb_thread_t *th = GET_THREAD();
03253     SET_STACK_END;
03254     if (p) *p = STACK_UPPER(STACK_END, STACK_START, STACK_END);
03255     return STACK_LENGTH;
03256 }
03257 
03258 #if !(defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK))
03259 static int
03260 stack_check(int water_mark)
03261 {
03262     int ret;
03263     rb_thread_t *th = GET_THREAD();
03264     SET_STACK_END;
03265     ret = STACK_LENGTH > STACK_LEVEL_MAX - water_mark;
03266 #ifdef __ia64
03267     if (!ret) {
03268         ret = (VALUE*)rb_ia64_bsp() - th->machine.register_stack_start >
03269               th->machine.register_stack_maxsize/sizeof(VALUE) - water_mark;
03270     }
03271 #endif
03272     return ret;
03273 }
03274 #endif
03275 
03276 #define STACKFRAME_FOR_CALL_CFUNC 512
03277 
03278 int
03279 ruby_stack_check(void)
03280 {
03281 #if defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK)
03282     return 0;
03283 #else
03284     return stack_check(STACKFRAME_FOR_CALL_CFUNC);
03285 #endif
03286 }
03287 
03288 ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
03289 static void
03290 mark_locations_array(rb_objspace_t *objspace, register VALUE *x, register long n)
03291 {
03292     VALUE v;
03293     while (n--) {
03294         v = *x;
03295         gc_mark_maybe(objspace, v);
03296         x++;
03297     }
03298 }
03299 
03300 static void
03301 gc_mark_locations(rb_objspace_t *objspace, VALUE *start, VALUE *end)
03302 {
03303     long n;
03304 
03305     if (end <= start) return;
03306     n = end - start;
03307     mark_locations_array(objspace, start, n);
03308 }
03309 
03310 void
03311 rb_gc_mark_locations(VALUE *start, VALUE *end)
03312 {
03313     gc_mark_locations(&rb_objspace, start, end);
03314 }
03315 
03316 #define rb_gc_mark_locations(start, end) gc_mark_locations(objspace, (start), (end))
03317 
03318 struct mark_tbl_arg {
03319     rb_objspace_t *objspace;
03320 };
03321 
03322 static int
03323 mark_entry(st_data_t key, st_data_t value, st_data_t data)
03324 {
03325     struct mark_tbl_arg *arg = (void*)data;
03326     gc_mark(arg->objspace, (VALUE)value);
03327     return ST_CONTINUE;
03328 }
03329 
03330 static void
03331 mark_tbl(rb_objspace_t *objspace, st_table *tbl)
03332 {
03333     struct mark_tbl_arg arg;
03334     if (!tbl || tbl->num_entries == 0) return;
03335     arg.objspace = objspace;
03336     st_foreach(tbl, mark_entry, (st_data_t)&arg);
03337 }
03338 
03339 static int
03340 mark_key(st_data_t key, st_data_t value, st_data_t data)
03341 {
03342     struct mark_tbl_arg *arg = (void*)data;
03343     gc_mark(arg->objspace, (VALUE)key);
03344     return ST_CONTINUE;
03345 }
03346 
03347 static void
03348 mark_set(rb_objspace_t *objspace, st_table *tbl)
03349 {
03350     struct mark_tbl_arg arg;
03351     if (!tbl) return;
03352     arg.objspace = objspace;
03353     st_foreach(tbl, mark_key, (st_data_t)&arg);
03354 }
03355 
03356 void
03357 rb_mark_set(st_table *tbl)
03358 {
03359     mark_set(&rb_objspace, tbl);
03360 }
03361 
03362 static int
03363 mark_keyvalue(st_data_t key, st_data_t value, st_data_t data)
03364 {
03365     struct mark_tbl_arg *arg = (void*)data;
03366     gc_mark(arg->objspace, (VALUE)key);
03367     gc_mark(arg->objspace, (VALUE)value);
03368     return ST_CONTINUE;
03369 }
03370 
03371 static void
03372 mark_hash(rb_objspace_t *objspace, st_table *tbl)
03373 {
03374     struct mark_tbl_arg arg;
03375     if (!tbl) return;
03376     arg.objspace = objspace;
03377     st_foreach(tbl, mark_keyvalue, (st_data_t)&arg);
03378 }
03379 
03380 void
03381 rb_mark_hash(st_table *tbl)
03382 {
03383     mark_hash(&rb_objspace, tbl);
03384 }
03385 
03386 static void
03387 mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
03388 {
03389     const rb_method_definition_t *def = me->def;
03390 
03391     gc_mark(objspace, me->klass);
03392   again:
03393     if (!def) return;
03394     switch (def->type) {
03395       case VM_METHOD_TYPE_ISEQ:
03396         gc_mark(objspace, def->body.iseq->self);
03397         break;
03398       case VM_METHOD_TYPE_BMETHOD:
03399         gc_mark(objspace, def->body.proc);
03400         break;
03401       case VM_METHOD_TYPE_ATTRSET:
03402       case VM_METHOD_TYPE_IVAR:
03403         gc_mark(objspace, def->body.attr.location);
03404         break;
03405       case VM_METHOD_TYPE_REFINED:
03406         if (def->body.orig_me) {
03407             def = def->body.orig_me->def;
03408             goto again;
03409         }
03410         break;
03411       default:
03412         break; /* ignore */
03413     }
03414 }
03415 
03416 void
03417 rb_mark_method_entry(const rb_method_entry_t *me)
03418 {
03419     mark_method_entry(&rb_objspace, me);
03420 }
03421 
03422 static int
03423 mark_method_entry_i(ID key, const rb_method_entry_t *me, st_data_t data)
03424 {
03425     struct mark_tbl_arg *arg = (void*)data;
03426     mark_method_entry(arg->objspace, me);
03427     return ST_CONTINUE;
03428 }
03429 
03430 static void
03431 mark_m_tbl_wrapper(rb_objspace_t *objspace, struct method_table_wrapper *wrapper)
03432 {
03433     struct mark_tbl_arg arg;
03434     if (!wrapper || !wrapper->tbl) return;
03435     if (LIKELY(objspace->mark_func_data == 0)) {
03436         /* prevent multiple marking during same GC cycle,
03437          * since m_tbl is shared between several T_ICLASS */
03438         size_t serial = rb_gc_count();
03439         if (wrapper->serial == serial) return;
03440         wrapper->serial = serial;
03441     }
03442     arg.objspace = objspace;
03443     st_foreach(wrapper->tbl, mark_method_entry_i, (st_data_t)&arg);
03444 }
03445 
03446 static int
03447 mark_const_entry_i(ID key, const rb_const_entry_t *ce, st_data_t data)
03448 {
03449     struct mark_tbl_arg *arg = (void*)data;
03450     gc_mark(arg->objspace, ce->value);
03451     gc_mark(arg->objspace, ce->file);
03452     return ST_CONTINUE;
03453 }
03454 
03455 static void
03456 mark_const_tbl(rb_objspace_t *objspace, st_table *tbl)
03457 {
03458     struct mark_tbl_arg arg;
03459     if (!tbl) return;
03460     arg.objspace = objspace;
03461     st_foreach(tbl, mark_const_entry_i, (st_data_t)&arg);
03462 }
03463 
03464 #if STACK_GROW_DIRECTION < 0
03465 #define GET_STACK_BOUNDS(start, end, appendix) ((start) = STACK_END, (end) = STACK_START)
03466 #elif STACK_GROW_DIRECTION > 0
03467 #define GET_STACK_BOUNDS(start, end, appendix) ((start) = STACK_START, (end) = STACK_END+(appendix))
03468 #else
03469 #define GET_STACK_BOUNDS(start, end, appendix) \
03470     ((STACK_END < STACK_START) ? \
03471      ((start) = STACK_END, (end) = STACK_START) : ((start) = STACK_START, (end) = STACK_END+(appendix)))
03472 #endif
03473 
03474 static void
03475 mark_current_machine_context(rb_objspace_t *objspace, rb_thread_t *th)
03476 {
03477     union {
03478         rb_jmp_buf j;
03479         VALUE v[sizeof(rb_jmp_buf) / sizeof(VALUE)];
03480     } save_regs_gc_mark;
03481     VALUE *stack_start, *stack_end;
03482 
03483     FLUSH_REGISTER_WINDOWS;
03484     /* This assumes that all registers are saved into the jmp_buf (and stack) */
03485     rb_setjmp(save_regs_gc_mark.j);
03486 
03487     /* SET_STACK_END must be called in this function because
03488      * the stack frame of this function may contain
03489      * callee save registers and they should be marked. */
03490     SET_STACK_END;
03491     GET_STACK_BOUNDS(stack_start, stack_end, 1);
03492 
03493     mark_locations_array(objspace, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v));
03494 
03495     rb_gc_mark_locations(stack_start, stack_end);
03496 #ifdef __ia64
03497     rb_gc_mark_locations(th->machine.register_stack_start, th->machine.register_stack_end);
03498 #endif
03499 #if defined(__mc68000__)
03500     mark_locations_array(objspace, (VALUE*)((char*)STACK_END + 2),
03501                          (STACK_START - STACK_END));
03502 #endif
03503 }
03504 
03505 void
03506 rb_gc_mark_machine_stack(rb_thread_t *th)
03507 {
03508     rb_objspace_t *objspace = &rb_objspace;
03509     VALUE *stack_start, *stack_end;
03510 
03511     GET_STACK_BOUNDS(stack_start, stack_end, 0);
03512     rb_gc_mark_locations(stack_start, stack_end);
03513 #ifdef __ia64
03514     rb_gc_mark_locations(th->machine.register_stack_start, th->machine.register_stack_end);
03515 #endif
03516 }
03517 
03518 void
03519 rb_mark_tbl(st_table *tbl)
03520 {
03521     mark_tbl(&rb_objspace, tbl);
03522 }
03523 
03524 static void
03525 gc_mark_maybe(rb_objspace_t *objspace, VALUE obj)
03526 {
03527     (void)VALGRIND_MAKE_MEM_DEFINED(&obj, sizeof(obj));
03528     if (is_pointer_to_heap(objspace, (void *)obj)) {
03529         int type = BUILTIN_TYPE(obj);
03530         if (type != T_ZOMBIE && type != T_NONE) {
03531             gc_mark(objspace, obj);
03532         }
03533     }
03534 }
03535 
03536 void
03537 rb_gc_mark_maybe(VALUE obj)
03538 {
03539     gc_mark_maybe(&rb_objspace, obj);
03540 }
03541 
03542 static inline int
03543 gc_marked(rb_objspace_t *objspace, VALUE ptr)
03544 {
03545     register bits_t *bits = GET_HEAP_MARK_BITS(ptr);
03546     if (MARKED_IN_BITMAP(bits, ptr)) return 1;
03547     return 0;
03548 }
03549 
03550 static inline int
03551 gc_mark_ptr(rb_objspace_t *objspace, VALUE ptr)
03552 {
03553     register bits_t *bits = GET_HEAP_MARK_BITS(ptr);
03554     if (gc_marked(objspace, ptr)) return 0;
03555     MARK_IN_BITMAP(bits, ptr);
03556     return 1;
03557 }
03558 
03559 static void
03560 rgengc_check_relation(rb_objspace_t *objspace, VALUE obj)
03561 {
03562 #if USE_RGENGC
03563     if (objspace->rgengc.parent_object_is_old) {
03564         if (!RVALUE_WB_PROTECTED(obj)) {
03565             if (rgengc_remember(objspace, obj)) {
03566                 objspace->rgengc.remembered_shady_object_count++;
03567             }
03568         }
03569 #if RGENGC_THREEGEN
03570         else {
03571             if (gc_marked(objspace, obj)) {
03572                 if (!RVALUE_OLD_P(obj)) {
03573                     /* An object pointed from an OLD object should be OLD. */
03574                     rgengc_remember(objspace, obj);
03575                 }
03576             }
03577             else {
03578                 if (RVALUE_INFANT_P(obj)) {
03579                     RVALUE_PROMOTE_INFANT(obj);
03580                 }
03581             }
03582         }
03583 #endif
03584     }
03585 #endif
03586 }
03587 
03588 static void
03589 gc_mark(rb_objspace_t *objspace, VALUE ptr)
03590 {
03591     if (!is_markable_object(objspace, ptr)) return;
03592 
03593     if (LIKELY(objspace->mark_func_data == 0)) {
03594         rgengc_check_relation(objspace, ptr);
03595         if (!gc_mark_ptr(objspace, ptr)) return; /* already marked */
03596         push_mark_stack(&objspace->mark_stack, ptr);
03597     }
03598     else {
03599         objspace->mark_func_data->mark_func(ptr, objspace->mark_func_data->data);
03600     }
03601 }
03602 
03603 void
03604 rb_gc_mark(VALUE ptr)
03605 {
03606     gc_mark(&rb_objspace, ptr);
03607 }
03608 
03609 /* resurrect non-marked `obj' if obj is before swept */
03610 
03611 void
03612 rb_gc_resurrect(VALUE obj)
03613 {
03614     rb_objspace_t *objspace = &rb_objspace;
03615 
03616     if (is_lazy_sweeping(heap_eden) &&
03617         !gc_marked(objspace, obj) &&
03618         !is_swept_object(objspace, obj)) {
03619         gc_mark_ptr(objspace, obj);
03620     }
03621 }
03622 
03623 static void
03624 gc_mark_children(rb_objspace_t *objspace, VALUE ptr)
03625 {
03626     register RVALUE *obj = RANY(ptr);
03627 
03628     goto marking;               /* skip */
03629 
03630   again:
03631     if (LIKELY(objspace->mark_func_data == 0)) {
03632         obj = RANY(ptr);
03633         if (!is_markable_object(objspace, ptr)) return;
03634         rgengc_check_relation(objspace, ptr);
03635         if (!gc_mark_ptr(objspace, ptr)) return;  /* already marked */
03636     }
03637     else {
03638         gc_mark(objspace, ptr);
03639         return;
03640     }
03641 
03642   marking:
03643 
03644 #if USE_RGENGC
03645     check_gen_consistency((VALUE)obj);
03646 
03647     if (LIKELY(objspace->mark_func_data == 0)) {
03648         /* minor/major common */
03649         if (RVALUE_WB_PROTECTED(obj)) {
03650             if (RVALUE_INFANT_P((VALUE)obj)) {
03651                 /* infant -> young */
03652                 RVALUE_PROMOTE_INFANT((VALUE)obj);
03653 #if RGENGC_THREEGEN
03654                 /* infant -> young */
03655                 objspace->rgengc.young_object_count++;
03656                 objspace->rgengc.parent_object_is_old = FALSE;
03657 #else
03658                 /* infant -> old */
03659                 objspace->rgengc.old_object_count++;
03660                 objspace->rgengc.parent_object_is_old = TRUE;
03661 #endif
03662                 rgengc_report(3, objspace, "gc_mark_children: promote infant -> young %p (%s).\n", (void *)obj, obj_type_name((VALUE)obj));
03663             }
03664             else {
03665                 objspace->rgengc.parent_object_is_old = TRUE;
03666 
03667 #if RGENGC_THREEGEN
03668                 if (RVALUE_YOUNG_P((VALUE)obj)) {
03669                     /* young -> old */
03670                     RVALUE_PROMOTE_YOUNG((VALUE)obj);
03671                     objspace->rgengc.old_object_count++;
03672                     rgengc_report(3, objspace, "gc_mark_children: promote young -> old %p (%s).\n", (void *)obj, obj_type_name((VALUE)obj));
03673                 }
03674                 else {
03675 #endif
03676                     if (!objspace->rgengc.during_minor_gc) {
03677                         /* major/full GC */
03678                         objspace->rgengc.old_object_count++;
03679                     }
03680 #if RGENGC_THREEGEN
03681                 }
03682 #endif
03683             }
03684         }
03685         else {
03686             rgengc_report(3, objspace, "gc_mark_children: do not promote non-WB-protected %p (%s).\n", (void *)obj, obj_type_name((VALUE)obj));
03687             objspace->rgengc.parent_object_is_old = FALSE;
03688         }
03689     }
03690 
03691     check_gen_consistency((VALUE)obj);
03692 #endif /* USE_RGENGC */
03693 
03694     if (FL_TEST(obj, FL_EXIVAR)) {
03695         rb_mark_generic_ivar(ptr);
03696     }
03697 
03698     switch (BUILTIN_TYPE(obj)) {
03699       case T_NIL:
03700       case T_FIXNUM:
03701         rb_bug("rb_gc_mark() called for broken object");
03702         break;
03703 
03704       case T_NODE:
03705         switch (nd_type(obj)) {
03706           case NODE_IF:         /* 1,2,3 */
03707           case NODE_FOR:
03708           case NODE_ITER:
03709           case NODE_WHEN:
03710           case NODE_MASGN:
03711           case NODE_RESCUE:
03712           case NODE_RESBODY:
03713           case NODE_CLASS:
03714           case NODE_BLOCK_PASS:
03715             gc_mark(objspace, (VALUE)obj->as.node.u2.node);
03716             /* fall through */
03717           case NODE_BLOCK:      /* 1,3 */
03718           case NODE_ARRAY:
03719           case NODE_DSTR:
03720           case NODE_DXSTR:
03721           case NODE_DREGX:
03722           case NODE_DREGX_ONCE:
03723           case NODE_ENSURE:
03724           case NODE_CALL:
03725           case NODE_DEFS:
03726           case NODE_OP_ASGN1:
03727             gc_mark(objspace, (VALUE)obj->as.node.u1.node);
03728             /* fall through */
03729           case NODE_SUPER:      /* 3 */
03730           case NODE_FCALL:
03731           case NODE_DEFN:
03732           case NODE_ARGS_AUX:
03733             ptr = (VALUE)obj->as.node.u3.node;
03734             goto again;
03735 
03736           case NODE_WHILE:      /* 1,2 */
03737           case NODE_UNTIL:
03738           case NODE_AND:
03739           case NODE_OR:
03740           case NODE_CASE:
03741           case NODE_SCLASS:
03742           case NODE_DOT2:
03743           case NODE_DOT3:
03744           case NODE_FLIP2:
03745           case NODE_FLIP3:
03746           case NODE_MATCH2:
03747           case NODE_MATCH3:
03748           case NODE_OP_ASGN_OR:
03749           case NODE_OP_ASGN_AND:
03750           case NODE_MODULE:
03751           case NODE_ALIAS:
03752           case NODE_VALIAS:
03753           case NODE_ARGSCAT:
03754             gc_mark(objspace, (VALUE)obj->as.node.u1.node);
03755             /* fall through */
03756           case NODE_GASGN:      /* 2 */
03757           case NODE_LASGN:
03758           case NODE_DASGN:
03759           case NODE_DASGN_CURR:
03760           case NODE_IASGN:
03761           case NODE_IASGN2:
03762           case NODE_CVASGN:
03763           case NODE_COLON3:
03764           case NODE_OPT_N:
03765           case NODE_EVSTR:
03766           case NODE_UNDEF:
03767           case NODE_POSTEXE:
03768             ptr = (VALUE)obj->as.node.u2.node;
03769             goto again;
03770 
03771           case NODE_HASH:       /* 1 */
03772           case NODE_LIT:
03773           case NODE_STR:
03774           case NODE_XSTR:
03775           case NODE_DEFINED:
03776           case NODE_MATCH:
03777           case NODE_RETURN:
03778           case NODE_BREAK:
03779           case NODE_NEXT:
03780           case NODE_YIELD:
03781           case NODE_COLON2:
03782           case NODE_SPLAT:
03783           case NODE_TO_ARY:
03784             ptr = (VALUE)obj->as.node.u1.node;
03785             goto again;
03786 
03787           case NODE_SCOPE:      /* 2,3 */
03788           case NODE_CDECL:
03789           case NODE_OPT_ARG:
03790             gc_mark(objspace, (VALUE)obj->as.node.u3.node);
03791             ptr = (VALUE)obj->as.node.u2.node;
03792             goto again;
03793 
03794           case NODE_ARGS:       /* custom */
03795             {
03796                 struct rb_args_info *args = obj->as.node.u3.args;
03797                 if (args) {
03798                     if (args->pre_init)    gc_mark(objspace, (VALUE)args->pre_init);
03799                     if (args->post_init)   gc_mark(objspace, (VALUE)args->post_init);
03800                     if (args->opt_args)    gc_mark(objspace, (VALUE)args->opt_args);
03801                     if (args->kw_args)     gc_mark(objspace, (VALUE)args->kw_args);
03802                     if (args->kw_rest_arg) gc_mark(objspace, (VALUE)args->kw_rest_arg);
03803                 }
03804             }
03805             ptr = (VALUE)obj->as.node.u2.node;
03806             goto again;
03807 
03808           case NODE_ZARRAY:     /* - */
03809           case NODE_ZSUPER:
03810           case NODE_VCALL:
03811           case NODE_GVAR:
03812           case NODE_LVAR:
03813           case NODE_DVAR:
03814           case NODE_IVAR:
03815           case NODE_CVAR:
03816           case NODE_NTH_REF:
03817           case NODE_BACK_REF:
03818           case NODE_REDO:
03819           case NODE_RETRY:
03820           case NODE_SELF:
03821           case NODE_NIL:
03822           case NODE_TRUE:
03823           case NODE_FALSE:
03824           case NODE_ERRINFO:
03825           case NODE_BLOCK_ARG:
03826             break;
03827           case NODE_ALLOCA:
03828             mark_locations_array(objspace,
03829                                  (VALUE*)obj->as.node.u1.value,
03830                                  obj->as.node.u3.cnt);
03831             gc_mark(objspace, (VALUE)obj->as.node.u2.node);
03832             break;
03833 
03834           case NODE_CREF:
03835             gc_mark(objspace, obj->as.node.nd_refinements);
03836             gc_mark(objspace, (VALUE)obj->as.node.nd_clss);
03837             ptr = (VALUE)obj->as.node.nd_next;
03838             goto again;
03839 
03840           default:              /* unlisted NODE */
03841             gc_mark_maybe(objspace, (VALUE)obj->as.node.u1.node);
03842             gc_mark_maybe(objspace, (VALUE)obj->as.node.u2.node);
03843             gc_mark_maybe(objspace, (VALUE)obj->as.node.u3.node);
03844         }
03845         return;                 /* no need to mark class. */
03846     }
03847 
03848     gc_mark(objspace, obj->as.basic.klass);
03849     switch (BUILTIN_TYPE(obj)) {
03850       case T_ICLASS:
03851       case T_CLASS:
03852       case T_MODULE:
03853         mark_m_tbl_wrapper(objspace, RCLASS_M_TBL_WRAPPER(obj));
03854         if (!RCLASS_EXT(obj)) break;
03855         mark_tbl(objspace, RCLASS_IV_TBL(obj));
03856         mark_const_tbl(objspace, RCLASS_CONST_TBL(obj));
03857         ptr = RCLASS_SUPER((VALUE)obj);
03858         goto again;
03859 
03860       case T_ARRAY:
03861         if (FL_TEST(obj, ELTS_SHARED)) {
03862             ptr = obj->as.array.as.heap.aux.shared;
03863             goto again;
03864         }
03865         else {
03866             long i, len = RARRAY_LEN(obj);
03867             const VALUE *ptr = RARRAY_CONST_PTR(obj);
03868             for (i=0; i < len; i++) {
03869                 gc_mark(objspace, *ptr++);
03870             }
03871         }
03872         break;
03873 
03874       case T_HASH:
03875         mark_hash(objspace, obj->as.hash.ntbl);
03876         ptr = obj->as.hash.ifnone;
03877         goto again;
03878 
03879       case T_STRING:
03880 #define STR_ASSOC FL_USER3   /* copied from string.c */
03881         if (FL_TEST(obj, RSTRING_NOEMBED) && FL_ANY(obj, ELTS_SHARED|STR_ASSOC)) {
03882             ptr = obj->as.string.as.heap.aux.shared;
03883             goto again;
03884         }
03885         break;
03886 
03887       case T_DATA:
03888         if (RTYPEDDATA_P(obj)) {
03889             RUBY_DATA_FUNC mark_func = obj->as.typeddata.type->function.dmark;
03890             if (mark_func) (*mark_func)(DATA_PTR(obj));
03891         }
03892         else {
03893             if (obj->as.data.dmark) (*obj->as.data.dmark)(DATA_PTR(obj));
03894         }
03895         break;
03896 
03897       case T_OBJECT:
03898         {
03899             long i, len = ROBJECT_NUMIV(obj);
03900             VALUE *ptr = ROBJECT_IVPTR(obj);
03901             for (i  = 0; i < len; i++) {
03902                 gc_mark(objspace, *ptr++);
03903             }
03904         }
03905         break;
03906 
03907       case T_FILE:
03908         if (obj->as.file.fptr) {
03909             gc_mark(objspace, obj->as.file.fptr->pathv);
03910             gc_mark(objspace, obj->as.file.fptr->tied_io_for_writing);
03911             gc_mark(objspace, obj->as.file.fptr->writeconv_asciicompat);
03912             gc_mark(objspace, obj->as.file.fptr->writeconv_pre_ecopts);
03913             gc_mark(objspace, obj->as.file.fptr->encs.ecopts);
03914             gc_mark(objspace, obj->as.file.fptr->write_lock);
03915         }
03916         break;
03917 
03918       case T_REGEXP:
03919         ptr = obj->as.regexp.src;
03920         goto again;
03921 
03922       case T_FLOAT:
03923       case T_BIGNUM:
03924         break;
03925 
03926       case T_MATCH:
03927         gc_mark(objspace, obj->as.match.regexp);
03928         if (obj->as.match.str) {
03929             ptr = obj->as.match.str;
03930             goto again;
03931         }
03932         break;
03933 
03934       case T_RATIONAL:
03935         gc_mark(objspace, obj->as.rational.num);
03936         ptr = obj->as.rational.den;
03937         goto again;
03938 
03939       case T_COMPLEX:
03940         gc_mark(objspace, obj->as.complex.real);
03941         ptr = obj->as.complex.imag;
03942         goto again;
03943 
03944       case T_STRUCT:
03945         {
03946             long len = RSTRUCT_LEN(obj);
03947             const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
03948 
03949             while (len--) {
03950                 gc_mark(objspace, *ptr++);
03951             }
03952         }
03953         break;
03954 
03955       default:
03956 #if GC_DEBUG
03957         rb_gcdebug_print_obj_condition((VALUE)obj);
03958 #endif
03959         if (BUILTIN_TYPE(obj) == T_NONE)   rb_bug("rb_gc_mark(): %p is T_NONE", (void *)obj);
03960         if (BUILTIN_TYPE(obj) == T_ZOMBIE) rb_bug("rb_gc_mark(): %p is T_ZOMBIE", (void *)obj);
03961         rb_bug("rb_gc_mark(): unknown data type 0x%x(%p) %s",
03962                BUILTIN_TYPE(obj), (void *)obj,
03963                is_pointer_to_heap(objspace, obj) ? "corrupted object" : "non object");
03964     }
03965 }
03966 
03967 static void
03968 gc_mark_stacked_objects(rb_objspace_t *objspace)
03969 {
03970     mark_stack_t *mstack = &objspace->mark_stack;
03971     VALUE obj = 0;
03972 
03973     if (!mstack->index) return;
03974     while (pop_mark_stack(mstack, &obj)) {
03975         if (RGENGC_CHECK_MODE > 0 && !gc_marked(objspace, obj)) {
03976             rb_bug("gc_mark_stacked_objects: %p (%s) is infant, but not marked.", (void *)obj, obj_type_name(obj));
03977         }
03978         gc_mark_children(objspace, obj);
03979     }
03980     shrink_stack_chunk_cache(mstack);
03981 }
03982 
03983 #ifndef RGENGC_PRINT_TICK
03984 #define RGENGC_PRINT_TICK 0
03985 #endif
03986 /* the following code is only for internal tuning. */
03987 
03988 /* Source code to use RDTSC is quoted and modified from
03989  * http://www.mcs.anl.gov/~kazutomo/rdtsc.html
03990  * written by Kazutomo Yoshii <kazutomo@mcs.anl.gov>
03991  */
03992 
03993 #if RGENGC_PRINT_TICK
03994 #if defined(__GNUC__) && defined(__i386__)
03995 typedef unsigned long long tick_t;
03996 
03997 static inline tick_t
03998 tick(void)
03999 {
04000     unsigned long long int x;
04001     __asm__ __volatile__ ("rdtsc" : "=A" (x));
04002     return x;
04003 }
04004 
04005 #elif defined(__GNUC__) && defined(__x86_64__)
04006 typedef unsigned long long tick_t;
04007 
04008 static __inline__ tick_t
04009 tick(void)
04010 {
04011     unsigned long hi, lo;
04012     __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
04013     return ((unsigned long long)lo)|( ((unsigned long long)hi)<<32);
04014 }
04015 
04016 #elif defined(_WIN32) && defined(_MSC_VER)
04017 #include <intrin.h>
04018 typedef unsigned __int64 tick_t;
04019 
04020 static inline tick_t
04021 tick(void)
04022 {
04023     return __rdtsc();
04024 }
04025 
04026 #else /* use clock */
04027 typedef clock_t tick_t;
04028 static inline tick_t
04029 tick(void)
04030 {
04031     return clock();
04032 }
04033 #endif
04034 
04035 #define MAX_TICKS 0x100
04036 static tick_t mark_ticks[MAX_TICKS];
04037 static const char *mark_ticks_categories[MAX_TICKS];
04038 
04039 static void
04040 show_mark_ticks(void)
04041 {
04042     int i;
04043     fprintf(stderr, "mark ticks result:\n");
04044     for (i=0; i<MAX_TICKS; i++) {
04045         const char *category = mark_ticks_categories[i];
04046         if (category) {
04047             fprintf(stderr, "%s\t%8lu\n", category, (unsigned long)mark_ticks[i]);
04048         }
04049         else {
04050             break;
04051         }
04052     }
04053 }
04054 
04055 #endif /* RGENGC_PRINT_TICK */
04056 
04057 static void
04058 gc_mark_roots(rb_objspace_t *objspace, int full_mark, const char **categoryp)
04059 {
04060     struct gc_list *list;
04061     rb_thread_t *th = GET_THREAD();
04062     if (categoryp) *categoryp = "xxx";
04063 
04064 #if RGENGC_PRINT_TICK
04065     tick_t start_tick = tick();
04066     int tick_count = 0;
04067     const char *prev_category = 0;
04068 
04069     if (mark_ticks_categories[0] == 0) {
04070         atexit(show_mark_ticks);
04071     }
04072 #endif
04073 
04074 #if RGENGC_PRINT_TICK
04075 #define MARK_CHECKPOINT_PRINT_TICK(category) do { \
04076     if (prev_category) { \
04077         tick_t t = tick(); \
04078         mark_ticks[tick_count] = t - start_tick; \
04079         mark_ticks_categories[tick_count] = prev_category; \
04080         tick_count++; \
04081     } \
04082     prev_category = category; \
04083     start_tick = tick(); \
04084 } while (0)
04085 #else /* RGENGC_PRINT_TICK */
04086 #define MARK_CHECKPOINT_PRINT_TICK(category)
04087 #endif
04088 
04089 #define MARK_CHECKPOINT(category) do { \
04090     if (categoryp) *categoryp = category; \
04091     MARK_CHECKPOINT_PRINT_TICK(category); \
04092 } while (0)
04093 
04094     MARK_CHECKPOINT("vm");
04095     SET_STACK_END;
04096     th->vm->self ? rb_gc_mark(th->vm->self) : rb_vm_mark(th->vm);
04097 
04098     MARK_CHECKPOINT("finalizers");
04099     mark_tbl(objspace, finalizer_table);
04100 
04101     MARK_CHECKPOINT("machine_context");
04102     mark_current_machine_context(objspace, th);
04103 
04104     MARK_CHECKPOINT("symbols");
04105 #if USE_RGENGC
04106     objspace->rgengc.parent_object_is_old = TRUE;
04107     rb_gc_mark_symbols(full_mark);
04108     objspace->rgengc.parent_object_is_old = FALSE;
04109 #else
04110     rb_gc_mark_symbols(full_mark);
04111 #endif
04112 
04113     MARK_CHECKPOINT("encodings");
04114     rb_gc_mark_encodings();
04115 
04116     /* mark protected global variables */
04117     MARK_CHECKPOINT("global_list");
04118     for (list = global_List; list; list = list->next) {
04119         rb_gc_mark_maybe(*list->varptr);
04120     }
04121 
04122     MARK_CHECKPOINT("end_proc");
04123     rb_mark_end_proc();
04124 
04125     MARK_CHECKPOINT("global_tbl");
04126     rb_gc_mark_global_tbl();
04127 
04128     /* mark generic instance variables for special constants */
04129     MARK_CHECKPOINT("generic_ivars");
04130     rb_mark_generic_ivar_tbl();
04131 
04132     MARK_CHECKPOINT("parser");
04133     rb_gc_mark_parser();
04134 
04135     MARK_CHECKPOINT("live_method_entries");
04136     rb_gc_mark_unlinked_live_method_entries(th->vm);
04137 
04138     MARK_CHECKPOINT("finish");
04139 #undef MARK_CHECKPOINT
04140 }
04141 
04142 static void
04143 gc_marks_body(rb_objspace_t *objspace, int full_mark)
04144 {
04145     /* start marking */
04146     rgengc_report(1, objspace, "gc_marks_body: start (%s)\n", full_mark ? "full" : "minor");
04147 
04148 #if USE_RGENGC
04149     objspace->rgengc.parent_object_is_old = FALSE;
04150     objspace->rgengc.during_minor_gc = full_mark ? FALSE : TRUE;
04151 
04152     if (objspace->rgengc.during_minor_gc) {
04153         objspace->profile.minor_gc_count++;
04154         rgengc_rememberset_mark(objspace, heap_eden);
04155     }
04156     else {
04157         objspace->profile.major_gc_count++;
04158         rgengc_mark_and_rememberset_clear(objspace, heap_eden);
04159     }
04160 #endif
04161     gc_mark_roots(objspace, full_mark, 0);
04162     gc_mark_stacked_objects(objspace);
04163 
04164     gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_END_MARK, 0);
04165     rgengc_report(1, objspace, "gc_marks_body: end (%s)\n", full_mark ? "full" : "minor");
04166 }
04167 
04168 struct verify_internal_consistency_struct {
04169     rb_objspace_t *objspace;
04170     int err_count;
04171     VALUE parent;
04172 };
04173 
04174 #if USE_RGENGC
04175 static void
04176 verify_internal_consistency_reachable_i(VALUE child, void *ptr)
04177 {
04178     struct verify_internal_consistency_struct *data = (struct verify_internal_consistency_struct *)ptr;
04179 
04180     assert(RVALUE_OLD_P(data->parent));
04181 
04182     if (!RVALUE_OLD_P(child)) {
04183         if (!MARKED_IN_BITMAP(GET_HEAP_PAGE(data->parent)->rememberset_bits, data->parent) &&
04184             !MARKED_IN_BITMAP(GET_HEAP_PAGE(child)->rememberset_bits, child)) {
04185             fprintf(stderr, "verify_internal_consistency_reachable_i: WB miss %p (%s) -> %p (%s)\n",
04186                     (void *)data->parent, obj_type_name(data->parent),
04187                     (void *)child, obj_type_name(child));
04188             data->err_count++;
04189         }
04190     }
04191 }
04192 
04193 static int
04194 verify_internal_consistency_i(void *page_start, void *page_end, size_t stride, void *ptr)
04195 {
04196     struct verify_internal_consistency_struct *data = (struct verify_internal_consistency_struct *)ptr;
04197     VALUE v;
04198 
04199     for (v = (VALUE)page_start; v != (VALUE)page_end; v += stride) {
04200         if (is_live_object(data->objspace, v)) {
04201             if (RVALUE_OLD_P(v)) {
04202                 data->parent = v;
04203                 /* reachable objects from an oldgen object should be old or (young with remember) */
04204                 rb_objspace_reachable_objects_from(v, verify_internal_consistency_reachable_i, (void *)data);
04205             }
04206         }
04207     }
04208 
04209     return 0;
04210 }
04211 #endif /* USE_RGENGC */
04212 
04213 /*
04214  *  call-seq:
04215  *     GC.verify_internal_consistency                  -> nil
04216  *
04217  *  Verify internal consistency.
04218  *
04219  *  This method is implementation specific.
04220  *  Now this method checks generatioanl consistency
04221  *  if RGenGC is supported.
04222  */
04223 static VALUE
04224 gc_verify_internal_consistency(VALUE self)
04225 {
04226     struct verify_internal_consistency_struct data;
04227     data.objspace = &rb_objspace;
04228     data.err_count = 0;
04229 
04230 #if USE_RGENGC
04231     {
04232         struct each_obj_args eo_args;
04233         eo_args.callback = verify_internal_consistency_i;
04234         eo_args.data = (void *)&data;
04235         objspace_each_objects((VALUE)&eo_args);
04236     }
04237 #endif
04238     if (data.err_count != 0) {
04239         rb_bug("gc_verify_internal_consistency: found internal consistency.\n");
04240     }
04241     return Qnil;
04242 }
04243 
04244 #if RGENGC_CHECK_MODE >= 3
04245 
04246 #define MAKE_ROOTSIG(obj) (((VALUE)(obj) << 1) | 0x01)
04247 #define IS_ROOTSIG(obj)   ((VALUE)(obj) & 0x01)
04248 #define GET_ROOTSIG(obj)  ((const char *)((VALUE)(obj) >> 1))
04249 
04250 struct reflist {
04251     VALUE *list;
04252     int pos;
04253     int size;
04254 };
04255 
04256 static struct reflist *
04257 reflist_create(VALUE obj)
04258 {
04259     struct reflist *refs = xmalloc(sizeof(struct reflist));
04260     refs->size = 1;
04261     refs->list = ALLOC_N(VALUE, refs->size);
04262     refs->list[0] = obj;
04263     refs->pos = 1;
04264     return refs;
04265 }
04266 
04267 static void
04268 reflist_destruct(struct reflist *refs)
04269 {
04270     xfree(refs->list);
04271     xfree(refs);
04272 }
04273 
04274 static void
04275 reflist_add(struct reflist *refs, VALUE obj)
04276 {
04277     if (refs->pos == refs->size) {
04278         refs->size *= 2;
04279         SIZED_REALLOC_N(refs->list, VALUE, refs->size, refs->size/2);
04280     }
04281 
04282     refs->list[refs->pos++] = obj;
04283 }
04284 
04285 static void
04286 reflist_dump(struct reflist *refs)
04287 {
04288     int i;
04289     for (i=0; i<refs->pos; i++) {
04290         VALUE obj = refs->list[i];
04291         if (IS_ROOTSIG(obj)) { /* root */
04292             fprintf(stderr, "<root@%s>", GET_ROOTSIG(obj));
04293         }
04294         else {
04295             fprintf(stderr, "<%p@%s>", (void *)obj, obj_type_name(obj));
04296         }
04297         if (i+1 < refs->pos) fprintf(stderr, ", ");
04298     }
04299 }
04300 
04301 #if RGENGC_CHECK_MODE >= 3
04302 static int
04303 reflist_refered_from_machine_context(struct reflist *refs)
04304 {
04305     int i;
04306     for (i=0; i<refs->pos; i++) {
04307         VALUE obj = refs->list[i];
04308         if (IS_ROOTSIG(obj) && strcmp(GET_ROOTSIG(obj), "machine_context") == 0) return 1;
04309     }
04310     return 0;
04311 }
04312 #endif
04313 
04314 struct allrefs {
04315     rb_objspace_t *objspace;
04316     /* a -> obj1
04317      * b -> obj1
04318      * c -> obj1
04319      * c -> obj2
04320      * d -> obj3
04321      * #=> {obj1 => [a, b, c], obj2 => [c, d]}
04322      */
04323     struct st_table *references;
04324     const char *category;
04325     VALUE root_obj;
04326 };
04327 
04328 static int
04329 allrefs_add(struct allrefs *data, VALUE obj)
04330 {
04331     struct reflist *refs;
04332 
04333     if (st_lookup(data->references, obj, (st_data_t *)&refs)) {
04334         reflist_add(refs, data->root_obj);
04335         return 0;
04336     }
04337     else {
04338         refs = reflist_create(data->root_obj);
04339         st_insert(data->references, obj, (st_data_t)refs);
04340         return 1;
04341     }
04342 }
04343 
04344 static void
04345 allrefs_i(VALUE obj, void *ptr)
04346 {
04347     struct allrefs *data = (struct allrefs *)ptr;
04348 
04349     if (allrefs_add(data, obj)) {
04350         push_mark_stack(&data->objspace->mark_stack, obj);
04351     }
04352 }
04353 
04354 static void
04355 allrefs_roots_i(VALUE obj, void *ptr)
04356 {
04357     struct allrefs *data = (struct allrefs *)ptr;
04358     if (strlen(data->category) == 0) rb_bug("!!!");
04359     data->root_obj = MAKE_ROOTSIG(data->category);
04360 
04361     if (allrefs_add(data, obj)) {
04362         push_mark_stack(&data->objspace->mark_stack, obj);
04363     }
04364 }
04365 
04366 static st_table *
04367 objspace_allrefs(rb_objspace_t *objspace)
04368 {
04369     struct allrefs data;
04370     struct mark_func_data_struct mfd;
04371     VALUE obj;
04372 
04373     data.objspace = objspace;
04374     data.references = st_init_numtable();
04375 
04376     mfd.mark_func = allrefs_roots_i;
04377     mfd.data = &data;
04378 
04379     /* traverse root objects */
04380     objspace->mark_func_data = &mfd;
04381     gc_mark_roots(objspace, TRUE, &data.category);
04382     objspace->mark_func_data = 0;
04383 
04384     /* traverse rest objects reachable from root objects */
04385     while (pop_mark_stack(&objspace->mark_stack, &obj)) {
04386         rb_objspace_reachable_objects_from(data.root_obj = obj, allrefs_i, &data);
04387     }
04388     shrink_stack_chunk_cache(&objspace->mark_stack);
04389 
04390     return data.references;
04391 }
04392 
04393 static int
04394 objspaec_allrefs_destruct_i(st_data_t key, st_data_t value, void *ptr)
04395 {
04396     struct reflist *refs = (struct reflist *)value;
04397     reflist_destruct(refs);
04398     return ST_CONTINUE;
04399 }
04400 
04401 static void
04402 objspace_allrefs_destruct(struct st_table *refs)
04403 {
04404     st_foreach(refs, objspaec_allrefs_destruct_i, 0);
04405     st_free_table(refs);
04406 }
04407 
04408 #if RGENGC_CHECK_MODE >= 4
04409 static int
04410 allrefs_dump_i(st_data_t k, st_data_t v, st_data_t ptr)
04411 {
04412     VALUE obj = (VALUE)k;
04413     struct reflist *refs = (struct reflist *)v;
04414     fprintf(stderr, "[allrefs_dump_i] %p (%s%s%s%s) <- ",
04415             (void *)obj, obj_type_name(obj),
04416             RVALUE_OLD_P(obj)        ? "[O]" : "[Y]",
04417             RVALUE_WB_PROTECTED(obj) ? "[W]" : "",
04418             MARKED_IN_BITMAP(GET_HEAP_REMEMBERSET_BITS(obj), obj) ? "[R]" : "");
04419     reflist_dump(refs);
04420     fprintf(stderr, "\n");
04421     return ST_CONTINUE;
04422 }
04423 
04424 static void
04425 allrefs_dump(rb_objspace_t *objspace)
04426 {
04427     fprintf(stderr, "[all refs] (size: %d)\n", (int)objspace->rgengc.allrefs_table->num_entries);
04428     st_foreach(objspace->rgengc.allrefs_table, allrefs_dump_i, 0);
04429 }
04430 #endif
04431 
04432 #if RGENGC_CHECK_MODE >= 3
04433 static int
04434 gc_check_after_marks_i(st_data_t k, st_data_t v, void *ptr)
04435 {
04436     VALUE obj = k;
04437     struct reflist *refs = (struct reflist *)v;
04438     rb_objspace_t *objspace = (rb_objspace_t *)ptr;
04439 
04440     /* object should be marked or oldgen */
04441     if (!MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(obj), obj)) {
04442         fprintf(stderr, "gc_check_after_marks_i: %p (%s) is not marked and not oldgen.\n", (void *)obj, obj_type_name(obj));
04443         fprintf(stderr, "gc_check_after_marks_i: %p is referred from ", (void *)obj);
04444         reflist_dump(refs);
04445 
04446         if (reflist_refered_from_machine_context(refs)) {
04447             fprintf(stderr, " (marked from machine stack).\n");
04448             /* marked from machine context can be false positive */
04449         }
04450         else {
04451             objspace->rgengc.error_count++;
04452             fprintf(stderr, "\n");
04453         }
04454     }
04455     return ST_CONTINUE;
04456 }
04457 #endif
04458 
04459 static void
04460 gc_marks_check(rb_objspace_t *objspace, int (*checker_func)(ANYARGS), const char *checker_name)
04461 {
04462 
04463     size_t saved_malloc_increase = objspace->malloc_params.increase;
04464 #if RGENGC_ESTIMATE_OLDMALLOC
04465     size_t saved_oldmalloc_increase = objspace->rgengc.oldmalloc_increase;
04466 #endif
04467     VALUE already_disabled = rb_gc_disable();
04468 
04469     objspace->rgengc.allrefs_table = objspace_allrefs(objspace);
04470     st_foreach(objspace->rgengc.allrefs_table, checker_func, (st_data_t)objspace);
04471 
04472     if (objspace->rgengc.error_count > 0) {
04473 #if RGENGC_CHECK_MODE >= 4
04474         allrefs_dump(objspace);
04475 #endif
04476         rb_bug("%s: GC has problem.", checker_name);
04477     }
04478 
04479     objspace_allrefs_destruct(objspace->rgengc.allrefs_table);
04480     objspace->rgengc.allrefs_table = 0;
04481 
04482     if (already_disabled == Qfalse) rb_gc_enable();
04483     objspace->malloc_params.increase = saved_malloc_increase;
04484 #if RGENGC_ESTIMATE_OLDMALLOC
04485     objspace->rgengc.oldmalloc_increase = saved_oldmalloc_increase;
04486 #endif
04487 }
04488 
04489 #endif /* RGENGC_CHECK_MODE >= 2 */
04490 
04491 static void
04492 gc_marks(rb_objspace_t *objspace, int full_mark)
04493 {
04494     struct mark_func_data_struct *prev_mark_func_data;
04495 
04496     gc_prof_mark_timer_start(objspace);
04497     {
04498         /* setup marking */
04499         prev_mark_func_data = objspace->mark_func_data;
04500         objspace->mark_func_data = 0;
04501 
04502 #if USE_RGENGC
04503 
04504 #if RGENGC_CHECK_MODE >= 2
04505         gc_verify_internal_consistency(Qnil);
04506 #endif
04507         if (full_mark == TRUE) { /* major/full GC */
04508             objspace->rgengc.remembered_shady_object_count = 0;
04509             objspace->rgengc.old_object_count = 0;
04510 #if RGENGC_THREEGEN
04511             objspace->rgengc.young_object_count = 0;
04512 #endif
04513 
04514             gc_marks_body(objspace, TRUE);
04515             {
04516                 /* See the comment about RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR */
04517                 const double r = gc_params.oldobject_limit_factor;
04518                 objspace->rgengc.remembered_shady_object_limit = (size_t)(objspace->rgengc.remembered_shady_object_count * r);
04519                 objspace->rgengc.old_object_limit = (size_t)(objspace->rgengc.old_object_count * r);
04520             }
04521         }
04522         else { /* minor GC */
04523             gc_marks_body(objspace, FALSE);
04524         }
04525 
04526 #if RGENGC_PROFILE > 0
04527         if (gc_prof_record(objspace)) {
04528             gc_profile_record *record = gc_prof_record(objspace);
04529             record->old_objects = objspace->rgengc.old_object_count;
04530         }
04531 #endif
04532 
04533 #if RGENGC_CHECK_MODE >= 3
04534         gc_marks_check(objspace, gc_check_after_marks_i, "after_marks");
04535 #endif
04536 
04537 #else /* USE_RGENGC */
04538         gc_marks_body(objspace, TRUE);
04539 #endif
04540 
04541         objspace->mark_func_data = prev_mark_func_data;
04542     }
04543     gc_prof_mark_timer_stop(objspace);
04544 }
04545 
04546 /* RGENGC */
04547 
04548 static void
04549 rgengc_report_body(int level, rb_objspace_t *objspace, const char *fmt, ...)
04550 {
04551     if (level <= RGENGC_DEBUG) {
04552         char buf[1024];
04553         FILE *out = stderr;
04554         va_list args;
04555         const char *status = " ";
04556 
04557 #if USE_RGENGC
04558         if (during_gc) {
04559             status = objspace->rgengc.during_minor_gc ? "-" : "+";
04560         }
04561 #endif
04562 
04563         va_start(args, fmt);
04564         vsnprintf(buf, 1024, fmt, args);
04565         va_end(args);
04566 
04567         fprintf(out, "%s|", status);
04568         fputs(buf, out);
04569     }
04570 }
04571 
04572 #if USE_RGENGC
04573 
04574 /* bit operations */
04575 
04576 static int
04577 rgengc_remembersetbits_get(rb_objspace_t *objspace, VALUE obj)
04578 {
04579     bits_t *bits = GET_HEAP_REMEMBERSET_BITS(obj);
04580     return MARKED_IN_BITMAP(bits, obj) ? 1 : 0;
04581 }
04582 
04583 static int
04584 rgengc_remembersetbits_set(rb_objspace_t *objspace, VALUE obj)
04585 {
04586     bits_t *bits = GET_HEAP_REMEMBERSET_BITS(obj);
04587     if (MARKED_IN_BITMAP(bits, obj)) {
04588         return FALSE;
04589     }
04590     else {
04591         MARK_IN_BITMAP(bits, obj);
04592         return TRUE;
04593     }
04594 }
04595 
04596 /* wb, etc */
04597 
04598 /* return FALSE if already remembered */
04599 static int
04600 rgengc_remember(rb_objspace_t *objspace, VALUE obj)
04601 {
04602     rgengc_report(2, objspace, "rgengc_remember: %p (%s, %s) %s\n", (void *)obj, obj_type_name(obj),
04603                   RVALUE_WB_PROTECTED(obj) ? "WB-protected" : "non-WB-protected",
04604                   rgengc_remembersetbits_get(objspace, obj) ? "was already remembered" : "is remembered now");
04605 
04606 #if RGENGC_CHECK_MODE > 0
04607     {
04608         switch (BUILTIN_TYPE(obj)) {
04609           case T_NONE:
04610           case T_ZOMBIE:
04611             rb_bug("rgengc_remember: should not remember %p (%s)\n",
04612                    (void *)obj, obj_type_name(obj));
04613           default:
04614             ;
04615         }
04616     }
04617 #endif
04618 
04619     if (RGENGC_PROFILE) {
04620         if (!rgengc_remembered(objspace, obj)) {
04621 #if RGENGC_PROFILE > 0
04622             if (RVALUE_WB_PROTECTED(obj)) {
04623                 objspace->profile.remembered_normal_object_count++;
04624 #if RGENGC_PROFILE >= 2
04625                 objspace->profile.remembered_normal_object_count_types[BUILTIN_TYPE(obj)]++;
04626 #endif
04627             }
04628             else {
04629                 objspace->profile.remembered_shady_object_count++;
04630 #if RGENGC_PROFILE >= 2
04631                 objspace->profile.remembered_shady_object_count_types[BUILTIN_TYPE(obj)]++;
04632 #endif
04633             }
04634 #endif /* RGENGC_PROFILE > 0 */
04635         }
04636     }
04637 
04638     return rgengc_remembersetbits_set(objspace, obj);
04639 }
04640 
04641 static int
04642 rgengc_remembered(rb_objspace_t *objspace, VALUE obj)
04643 {
04644     int result = rgengc_remembersetbits_get(objspace, obj);
04645     check_gen_consistency(obj);
04646     rgengc_report(6, objspace, "gc_remembered: %p (%s) => %d\n", (void *)obj, obj_type_name(obj), result);
04647     return result;
04648 }
04649 
04650 static void
04651 rgengc_rememberset_mark(rb_objspace_t *objspace, rb_heap_t *heap)
04652 {
04653     size_t j;
04654     RVALUE *p, *offset;
04655     bits_t *bits, bitset;
04656     struct heap_page *page = heap->pages;
04657 
04658 #if RGENGC_PROFILE > 0
04659     size_t shady_object_count = 0, clear_count = 0;
04660 #endif
04661 
04662     while (page) {
04663         p = page->start;
04664         bits = page->rememberset_bits;
04665         offset = p - NUM_IN_PAGE(p);
04666 
04667         for (j=0; j < HEAP_BITMAP_LIMIT; j++) {
04668             if (bits[j]) {
04669                 p = offset  + j * BITS_BITLENGTH;
04670                 bitset = bits[j];
04671                 do {
04672                     if (bitset & 1) {
04673                         /* mark before RVALUE_PROMOTE_... */
04674                         gc_mark_ptr(objspace, (VALUE)p);
04675 
04676                         if (RVALUE_WB_PROTECTED(p)) {
04677                             rgengc_report(2, objspace, "rgengc_rememberset_mark: clear %p (%s)\n", p, obj_type_name((VALUE)p));
04678 #if RGENGC_THREEGEN
04679                             if (RVALUE_INFANT_P((VALUE)p)) RVALUE_PROMOTE_INFANT((VALUE)p);
04680                             if (RVALUE_YOUNG_P((VALUE)p)) RVALUE_PROMOTE_YOUNG((VALUE)p);
04681 #endif
04682                             CLEAR_IN_BITMAP(bits, p);
04683 #if RGENGC_PROFILE > 0
04684                             clear_count++;
04685 #endif
04686                         }
04687                         else {
04688 #if RGENGC_PROFILE > 0
04689                             shady_object_count++;
04690 #endif
04691                         }
04692 
04693                         rgengc_report(2, objspace, "rgengc_rememberset_mark: mark %p (%s)\n", p, obj_type_name((VALUE)p));
04694                         gc_mark_children(objspace, (VALUE) p);
04695                     }
04696                     p++;
04697                     bitset >>= 1;
04698                 } while (bitset);
04699             }
04700         }
04701         page = page->next;
04702     }
04703 
04704     rgengc_report(2, objspace, "rgengc_rememberset_mark: finished\n");
04705 
04706 #if RGENGC_PROFILE > 0
04707     rgengc_report(2, objspace, "rgengc_rememberset_mark: clear_count: %"PRIdSIZE", shady_object_count: %"PRIdSIZE"\n", clear_count, shady_object_count);
04708     if (gc_prof_record(objspace)) {
04709         gc_profile_record *record = gc_prof_record(objspace);
04710         record->remembered_normal_objects = clear_count;
04711         record->remembered_shady_objects = shady_object_count;
04712     }
04713 #endif
04714 }
04715 
04716 static void
04717 rgengc_mark_and_rememberset_clear(rb_objspace_t *objspace, rb_heap_t *heap)
04718 {
04719     struct heap_page *page = heap->pages;
04720 
04721     while (page) {
04722         memset(&page->mark_bits[0],        0, HEAP_BITMAP_SIZE);
04723         memset(&page->rememberset_bits[0], 0, HEAP_BITMAP_SIZE);
04724         page = page->next;
04725     }
04726 }
04727 
04728 /* RGENGC: APIs */
04729 
04730 void
04731 rb_gc_writebarrier(VALUE a, VALUE b)
04732 {
04733     if (RGENGC_CHECK_MODE) {
04734         if (!RVALUE_PROMOTED_P(a)) rb_bug("rb_gc_writebarrier: referer object %p (%s) is not promoted.\n", (void *)a, obj_type_name(a));
04735     }
04736 
04737     if (!RVALUE_OLD_P(b) && RVALUE_OLD_BITMAP_P(a)) {
04738         rb_objspace_t *objspace = &rb_objspace;
04739 
04740         if (!rgengc_remembered(objspace, a)) {
04741             rgengc_report(2, objspace, "rb_gc_wb: %p (%s) -> %p (%s)\n",
04742                           (void *)a, obj_type_name(a), (void *)b, obj_type_name(b));
04743             rgengc_remember(objspace, a);
04744         }
04745     }
04746 }
04747 
04748 void
04749 rb_gc_writebarrier_unprotect_promoted(VALUE obj)
04750 {
04751     rb_objspace_t *objspace = &rb_objspace;
04752 
04753     if (RGENGC_CHECK_MODE) {
04754         if (!RVALUE_PROMOTED_P(obj)) rb_bug("rb_gc_writebarrier_unprotect_promoted: called on non-promoted object");
04755         if (!RVALUE_WB_PROTECTED(obj)) rb_bug("rb_gc_writebarrier_unprotect_promoted: called on shady object");
04756     }
04757 
04758     rgengc_report(0, objspace, "rb_gc_writebarrier_unprotect_promoted: %p (%s)%s\n", (void *)obj, obj_type_name(obj),
04759                   rgengc_remembered(objspace, obj) ? " (already remembered)" : "");
04760 
04761     if (RVALUE_OLD_P(obj)) {
04762         RVALUE_DEMOTE_FROM_OLD(obj);
04763 
04764         rgengc_remember(objspace, obj);
04765         objspace->rgengc.remembered_shady_object_count++;
04766 
04767 #if RGENGC_PROFILE
04768         objspace->profile.shade_operation_count++;
04769 #if RGENGC_PROFILE >= 2
04770         objspace->profile.shade_operation_count_types[BUILTIN_TYPE(obj)]++;
04771 #endif /* RGENGC_PROFILE >= 2 */
04772 #endif /* RGENGC_PROFILE */
04773     }
04774 #if RGENGC_THREEGEN
04775     else {
04776         RVALUE_DEMOTE_FROM_YOUNG(obj);
04777     }
04778 #endif
04779 }
04780 
04781 void
04782 rb_gc_writebarrier_remember_promoted(VALUE obj)
04783 {
04784     rb_objspace_t *objspace = &rb_objspace;
04785     rgengc_remember(objspace, obj);
04786 }
04787 
04788 static st_table *rgengc_unprotect_logging_table;
04789 
04790 static int
04791 rgengc_unprotect_logging_exit_func_i(st_data_t key, st_data_t val)
04792 {
04793     fprintf(stderr, "%s\t%d\n", (char *)key, (int)val);
04794     return ST_CONTINUE;
04795 }
04796 
04797 static void
04798 rgengc_unprotect_logging_exit_func(void)
04799 {
04800     st_foreach(rgengc_unprotect_logging_table, rgengc_unprotect_logging_exit_func_i, 0);
04801 }
04802 
04803 void
04804 rb_gc_unprotect_logging(void *objptr, const char *filename, int line)
04805 {
04806     VALUE obj = (VALUE)objptr;
04807 
04808     if (rgengc_unprotect_logging_table == 0) {
04809         rgengc_unprotect_logging_table = st_init_strtable();
04810         atexit(rgengc_unprotect_logging_exit_func);
04811     }
04812 
04813     if (OBJ_WB_PROTECTED(obj)) {
04814         char buff[0x100];
04815         st_data_t cnt = 1;
04816         char *ptr = buff;
04817 
04818         snprintf(ptr, 0x100 - 1, "%s|%s:%d", obj_type_name(obj), filename, line);
04819 
04820         if (st_lookup(rgengc_unprotect_logging_table, (st_data_t)ptr, &cnt)) {
04821             cnt++;
04822         }
04823         else {
04824             ptr = (char *)malloc(strlen(buff) + 1);
04825             strcpy(ptr, buff);
04826         }
04827         st_insert(rgengc_unprotect_logging_table, (st_data_t)ptr, cnt);
04828     }
04829 }
04830 
04831 #endif /* USE_RGENGC */
04832 
04833 /* RGENGC analysis information */
04834 
04835 VALUE
04836 rb_obj_rgengc_writebarrier_protected_p(VALUE obj)
04837 {
04838     return OBJ_WB_PROTECTED(obj) ? Qtrue : Qfalse;
04839 }
04840 
04841 VALUE
04842 rb_obj_rgengc_promoted_p(VALUE obj)
04843 {
04844     return OBJ_PROMOTED(obj) ? Qtrue : Qfalse;
04845 }
04846 
04847 size_t
04848 rb_obj_gc_flags(VALUE obj, ID* flags, size_t max)
04849 {
04850     size_t n = 0;
04851     static ID ID_marked;
04852 #if USE_RGENGC
04853     static ID ID_wb_protected, ID_old, ID_remembered;
04854 #if RGENGC_THREEGEN
04855     static ID ID_young, ID_infant;
04856 #endif
04857 #endif
04858 
04859     if (!ID_marked) {
04860 #define I(s) ID_##s = rb_intern(#s);
04861         I(marked);
04862 #if USE_RGENGC
04863         I(wb_protected);
04864         I(old);
04865         I(remembered);
04866 #if RGENGC_THREEGEN
04867         I(young);
04868         I(infant);
04869 #endif
04870 #endif
04871 #undef I
04872     }
04873 
04874 #if USE_RGENGC
04875     if (OBJ_WB_PROTECTED(obj) && n<max)
04876         flags[n++] = ID_wb_protected;
04877     if (RVALUE_OLD_P(obj) && n<max)
04878         flags[n++] = ID_old;
04879 #if RGENGC_THREEGEN
04880     if (RVALUE_YOUNG_P(obj) && n<max)
04881         flags[n++] = ID_young;
04882     if (RVALUE_INFANT_P(obj) && n<max)
04883         flags[n++] = ID_infant;
04884 #endif
04885     if (MARKED_IN_BITMAP(GET_HEAP_REMEMBERSET_BITS(obj), obj) && n<max)
04886         flags[n++] = ID_remembered;
04887 #endif
04888     if (MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(obj), obj) && n<max)
04889         flags[n++] = ID_marked;
04890 
04891     return n;
04892 }
04893 
04894 /* GC */
04895 
04896 void
04897 rb_gc_force_recycle(VALUE p)
04898 {
04899     rb_objspace_t *objspace = &rb_objspace;
04900 
04901 #if USE_RGENGC
04902     CLEAR_IN_BITMAP(GET_HEAP_REMEMBERSET_BITS(p), p);
04903     CLEAR_IN_BITMAP(GET_HEAP_OLDGEN_BITS(p), p);
04904     if (!GET_HEAP_PAGE(p)->before_sweep) {
04905         CLEAR_IN_BITMAP(GET_HEAP_MARK_BITS(p), p);
04906     }
04907 #endif
04908 
04909     objspace->profile.total_freed_object_num++;
04910     heap_page_add_freeobj(objspace, GET_HEAP_PAGE(p), p);
04911 
04912     /* Disable counting swept_slots because there are no meaning.
04913      * if (!MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(p), p)) {
04914      *   objspace->heap.swept_slots++;
04915      * }
04916      */
04917 }
04918 
04919 void
04920 rb_gc_register_mark_object(VALUE obj)
04921 {
04922     VALUE ary = GET_THREAD()->vm->mark_object_ary;
04923     rb_ary_push(ary, obj);
04924 }
04925 
04926 void
04927 rb_gc_register_address(VALUE *addr)
04928 {
04929     rb_objspace_t *objspace = &rb_objspace;
04930     struct gc_list *tmp;
04931 
04932     tmp = ALLOC(struct gc_list);
04933     tmp->next = global_List;
04934     tmp->varptr = addr;
04935     global_List = tmp;
04936 }
04937 
04938 void
04939 rb_gc_unregister_address(VALUE *addr)
04940 {
04941     rb_objspace_t *objspace = &rb_objspace;
04942     struct gc_list *tmp = global_List;
04943 
04944     if (tmp->varptr == addr) {
04945         global_List = tmp->next;
04946         xfree(tmp);
04947         return;
04948     }
04949     while (tmp->next) {
04950         if (tmp->next->varptr == addr) {
04951             struct gc_list *t = tmp->next;
04952 
04953             tmp->next = tmp->next->next;
04954             xfree(t);
04955             break;
04956         }
04957         tmp = tmp->next;
04958     }
04959 }
04960 
04961 void
04962 rb_global_variable(VALUE *var)
04963 {
04964     rb_gc_register_address(var);
04965 }
04966 
04967 #define GC_NOTIFY 0
04968 
04969 static int
04970 garbage_collect_body(rb_objspace_t *objspace, int full_mark, int immediate_sweep, int reason)
04971 {
04972     if (ruby_gc_stress && !ruby_disable_gc_stress) {
04973         int flag = FIXNUM_P(ruby_gc_stress) ? FIX2INT(ruby_gc_stress) : 0;
04974 
04975         if (flag & 0x01)
04976             reason &= ~GPR_FLAG_MAJOR_MASK;
04977         else
04978             reason |= GPR_FLAG_MAJOR_BY_STRESS;
04979         immediate_sweep = !(flag & 0x02);
04980     }
04981     else {
04982         if (!GC_ENABLE_LAZY_SWEEP || objspace->flags.dont_lazy_sweep) {
04983             immediate_sweep = TRUE;
04984         }
04985 #if USE_RGENGC
04986         if (full_mark) {
04987             reason |= GPR_FLAG_MAJOR_BY_NOFREE;
04988         }
04989         if (objspace->rgengc.need_major_gc) {
04990             reason |= objspace->rgengc.need_major_gc;
04991             objspace->rgengc.need_major_gc = GPR_FLAG_NONE;
04992         }
04993         if (objspace->rgengc.remembered_shady_object_count > objspace->rgengc.remembered_shady_object_limit) {
04994             reason |= GPR_FLAG_MAJOR_BY_SHADY;
04995         }
04996         if (objspace->rgengc.old_object_count > objspace->rgengc.old_object_limit) {
04997             reason |= GPR_FLAG_MAJOR_BY_OLDGEN;
04998         }
04999 #endif
05000     }
05001 
05002     if (immediate_sweep) reason |= GPR_FLAG_IMMEDIATE_SWEEP;
05003     full_mark = (reason & GPR_FLAG_MAJOR_MASK) ? TRUE : FALSE;
05004 
05005     if (GC_NOTIFY)  fprintf(stderr, "start garbage_collect(%d, %d, %d)\n", full_mark, immediate_sweep, reason);
05006 
05007     objspace->profile.count++;
05008     objspace->profile.latest_gc_info = reason;
05009 
05010     gc_event_hook(objspace, RUBY_INTERNAL_EVENT_GC_START, 0 /* TODO: pass minor/immediate flag? */);
05011 
05012     objspace->profile.total_allocated_object_num_at_gc_start = objspace->profile.total_allocated_object_num;
05013     objspace->profile.heap_used_at_gc_start = heap_pages_used;
05014 
05015     gc_prof_setup_new_record(objspace, reason);
05016     gc_prof_timer_start(objspace);
05017     {
05018         if (during_gc == 0) {
05019             rb_bug("during_gc should not be 0. RUBY_INTERNAL_EVENT_GC_START user should not cause GC in events.");
05020         }
05021         gc_marks(objspace, full_mark);
05022         gc_sweep(objspace, immediate_sweep);
05023         during_gc = 0;
05024     }
05025     gc_prof_timer_stop(objspace);
05026 
05027     if (GC_NOTIFY) fprintf(stderr, "end garbage_collect()\n");
05028     return TRUE;
05029 }
05030 
05031 static int
05032 heap_ready_to_gc(rb_objspace_t *objspace, rb_heap_t *heap)
05033 {
05034     if (dont_gc || during_gc) {
05035         if (!heap->freelist && !heap->free_pages) {
05036             if (!heap_increment(objspace, heap)) {
05037                 heap_set_increment(objspace, 0);
05038                 heap_increment(objspace, heap);
05039             }
05040         }
05041         return FALSE;
05042     }
05043     return TRUE;
05044 }
05045 
05046 static int
05047 ready_to_gc(rb_objspace_t *objspace)
05048 {
05049     return heap_ready_to_gc(objspace, heap_eden);
05050 }
05051 
05052 static int
05053 garbage_collect(rb_objspace_t *objspace, int full_mark, int immediate_sweep, int reason)
05054 {
05055     if (!heap_pages_used) {
05056         during_gc = 0;
05057         return FALSE;
05058     }
05059     if (!ready_to_gc(objspace)) {
05060         during_gc = 0;
05061         return TRUE;
05062     }
05063 
05064 #if GC_PROFILE_MORE_DETAIL
05065     objspace->profile.prepare_time = getrusage_time();
05066 #endif
05067     gc_rest_sweep(objspace);
05068 #if GC_PROFILE_MORE_DETAIL
05069     objspace->profile.prepare_time = getrusage_time() - objspace->profile.prepare_time;
05070 #endif
05071 
05072     during_gc++;
05073 
05074     return garbage_collect_body(objspace, full_mark, immediate_sweep, reason);
05075 }
05076 
05077 struct objspace_and_reason {
05078     rb_objspace_t *objspace;
05079     int reason;
05080     int full_mark;
05081     int immediate_sweep;
05082 };
05083 
05084 static void *
05085 gc_with_gvl(void *ptr)
05086 {
05087     struct objspace_and_reason *oar = (struct objspace_and_reason *)ptr;
05088     return (void *)(VALUE)garbage_collect(oar->objspace, oar->full_mark, oar->immediate_sweep, oar->reason);
05089 }
05090 
05091 static int
05092 garbage_collect_with_gvl(rb_objspace_t *objspace, int full_mark, int immediate_sweep, int reason)
05093 {
05094     if (dont_gc) return TRUE;
05095     if (ruby_thread_has_gvl_p()) {
05096         return garbage_collect(objspace, full_mark, immediate_sweep, reason);
05097     }
05098     else {
05099         if (ruby_native_thread_p()) {
05100             struct objspace_and_reason oar;
05101             oar.objspace = objspace;
05102             oar.reason = reason;
05103             oar.full_mark = full_mark;
05104             oar.immediate_sweep = immediate_sweep;
05105             return (int)(VALUE)rb_thread_call_with_gvl(gc_with_gvl, (void *)&oar);
05106         }
05107         else {
05108             /* no ruby thread */
05109             fprintf(stderr, "[FATAL] failed to allocate memory\n");
05110             exit(EXIT_FAILURE);
05111         }
05112     }
05113 }
05114 
05115 int
05116 rb_garbage_collect(void)
05117 {
05118     return garbage_collect(&rb_objspace, TRUE, TRUE, GPR_FLAG_CAPI);
05119 }
05120 
05121 #undef Init_stack
05122 
05123 void
05124 Init_stack(volatile VALUE *addr)
05125 {
05126     ruby_init_stack(addr);
05127 }
05128 
05129 /*
05130  *  call-seq:
05131  *     GC.start                     -> nil
05132  *     GC.garbage_collect           -> nil
05133  *     ObjectSpace.garbage_collect  -> nil
05134  *     GC.start(full_mark: false)   -> nil
05135  *
05136  *  Initiates garbage collection, unless manually disabled.
05137  *
05138  *  This method is defined with keyword arguments that default to true:
05139  *
05140  *     def GC.start(full_mark: true, immediate_sweep: true) end
05141  *
05142  *  Use full_mark: false to perform a minor GC.
05143  *  Use immediate_sweep: false to defer sweeping (use lazy sweep).
05144  *
05145  *  Note: These keyword arguments are implementation and version dependent. They
05146  *  are not guaranteed to be future-compatible, and may be ignored if the
05147  *  underlying implementation does not support them.
05148  */
05149 
05150 static VALUE
05151 gc_start_internal(int argc, VALUE *argv, VALUE self)
05152 {
05153     rb_objspace_t *objspace = &rb_objspace;
05154     int full_mark = TRUE, immediate_sweep = TRUE;
05155     VALUE opt = Qnil;
05156     static ID keyword_ids[2];
05157 
05158     rb_scan_args(argc, argv, "0:", &opt);
05159 
05160     if (!NIL_P(opt)) {
05161         VALUE kwvals[2];
05162 
05163         if (!keyword_ids[0]) {
05164             keyword_ids[0] = rb_intern("full_mark");
05165             keyword_ids[1] = rb_intern("immediate_sweep");
05166         }
05167 
05168         rb_get_kwargs(opt, keyword_ids, 0, 2, kwvals);
05169 
05170         if (kwvals[0] != Qundef)
05171             full_mark = RTEST(kwvals[0]);
05172         if (kwvals[1] != Qundef)
05173             immediate_sweep = RTEST(kwvals[1]);
05174     }
05175 
05176     garbage_collect(objspace, full_mark, immediate_sweep, GPR_FLAG_METHOD);
05177     if (!finalizing) finalize_deferred(objspace);
05178 
05179     return Qnil;
05180 }
05181 
05182 VALUE
05183 rb_gc_start(void)
05184 {
05185     rb_gc();
05186     return Qnil;
05187 }
05188 
05189 void
05190 rb_gc(void)
05191 {
05192     rb_objspace_t *objspace = &rb_objspace;
05193     garbage_collect(objspace, TRUE, TRUE, GPR_FLAG_CAPI);
05194     if (!finalizing) finalize_deferred(objspace);
05195 }
05196 
05197 int
05198 rb_during_gc(void)
05199 {
05200     rb_objspace_t *objspace = &rb_objspace;
05201     return during_gc;
05202 }
05203 
05204 #if RGENGC_PROFILE >= 2
05205 static void
05206 gc_count_add_each_types(VALUE hash, const char *name, const size_t *types)
05207 {
05208     VALUE result = rb_hash_new();
05209     int i;
05210     for (i=0; i<T_MASK; i++) {
05211         const char *type = type_name(i, 0);
05212         rb_hash_aset(result, ID2SYM(rb_intern(type)), SIZET2NUM(types[i]));
05213     }
05214     rb_hash_aset(hash, ID2SYM(rb_intern(name)), result);
05215 }
05216 #endif
05217 
05218 size_t
05219 rb_gc_count(void)
05220 {
05221     return rb_objspace.profile.count;
05222 }
05223 
05224 /*
05225  *  call-seq:
05226  *     GC.count -> Integer
05227  *
05228  *  The number of times GC occurred.
05229  *
05230  *  It returns the number of times GC occurred since the process started.
05231  *
05232  */
05233 
05234 static VALUE
05235 gc_count(VALUE self)
05236 {
05237     return SIZET2NUM(rb_gc_count());
05238 }
05239 
05240 static VALUE
05241 gc_info_decode(int flags, VALUE hash_or_key)
05242 {
05243     static VALUE sym_major_by = Qnil, sym_gc_by, sym_immediate_sweep, sym_have_finalizer;
05244     static VALUE sym_nofree, sym_oldgen, sym_shady, sym_rescan, sym_stress;
05245 #if RGENGC_ESTIMATE_OLDMALLOC
05246     static VALUE sym_oldmalloc;
05247 #endif
05248     static VALUE sym_newobj, sym_malloc, sym_method, sym_capi;
05249     VALUE hash = Qnil, key = Qnil;
05250     VALUE major_by;
05251 
05252     if (SYMBOL_P(hash_or_key))
05253         key = hash_or_key;
05254     else if (RB_TYPE_P(hash_or_key, T_HASH))
05255         hash = hash_or_key;
05256     else
05257         rb_raise(rb_eTypeError, "non-hash or symbol given");
05258 
05259     if (sym_major_by == Qnil) {
05260 #define S(s) sym_##s = ID2SYM(rb_intern_const(#s))
05261         S(major_by);
05262         S(gc_by);
05263         S(immediate_sweep);
05264         S(have_finalizer);
05265         S(nofree);
05266         S(oldgen);
05267         S(shady);
05268         S(rescan);
05269         S(stress);
05270 #if RGENGC_ESTIMATE_OLDMALLOC
05271         S(oldmalloc);
05272 #endif
05273         S(newobj);
05274         S(malloc);
05275         S(method);
05276         S(capi);
05277 #undef S
05278     }
05279 
05280 #define SET(name, attr) \
05281     if (key == sym_##name) \
05282         return (attr); \
05283     else if (hash != Qnil) \
05284         rb_hash_aset(hash, sym_##name, (attr));
05285 
05286     major_by =
05287       (flags & GPR_FLAG_MAJOR_BY_OLDGEN) ? sym_oldgen :
05288       (flags & GPR_FLAG_MAJOR_BY_SHADY)  ? sym_shady :
05289       (flags & GPR_FLAG_MAJOR_BY_RESCAN) ? sym_rescan :
05290       (flags & GPR_FLAG_MAJOR_BY_STRESS) ? sym_stress :
05291 #if RGENGC_ESTIMATE_OLDMALLOC
05292       (flags & GPR_FLAG_MAJOR_BY_OLDMALLOC) ? sym_oldmalloc :
05293 #endif
05294       (flags & GPR_FLAG_MAJOR_BY_NOFREE) ? sym_nofree :
05295       Qnil;
05296     SET(major_by, major_by);
05297 
05298     SET(gc_by,
05299         (flags & GPR_FLAG_NEWOBJ) ? sym_newobj :
05300         (flags & GPR_FLAG_MALLOC) ? sym_malloc :
05301         (flags & GPR_FLAG_METHOD) ? sym_method :
05302         (flags & GPR_FLAG_CAPI)   ? sym_capi :
05303         (flags & GPR_FLAG_STRESS) ? sym_stress :
05304         Qnil
05305     );
05306 
05307     SET(have_finalizer, (flags & GPR_FLAG_HAVE_FINALIZE) ? Qtrue : Qfalse);
05308     SET(immediate_sweep, (flags & GPR_FLAG_IMMEDIATE_SWEEP) ? Qtrue : Qfalse);
05309 #undef SET
05310 
05311     if (key != Qnil) /* matched key should return above */
05312         rb_raise(rb_eArgError, "unknown key: %s", RSTRING_PTR(rb_id2str(SYM2ID(key))));
05313 
05314     return hash;
05315 }
05316 
05317 VALUE
05318 rb_gc_latest_gc_info(VALUE key)
05319 {
05320     rb_objspace_t *objspace = &rb_objspace;
05321     return gc_info_decode(objspace->profile.latest_gc_info, key);
05322 }
05323 
05324 /*
05325  *  call-seq:
05326  *     GC.latest_gc_info -> {:gc_by=>:newobj}
05327  *     GC.latest_gc_info(hash) -> hash
05328  *     GC.latest_gc_info(:major_by) -> :malloc
05329  *
05330  *  Returns information about the most recent garbage collection.
05331  */
05332 
05333 static VALUE
05334 gc_latest_gc_info(int argc, VALUE *argv, VALUE self)
05335 {
05336     rb_objspace_t *objspace = &rb_objspace;
05337     VALUE arg = Qnil;
05338 
05339     if (rb_scan_args(argc, argv, "01", &arg) == 1) {
05340         if (!SYMBOL_P(arg) && !RB_TYPE_P(arg, T_HASH)) {
05341             rb_raise(rb_eTypeError, "non-hash or symbol given");
05342         }
05343     }
05344 
05345     if (arg == Qnil)
05346         arg = rb_hash_new();
05347 
05348     return gc_info_decode(objspace->profile.latest_gc_info, arg);
05349 }
05350 
05351 static VALUE
05352 gc_stat_internal(VALUE hash_or_sym, size_t *out)
05353 {
05354     static VALUE sym_count;
05355     static VALUE sym_heap_used, sym_heap_length, sym_heap_increment;
05356     static VALUE sym_heap_live_slot, sym_heap_free_slot, sym_heap_final_slot, sym_heap_swept_slot;
05357     static VALUE sym_heap_eden_page_length, sym_heap_tomb_page_length;
05358     static VALUE sym_total_allocated_object, sym_total_freed_object;
05359     static VALUE sym_malloc_increase, sym_malloc_limit;
05360 #if USE_RGENGC
05361     static VALUE sym_minor_gc_count, sym_major_gc_count;
05362     static VALUE sym_remembered_shady_object, sym_remembered_shady_object_limit;
05363     static VALUE sym_old_object, sym_old_object_limit;
05364 #if RGENGC_ESTIMATE_OLDMALLOC
05365     static VALUE sym_oldmalloc_increase, sym_oldmalloc_limit;
05366 #endif
05367 #if RGENGC_PROFILE
05368     static VALUE sym_generated_normal_object_count, sym_generated_shady_object_count;
05369     static VALUE sym_shade_operation_count, sym_promote_infant_count, sym_promote_young_count;
05370     static VALUE sym_remembered_normal_object_count, sym_remembered_shady_object_count;
05371 #endif /* RGENGC_PROFILE */
05372 #endif /* USE_RGENGC */
05373 
05374     rb_objspace_t *objspace = &rb_objspace;
05375     VALUE hash = Qnil, key = Qnil;
05376 
05377     if (RB_TYPE_P(hash_or_sym, T_HASH))
05378         hash = hash_or_sym;
05379     else if (SYMBOL_P(hash_or_sym) && out)
05380         key = hash_or_sym;
05381     else
05382         rb_raise(rb_eTypeError, "non-hash or symbol argument");
05383 
05384     if (sym_count == 0) {
05385 #define S(s) sym_##s = ID2SYM(rb_intern_const(#s))
05386         S(count);
05387         S(heap_used);
05388         S(heap_length);
05389         S(heap_increment);
05390         S(heap_live_slot);
05391         S(heap_free_slot);
05392         S(heap_final_slot);
05393         S(heap_swept_slot);
05394         S(heap_eden_page_length);
05395         S(heap_tomb_page_length);
05396         S(total_allocated_object);
05397         S(total_freed_object);
05398         S(malloc_increase);
05399         S(malloc_limit);
05400 #if USE_RGENGC
05401         S(minor_gc_count);
05402         S(major_gc_count);
05403         S(remembered_shady_object);
05404         S(remembered_shady_object_limit);
05405         S(old_object);
05406         S(old_object_limit);
05407 #if RGENGC_ESTIMATE_OLDMALLOC
05408         S(oldmalloc_increase);
05409         S(oldmalloc_limit);
05410 #endif
05411 #if RGENGC_PROFILE
05412         S(generated_normal_object_count);
05413         S(generated_shady_object_count);
05414         S(shade_operation_count);
05415         S(promote_infant_count);
05416         S(promote_young_count);
05417         S(remembered_normal_object_count);
05418         S(remembered_shady_object_count);
05419 #endif /* USE_RGENGC */
05420 #endif /* RGENGC_PROFILE */
05421 #undef S
05422     }
05423 
05424 #define SET(name, attr) \
05425     if (key == sym_##name) \
05426         return (*out = attr, Qnil); \
05427     else if (hash != Qnil) \
05428         rb_hash_aset(hash, sym_##name, SIZET2NUM(attr));
05429 
05430     SET(count, objspace->profile.count);
05431 
05432     /* implementation dependent counters */
05433     SET(heap_used, heap_pages_used);
05434     SET(heap_length, heap_pages_length);
05435     SET(heap_increment, heap_pages_increment);
05436     SET(heap_live_slot, objspace_live_slot(objspace));
05437     SET(heap_free_slot, objspace_free_slot(objspace));
05438     SET(heap_final_slot, heap_pages_final_slots);
05439     SET(heap_swept_slot, heap_pages_swept_slots);
05440     SET(heap_eden_page_length, heap_eden->page_length);
05441     SET(heap_tomb_page_length, heap_tomb->page_length);
05442     SET(total_allocated_object, objspace->profile.total_allocated_object_num);
05443     SET(total_freed_object, objspace->profile.total_freed_object_num);
05444     SET(malloc_increase, malloc_increase);
05445     SET(malloc_limit, malloc_limit);
05446 #if USE_RGENGC
05447     SET(minor_gc_count, objspace->profile.minor_gc_count);
05448     SET(major_gc_count, objspace->profile.major_gc_count);
05449     SET(remembered_shady_object, objspace->rgengc.remembered_shady_object_count);
05450     SET(remembered_shady_object_limit, objspace->rgengc.remembered_shady_object_limit);
05451     SET(old_object, objspace->rgengc.old_object_count);
05452     SET(old_object_limit, objspace->rgengc.old_object_limit);
05453 #if RGENGC_ESTIMATE_OLDMALLOC
05454     SET(oldmalloc_increase, objspace->rgengc.oldmalloc_increase);
05455     SET(oldmalloc_limit, objspace->rgengc.oldmalloc_increase_limit);
05456 #endif
05457 
05458 #if RGENGC_PROFILE
05459     SET(generated_normal_object_count, objspace->profile.generated_normal_object_count);
05460     SET(generated_shady_object_count, objspace->profile.generated_shady_object_count);
05461     SET(shade_operation_count, objspace->profile.shade_operation_count);
05462     SET(promote_infant_count, objspace->profile.promote_infant_count);
05463 #if RGENGC_THREEGEN
05464     SET(promote_young_count, objspace->profile.promote_young_count);
05465 #endif
05466     SET(remembered_normal_object_count, objspace->profile.remembered_normal_object_count);
05467     SET(remembered_shady_object_count, objspace->profile.remembered_shady_object_count);
05468 #endif /* RGENGC_PROFILE */
05469 #endif /* USE_RGENGC */
05470 #undef SET
05471 
05472     if (key != Qnil) /* matched key should return above */
05473         rb_raise(rb_eArgError, "unknown key: %s", RSTRING_PTR(rb_id2str(SYM2ID(key))));
05474 
05475 #if defined(RGENGC_PROFILE) && RGENGC_PROFILE >= 2
05476     if (hash != Qnil) {
05477         gc_count_add_each_types(hash, "generated_normal_object_count_types", objspace->profile.generated_normal_object_count_types);
05478         gc_count_add_each_types(hash, "generated_shady_object_count_types", objspace->profile.generated_shady_object_count_types);
05479         gc_count_add_each_types(hash, "shade_operation_count_types", objspace->profile.shade_operation_count_types);
05480         gc_count_add_each_types(hash, "promote_infant_types", objspace->profile.promote_infant_types);
05481 #if RGENGC_THREEGEN
05482         gc_count_add_each_types(hash, "promote_young_types", objspace->profile.promote_young_types);
05483 #endif
05484         gc_count_add_each_types(hash, "remembered_normal_object_count_types", objspace->profile.remembered_normal_object_count_types);
05485         gc_count_add_each_types(hash, "remembered_shady_object_count_types", objspace->profile.remembered_shady_object_count_types);
05486     }
05487 #endif
05488 
05489     return hash;
05490 }
05491 
05492 /*
05493  *  call-seq:
05494  *     GC.stat -> Hash
05495  *     GC.stat(hash) -> hash
05496  *     GC.stat(:key) -> Numeric
05497  *
05498  *  Returns a Hash containing information about the GC.
05499  *
05500  *  The hash includes information about internal statistics about GC such as:
05501  *
05502  *      {
05503  *          :count=>2,
05504  *          :heap_used=>9,
05505  *          :heap_length=>11,
05506  *          :heap_increment=>2,
05507  *          :heap_live_slot=>6836,
05508  *          :heap_free_slot=>519,
05509  *          :heap_final_slot=>0,
05510  *          :heap_swept_slot=>818,
05511  *          :total_allocated_object=>7674,
05512  *          :total_freed_object=>838,
05513  *          :malloc_increase=>181034,
05514  *          :malloc_limit=>16777216,
05515  *          :minor_gc_count=>2,
05516  *          :major_gc_count=>0,
05517  *          :remembered_shady_object=>55,
05518  *          :remembered_shady_object_limit=>0,
05519  *          :old_object=>2422,
05520  *          :old_object_limit=>0,
05521  *          :oldmalloc_increase=>277386,
05522  *          :oldmalloc_limit=>16777216
05523  *      }
05524  *
05525  *  The contents of the hash are implementation specific and may be changed in
05526  *  the future.
05527  *
05528  *  This method is only expected to work on C Ruby.
05529  *
05530  */
05531 
05532 static VALUE
05533 gc_stat(int argc, VALUE *argv, VALUE self)
05534 {
05535     VALUE arg = Qnil;
05536 
05537     if (rb_scan_args(argc, argv, "01", &arg) == 1) {
05538         if (SYMBOL_P(arg)) {
05539             size_t value = 0;
05540             gc_stat_internal(arg, &value);
05541             return SIZET2NUM(value);
05542         } else if (!RB_TYPE_P(arg, T_HASH)) {
05543             rb_raise(rb_eTypeError, "non-hash or symbol given");
05544         }
05545     }
05546 
05547     if (arg == Qnil) {
05548         arg = rb_hash_new();
05549     }
05550     gc_stat_internal(arg, 0);
05551     return arg;
05552 }
05553 
05554 size_t
05555 rb_gc_stat(VALUE key)
05556 {
05557     if (SYMBOL_P(key)) {
05558         size_t value = 0;
05559         gc_stat_internal(key, &value);
05560         return value;
05561     } else {
05562         gc_stat_internal(key, 0);
05563         return 0;
05564     }
05565 }
05566 
05567 /*
05568  *  call-seq:
05569  *    GC.stress     -> fixnum, true or false
05570  *
05571  *  Returns current status of GC stress mode.
05572  */
05573 
05574 static VALUE
05575 gc_stress_get(VALUE self)
05576 {
05577     rb_objspace_t *objspace = &rb_objspace;
05578     return ruby_gc_stress;
05579 }
05580 
05581 /*
05582  *  call-seq:
05583  *    GC.stress = bool          -> bool
05584  *
05585  *  Updates the GC stress mode.
05586  *
05587  *  When stress mode is enabled, the GC is invoked at every GC opportunity:
05588  *  all memory and object allocations.
05589  *
05590  *  Enabling stress mode will degrade performance, it is only for debugging.
05591  */
05592 
05593 static VALUE
05594 gc_stress_set(VALUE self, VALUE flag)
05595 {
05596     rb_objspace_t *objspace = &rb_objspace;
05597     rb_secure(2);
05598     ruby_gc_stress = FIXNUM_P(flag) ? flag : (RTEST(flag) ? Qtrue : Qfalse);
05599     return flag;
05600 }
05601 
05602 /*
05603  *  call-seq:
05604  *     GC.enable    -> true or false
05605  *
05606  *  Enables garbage collection, returning +true+ if garbage
05607  *  collection was previously disabled.
05608  *
05609  *     GC.disable   #=> false
05610  *     GC.enable    #=> true
05611  *     GC.enable    #=> false
05612  *
05613  */
05614 
05615 VALUE
05616 rb_gc_enable(void)
05617 {
05618     rb_objspace_t *objspace = &rb_objspace;
05619     int old = dont_gc;
05620 
05621     dont_gc = FALSE;
05622     return old ? Qtrue : Qfalse;
05623 }
05624 
05625 /*
05626  *  call-seq:
05627  *     GC.disable    -> true or false
05628  *
05629  *  Disables garbage collection, returning +true+ if garbage
05630  *  collection was already disabled.
05631  *
05632  *     GC.disable   #=> false
05633  *     GC.disable   #=> true
05634  *
05635  */
05636 
05637 VALUE
05638 rb_gc_disable(void)
05639 {
05640     rb_objspace_t *objspace = &rb_objspace;
05641     int old = dont_gc;
05642 
05643     gc_rest_sweep(objspace);
05644 
05645     dont_gc = TRUE;
05646     return old ? Qtrue : Qfalse;
05647 }
05648 
05649 static int
05650 get_envparam_int(const char *name, unsigned int *default_value, int lower_bound)
05651 {
05652     char *ptr = getenv(name);
05653     int val;
05654 
05655     if (ptr != NULL) {
05656         val = atoi(ptr);
05657         if (val > lower_bound) {
05658             if (RTEST(ruby_verbose)) fprintf(stderr, "%s=%d (default value: %d)\n", name, val, *default_value);
05659             *default_value = val;
05660             return 1;
05661         }
05662         else {
05663             if (RTEST(ruby_verbose)) fprintf(stderr, "%s=%d (default value: %d) is ignored because it must be greater than %d.\n", name, val, *default_value, lower_bound);
05664         }
05665     }
05666     return 0;
05667 }
05668 
05669 static int
05670 get_envparam_double(const char *name, double *default_value, double lower_bound)
05671 {
05672     char *ptr = getenv(name);
05673     double val;
05674 
05675     if (ptr != NULL) {
05676         val = strtod(ptr, NULL);
05677         if (val > lower_bound) {
05678             if (RTEST(ruby_verbose)) fprintf(stderr, "%s=%f (%f)\n", name, val, *default_value);
05679             *default_value = val;
05680             return 1;
05681         }
05682         else {
05683             if (RTEST(ruby_verbose)) fprintf(stderr, "%s=%f (default value: %f) is ignored because it must be greater than %f.\n", name, val, *default_value, lower_bound);
05684         }
05685     }
05686     return 0;
05687 }
05688 
05689 static void
05690 gc_set_initial_pages(void)
05691 {
05692     size_t min_pages;
05693     rb_objspace_t *objspace = &rb_objspace;
05694 
05695     min_pages = gc_params.heap_init_slots / HEAP_OBJ_LIMIT;
05696     if (min_pages > heap_eden->page_length) {
05697         heap_add_pages(objspace, heap_eden, min_pages - heap_eden->page_length);
05698     }
05699 }
05700 
05701 /*
05702  * GC tuning environment variables
05703  *
05704  * * RUBY_GC_HEAP_INIT_SLOTS
05705  *   - Initial allocation slots.
05706  * * RUBY_GC_HEAP_FREE_SLOTS
05707  *   - Prepare at least this ammount of slots after GC.
05708  *   - Allocate slots if there are not enough slots.
05709  * * RUBY_GC_HEAP_GROWTH_FACTOR (new from 2.1)
05710  *   - Allocate slots by this factor.
05711  *   - (next slots number) = (current slots number) * (this factor)
05712  * * RUBY_GC_HEAP_GROWTH_MAX_SLOTS (new from 2.1)
05713  *   - Allocation rate is limited to this factor.
05714  * * RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR (new from 2.1.1)
05715  *   - Do full GC when the number of old objects is more than R * N
05716  *     where R is this factor and
05717  *           N is the number of old objects just after last full GC.
05718  *
05719  *  * obsolete
05720  *    * RUBY_FREE_MIN       -> RUBY_GC_HEAP_FREE_SLOTS (from 2.1)
05721  *    * RUBY_HEAP_MIN_SLOTS -> RUBY_GC_HEAP_INIT_SLOTS (from 2.1)
05722  *
05723  * * RUBY_GC_MALLOC_LIMIT
05724  * * RUBY_GC_MALLOC_LIMIT_MAX (new from 2.1)
05725  * * RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR (new from 2.1)
05726  *
05727  * * RUBY_GC_OLDMALLOC_LIMIT (new from 2.1)
05728  * * RUBY_GC_OLDMALLOC_LIMIT_MAX (new from 2.1)
05729  * * RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR (new from 2.1)
05730  */
05731 
05732 void
05733 ruby_gc_set_params(int safe_level)
05734 {
05735     if (safe_level > 0) return;
05736 
05737     /* RUBY_GC_HEAP_FREE_SLOTS */
05738     if (get_envparam_int("RUBY_GC_HEAP_FREE_SLOTS", &gc_params.heap_free_slots, 0)) {
05739         /* ok */
05740     }
05741     else if (get_envparam_int("RUBY_FREE_MIN", &gc_params.heap_free_slots, 0)) {
05742         rb_warn("RUBY_FREE_MIN is obsolete. Use RUBY_GC_HEAP_FREE_SLOTS instead.");
05743     }
05744 
05745     /* RUBY_GC_HEAP_INIT_SLOTS */
05746     if (get_envparam_int("RUBY_GC_HEAP_INIT_SLOTS", &gc_params.heap_init_slots, 0)) {
05747         gc_set_initial_pages();
05748     }
05749     else if (get_envparam_int("RUBY_HEAP_MIN_SLOTS", &gc_params.heap_init_slots, 0)) {
05750         rb_warn("RUBY_HEAP_MIN_SLOTS is obsolete. Use RUBY_GC_HEAP_INIT_SLOTS instead.");
05751         gc_set_initial_pages();
05752     }
05753 
05754     get_envparam_double("RUBY_GC_HEAP_GROWTH_FACTOR", &gc_params.growth_factor, 1.0);
05755     get_envparam_int   ("RUBY_GC_HEAP_GROWTH_MAX_SLOTS", &gc_params.growth_max_slots, 0);
05756     get_envparam_double("RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR", &gc_params.oldobject_limit_factor, 0.0);
05757 
05758     get_envparam_int("RUBY_GC_MALLOC_LIMIT", &gc_params.malloc_limit_min, 0);
05759     get_envparam_int("RUBY_GC_MALLOC_LIMIT_MAX", &gc_params.malloc_limit_max, 0);
05760     get_envparam_double("RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR", &gc_params.malloc_limit_growth_factor, 1.0);
05761 
05762 #if RGENGC_ESTIMATE_OLDMALLOC
05763     if (get_envparam_int("RUBY_GC_OLDMALLOC_LIMIT", &gc_params.oldmalloc_limit_min, 0)) {
05764         rb_objspace_t *objspace = &rb_objspace;
05765         objspace->rgengc.oldmalloc_increase_limit = gc_params.oldmalloc_limit_min;
05766     }
05767     get_envparam_int("RUBY_GC_OLDMALLOC_LIMIT_MAX", &gc_params.oldmalloc_limit_max, 0);
05768     get_envparam_double("RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR", &gc_params.oldmalloc_limit_growth_factor, 1.0);
05769 #endif
05770 }
05771 
05772 void
05773 rb_gc_set_params(void)
05774 {
05775     ruby_gc_set_params(rb_safe_level());
05776 }
05777 
05778 void
05779 rb_objspace_reachable_objects_from(VALUE obj, void (func)(VALUE, void *), void *data)
05780 {
05781     rb_objspace_t *objspace = &rb_objspace;
05782 
05783     if (is_markable_object(objspace, obj)) {
05784         struct mark_func_data_struct mfd;
05785         mfd.mark_func = func;
05786         mfd.data = data;
05787         objspace->mark_func_data = &mfd;
05788         gc_mark_children(objspace, obj);
05789         objspace->mark_func_data = 0;
05790     }
05791 }
05792 
05793 struct root_objects_data {
05794     const char *category;
05795     void (*func)(const char *category, VALUE, void *);
05796     void *data;
05797 };
05798 
05799 static void
05800 root_objects_from(VALUE obj, void *ptr)
05801 {
05802     const struct root_objects_data *data = (struct root_objects_data *)ptr;
05803     (*data->func)(data->category, obj, data->data);
05804 }
05805 
05806 void
05807 rb_objspace_reachable_objects_from_root(void (func)(const char *category, VALUE, void *), void *passing_data)
05808 {
05809     rb_objspace_t *objspace = &rb_objspace;
05810     struct root_objects_data data;
05811     struct mark_func_data_struct mfd;
05812 
05813     data.func = func;
05814     data.data = passing_data;
05815 
05816     mfd.mark_func = root_objects_from;
05817     mfd.data = &data;
05818 
05819     objspace->mark_func_data = &mfd;
05820     {
05821         gc_mark_roots(objspace, TRUE, &data.category);
05822     }
05823     objspace->mark_func_data = 0;
05824 }
05825 
05826 /*
05827   ------------------------ Extended allocator ------------------------
05828 */
05829 
05830 static void objspace_xfree(rb_objspace_t *objspace, void *ptr, size_t size);
05831 
05832 static void *
05833 negative_size_allocation_error_with_gvl(void *ptr)
05834 {
05835     rb_raise(rb_eNoMemError, "%s", (const char *)ptr);
05836     return 0; /* should not be reached */
05837 }
05838 
05839 static void
05840 negative_size_allocation_error(const char *msg)
05841 {
05842     if (ruby_thread_has_gvl_p()) {
05843         rb_raise(rb_eNoMemError, "%s", msg);
05844     }
05845     else {
05846         if (ruby_native_thread_p()) {
05847             rb_thread_call_with_gvl(negative_size_allocation_error_with_gvl, (void *)msg);
05848         }
05849         else {
05850             fprintf(stderr, "[FATAL] %s\n", msg);
05851             exit(EXIT_FAILURE);
05852         }
05853     }
05854 }
05855 
05856 static void *
05857 ruby_memerror_body(void *dummy)
05858 {
05859     rb_memerror();
05860     return 0;
05861 }
05862 
05863 static void
05864 ruby_memerror(void)
05865 {
05866     if (ruby_thread_has_gvl_p()) {
05867         rb_memerror();
05868     }
05869     else {
05870         if (ruby_native_thread_p()) {
05871             rb_thread_call_with_gvl(ruby_memerror_body, 0);
05872         }
05873         else {
05874             /* no ruby thread */
05875             fprintf(stderr, "[FATAL] failed to allocate memory\n");
05876             exit(EXIT_FAILURE);
05877         }
05878     }
05879 }
05880 
05881 void
05882 rb_memerror(void)
05883 {
05884     rb_thread_t *th = GET_THREAD();
05885     if (!nomem_error ||
05886         rb_thread_raised_p(th, RAISED_NOMEMORY)) {
05887         fprintf(stderr, "[FATAL] failed to allocate memory\n");
05888         exit(EXIT_FAILURE);
05889     }
05890     if (rb_thread_raised_p(th, RAISED_NOMEMORY)) {
05891         rb_thread_raised_clear(th);
05892         GET_THREAD()->errinfo = nomem_error;
05893         JUMP_TAG(TAG_RAISE);
05894     }
05895     rb_thread_raised_set(th, RAISED_NOMEMORY);
05896     rb_exc_raise(nomem_error);
05897 }
05898 
05899 static void *
05900 aligned_malloc(size_t alignment, size_t size)
05901 {
05902     void *res;
05903 
05904 #if defined __MINGW32__
05905     res = __mingw_aligned_malloc(size, alignment);
05906 #elif defined _WIN32 && !defined __CYGWIN__
05907     void *_aligned_malloc(size_t, size_t);
05908     res = _aligned_malloc(size, alignment);
05909 #elif defined(HAVE_POSIX_MEMALIGN)
05910     if (posix_memalign(&res, alignment, size) == 0) {
05911         return res;
05912     }
05913     else {
05914         return NULL;
05915     }
05916 #elif defined(HAVE_MEMALIGN)
05917     res = memalign(alignment, size);
05918 #else
05919     char* aligned;
05920     res = malloc(alignment + size + sizeof(void*));
05921     aligned = (char*)res + alignment + sizeof(void*);
05922     aligned -= ((VALUE)aligned & (alignment - 1));
05923     ((void**)aligned)[-1] = res;
05924     res = (void*)aligned;
05925 #endif
05926 
05927 #if defined(_DEBUG) || GC_DEBUG
05928     /* alignment must be a power of 2 */
05929     assert(((alignment - 1) & alignment) == 0);
05930     assert(alignment % sizeof(void*) == 0);
05931 #endif
05932     return res;
05933 }
05934 
05935 static void
05936 aligned_free(void *ptr)
05937 {
05938 #if defined __MINGW32__
05939     __mingw_aligned_free(ptr);
05940 #elif defined _WIN32 && !defined __CYGWIN__
05941     _aligned_free(ptr);
05942 #elif defined(HAVE_MEMALIGN) || defined(HAVE_POSIX_MEMALIGN)
05943     free(ptr);
05944 #else
05945     free(((void**)ptr)[-1]);
05946 #endif
05947 }
05948 
05949 static inline size_t
05950 objspace_malloc_size(rb_objspace_t *objspace, void *ptr, size_t hint)
05951 {
05952 #ifdef HAVE_MALLOC_USABLE_SIZE
05953     return malloc_usable_size(ptr);
05954 #else
05955     return hint;
05956 #endif
05957 }
05958 
05959 enum memop_type {
05960     MEMOP_TYPE_MALLOC  = 1,
05961     MEMOP_TYPE_FREE    = 2,
05962     MEMOP_TYPE_REALLOC = 3
05963 };
05964 
05965 static inline void
05966 atomic_sub_nounderflow(size_t *var, size_t sub)
05967 {
05968     if (sub == 0) return;
05969 
05970     while (1) {
05971         size_t val = *var;
05972         if (val < sub) sub = val;
05973         if (ATOMIC_SIZE_CAS(*var, val, val-sub) == val) break;
05974     }
05975 }
05976 
05977 static void
05978 objspace_malloc_increase(rb_objspace_t *objspace, void *mem, size_t new_size, size_t old_size, enum memop_type type)
05979 {
05980     if (new_size > old_size) {
05981         ATOMIC_SIZE_ADD(malloc_increase, new_size - old_size);
05982 #if RGENGC_ESTIMATE_OLDMALLOC
05983         ATOMIC_SIZE_ADD(objspace->rgengc.oldmalloc_increase, new_size - old_size);
05984 #endif
05985     }
05986     else {
05987         atomic_sub_nounderflow(&malloc_increase, old_size - new_size);
05988 #if RGENGC_ESTIMATE_OLDMALLOC
05989         atomic_sub_nounderflow(&objspace->rgengc.oldmalloc_increase, old_size - new_size);
05990 #endif
05991     }
05992 
05993     if (type == MEMOP_TYPE_MALLOC) {
05994         if (ruby_gc_stress && !ruby_disable_gc_stress) {
05995             garbage_collect_with_gvl(objspace, FALSE, TRUE, GPR_FLAG_MALLOC);
05996         }
05997         else {
05998           retry:
05999             if (malloc_increase > malloc_limit) {
06000                 if (ruby_thread_has_gvl_p() && is_lazy_sweeping(heap_eden)) {
06001                     gc_rest_sweep(objspace); /* rest_sweep can reduce malloc_increase */
06002                     goto retry;
06003                 }
06004                 garbage_collect_with_gvl(objspace, FALSE, TRUE, GPR_FLAG_MALLOC);
06005             }
06006         }
06007     }
06008 
06009 #if MALLOC_ALLOCATED_SIZE
06010     if (new_size >= old_size) {
06011         ATOMIC_SIZE_ADD(objspace->malloc_params.allocated_size, new_size - old_size);
06012     }
06013     else {
06014         size_t dec_size = old_size - new_size;
06015         size_t allocated_size = objspace->malloc_params.allocated_size;
06016 
06017 #if MALLOC_ALLOCATED_SIZE_CHECK
06018         if (allocated_size < dec_size) {
06019             rb_bug("objspace_malloc_increase: underflow malloc_params.allocated_size.");
06020         }
06021 #endif
06022         atomic_sub_nounderflow(objspace->malloc_params.allocated_size, dec_size);
06023     }
06024 
06025     if (0) fprintf(stderr, "incraese - ptr: %p, type: %s, new_size: %d, old_size: %d\n",
06026                    mem,
06027                    type == MEMOP_TYPE_MALLOC  ? "malloc" :
06028                    type == MEMOP_TYPE_FREE    ? "free  " :
06029                    type == MEMOP_TYPE_REALLOC ? "realloc": "error",
06030                    (int)new_size, (int)old_size);
06031 
06032     switch (type) {
06033       case MEMOP_TYPE_MALLOC:
06034         ATOMIC_SIZE_INC(objspace->malloc_params.allocations);
06035         break;
06036       case MEMOP_TYPE_FREE:
06037         {
06038             size_t allocations = objspace->malloc_params.allocations;
06039             if (allocations > 0) {
06040                 atomic_sub_nounderflow(objspace->malloc_params.allocations, 1);
06041             }
06042 #if MALLOC_ALLOCATED_SIZE_CHECK
06043             else {
06044                 assert(objspace->malloc_params.allocations > 0);
06045             }
06046 #endif
06047         }
06048         break;
06049       case MEMOP_TYPE_REALLOC: /* ignore */ break;
06050     }
06051 #endif
06052 }
06053 
06054 static inline size_t
06055 objspace_malloc_prepare(rb_objspace_t *objspace, size_t size)
06056 {
06057     if ((ssize_t)size < 0) {
06058         negative_size_allocation_error("negative allocation size (or too big)");
06059     }
06060     if (size == 0) size = 1;
06061 
06062 #if CALC_EXACT_MALLOC_SIZE
06063     size += sizeof(size_t);
06064 #endif
06065 
06066     return size;
06067 }
06068 
06069 static inline void *
06070 objspace_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
06071 {
06072 #if CALC_EXACT_MALLOC_SIZE
06073     ((size_t *)mem)[0] = size;
06074     mem = (size_t *)mem + 1;
06075 #endif
06076 
06077     return mem;
06078 }
06079 
06080 #define TRY_WITH_GC(alloc) do { \
06081         if (!(alloc) && \
06082             (!garbage_collect_with_gvl(objspace, 1, 1, GPR_FLAG_MALLOC) || /* full mark && immediate sweep */ \
06083              !(alloc))) { \
06084             ruby_memerror(); \
06085         } \
06086     } while (0)
06087 
06088 static void *
06089 objspace_xmalloc(rb_objspace_t *objspace, size_t size)
06090 {
06091     void *mem;
06092 
06093     size = objspace_malloc_prepare(objspace, size);
06094     TRY_WITH_GC(mem = malloc(size));
06095     size = objspace_malloc_size(objspace, mem, size);
06096     objspace_malloc_increase(objspace, mem, size, 0, MEMOP_TYPE_MALLOC);
06097     return objspace_malloc_fixup(objspace, mem, size);
06098 }
06099 
06100 static void *
06101 objspace_xrealloc(rb_objspace_t *objspace, void *ptr, size_t new_size, size_t old_size)
06102 {
06103     void *mem;
06104 
06105     if ((ssize_t)new_size < 0) {
06106         negative_size_allocation_error("negative re-allocation size");
06107     }
06108 
06109     if (!ptr) return objspace_xmalloc(objspace, new_size);
06110 
06111     /*
06112      * The behavior of realloc(ptr, 0) is implementation defined.
06113      * Therefore we don't use realloc(ptr, 0) for portability reason.
06114      * see http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_400.htm
06115      */
06116     if (new_size == 0) {
06117         objspace_xfree(objspace, ptr, old_size);
06118         return 0;
06119     }
06120 
06121 #if CALC_EXACT_MALLOC_SIZE
06122     new_size += sizeof(size_t);
06123     ptr = (size_t *)ptr - 1;
06124     oldsize = ((size_t *)ptr)[0];
06125 #endif
06126 
06127     old_size = objspace_malloc_size(objspace, ptr, old_size);
06128     TRY_WITH_GC(mem = realloc(ptr, new_size));
06129     new_size = objspace_malloc_size(objspace, mem, new_size);
06130 
06131 #if CALC_EXACT_MALLOC_SIZE
06132     ((size_t *)mem)[0] = new_size;
06133     mem = (size_t *)mem + 1;
06134 #endif
06135 
06136     objspace_malloc_increase(objspace, mem, new_size, old_size, MEMOP_TYPE_REALLOC);
06137 
06138     return mem;
06139 }
06140 
06141 static void
06142 objspace_xfree(rb_objspace_t *objspace, void *ptr, size_t old_size)
06143 {
06144 #if CALC_EXACT_MALLOC_SIZE
06145     ptr = ((size_t *)ptr) - 1;
06146     oldsize = ((size_t*)ptr)[0];
06147 #endif
06148     old_size = objspace_malloc_size(objspace, ptr, old_size);
06149 
06150     free(ptr);
06151 
06152     objspace_malloc_increase(objspace, ptr, 0, old_size, MEMOP_TYPE_FREE);
06153 }
06154 
06155 void *
06156 ruby_xmalloc(size_t size)
06157 {
06158     return objspace_xmalloc(&rb_objspace, size);
06159 }
06160 
06161 static inline size_t
06162 xmalloc2_size(size_t n, size_t size)
06163 {
06164     size_t len = size * n;
06165     if (n != 0 && size != len / n) {
06166         rb_raise(rb_eArgError, "malloc: possible integer overflow");
06167     }
06168     return len;
06169 }
06170 
06171 void *
06172 ruby_xmalloc2(size_t n, size_t size)
06173 {
06174     return objspace_xmalloc(&rb_objspace, xmalloc2_size(n, size));
06175 }
06176 
06177 static void *
06178 objspace_xcalloc(rb_objspace_t *objspace, size_t count, size_t elsize)
06179 {
06180     void *mem;
06181     size_t size;
06182 
06183     size = xmalloc2_size(count, elsize);
06184     size = objspace_malloc_prepare(objspace, size);
06185 
06186     TRY_WITH_GC(mem = calloc(1, size));
06187     return objspace_malloc_fixup(objspace, mem, size);
06188 }
06189 
06190 void *
06191 ruby_xcalloc(size_t n, size_t size)
06192 {
06193     return objspace_xcalloc(&rb_objspace, n, size);
06194 }
06195 
06196 #ifdef ruby_sized_xrealloc
06197 #undef ruby_sized_xrealloc
06198 #endif
06199 void *
06200 ruby_sized_xrealloc(void *ptr, size_t new_size, size_t old_size)
06201 {
06202     return objspace_xrealloc(&rb_objspace, ptr, new_size, old_size);
06203 }
06204 
06205 void *
06206 ruby_xrealloc(void *ptr, size_t new_size)
06207 {
06208     return ruby_sized_xrealloc(ptr, new_size, 0);
06209 }
06210 
06211 #ifdef ruby_sized_xrealloc2
06212 #undef ruby_sized_xrealloc2
06213 #endif
06214 void *
06215 ruby_sized_xrealloc2(void *ptr, size_t n, size_t size, size_t old_n)
06216 {
06217     size_t len = size * n;
06218     if (n != 0 && size != len / n) {
06219         rb_raise(rb_eArgError, "realloc: possible integer overflow");
06220     }
06221     return objspace_xrealloc(&rb_objspace, ptr, len, old_n * size);
06222 }
06223 
06224 void *
06225 ruby_xrealloc2(void *ptr, size_t n, size_t size)
06226 {
06227     return ruby_sized_xrealloc2(ptr, n, size, 0);
06228 }
06229 
06230 #ifdef ruby_sized_xfree
06231 #undef ruby_sized_xfree
06232 #endif
06233 void
06234 ruby_sized_xfree(void *x, size_t size)
06235 {
06236     if (x) {
06237         objspace_xfree(&rb_objspace, x, size);
06238     }
06239 }
06240 
06241 void
06242 ruby_xfree(void *x)
06243 {
06244     ruby_sized_xfree(x, 0);
06245 }
06246 
06247 /* Mimic ruby_xmalloc, but need not rb_objspace.
06248  * should return pointer suitable for ruby_xfree
06249  */
06250 void *
06251 ruby_mimmalloc(size_t size)
06252 {
06253     void *mem;
06254 #if CALC_EXACT_MALLOC_SIZE
06255     size += sizeof(size_t);
06256 #endif
06257     mem = malloc(size);
06258 #if CALC_EXACT_MALLOC_SIZE
06259     /* set 0 for consistency of allocated_size/allocations */
06260     ((size_t *)mem)[0] = 0;
06261     mem = (size_t *)mem + 1;
06262 #endif
06263     return mem;
06264 }
06265 
06266 void
06267 ruby_mimfree(void *ptr)
06268 {
06269     size_t *mem = (size_t *)ptr;
06270 #if CALC_EXACT_MALLOC_SIZE
06271     mem = mem - 1;
06272 #endif
06273     free(mem);
06274 }
06275 
06276 #if MALLOC_ALLOCATED_SIZE
06277 /*
06278  *  call-seq:
06279  *     GC.malloc_allocated_size -> Integer
06280  *
06281  *  Returns the size of memory allocated by malloc().
06282  *
06283  *  Only available if ruby was built with +CALC_EXACT_MALLOC_SIZE+.
06284  */
06285 
06286 static VALUE
06287 gc_malloc_allocated_size(VALUE self)
06288 {
06289     return UINT2NUM(rb_objspace.malloc_params.allocated_size);
06290 }
06291 
06292 /*
06293  *  call-seq:
06294  *     GC.malloc_allocations -> Integer
06295  *
06296  *  Returns the number of malloc() allocations.
06297  *
06298  *  Only available if ruby was built with +CALC_EXACT_MALLOC_SIZE+.
06299  */
06300 
06301 static VALUE
06302 gc_malloc_allocations(VALUE self)
06303 {
06304     return UINT2NUM(rb_objspace.malloc_params.allocations);
06305 }
06306 #endif
06307 
06308 /*
06309   ------------------------------ WeakMap ------------------------------
06310 */
06311 
06312 struct weakmap {
06313     st_table *obj2wmap;         /* obj -> [ref,...] */
06314     st_table *wmap2obj;         /* ref -> obj */
06315     VALUE final;
06316 };
06317 
06318 #define WMAP_DELETE_DEAD_OBJECT_IN_MARK 0
06319 
06320 #if WMAP_DELETE_DEAD_OBJECT_IN_MARK
06321 static int
06322 wmap_mark_map(st_data_t key, st_data_t val, st_data_t arg)
06323 {
06324     rb_objspace_t *objspace = (rb_objspace_t *)arg;
06325     VALUE obj = (VALUE)val;
06326     if (!is_live_object(objspace, obj)) return ST_DELETE;
06327     return ST_CONTINUE;
06328 }
06329 #endif
06330 
06331 static void
06332 wmap_mark(void *ptr)
06333 {
06334     struct weakmap *w = ptr;
06335 #if WMAP_DELETE_DEAD_OBJECT_IN_MARK
06336     if (w->obj2wmap) st_foreach(w->obj2wmap, wmap_mark_map, (st_data_t)&rb_objspace);
06337 #endif
06338     rb_gc_mark(w->final);
06339 }
06340 
06341 static int
06342 wmap_free_map(st_data_t key, st_data_t val, st_data_t arg)
06343 {
06344     VALUE *ptr = (VALUE *)val;
06345     ruby_sized_xfree(ptr, (ptr[0] + 1) * sizeof(VALUE));
06346     return ST_CONTINUE;
06347 }
06348 
06349 static void
06350 wmap_free(void *ptr)
06351 {
06352     struct weakmap *w = ptr;
06353     st_foreach(w->obj2wmap, wmap_free_map, 0);
06354     st_free_table(w->obj2wmap);
06355     st_free_table(w->wmap2obj);
06356 }
06357 
06358 static int
06359 wmap_memsize_map(st_data_t key, st_data_t val, st_data_t arg)
06360 {
06361     VALUE *ptr = (VALUE *)val;
06362     *(size_t *)arg += (ptr[0] + 1) * sizeof(VALUE);
06363     return ST_CONTINUE;
06364 }
06365 
06366 static size_t
06367 wmap_memsize(const void *ptr)
06368 {
06369     size_t size;
06370     const struct weakmap *w = ptr;
06371     if (!w) return 0;
06372     size = sizeof(*w);
06373     size += st_memsize(w->obj2wmap);
06374     size += st_memsize(w->wmap2obj);
06375     st_foreach(w->obj2wmap, wmap_memsize_map, (st_data_t)&size);
06376     return size;
06377 }
06378 
06379 static const rb_data_type_t weakmap_type = {
06380     "weakmap",
06381     {
06382         wmap_mark,
06383         wmap_free,
06384         wmap_memsize,
06385     },
06386     NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
06387 };
06388 
06389 static VALUE
06390 wmap_allocate(VALUE klass)
06391 {
06392     struct weakmap *w;
06393     VALUE obj = TypedData_Make_Struct(klass, struct weakmap, &weakmap_type, w);
06394     w->obj2wmap = st_init_numtable();
06395     w->wmap2obj = st_init_numtable();
06396     w->final = rb_obj_method(obj, ID2SYM(rb_intern("finalize")));
06397     return obj;
06398 }
06399 
06400 static int
06401 wmap_final_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
06402 {
06403     VALUE wmap, *ptr, size, i, j;
06404     if (!existing) return ST_STOP;
06405     wmap = (VALUE)arg, ptr = (VALUE *)*value;
06406     for (i = j = 1, size = ptr[0]; i <= size; ++i) {
06407         if (ptr[i] != wmap) {
06408             ptr[j++] = ptr[i];
06409         }
06410     }
06411     if (j == 1) {
06412         ruby_sized_xfree(ptr, i * sizeof(VALUE));
06413         return ST_DELETE;
06414     }
06415     if (j < i) {
06416         ptr = ruby_sized_xrealloc2(ptr, j, sizeof(VALUE), i);
06417         ptr[0] = j;
06418         *value = (st_data_t)ptr;
06419     }
06420     return ST_CONTINUE;
06421 }
06422 
06423 static VALUE
06424 wmap_finalize(VALUE self, VALUE objid)
06425 {
06426     st_data_t orig, wmap, data;
06427     VALUE obj, *rids, i, size;
06428     struct weakmap *w;
06429 
06430     TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
06431     /* Get reference from object id. */
06432     obj = obj_id_to_ref(objid);
06433 
06434     /* obj is original referenced object and/or weak reference. */
06435     orig = (st_data_t)obj;
06436     if (st_delete(w->obj2wmap, &orig, &data)) {
06437         rids = (VALUE *)data;
06438         size = *rids++;
06439         for (i = 0; i < size; ++i) {
06440             wmap = (st_data_t)rids[i];
06441             st_delete(w->wmap2obj, &wmap, NULL);
06442         }
06443         ruby_sized_xfree((VALUE *)data, (size + 1) * sizeof(VALUE));
06444     }
06445 
06446     wmap = (st_data_t)obj;
06447     if (st_delete(w->wmap2obj, &wmap, &orig)) {
06448         wmap = (st_data_t)obj;
06449         st_update(w->obj2wmap, orig, wmap_final_func, wmap);
06450     }
06451     return self;
06452 }
06453 
06454 struct wmap_iter_arg {
06455     rb_objspace_t *objspace;
06456     VALUE value;
06457 };
06458 
06459 static int
06460 wmap_inspect_i(st_data_t key, st_data_t val, st_data_t arg)
06461 {
06462     VALUE str = (VALUE)arg;
06463     VALUE k = (VALUE)key, v = (VALUE)val;
06464 
06465     if (RSTRING_PTR(str)[0] == '#') {
06466         rb_str_cat2(str, ", ");
06467     }
06468     else {
06469         rb_str_cat2(str, ": ");
06470         RSTRING_PTR(str)[0] = '#';
06471     }
06472     k = SPECIAL_CONST_P(k) ? rb_inspect(k) : rb_any_to_s(k);
06473     rb_str_append(str, k);
06474     rb_str_cat2(str, " => ");
06475     v = SPECIAL_CONST_P(v) ? rb_inspect(v) : rb_any_to_s(v);
06476     rb_str_append(str, v);
06477     OBJ_INFECT(str, k);
06478     OBJ_INFECT(str, v);
06479 
06480     return ST_CONTINUE;
06481 }
06482 
06483 static VALUE
06484 wmap_inspect(VALUE self)
06485 {
06486     VALUE str;
06487     VALUE c = rb_class_name(CLASS_OF(self));
06488     struct weakmap *w;
06489 
06490     TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
06491     str = rb_sprintf("-<%"PRIsVALUE":%p", c, (void *)self);
06492     if (w->wmap2obj) {
06493         st_foreach(w->wmap2obj, wmap_inspect_i, str);
06494     }
06495     RSTRING_PTR(str)[0] = '#';
06496     rb_str_cat2(str, ">");
06497     return str;
06498 }
06499 
06500 static int
06501 wmap_each_i(st_data_t key, st_data_t val, st_data_t arg)
06502 {
06503     rb_objspace_t *objspace = (rb_objspace_t *)arg;
06504     VALUE obj = (VALUE)val;
06505     if (is_id_value(objspace, obj) && is_live_object(objspace, obj)) {
06506         rb_yield_values(2, (VALUE)key, obj);
06507     }
06508     return ST_CONTINUE;
06509 }
06510 
06511 /* Iterates over keys and objects in a weakly referenced object */
06512 static VALUE
06513 wmap_each(VALUE self)
06514 {
06515     struct weakmap *w;
06516     rb_objspace_t *objspace = &rb_objspace;
06517 
06518     TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
06519     st_foreach(w->wmap2obj, wmap_each_i, (st_data_t)objspace);
06520     return self;
06521 }
06522 
06523 static int
06524 wmap_each_key_i(st_data_t key, st_data_t val, st_data_t arg)
06525 {
06526     rb_objspace_t *objspace = (rb_objspace_t *)arg;
06527     VALUE obj = (VALUE)val;
06528     if (is_id_value(objspace, obj) && is_live_object(objspace, obj)) {
06529         rb_yield((VALUE)key);
06530     }
06531     return ST_CONTINUE;
06532 }
06533 
06534 /* Iterates over keys and objects in a weakly referenced object */
06535 static VALUE
06536 wmap_each_key(VALUE self)
06537 {
06538     struct weakmap *w;
06539     rb_objspace_t *objspace = &rb_objspace;
06540 
06541     TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
06542     st_foreach(w->wmap2obj, wmap_each_key_i, (st_data_t)objspace);
06543     return self;
06544 }
06545 
06546 static int
06547 wmap_each_value_i(st_data_t key, st_data_t val, st_data_t arg)
06548 {
06549     rb_objspace_t *objspace = (rb_objspace_t *)arg;
06550     VALUE obj = (VALUE)val;
06551     if (is_id_value(objspace, obj) && is_live_object(objspace, obj)) {
06552         rb_yield(obj);
06553     }
06554     return ST_CONTINUE;
06555 }
06556 
06557 /* Iterates over keys and objects in a weakly referenced object */
06558 static VALUE
06559 wmap_each_value(VALUE self)
06560 {
06561     struct weakmap *w;
06562     rb_objspace_t *objspace = &rb_objspace;
06563 
06564     TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
06565     st_foreach(w->wmap2obj, wmap_each_value_i, (st_data_t)objspace);
06566     return self;
06567 }
06568 
06569 static int
06570 wmap_keys_i(st_data_t key, st_data_t val, st_data_t arg)
06571 {
06572     struct wmap_iter_arg *argp = (struct wmap_iter_arg *)arg;
06573     rb_objspace_t *objspace = argp->objspace;
06574     VALUE ary = argp->value;
06575     VALUE obj = (VALUE)val;
06576     if (is_id_value(objspace, obj) && is_live_object(objspace, obj)) {
06577         rb_ary_push(ary, (VALUE)key);
06578     }
06579     return ST_CONTINUE;
06580 }
06581 
06582 /* Iterates over keys and objects in a weakly referenced object */
06583 static VALUE
06584 wmap_keys(VALUE self)
06585 {
06586     struct weakmap *w;
06587     struct wmap_iter_arg args;
06588 
06589     TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
06590     args.objspace = &rb_objspace;
06591     args.value = rb_ary_new();
06592     st_foreach(w->wmap2obj, wmap_keys_i, (st_data_t)&args);
06593     return args.value;
06594 }
06595 
06596 static int
06597 wmap_values_i(st_data_t key, st_data_t val, st_data_t arg)
06598 {
06599     struct wmap_iter_arg *argp = (struct wmap_iter_arg *)arg;
06600     rb_objspace_t *objspace = argp->objspace;
06601     VALUE ary = argp->value;
06602     VALUE obj = (VALUE)val;
06603     if (is_id_value(objspace, obj) && is_live_object(objspace, obj)) {
06604         rb_ary_push(ary, obj);
06605     }
06606     return ST_CONTINUE;
06607 }
06608 
06609 /* Iterates over values and objects in a weakly referenced object */
06610 static VALUE
06611 wmap_values(VALUE self)
06612 {
06613     struct weakmap *w;
06614     struct wmap_iter_arg args;
06615 
06616     TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
06617     args.objspace = &rb_objspace;
06618     args.value = rb_ary_new();
06619     st_foreach(w->wmap2obj, wmap_values_i, (st_data_t)&args);
06620     return args.value;
06621 }
06622 
06623 static int
06624 wmap_aset_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
06625 {
06626     VALUE size, *ptr, *optr;
06627     if (existing) {
06628         size = (ptr = optr = (VALUE *)*val)[0];
06629         ++size;
06630         ptr = ruby_sized_xrealloc2(ptr, size + 1, sizeof(VALUE), size);
06631     }
06632     else {
06633         optr = 0;
06634         size = 1;
06635         ptr = ruby_xmalloc2(2, sizeof(VALUE));
06636     }
06637     ptr[0] = size;
06638     ptr[size] = (VALUE)arg;
06639     if (ptr == optr) return ST_STOP;
06640     *val = (st_data_t)ptr;
06641     return ST_CONTINUE;
06642 }
06643 
06644 /* Creates a weak reference from the given key to the given value */
06645 static VALUE
06646 wmap_aset(VALUE self, VALUE wmap, VALUE orig)
06647 {
06648     struct weakmap *w;
06649 
06650     TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
06651     should_be_finalizable(orig);
06652     should_be_finalizable(wmap);
06653     define_final0(orig, w->final);
06654     define_final0(wmap, w->final);
06655     st_update(w->obj2wmap, (st_data_t)orig, wmap_aset_update, wmap);
06656     st_insert(w->wmap2obj, (st_data_t)wmap, (st_data_t)orig);
06657     return nonspecial_obj_id(orig);
06658 }
06659 
06660 /* Retrieves a weakly referenced object with the given key */
06661 static VALUE
06662 wmap_aref(VALUE self, VALUE wmap)
06663 {
06664     st_data_t data;
06665     VALUE obj;
06666     struct weakmap *w;
06667     rb_objspace_t *objspace = &rb_objspace;
06668 
06669     TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
06670     if (!st_lookup(w->wmap2obj, (st_data_t)wmap, &data)) return Qnil;
06671     obj = (VALUE)data;
06672     if (!is_id_value(objspace, obj)) return Qnil;
06673     if (!is_live_object(objspace, obj)) return Qnil;
06674     return obj;
06675 }
06676 
06677 /* Returns +true+ if +key+ is registered */
06678 static VALUE
06679 wmap_has_key(VALUE self, VALUE key)
06680 {
06681     return NIL_P(wmap_aref(self, key)) ? Qfalse : Qtrue;
06682 }
06683 
06684 static VALUE
06685 wmap_size(VALUE self)
06686 {
06687     struct weakmap *w;
06688     st_index_t n;
06689 
06690     TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
06691     n = w->wmap2obj->num_entries;
06692 #if SIZEOF_ST_INDEX_T <= SIZEOF_LONG
06693     return ULONG2NUM(n);
06694 #else
06695     return ULL2NUM(n);
06696 #endif
06697 }
06698 
06699 /*
06700   ------------------------------ GC profiler ------------------------------
06701 */
06702 
06703 #define GC_PROFILE_RECORD_DEFAULT_SIZE 100
06704 
06705 static double
06706 getrusage_time(void)
06707 {
06708 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)
06709     {
06710         static int try_clock_gettime = 1;
06711         struct timespec ts;
06712         if (try_clock_gettime && clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0) {
06713             return ts.tv_sec + ts.tv_nsec * 1e-9;
06714         }
06715         else {
06716             try_clock_gettime = 0;
06717         }
06718     }
06719 #endif
06720 
06721 #ifdef RUSAGE_SELF
06722     {
06723         struct rusage usage;
06724         struct timeval time;
06725         if (getrusage(RUSAGE_SELF, &usage) == 0) {
06726             time = usage.ru_utime;
06727             return time.tv_sec + time.tv_usec * 1e-6;
06728         }
06729     }
06730 #endif
06731 
06732 #ifdef _WIN32
06733     {
06734         FILETIME creation_time, exit_time, kernel_time, user_time;
06735         ULARGE_INTEGER ui;
06736         LONG_LONG q;
06737         double t;
06738 
06739         if (GetProcessTimes(GetCurrentProcess(),
06740                             &creation_time, &exit_time, &kernel_time, &user_time) != 0) {
06741             memcpy(&ui, &user_time, sizeof(FILETIME));
06742             q = ui.QuadPart / 10L;
06743             t = (DWORD)(q % 1000000L) * 1e-6;
06744             q /= 1000000L;
06745 #ifdef __GNUC__
06746             t += q;
06747 #else
06748             t += (double)(DWORD)(q >> 16) * (1 << 16);
06749             t += (DWORD)q & ~(~0 << 16);
06750 #endif
06751             return t;
06752         }
06753     }
06754 #endif
06755 
06756     return 0.0;
06757 }
06758 
06759 static inline void
06760 gc_prof_setup_new_record(rb_objspace_t *objspace, int reason)
06761 {
06762     if (objspace->profile.run) {
06763         size_t index = objspace->profile.next_index;
06764         gc_profile_record *record;
06765 
06766         /* create new record */
06767         objspace->profile.next_index++;
06768 
06769         if (!objspace->profile.records) {
06770             objspace->profile.size = GC_PROFILE_RECORD_DEFAULT_SIZE;
06771             objspace->profile.records = malloc(sizeof(gc_profile_record) * objspace->profile.size);
06772         }
06773         if (index >= objspace->profile.size) {
06774             objspace->profile.size += 1000;
06775             objspace->profile.records = realloc(objspace->profile.records, sizeof(gc_profile_record) * objspace->profile.size);
06776         }
06777         if (!objspace->profile.records) {
06778             rb_bug("gc_profile malloc or realloc miss");
06779         }
06780         record = objspace->profile.current_record = &objspace->profile.records[objspace->profile.next_index - 1];
06781         MEMZERO(record, gc_profile_record, 1);
06782 
06783         /* setup before-GC parameter */
06784         record->flags = reason | ((ruby_gc_stress && !ruby_disable_gc_stress) ? GPR_FLAG_STRESS : 0);
06785 #if MALLOC_ALLOCATED_SIZE
06786         record->allocated_size = malloc_allocated_size;
06787 #endif
06788 #if GC_PROFILE_DETAIL_MEMORY
06789 #ifdef RUSAGE_SELF
06790         {
06791             struct rusage usage;
06792             if (getrusage(RUSAGE_SELF, &usage) == 0) {
06793                 record->maxrss = usage.ru_maxrss;
06794                 record->minflt = usage.ru_minflt;
06795                 record->majflt = usage.ru_majflt;
06796             }
06797         }
06798 #endif
06799 #endif
06800     }
06801 }
06802 
06803 static inline void
06804 gc_prof_timer_start(rb_objspace_t *objspace)
06805 {
06806     if (gc_prof_enabled(objspace)) {
06807         gc_profile_record *record = gc_prof_record(objspace);
06808 #if GC_PROFILE_MORE_DETAIL
06809         record->prepare_time = objspace->profile.prepare_time;
06810 #endif
06811         record->gc_time = 0;
06812         record->gc_invoke_time = getrusage_time();
06813     }
06814 }
06815 
06816 static double
06817 elapsed_time_from(double time)
06818 {
06819     double now = getrusage_time();
06820     if (now > time) {
06821         return now - time;
06822     }
06823     else {
06824         return 0;
06825     }
06826 }
06827 
06828 static inline void
06829 gc_prof_timer_stop(rb_objspace_t *objspace)
06830 {
06831     if (gc_prof_enabled(objspace)) {
06832         gc_profile_record *record = gc_prof_record(objspace);
06833         record->gc_time = elapsed_time_from(record->gc_invoke_time);
06834         record->gc_invoke_time -= objspace->profile.invoke_time;
06835     }
06836 }
06837 
06838 static inline void
06839 gc_prof_mark_timer_start(rb_objspace_t *objspace)
06840 {
06841     if (RUBY_DTRACE_GC_MARK_BEGIN_ENABLED()) {
06842         RUBY_DTRACE_GC_MARK_BEGIN();
06843     }
06844 #if GC_PROFILE_MORE_DETAIL
06845     if (gc_prof_enabled(objspace)) {
06846         gc_prof_record(objspace)->gc_mark_time = getrusage_time();
06847     }
06848 #endif
06849 }
06850 
06851 static inline void
06852 gc_prof_mark_timer_stop(rb_objspace_t *objspace)
06853 {
06854     if (RUBY_DTRACE_GC_MARK_END_ENABLED()) {
06855         RUBY_DTRACE_GC_MARK_END();
06856     }
06857 #if GC_PROFILE_MORE_DETAIL
06858     if (gc_prof_enabled(objspace)) {
06859         gc_profile_record *record = gc_prof_record(objspace);
06860         record->gc_mark_time = elapsed_time_from(record->gc_mark_time);
06861     }
06862 #endif
06863 }
06864 
06865 static inline void
06866 gc_prof_sweep_timer_start(rb_objspace_t *objspace)
06867 {
06868     if (RUBY_DTRACE_GC_SWEEP_BEGIN_ENABLED()) {
06869         RUBY_DTRACE_GC_SWEEP_BEGIN();
06870     }
06871     if (gc_prof_enabled(objspace)) {
06872         gc_profile_record *record = gc_prof_record(objspace);
06873 
06874         if (record->gc_time > 0 || GC_PROFILE_MORE_DETAIL) {
06875             objspace->profile.gc_sweep_start_time = getrusage_time();
06876         }
06877     }
06878 }
06879 
06880 static inline void
06881 gc_prof_sweep_timer_stop(rb_objspace_t *objspace)
06882 {
06883     if (RUBY_DTRACE_GC_SWEEP_END_ENABLED()) {
06884         RUBY_DTRACE_GC_SWEEP_END();
06885     }
06886 
06887     if (gc_prof_enabled(objspace)) {
06888         double sweep_time;
06889         gc_profile_record *record = gc_prof_record(objspace);
06890 
06891         if (record->gc_time > 0) {
06892             sweep_time = elapsed_time_from(objspace->profile.gc_sweep_start_time);
06893             /* need to accumulate GC time for lazy sweep after gc() */
06894             record->gc_time += sweep_time;
06895         }
06896         else if (GC_PROFILE_MORE_DETAIL) {
06897             sweep_time = elapsed_time_from(objspace->profile.gc_sweep_start_time);
06898         }
06899 
06900 #if GC_PROFILE_MORE_DETAIL
06901         record->gc_sweep_time += sweep_time;
06902         if (heap_pages_deferred_final) record->flags |= GPR_FLAG_HAVE_FINALIZE;
06903 #endif
06904         if (heap_pages_deferred_final) objspace->profile.latest_gc_info |= GPR_FLAG_HAVE_FINALIZE;
06905     }
06906 }
06907 
06908 static inline void
06909 gc_prof_set_malloc_info(rb_objspace_t *objspace)
06910 {
06911 #if GC_PROFILE_MORE_DETAIL
06912     if (gc_prof_enabled(objspace)) {
06913         gc_profile_record *record = gc_prof_record(objspace);
06914         record->allocate_increase = malloc_increase;
06915         record->allocate_limit = malloc_limit;
06916     }
06917 #endif
06918 }
06919 
06920 static inline void
06921 gc_prof_set_heap_info(rb_objspace_t *objspace)
06922 {
06923     if (gc_prof_enabled(objspace)) {
06924         gc_profile_record *record = gc_prof_record(objspace);
06925         size_t live = objspace->profile.total_allocated_object_num_at_gc_start - objspace->profile.total_freed_object_num;
06926         size_t total = objspace->profile.heap_used_at_gc_start * HEAP_OBJ_LIMIT;
06927 
06928 #if GC_PROFILE_MORE_DETAIL
06929         record->heap_use_pages = objspace->profile.heap_used_at_gc_start;
06930         record->heap_live_objects = live;
06931         record->heap_free_objects = total - live;
06932 #endif
06933 
06934         record->heap_total_objects = total;
06935         record->heap_use_size = live * sizeof(RVALUE);
06936         record->heap_total_size = total * sizeof(RVALUE);
06937     }
06938 }
06939 
06940 /*
06941  *  call-seq:
06942  *    GC::Profiler.clear          -> nil
06943  *
06944  *  Clears the GC profiler data.
06945  *
06946  */
06947 
06948 static VALUE
06949 gc_profile_clear(void)
06950 {
06951     rb_objspace_t *objspace = &rb_objspace;
06952     if (GC_PROFILE_RECORD_DEFAULT_SIZE * 2 < objspace->profile.size) {
06953         objspace->profile.size = GC_PROFILE_RECORD_DEFAULT_SIZE * 2;
06954         objspace->profile.records = realloc(objspace->profile.records, sizeof(gc_profile_record) * objspace->profile.size);
06955         if (!objspace->profile.records) {
06956             rb_memerror();
06957         }
06958     }
06959     MEMZERO(objspace->profile.records, gc_profile_record, objspace->profile.size);
06960     objspace->profile.next_index = 0;
06961     objspace->profile.current_record = 0;
06962     return Qnil;
06963 }
06964 
06965 /*
06966  *  call-seq:
06967  *     GC::Profiler.raw_data    -> [Hash, ...]
06968  *
06969  *  Returns an Array of individual raw profile data Hashes ordered
06970  *  from earliest to latest by +:GC_INVOKE_TIME+.
06971  *
06972  *  For example:
06973  *
06974  *    [
06975  *      {
06976  *         :GC_TIME=>1.3000000000000858e-05,
06977  *         :GC_INVOKE_TIME=>0.010634999999999999,
06978  *         :HEAP_USE_SIZE=>289640,
06979  *         :HEAP_TOTAL_SIZE=>588960,
06980  *         :HEAP_TOTAL_OBJECTS=>14724,
06981  *         :GC_IS_MARKED=>false
06982  *      },
06983  *      # ...
06984  *    ]
06985  *
06986  *  The keys mean:
06987  *
06988  *  +:GC_TIME+::
06989  *      Time elapsed in seconds for this GC run
06990  *  +:GC_INVOKE_TIME+::
06991  *      Time elapsed in seconds from startup to when the GC was invoked
06992  *  +:HEAP_USE_SIZE+::
06993  *      Total bytes of heap used
06994  *  +:HEAP_TOTAL_SIZE+::
06995  *      Total size of heap in bytes
06996  *  +:HEAP_TOTAL_OBJECTS+::
06997  *      Total number of objects
06998  *  +:GC_IS_MARKED+::
06999  *      Returns +true+ if the GC is in mark phase
07000  *
07001  *  If ruby was built with +GC_PROFILE_MORE_DETAIL+, you will also have access
07002  *  to the following hash keys:
07003  *
07004  *  +:GC_MARK_TIME+::
07005  *  +:GC_SWEEP_TIME+::
07006  *  +:ALLOCATE_INCREASE+::
07007  *  +:ALLOCATE_LIMIT+::
07008  *  +:HEAP_USE_PAGES+::
07009  *  +:HEAP_LIVE_OBJECTS+::
07010  *  +:HEAP_FREE_OBJECTS+::
07011  *  +:HAVE_FINALIZE+::
07012  *
07013  */
07014 
07015 static VALUE
07016 gc_profile_record_get(void)
07017 {
07018     VALUE prof;
07019     VALUE gc_profile = rb_ary_new();
07020     size_t i;
07021     rb_objspace_t *objspace = (&rb_objspace);
07022 
07023     if (!objspace->profile.run) {
07024         return Qnil;
07025     }
07026 
07027     for (i =0; i < objspace->profile.next_index; i++) {
07028         gc_profile_record *record = &objspace->profile.records[i];
07029 
07030         prof = rb_hash_new();
07031         rb_hash_aset(prof, ID2SYM(rb_intern("GC_FLAGS")), gc_info_decode(record->flags, rb_hash_new()));
07032         rb_hash_aset(prof, ID2SYM(rb_intern("GC_TIME")), DBL2NUM(record->gc_time));
07033         rb_hash_aset(prof, ID2SYM(rb_intern("GC_INVOKE_TIME")), DBL2NUM(record->gc_invoke_time));
07034         rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SIZE")), SIZET2NUM(record->heap_use_size));
07035         rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_SIZE")), SIZET2NUM(record->heap_total_size));
07036         rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_OBJECTS")), SIZET2NUM(record->heap_total_objects));
07037         rb_hash_aset(prof, ID2SYM(rb_intern("GC_IS_MARKED")), Qtrue);
07038 #if GC_PROFILE_MORE_DETAIL
07039         rb_hash_aset(prof, ID2SYM(rb_intern("GC_MARK_TIME")), DBL2NUM(record->gc_mark_time));
07040         rb_hash_aset(prof, ID2SYM(rb_intern("GC_SWEEP_TIME")), DBL2NUM(record->gc_sweep_time));
07041         rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_INCREASE")), SIZET2NUM(record->allocate_increase));
07042         rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_LIMIT")), SIZET2NUM(record->allocate_limit));
07043         rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_PAGES")), SIZET2NUM(record->heap_use_pages));
07044         rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_LIVE_OBJECTS")), SIZET2NUM(record->heap_live_objects));
07045         rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_FREE_OBJECTS")), SIZET2NUM(record->heap_free_objects));
07046 
07047         rb_hash_aset(prof, ID2SYM(rb_intern("REMOVING_OBJECTS")), SIZET2NUM(record->removing_objects));
07048         rb_hash_aset(prof, ID2SYM(rb_intern("EMPTY_OBJECTS")), SIZET2NUM(record->empty_objects));
07049 
07050         rb_hash_aset(prof, ID2SYM(rb_intern("HAVE_FINALIZE")), (record->flags & GPR_FLAG_HAVE_FINALIZE) ? Qtrue : Qfalse);
07051 #endif
07052 
07053 #if RGENGC_PROFILE > 0
07054         rb_hash_aset(prof, ID2SYM(rb_intern("OLD_OBJECTS")), SIZET2NUM(record->old_objects));
07055         rb_hash_aset(prof, ID2SYM(rb_intern("REMEMBED_NORMAL_OBJECTS")), SIZET2NUM(record->remembered_normal_objects));
07056         rb_hash_aset(prof, ID2SYM(rb_intern("REMEMBED_SHADY_OBJECTS")), SIZET2NUM(record->remembered_shady_objects));
07057 #endif
07058         rb_ary_push(gc_profile, prof);
07059     }
07060 
07061     return gc_profile;
07062 }
07063 
07064 #if GC_PROFILE_MORE_DETAIL
07065 #define MAJOR_REASON_MAX 0x10
07066 
07067 static char *
07068 gc_profile_dump_major_reason(int flags, char *buff)
07069 {
07070     int reason = flags & GPR_FLAG_MAJOR_MASK;
07071     int i = 0;
07072 
07073     if (reason == GPR_FLAG_NONE) {
07074         buff[0] = '-';
07075         buff[1] = 0;
07076     }
07077     else {
07078 #define C(x, s) \
07079   if (reason & GPR_FLAG_MAJOR_BY_##x) { \
07080       buff[i++] = #x[0]; \
07081       if (i >= MAJOR_REASON_MAX) rb_bug("gc_profile_dump_major_reason: overflow"); \
07082       buff[i] = 0; \
07083   }
07084         C(NOFREE, N);
07085         C(OLDGEN, O);
07086         C(SHADY,  S);
07087         C(RESCAN, R);
07088         C(STRESS, T);
07089 #if RGENGC_ESTIMATE_OLDMALLOC
07090         C(OLDMALLOC, M);
07091 #endif
07092 #undef C
07093     }
07094     return buff;
07095 }
07096 #endif
07097 
07098 static void
07099 gc_profile_dump_on(VALUE out, VALUE (*append)(VALUE, VALUE))
07100 {
07101     rb_objspace_t *objspace = &rb_objspace;
07102     size_t count = objspace->profile.next_index;
07103 #ifdef MAJOR_REASON_MAX
07104     char reason_str[MAJOR_REASON_MAX];
07105 #endif
07106 
07107     if (objspace->profile.run && count /* > 1 */) {
07108         size_t i;
07109         const gc_profile_record *record;
07110 
07111         append(out, rb_sprintf("GC %"PRIuSIZE" invokes.\n", objspace->profile.count));
07112         append(out, rb_str_new_cstr("Index    Invoke Time(sec)       Use Size(byte)     Total Size(byte)         Total Object                    GC Time(ms)\n"));
07113 
07114         for (i = 0; i < count; i++) {
07115             record = &objspace->profile.records[i];
07116             append(out, rb_sprintf("%5"PRIdSIZE" %19.3f %20"PRIuSIZE" %20"PRIuSIZE" %20"PRIuSIZE" %30.20f\n",
07117                                    i+1, record->gc_invoke_time, record->heap_use_size,
07118                                    record->heap_total_size, record->heap_total_objects, record->gc_time*1000));
07119         }
07120 
07121 #if GC_PROFILE_MORE_DETAIL
07122         append(out, rb_str_new_cstr("\n\n" \
07123                                     "More detail.\n" \
07124                                     "Prepare Time = Previously GC's rest sweep time\n"
07125                                     "Index Flags          Allocate Inc.  Allocate Limit"
07126 #if CALC_EXACT_MALLOC_SIZE
07127                                     "  Allocated Size"
07128 #endif
07129                                     "  Use Page     Mark Time(ms)    Sweep Time(ms)  Prepare Time(ms)  LivingObj    FreeObj RemovedObj   EmptyObj"
07130 #if RGENGC_PROFILE
07131                                     " OldgenObj RemNormObj RemShadObj"
07132 #endif
07133 #if GC_PROFILE_DETAIL_MEMORY
07134                                     " MaxRSS(KB) MinorFLT MajorFLT"
07135 #endif
07136                                     "\n"));
07137 
07138         for (i = 0; i < count; i++) {
07139             record = &objspace->profile.records[i];
07140             append(out, rb_sprintf("%5"PRIdSIZE" %4s/%c/%6s%c %13"PRIuSIZE" %15"PRIuSIZE
07141 #if CALC_EXACT_MALLOC_SIZE
07142                                    " %15"PRIuSIZE
07143 #endif
07144                                    " %9"PRIuSIZE" %17.12f %17.12f %17.12f %10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE
07145 #if RGENGC_PROFILE
07146                                    "%10"PRIuSIZE" %10"PRIuSIZE" %10"PRIuSIZE
07147 #endif
07148 #if GC_PROFILE_DETAIL_MEMORY
07149                                    "%11ld %8ld %8ld"
07150 #endif
07151 
07152                                    "\n",
07153                                    i+1,
07154                                    gc_profile_dump_major_reason(record->flags, reason_str),
07155                                    (record->flags & GPR_FLAG_HAVE_FINALIZE) ? 'F' : '.',
07156                                    (record->flags & GPR_FLAG_NEWOBJ) ? "NEWOBJ" :
07157                                    (record->flags & GPR_FLAG_MALLOC) ? "MALLOC" :
07158                                    (record->flags & GPR_FLAG_METHOD) ? "METHOD" :
07159                                    (record->flags & GPR_FLAG_CAPI)   ? "CAPI__" : "??????",
07160                                    (record->flags & GPR_FLAG_STRESS) ? '!' : ' ',
07161                                    record->allocate_increase, record->allocate_limit,
07162 #if CALC_EXACT_MALLOC_SIZE
07163                                    record->allocated_size,
07164 #endif
07165                                    record->heap_use_pages,
07166                                    record->gc_mark_time*1000,
07167                                    record->gc_sweep_time*1000,
07168                                    record->prepare_time*1000,
07169 
07170                                    record->heap_live_objects,
07171                                    record->heap_free_objects,
07172                                    record->removing_objects,
07173                                    record->empty_objects
07174 #if RGENGC_PROFILE
07175                                    ,
07176                                    record->old_objects,
07177                                    record->remembered_normal_objects,
07178                                    record->remembered_shady_objects
07179 #endif
07180 #if GC_PROFILE_DETAIL_MEMORY
07181                                    ,
07182                                    record->maxrss / 1024,
07183                                    record->minflt,
07184                                    record->majflt
07185 #endif
07186 
07187                        ));
07188         }
07189 #endif
07190     }
07191 }
07192 
07193 /*
07194  *  call-seq:
07195  *     GC::Profiler.result  -> String
07196  *
07197  *  Returns a profile data report such as:
07198  *
07199  *    GC 1 invokes.
07200  *    Index    Invoke Time(sec)       Use Size(byte)     Total Size(byte)         Total Object                    GC time(ms)
07201  *        1               0.012               159240               212940                10647         0.00000000000001530000
07202  */
07203 
07204 static VALUE
07205 gc_profile_result(void)
07206 {
07207         VALUE str = rb_str_buf_new(0);
07208         gc_profile_dump_on(str, rb_str_buf_append);
07209         return str;
07210 }
07211 
07212 /*
07213  *  call-seq:
07214  *     GC::Profiler.report
07215  *     GC::Profiler.report(io)
07216  *
07217  *  Writes the GC::Profiler.result to <tt>$stdout</tt> or the given IO object.
07218  *
07219  */
07220 
07221 static VALUE
07222 gc_profile_report(int argc, VALUE *argv, VALUE self)
07223 {
07224     VALUE out;
07225 
07226     if (argc == 0) {
07227         out = rb_stdout;
07228     }
07229     else {
07230         rb_scan_args(argc, argv, "01", &out);
07231     }
07232     gc_profile_dump_on(out, rb_io_write);
07233 
07234     return Qnil;
07235 }
07236 
07237 /*
07238  *  call-seq:
07239  *     GC::Profiler.total_time  -> float
07240  *
07241  *  The total time used for garbage collection in seconds
07242  */
07243 
07244 static VALUE
07245 gc_profile_total_time(VALUE self)
07246 {
07247     double time = 0;
07248     rb_objspace_t *objspace = &rb_objspace;
07249 
07250     if (objspace->profile.run && objspace->profile.next_index > 0) {
07251         size_t i;
07252         size_t count = objspace->profile.next_index;
07253 
07254         for (i = 0; i < count; i++) {
07255             time += objspace->profile.records[i].gc_time;
07256         }
07257     }
07258     return DBL2NUM(time);
07259 }
07260 
07261 /*
07262  *  call-seq:
07263  *    GC::Profiler.enabled?     -> true or false
07264  *
07265  *  The current status of GC profile mode.
07266  */
07267 
07268 static VALUE
07269 gc_profile_enable_get(VALUE self)
07270 {
07271     rb_objspace_t *objspace = &rb_objspace;
07272     return objspace->profile.run ? Qtrue : Qfalse;
07273 }
07274 
07275 /*
07276  *  call-seq:
07277  *    GC::Profiler.enable       -> nil
07278  *
07279  *  Starts the GC profiler.
07280  *
07281  */
07282 
07283 static VALUE
07284 gc_profile_enable(void)
07285 {
07286     rb_objspace_t *objspace = &rb_objspace;
07287     objspace->profile.run = TRUE;
07288     objspace->profile.current_record = 0;
07289     return Qnil;
07290 }
07291 
07292 /*
07293  *  call-seq:
07294  *    GC::Profiler.disable      -> nil
07295  *
07296  *  Stops the GC profiler.
07297  *
07298  */
07299 
07300 static VALUE
07301 gc_profile_disable(void)
07302 {
07303     rb_objspace_t *objspace = &rb_objspace;
07304 
07305     objspace->profile.run = FALSE;
07306     objspace->profile.current_record = 0;
07307     return Qnil;
07308 }
07309 
07310 /*
07311   ------------------------------ DEBUG ------------------------------
07312 */
07313 
07314 static const char *
07315 type_name(int type, VALUE obj)
07316 {
07317     switch (type) {
07318 #define TYPE_NAME(t) case (t): return #t;
07319             TYPE_NAME(T_NONE);
07320             TYPE_NAME(T_OBJECT);
07321             TYPE_NAME(T_CLASS);
07322             TYPE_NAME(T_MODULE);
07323             TYPE_NAME(T_FLOAT);
07324             TYPE_NAME(T_STRING);
07325             TYPE_NAME(T_REGEXP);
07326             TYPE_NAME(T_ARRAY);
07327             TYPE_NAME(T_HASH);
07328             TYPE_NAME(T_STRUCT);
07329             TYPE_NAME(T_BIGNUM);
07330             TYPE_NAME(T_FILE);
07331             TYPE_NAME(T_MATCH);
07332             TYPE_NAME(T_COMPLEX);
07333             TYPE_NAME(T_RATIONAL);
07334             TYPE_NAME(T_NIL);
07335             TYPE_NAME(T_TRUE);
07336             TYPE_NAME(T_FALSE);
07337             TYPE_NAME(T_SYMBOL);
07338             TYPE_NAME(T_FIXNUM);
07339             TYPE_NAME(T_UNDEF);
07340             TYPE_NAME(T_NODE);
07341             TYPE_NAME(T_ICLASS);
07342             TYPE_NAME(T_ZOMBIE);
07343       case T_DATA:
07344         if (obj && rb_objspace_data_type_name(obj)) {
07345             return rb_objspace_data_type_name(obj);
07346         }
07347         return "T_DATA";
07348 #undef TYPE_NAME
07349     }
07350     return "unknown";
07351 }
07352 
07353 static const char *
07354 obj_type_name(VALUE obj)
07355 {
07356     return type_name(TYPE(obj), obj);
07357 }
07358 
07359 #if GC_DEBUG
07360 
07361 void
07362 rb_gcdebug_print_obj_condition(VALUE obj)
07363 {
07364     rb_objspace_t *objspace = &rb_objspace;
07365 
07366     fprintf(stderr, "created at: %s:%d\n", RSTRING_PTR(RANY(obj)->file), FIX2INT(RANY(obj)->line));
07367 
07368     if (is_pointer_to_heap(objspace, (void *)obj)) {
07369         fprintf(stderr, "pointer to heap?: true\n");
07370     }
07371     else {
07372         fprintf(stderr, "pointer to heap?: false\n");
07373         return;
07374     }
07375 
07376     fprintf(stderr, "marked?      : %s\n", MARKED_IN_BITMAP(GET_HEAP_MARK_BITS(obj), obj) ? "true" : "false");
07377 #if USE_RGENGC
07378 #if RGENGC_THREEGEN
07379     fprintf(stderr, "young?       : %s\n", RVALUE_YOUNG_P(obj) ? "true" : "false");
07380 #endif
07381     fprintf(stderr, "old?         : %s\n", RVALUE_OLD_P(obj) ? "true" : "false");
07382     fprintf(stderr, "WB-protected?: %s\n", RVALUE_WB_PROTECTED(obj) ? "true" : "false");
07383     fprintf(stderr, "remembered?  : %s\n", MARKED_IN_BITMAP(GET_HEAP_REMEMBERSET_BITS(obj), obj) ? "true" : "false");
07384 #endif
07385 
07386     if (is_lazy_sweeping(heap_eden)) {
07387         fprintf(stderr, "lazy sweeping?: true\n");
07388         fprintf(stderr, "swept?: %s\n", is_swept_object(objspace, obj) ? "done" : "not yet");
07389     }
07390     else {
07391         fprintf(stderr, "lazy sweeping?: false\n");
07392     }
07393 }
07394 
07395 static VALUE
07396 gcdebug_sential(VALUE obj, VALUE name)
07397 {
07398     fprintf(stderr, "WARNING: object %s(%p) is inadvertently collected\n", (char *)name, (void *)obj);
07399     return Qnil;
07400 }
07401 
07402 void
07403 rb_gcdebug_sentinel(VALUE obj, const char *name)
07404 {
07405     rb_define_finalizer(obj, rb_proc_new(gcdebug_sential, (VALUE)name));
07406 }
07407 #endif /* GC_DEBUG */
07408 
07409 /*
07410  * Document-module: ObjectSpace
07411  *
07412  *  The ObjectSpace module contains a number of routines
07413  *  that interact with the garbage collection facility and allow you to
07414  *  traverse all living objects with an iterator.
07415  *
07416  *  ObjectSpace also provides support for object finalizers, procs that will be
07417  *  called when a specific object is about to be destroyed by garbage
07418  *  collection.
07419  *
07420  *     a = "A"
07421  *     b = "B"
07422  *
07423  *     ObjectSpace.define_finalizer(a, proc {|id| puts "Finalizer one on #{id}" })
07424  *     ObjectSpace.define_finalizer(b, proc {|id| puts "Finalizer two on #{id}" })
07425  *
07426  *  _produces:_
07427  *
07428  *     Finalizer two on 537763470
07429  *     Finalizer one on 537763480
07430  */
07431 
07432 /*
07433  *  Document-class: ObjectSpace::WeakMap
07434  *
07435  *  An ObjectSpace::WeakMap object holds references to
07436  *  any objects, but those objects can get garbage collected.
07437  *
07438  *  This class is mostly used internally by WeakRef, please use
07439  *  +lib/weakref.rb+ for the public interface.
07440  */
07441 
07442 /*  Document-class: GC::Profiler
07443  *
07444  *  The GC profiler provides access to information on GC runs including time,
07445  *  length and object space size.
07446  *
07447  *  Example:
07448  *
07449  *    GC::Profiler.enable
07450  *
07451  *    require 'rdoc/rdoc'
07452  *
07453  *    GC::Profiler.report
07454  *
07455  *    GC::Profiler.disable
07456  *
07457  *  See also GC.count, GC.malloc_allocated_size and GC.malloc_allocations
07458  */
07459 
07460 /*
07461  *  The GC module provides an interface to Ruby's mark and
07462  *  sweep garbage collection mechanism.
07463  *
07464  *  Some of the underlying methods are also available via the ObjectSpace
07465  *  module.
07466  *
07467  *  You may obtain information about the operation of the GC through
07468  *  GC::Profiler.
07469  */
07470 
07471 void
07472 Init_GC(void)
07473 {
07474     VALUE rb_mObjSpace;
07475     VALUE rb_mProfiler;
07476     VALUE gc_constants;
07477 
07478     rb_mGC = rb_define_module("GC");
07479     rb_define_singleton_method(rb_mGC, "start", gc_start_internal, -1);
07480     rb_define_singleton_method(rb_mGC, "enable", rb_gc_enable, 0);
07481     rb_define_singleton_method(rb_mGC, "disable", rb_gc_disable, 0);
07482     rb_define_singleton_method(rb_mGC, "stress", gc_stress_get, 0);
07483     rb_define_singleton_method(rb_mGC, "stress=", gc_stress_set, 1);
07484     rb_define_singleton_method(rb_mGC, "count", gc_count, 0);
07485     rb_define_singleton_method(rb_mGC, "stat", gc_stat, -1);
07486     rb_define_singleton_method(rb_mGC, "latest_gc_info", gc_latest_gc_info, -1);
07487     rb_define_method(rb_mGC, "garbage_collect", gc_start_internal, -1);
07488 
07489     gc_constants = rb_hash_new();
07490     rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_SIZE")), SIZET2NUM(sizeof(RVALUE)));
07491     rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_OBJ_LIMIT")), SIZET2NUM(HEAP_OBJ_LIMIT));
07492     rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_BITMAP_SIZE")), SIZET2NUM(HEAP_BITMAP_SIZE));
07493     rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_BITMAP_PLANES")), SIZET2NUM(HEAP_BITMAP_PLANES));
07494     OBJ_FREEZE(gc_constants);
07495     rb_define_const(rb_mGC, "INTERNAL_CONSTANTS", gc_constants);
07496 
07497     rb_mProfiler = rb_define_module_under(rb_mGC, "Profiler");
07498     rb_define_singleton_method(rb_mProfiler, "enabled?", gc_profile_enable_get, 0);
07499     rb_define_singleton_method(rb_mProfiler, "enable", gc_profile_enable, 0);
07500     rb_define_singleton_method(rb_mProfiler, "raw_data", gc_profile_record_get, 0);
07501     rb_define_singleton_method(rb_mProfiler, "disable", gc_profile_disable, 0);
07502     rb_define_singleton_method(rb_mProfiler, "clear", gc_profile_clear, 0);
07503     rb_define_singleton_method(rb_mProfiler, "result", gc_profile_result, 0);
07504     rb_define_singleton_method(rb_mProfiler, "report", gc_profile_report, -1);
07505     rb_define_singleton_method(rb_mProfiler, "total_time", gc_profile_total_time, 0);
07506 
07507     rb_mObjSpace = rb_define_module("ObjectSpace");
07508     rb_define_module_function(rb_mObjSpace, "each_object", os_each_obj, -1);
07509     rb_define_module_function(rb_mObjSpace, "garbage_collect", gc_start_internal, -1);
07510 
07511     rb_define_module_function(rb_mObjSpace, "define_finalizer", define_final, -1);
07512     rb_define_module_function(rb_mObjSpace, "undefine_finalizer", undefine_final, 1);
07513 
07514     rb_define_module_function(rb_mObjSpace, "_id2ref", id2ref, 1);
07515 
07516     nomem_error = rb_exc_new3(rb_eNoMemError,
07517                               rb_obj_freeze(rb_str_new2("failed to allocate memory")));
07518     OBJ_TAINT(nomem_error);
07519     OBJ_FREEZE(nomem_error);
07520 
07521     rb_define_method(rb_cBasicObject, "__id__", rb_obj_id, 0);
07522     rb_define_method(rb_mKernel, "object_id", rb_obj_id, 0);
07523 
07524     rb_define_module_function(rb_mObjSpace, "count_objects", count_objects, -1);
07525 
07526     {
07527         VALUE rb_cWeakMap = rb_define_class_under(rb_mObjSpace, "WeakMap", rb_cObject);
07528         rb_define_alloc_func(rb_cWeakMap, wmap_allocate);
07529         rb_define_method(rb_cWeakMap, "[]=", wmap_aset, 2);
07530         rb_define_method(rb_cWeakMap, "[]", wmap_aref, 1);
07531         rb_define_method(rb_cWeakMap, "include?", wmap_has_key, 1);
07532         rb_define_method(rb_cWeakMap, "member?", wmap_has_key, 1);
07533         rb_define_method(rb_cWeakMap, "key?", wmap_has_key, 1);
07534         rb_define_method(rb_cWeakMap, "inspect", wmap_inspect, 0);
07535         rb_define_method(rb_cWeakMap, "each", wmap_each, 0);
07536         rb_define_method(rb_cWeakMap, "each_pair", wmap_each, 0);
07537         rb_define_method(rb_cWeakMap, "each_key", wmap_each_key, 0);
07538         rb_define_method(rb_cWeakMap, "each_value", wmap_each_value, 0);
07539         rb_define_method(rb_cWeakMap, "keys", wmap_keys, 0);
07540         rb_define_method(rb_cWeakMap, "values", wmap_values, 0);
07541         rb_define_method(rb_cWeakMap, "size", wmap_size, 0);
07542         rb_define_method(rb_cWeakMap, "length", wmap_size, 0);
07543         rb_define_private_method(rb_cWeakMap, "finalize", wmap_finalize, 1);
07544         rb_include_module(rb_cWeakMap, rb_mEnumerable);
07545     }
07546 
07547     /* internal methods */
07548     rb_define_singleton_method(rb_mGC, "verify_internal_consistency", gc_verify_internal_consistency, 0);
07549 #if MALLOC_ALLOCATED_SIZE
07550     rb_define_singleton_method(rb_mGC, "malloc_allocated_size", gc_malloc_allocated_size, 0);
07551     rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0);
07552 #endif
07553 
07554     /* ::GC::OPTS, which shows GC build options */
07555     {
07556         VALUE opts;
07557         rb_define_const(rb_mGC, "OPTS", opts = rb_ary_new());
07558 #define OPT(o) if (o) rb_ary_push(opts, rb_str_new2(#o))
07559         OPT(GC_DEBUG);
07560         OPT(USE_RGENGC);
07561         OPT(RGENGC_DEBUG);
07562         OPT(RGENGC_CHECK_MODE);
07563         OPT(RGENGC_PROFILE);
07564         OPT(RGENGC_THREEGEN);
07565         OPT(RGENGC_ESTIMATE_OLDMALLOC);
07566         OPT(GC_PROFILE_MORE_DETAIL);
07567         OPT(GC_ENABLE_LAZY_SWEEP);
07568         OPT(CALC_EXACT_MALLOC_SIZE);
07569         OPT(MALLOC_ALLOCATED_SIZE);
07570         OPT(MALLOC_ALLOCATED_SIZE_CHECK);
07571         OPT(GC_PROFILE_DETAIL_MEMORY);
07572 #undef OPT
07573     }
07574 }
07575 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7