00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
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 \
00058 __has_feature(address_sanitizer) || \
00059 defined(__SANITIZE_ADDRESS__)
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
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 )
00117 #endif
00118 #ifndef GC_MALLOC_LIMIT_MAX
00119 #define GC_MALLOC_LIMIT_MAX (32 * 1024 * 1024 )
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 )
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 )
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
00170
00171
00172 #ifndef GC_DEBUG
00173 #define GC_DEBUG 0
00174 #endif
00175
00176 #if USE_RGENGC
00177
00178
00179
00180
00181
00182
00183
00184 #ifndef RGENGC_DEBUG
00185 #define RGENGC_DEBUG 0
00186 #endif
00187
00188
00189
00190
00191
00192
00193
00194
00195 #ifndef RGENGC_CHECK_MODE
00196 #define RGENGC_CHECK_MODE 0
00197 #endif
00198
00199
00200
00201
00202
00203
00204 #ifndef RGENGC_PROFILE
00205 #define RGENGC_PROFILE 0
00206 #endif
00207
00208
00209
00210
00211
00212
00213 #ifndef RGENGC_THREEGEN
00214 #define RGENGC_THREEGEN 0
00215 #endif
00216
00217
00218
00219
00220
00221
00222
00223 #ifndef RGENGC_ESTIMATE_OLDMALLOC
00224 #define RGENGC_ESTIMATE_OLDMALLOC 1
00225 #endif
00226
00227 #else
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
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
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
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
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)
00328 #endif
00329
00330 typedef struct RVALUE {
00331 union {
00332 struct {
00333 VALUE flags;
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
00383
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;
00414 size_t total_slots;
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;
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
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
00494 #endif
00495
00496
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
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;
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
00544 } rb_objspace_t;
00545
00546
00547 #ifndef HEAP_ALIGN_LOG
00548
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
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
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
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)
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
00813
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
00866
00867
00868
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
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
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
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
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
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
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));
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
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)
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
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;
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
01676 rb_thread_t *th = GET_THREAD();
01677 void *tmp = th->altstack;
01678 th->altstack = malloc(rb_sigaltstack_size());
01679 free(tmp);
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
01726
01727
01728
01729
01730
01731
01732
01733
01734
01735
01736
01737
01738
01739
01740
01741
01742
01743
01744
01745
01746
01747
01748
01749
01750
01751
01752
01753
01754
01755
01756
01757
01758
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
01847
01848
01849
01850
01851
01852
01853
01854
01855
01856
01857
01858
01859
01860
01861
01862
01863
01864
01865
01866
01867
01868
01869
01870
01871
01872
01873
01874
01875
01876
01877
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
01898
01899
01900
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
01941
01942
01943
01944
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
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
02160 finalize_deferred(objspace);
02161 assert(heap_pages_deferred_final == 0);
02162
02163
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
02178 during_gc++;
02179
02180
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;
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
02289
02290
02291
02292
02293
02294
02295
02296
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
02340
02341
02342
02343
02344
02345
02346
02347
02348
02349
02350
02351
02352
02353
02354
02355
02356
02357
02358
02359
02360
02361
02362
02363
02364
02365
02366
02367
02368
02369
02370
02371
02372 VALUE
02373 rb_obj_id(VALUE obj)
02374 {
02375
02376
02377
02378
02379
02380
02381
02382
02383
02384
02385
02386
02387
02388
02389
02390
02391
02392
02393
02394
02395
02396
02397
02398
02399
02400
02401
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
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
02521 }
02522 break;
02523 case NODE_ALLOCA:
02524
02525 ;
02526 }
02527 break;
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
02564
02565
02566
02567
02568
02569
02570
02571
02572
02573
02574
02575
02576
02577
02578
02579
02580
02581
02582
02583
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
02672
02673
02674
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
02708 memcpy(&page->mark_bits[0], &page->oldgen_bits[0], HEAP_BITMAP_SIZE);
02709 #else
02710
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
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
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
02816 static void
02817 gc_heap_prepare_minimum_pages(rb_objspace_t *objspace, rb_heap_t *heap)
02818 {
02819 if (!heap->free_pages) {
02820
02821 heap_set_increment(objspace, 0);
02822 if (!heap_increment(objspace, heap)) {
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
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
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 &&
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);
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
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
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 ) {
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
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;
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
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
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;
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
03437
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
03485 rb_setjmp(save_regs_gc_mark.j);
03486
03487
03488
03489
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
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;
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
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;
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;
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
03649 if (RVALUE_WB_PROTECTED(obj)) {
03650 if (RVALUE_INFANT_P((VALUE)obj)) {
03651
03652 RVALUE_PROMOTE_INFANT((VALUE)obj);
03653 #if RGENGC_THREEGEN
03654
03655 objspace->rgengc.young_object_count++;
03656 objspace->rgengc.parent_object_is_old = FALSE;
03657 #else
03658
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
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
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
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:
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
03717 case NODE_BLOCK:
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
03729 case NODE_SUPER:
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:
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
03756 case NODE_GASGN:
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:
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:
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:
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:
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;
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
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
03987
03988
03989
03990
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
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
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
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
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
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
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
04204 rb_objspace_reachable_objects_from(v, verify_internal_consistency_reachable_i, (void *)data);
04205 }
04206 }
04207 }
04208
04209 return 0;
04210 }
04211 #endif
04212
04213
04214
04215
04216
04217
04218
04219
04220
04221
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)) {
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
04317
04318
04319
04320
04321
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
04380 objspace->mark_func_data = &mfd;
04381 gc_mark_roots(objspace, TRUE, &data.category);
04382 objspace->mark_func_data = 0;
04383
04384
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
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
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
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
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) {
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
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 {
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
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
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
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
04597
04598
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
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
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
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
04772 #endif
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
04832
04833
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
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
04913
04914
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 );
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
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
05131
05132
05133
05134
05135
05136
05137
05138
05139
05140
05141
05142
05143
05144
05145
05146
05147
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
05226
05227
05228
05229
05230
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)
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
05326
05327
05328
05329
05330
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
05372 #endif
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
05420 #endif
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
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
05469 #endif
05470 #undef SET
05471
05472 if (key != Qnil)
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
05494
05495
05496
05497
05498
05499
05500
05501
05502
05503
05504
05505
05506
05507
05508
05509
05510
05511
05512
05513
05514
05515
05516
05517
05518
05519
05520
05521
05522
05523
05524
05525
05526
05527
05528
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
05569
05570
05571
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
05583
05584
05585
05586
05587
05588
05589
05590
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
05604
05605
05606
05607
05608
05609
05610
05611
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
05627
05628
05629
05630
05631
05632
05633
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
05703
05704
05705
05706
05707
05708
05709
05710
05711
05712
05713
05714
05715
05716
05717
05718
05719
05720
05721
05722
05723
05724
05725
05726
05727
05728
05729
05730
05731
05732 void
05733 ruby_gc_set_params(int safe_level)
05734 {
05735 if (safe_level > 0) return;
05736
05737
05738 if (get_envparam_int("RUBY_GC_HEAP_FREE_SLOTS", &gc_params.heap_free_slots, 0)) {
05739
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
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
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;
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
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
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);
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: 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) || \
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
06113
06114
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
06248
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
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
06279
06280
06281
06282
06283
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
06294
06295
06296
06297
06298
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
06310
06311
06312 struct weakmap {
06313 st_table *obj2wmap;
06314 st_table *wmap2obj;
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
06432 obj = obj_id_to_ref(objid);
06433
06434
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
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
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
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
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
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
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
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
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
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
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
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
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
06942
06943
06944
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
06967
06968
06969
06970
06971
06972
06973
06974
06975
06976
06977
06978
06979
06980
06981
06982
06983
06984
06985
06986
06987
06988
06989
06990
06991
06992
06993
06994
06995
06996
06997
06998
06999
07000
07001
07002
07003
07004
07005
07006
07007
07008
07009
07010
07011
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 ) {
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
07195
07196
07197
07198
07199
07200
07201
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
07214
07215
07216
07217
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
07239
07240
07241
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
07263
07264
07265
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
07277
07278
07279
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
07294
07295
07296
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
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
07408
07409
07410
07411
07412
07413
07414
07415
07416
07417
07418
07419
07420
07421
07422
07423
07424
07425
07426
07427
07428
07429
07430
07431
07432
07433
07434
07435
07436
07437
07438
07439
07440
07441
07442
07443
07444
07445
07446
07447
07448
07449
07450
07451
07452
07453
07454
07455
07456
07457
07458
07459
07460
07461
07462
07463
07464
07465
07466
07467
07468
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
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
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