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 "eval_intern.h"
00021 #include "vm_core.h"
00022 #include "internal.h"
00023 #include "gc.h"
00024 #include "constant.h"
00025 #include "ruby_atomic.h"
00026 #include "probes.h"
00027 #include <stdio.h>
00028 #include <setjmp.h>
00029 #include <sys/types.h>
00030 #include <assert.h>
00031
00032 #ifdef HAVE_SYS_TIME_H
00033 #include <sys/time.h>
00034 #endif
00035
00036 #ifdef HAVE_SYS_RESOURCE_H
00037 #include <sys/resource.h>
00038 #endif
00039 #if defined(__native_client__) && defined(NACL_NEWLIB)
00040 # include "nacl/resource.h"
00041 # undef HAVE_POSIX_MEMALIGN
00042 # undef HAVE_MEMALIGN
00043
00044 #endif
00045
00046 #if defined _WIN32 || defined __CYGWIN__
00047 #include <windows.h>
00048 #elif defined(HAVE_POSIX_MEMALIGN)
00049 #elif defined(HAVE_MEMALIGN)
00050 #include <malloc.h>
00051 #endif
00052
00053 #ifdef HAVE_VALGRIND_MEMCHECK_H
00054 # include <valgrind/memcheck.h>
00055 # ifndef VALGRIND_MAKE_MEM_DEFINED
00056 # define VALGRIND_MAKE_MEM_DEFINED(p, n) VALGRIND_MAKE_READABLE((p), (n))
00057 # endif
00058 # ifndef VALGRIND_MAKE_MEM_UNDEFINED
00059 # define VALGRIND_MAKE_MEM_UNDEFINED(p, n) VALGRIND_MAKE_WRITABLE((p), (n))
00060 # endif
00061 #else
00062 # define VALGRIND_MAKE_MEM_DEFINED(p, n) 0
00063 # define VALGRIND_MAKE_MEM_UNDEFINED(p, n) 0
00064 #endif
00065
00066 #define rb_setjmp(env) RUBY_SETJMP(env)
00067 #define rb_jmp_buf rb_jmpbuf_t
00068
00069 #ifndef GC_MALLOC_LIMIT
00070 #define GC_MALLOC_LIMIT 8000000
00071 #endif
00072 #define HEAP_MIN_SLOTS 10000
00073 #define FREE_MIN 4096
00074
00075 typedef struct {
00076 unsigned int initial_malloc_limit;
00077 unsigned int initial_heap_min_slots;
00078 unsigned int initial_free_min;
00079 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00080 int gc_stress;
00081 #endif
00082 } ruby_gc_params_t;
00083
00084 static ruby_gc_params_t initial_params = {
00085 GC_MALLOC_LIMIT,
00086 HEAP_MIN_SLOTS,
00087 FREE_MIN,
00088 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00089 FALSE,
00090 #endif
00091 };
00092
00093 #define nomem_error GET_VM()->special_exceptions[ruby_error_nomemory]
00094
00095 #ifndef GC_PROFILE_MORE_DETAIL
00096 #define GC_PROFILE_MORE_DETAIL 0
00097 #endif
00098
00099 typedef struct gc_profile_record {
00100 double gc_time;
00101 double gc_invoke_time;
00102
00103 size_t heap_total_objects;
00104 size_t heap_use_size;
00105 size_t heap_total_size;
00106
00107 int is_marked;
00108
00109 #if GC_PROFILE_MORE_DETAIL
00110 double gc_mark_time;
00111 double gc_sweep_time;
00112
00113 size_t heap_use_slots;
00114 size_t heap_live_objects;
00115 size_t heap_free_objects;
00116
00117 int have_finalize;
00118
00119 size_t allocate_increase;
00120 size_t allocate_limit;
00121 #endif
00122 } gc_profile_record;
00123
00124 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
00125 #pragma pack(push, 1)
00126 #endif
00127
00128 typedef struct RVALUE {
00129 union {
00130 struct {
00131 VALUE flags;
00132 struct RVALUE *next;
00133 } free;
00134 struct RBasic basic;
00135 struct RObject object;
00136 struct RClass klass;
00137 struct RFloat flonum;
00138 struct RString string;
00139 struct RArray array;
00140 struct RRegexp regexp;
00141 struct RHash hash;
00142 struct RData data;
00143 struct RTypedData typeddata;
00144 struct RStruct rstruct;
00145 struct RBignum bignum;
00146 struct RFile file;
00147 struct RNode node;
00148 struct RMatch match;
00149 struct RRational rational;
00150 struct RComplex complex;
00151 } as;
00152 #ifdef GC_DEBUG
00153 const char *file;
00154 int line;
00155 #endif
00156 } RVALUE;
00157
00158 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
00159 #pragma pack(pop)
00160 #endif
00161
00162 struct heaps_slot {
00163 struct heaps_header *header;
00164 uintptr_t *bits;
00165 RVALUE *freelist;
00166 struct heaps_slot *next;
00167 struct heaps_slot *prev;
00168 struct heaps_slot *free_next;
00169 };
00170
00171 struct heaps_header {
00172 struct heaps_slot *base;
00173 uintptr_t *bits;
00174 RVALUE *start;
00175 RVALUE *end;
00176 size_t limit;
00177 };
00178
00179 struct heaps_free_bitmap {
00180 struct heaps_free_bitmap *next;
00181 };
00182
00183 struct gc_list {
00184 VALUE *varptr;
00185 struct gc_list *next;
00186 };
00187
00188 #define STACK_CHUNK_SIZE 500
00189
00190 typedef struct stack_chunk {
00191 VALUE data[STACK_CHUNK_SIZE];
00192 struct stack_chunk *next;
00193 } stack_chunk_t;
00194
00195 typedef struct mark_stack {
00196 stack_chunk_t *chunk;
00197 stack_chunk_t *cache;
00198 size_t index;
00199 size_t limit;
00200 size_t cache_size;
00201 size_t unused_cache_size;
00202 } mark_stack_t;
00203
00204 #ifndef CALC_EXACT_MALLOC_SIZE
00205 #define CALC_EXACT_MALLOC_SIZE 0
00206 #endif
00207
00208 typedef struct rb_objspace {
00209 struct {
00210 size_t limit;
00211 size_t increase;
00212 #if CALC_EXACT_MALLOC_SIZE
00213 size_t allocated_size;
00214 size_t allocations;
00215 #endif
00216 } malloc_params;
00217 struct {
00218 size_t increment;
00219 struct heaps_slot *ptr;
00220 struct heaps_slot *sweep_slots;
00221 struct heaps_slot *free_slots;
00222 struct heaps_header **sorted;
00223 size_t length;
00224 size_t used;
00225 struct heaps_free_bitmap *free_bitmap;
00226 RVALUE *range[2];
00227 struct heaps_header *freed;
00228 size_t marked_num;
00229 size_t free_num;
00230 size_t free_min;
00231 size_t final_num;
00232 size_t do_heap_free;
00233 } heap;
00234 struct {
00235 int dont_gc;
00236 int dont_lazy_sweep;
00237 int during_gc;
00238 rb_atomic_t finalizing;
00239 } flags;
00240 struct {
00241 st_table *table;
00242 RVALUE *deferred;
00243 } final;
00244 mark_stack_t mark_stack;
00245 struct {
00246 int run;
00247 gc_profile_record *record;
00248 size_t count;
00249 size_t size;
00250 double invoke_time;
00251 } profile;
00252 struct gc_list *global_list;
00253 size_t count;
00254 size_t total_allocated_object_num;
00255 size_t total_freed_object_num;
00256 int gc_stress;
00257
00258 struct mark_func_data_struct {
00259 void *data;
00260 void (*mark_func)(VALUE v, void *data);
00261 } *mark_func_data;
00262 } rb_objspace_t;
00263
00264 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00265 #define rb_objspace (*GET_VM()->objspace)
00266 #define ruby_initial_gc_stress initial_params.gc_stress
00267 int *ruby_initial_gc_stress_ptr = &ruby_initial_gc_stress;
00268 #else
00269 static rb_objspace_t rb_objspace = {{GC_MALLOC_LIMIT}};
00270 int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress;
00271 #endif
00272 #define malloc_limit objspace->malloc_params.limit
00273 #define malloc_increase objspace->malloc_params.increase
00274 #define heaps objspace->heap.ptr
00275 #define heaps_length objspace->heap.length
00276 #define heaps_used objspace->heap.used
00277 #define lomem objspace->heap.range[0]
00278 #define himem objspace->heap.range[1]
00279 #define heaps_inc objspace->heap.increment
00280 #define heaps_freed objspace->heap.freed
00281 #define dont_gc objspace->flags.dont_gc
00282 #define during_gc objspace->flags.during_gc
00283 #define finalizing objspace->flags.finalizing
00284 #define finalizer_table objspace->final.table
00285 #define deferred_final_list objspace->final.deferred
00286 #define global_List objspace->global_list
00287 #define ruby_gc_stress objspace->gc_stress
00288 #define initial_malloc_limit initial_params.initial_malloc_limit
00289 #define initial_heap_min_slots initial_params.initial_heap_min_slots
00290 #define initial_free_min initial_params.initial_free_min
00291
00292 #define is_lazy_sweeping(objspace) ((objspace)->heap.sweep_slots != 0)
00293
00294 #if SIZEOF_LONG == SIZEOF_VOIDP
00295 # define nonspecial_obj_id(obj) (VALUE)((SIGNED_VALUE)(obj)|FIXNUM_FLAG)
00296 # define obj_id_to_ref(objid) ((objid) ^ FIXNUM_FLAG)
00297 #elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
00298 # define nonspecial_obj_id(obj) LL2NUM((SIGNED_VALUE)(obj) / 2)
00299 # define obj_id_to_ref(objid) (FIXNUM_P(objid) ? \
00300 ((objid) ^ FIXNUM_FLAG) : (NUM2PTR(objid) << 1))
00301 #else
00302 # error not supported
00303 #endif
00304
00305 #define RANY(o) ((RVALUE*)(o))
00306 #define has_free_object (objspace->heap.free_slots && objspace->heap.free_slots->freelist)
00307
00308 #define HEAP_HEADER(p) ((struct heaps_header *)(p))
00309 #define GET_HEAP_HEADER(x) (HEAP_HEADER((uintptr_t)(x) & ~(HEAP_ALIGN_MASK)))
00310 #define GET_HEAP_SLOT(x) (GET_HEAP_HEADER(x)->base)
00311 #define GET_HEAP_BITMAP(x) (GET_HEAP_HEADER(x)->bits)
00312 #define NUM_IN_SLOT(p) (((uintptr_t)(p) & HEAP_ALIGN_MASK)/sizeof(RVALUE))
00313 #define BITMAP_INDEX(p) (NUM_IN_SLOT(p) / (sizeof(uintptr_t) * CHAR_BIT))
00314 #define BITMAP_OFFSET(p) (NUM_IN_SLOT(p) & ((sizeof(uintptr_t) * CHAR_BIT)-1))
00315 #define MARKED_IN_BITMAP(bits, p) (bits[BITMAP_INDEX(p)] & ((uintptr_t)1 << BITMAP_OFFSET(p)))
00316
00317 #ifndef HEAP_ALIGN_LOG
00318
00319 #define HEAP_ALIGN_LOG 14
00320 #endif
00321
00322 #define CEILDIV(i, mod) (((i) + (mod) - 1)/(mod))
00323
00324 enum {
00325 HEAP_ALIGN = (1UL << HEAP_ALIGN_LOG),
00326 HEAP_ALIGN_MASK = (~(~0UL << HEAP_ALIGN_LOG)),
00327 REQUIRED_SIZE_BY_MALLOC = (sizeof(size_t) * 5),
00328 HEAP_SIZE = (HEAP_ALIGN - REQUIRED_SIZE_BY_MALLOC),
00329 HEAP_OBJ_LIMIT = (unsigned int)((HEAP_SIZE - sizeof(struct heaps_header))/sizeof(struct RVALUE)),
00330 HEAP_BITMAP_LIMIT = CEILDIV(CEILDIV(HEAP_SIZE, sizeof(struct RVALUE)), sizeof(uintptr_t) * CHAR_BIT)
00331 };
00332
00333 int ruby_gc_debug_indent = 0;
00334 VALUE rb_mGC;
00335 extern st_table *rb_class_tbl;
00336 int ruby_disable_gc_stress = 0;
00337
00338 static void rb_objspace_call_finalizer(rb_objspace_t *objspace);
00339 static VALUE define_final0(VALUE obj, VALUE block);
00340 VALUE rb_define_final(VALUE obj, VALUE block);
00341 VALUE rb_undefine_final(VALUE obj);
00342 static void run_final(rb_objspace_t *objspace, VALUE obj);
00343 static void initial_expand_heap(rb_objspace_t *objspace);
00344
00345 static void negative_size_allocation_error(const char *);
00346 static void *aligned_malloc(size_t, size_t);
00347 static void aligned_free(void *);
00348
00349 static void init_mark_stack(mark_stack_t *stack);
00350
00351 static VALUE lazy_sweep_enable(void);
00352 static int garbage_collect(rb_objspace_t *);
00353 static int gc_prepare_free_objects(rb_objspace_t *);
00354 static void mark_tbl(rb_objspace_t *, st_table *);
00355 static void rest_sweep(rb_objspace_t *);
00356 static void gc_mark_stacked_objects(rb_objspace_t *);
00357
00358 static double getrusage_time(void);
00359 static inline void gc_prof_timer_start(rb_objspace_t *);
00360 static inline void gc_prof_timer_stop(rb_objspace_t *, int);
00361 static inline void gc_prof_mark_timer_start(rb_objspace_t *);
00362 static inline void gc_prof_mark_timer_stop(rb_objspace_t *);
00363 static inline void gc_prof_sweep_timer_start(rb_objspace_t *);
00364 static inline void gc_prof_sweep_timer_stop(rb_objspace_t *);
00365 static inline void gc_prof_set_malloc_info(rb_objspace_t *);
00366
00367
00368
00369
00370
00371
00372 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00373 rb_objspace_t *
00374 rb_objspace_alloc(void)
00375 {
00376 rb_objspace_t *objspace = malloc(sizeof(rb_objspace_t));
00377 memset(objspace, 0, sizeof(*objspace));
00378 malloc_limit = initial_malloc_limit;
00379 ruby_gc_stress = ruby_initial_gc_stress;
00380
00381 return objspace;
00382 }
00383 #endif
00384
00385 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00386 static void free_stack_chunks(mark_stack_t *);
00387
00388 void
00389 rb_objspace_free(rb_objspace_t *objspace)
00390 {
00391 rest_sweep(objspace);
00392 if (objspace->profile.record) {
00393 free(objspace->profile.record);
00394 objspace->profile.record = 0;
00395 }
00396 if (global_List) {
00397 struct gc_list *list, *next;
00398 for (list = global_List; list; list = next) {
00399 next = list->next;
00400 xfree(list);
00401 }
00402 }
00403 if (objspace->heap.free_bitmap) {
00404 struct heaps_free_bitmap *list, *next;
00405 for (list = objspace->heap.free_bitmap; list; list = next) {
00406 next = list->next;
00407 free(list);
00408 }
00409 }
00410 if (objspace->heap.sorted) {
00411 size_t i;
00412 for (i = 0; i < heaps_used; ++i) {
00413 free(objspace->heap.sorted[i]->bits);
00414 aligned_free(objspace->heap.sorted[i]);
00415 }
00416 free(objspace->heap.sorted);
00417 heaps_used = 0;
00418 heaps = 0;
00419 }
00420 free_stack_chunks(&objspace->mark_stack);
00421 free(objspace);
00422 }
00423 #endif
00424
00425 void
00426 rb_global_variable(VALUE *var)
00427 {
00428 rb_gc_register_address(var);
00429 }
00430
00431 static void
00432 allocate_sorted_heaps(rb_objspace_t *objspace, size_t next_heaps_length)
00433 {
00434 struct heaps_header **p;
00435 struct heaps_free_bitmap *bits;
00436 size_t size, add, i;
00437
00438 size = next_heaps_length*sizeof(struct heaps_header *);
00439 add = next_heaps_length - heaps_used;
00440
00441 if (heaps_used > 0) {
00442 p = (struct heaps_header **)realloc(objspace->heap.sorted, size);
00443 if (p) objspace->heap.sorted = p;
00444 }
00445 else {
00446 p = objspace->heap.sorted = (struct heaps_header **)malloc(size);
00447 }
00448
00449 if (p == 0) {
00450 during_gc = 0;
00451 rb_memerror();
00452 }
00453
00454 for (i = 0; i < add; i++) {
00455 bits = (struct heaps_free_bitmap *)malloc(HEAP_BITMAP_LIMIT * sizeof(uintptr_t));
00456 if (bits == 0) {
00457 during_gc = 0;
00458 rb_memerror();
00459 return;
00460 }
00461 bits->next = objspace->heap.free_bitmap;
00462 objspace->heap.free_bitmap = bits;
00463 }
00464 }
00465
00466 static void
00467 link_free_heap_slot(rb_objspace_t *objspace, struct heaps_slot *slot)
00468 {
00469 slot->free_next = objspace->heap.free_slots;
00470 objspace->heap.free_slots = slot;
00471 }
00472
00473 static void
00474 unlink_free_heap_slot(rb_objspace_t *objspace, struct heaps_slot *slot)
00475 {
00476 objspace->heap.free_slots = slot->free_next;
00477 slot->free_next = NULL;
00478 }
00479
00480 static void
00481 assign_heap_slot(rb_objspace_t *objspace)
00482 {
00483 RVALUE *p, *pend, *membase;
00484 struct heaps_slot *slot;
00485 size_t hi, lo, mid;
00486 size_t objs;
00487
00488 objs = HEAP_OBJ_LIMIT;
00489 p = (RVALUE*)aligned_malloc(HEAP_ALIGN, HEAP_SIZE);
00490 if (p == 0) {
00491 during_gc = 0;
00492 rb_memerror();
00493 }
00494 slot = (struct heaps_slot *)malloc(sizeof(struct heaps_slot));
00495 if (slot == 0) {
00496 aligned_free(p);
00497 during_gc = 0;
00498 rb_memerror();
00499 }
00500 MEMZERO((void*)slot, struct heaps_slot, 1);
00501
00502 slot->next = heaps;
00503 if (heaps) heaps->prev = slot;
00504 heaps = slot;
00505
00506 membase = p;
00507 p = (RVALUE*)((VALUE)p + sizeof(struct heaps_header));
00508 if ((VALUE)p % sizeof(RVALUE) != 0) {
00509 p = (RVALUE*)((VALUE)p + sizeof(RVALUE) - ((VALUE)p % sizeof(RVALUE)));
00510 objs = (HEAP_SIZE - (size_t)((VALUE)p - (VALUE)membase))/sizeof(RVALUE);
00511 }
00512
00513 lo = 0;
00514 hi = heaps_used;
00515 while (lo < hi) {
00516 register RVALUE *mid_membase;
00517 mid = (lo + hi) / 2;
00518 mid_membase = (RVALUE *)objspace->heap.sorted[mid];
00519 if (mid_membase < membase) {
00520 lo = mid + 1;
00521 }
00522 else if (mid_membase > membase) {
00523 hi = mid;
00524 }
00525 else {
00526 rb_bug("same heap slot is allocated: %p at %"PRIuVALUE, (void *)membase, (VALUE)mid);
00527 }
00528 }
00529 if (hi < heaps_used) {
00530 MEMMOVE(&objspace->heap.sorted[hi+1], &objspace->heap.sorted[hi], struct heaps_header*, heaps_used - hi);
00531 }
00532 heaps->header = (struct heaps_header *)membase;
00533 objspace->heap.sorted[hi] = heaps->header;
00534 objspace->heap.sorted[hi]->start = p;
00535 objspace->heap.sorted[hi]->end = (p + objs);
00536 objspace->heap.sorted[hi]->base = heaps;
00537 objspace->heap.sorted[hi]->limit = objs;
00538 assert(objspace->heap.free_bitmap != NULL);
00539 heaps->bits = (uintptr_t *)objspace->heap.free_bitmap;
00540 objspace->heap.sorted[hi]->bits = (uintptr_t *)objspace->heap.free_bitmap;
00541 objspace->heap.free_bitmap = objspace->heap.free_bitmap->next;
00542 memset(heaps->bits, 0, HEAP_BITMAP_LIMIT * sizeof(uintptr_t));
00543 pend = p + objs;
00544 if (lomem == 0 || lomem > p) lomem = p;
00545 if (himem < pend) himem = pend;
00546 heaps_used++;
00547
00548 while (p < pend) {
00549 p->as.free.flags = 0;
00550 p->as.free.next = heaps->freelist;
00551 heaps->freelist = p;
00552 p++;
00553 }
00554 link_free_heap_slot(objspace, heaps);
00555 }
00556
00557 static void
00558 add_heap_slots(rb_objspace_t *objspace, size_t add)
00559 {
00560 size_t i;
00561 size_t next_heaps_length;
00562
00563 next_heaps_length = heaps_used + add;
00564
00565 if (next_heaps_length > heaps_length) {
00566 allocate_sorted_heaps(objspace, next_heaps_length);
00567 heaps_length = next_heaps_length;
00568 }
00569
00570 for (i = 0; i < add; i++) {
00571 assign_heap_slot(objspace);
00572 }
00573 heaps_inc = 0;
00574 }
00575
00576 static void
00577 init_heap(rb_objspace_t *objspace)
00578 {
00579 add_heap_slots(objspace, HEAP_MIN_SLOTS / HEAP_OBJ_LIMIT);
00580 init_mark_stack(&objspace->mark_stack);
00581
00582 #ifdef USE_SIGALTSTACK
00583 {
00584
00585 rb_thread_t *th = GET_THREAD();
00586 void *tmp = th->altstack;
00587 th->altstack = malloc(rb_sigaltstack_size());
00588 free(tmp);
00589 }
00590 #endif
00591
00592 objspace->profile.invoke_time = getrusage_time();
00593 finalizer_table = st_init_numtable();
00594 }
00595
00596 static void
00597 initial_expand_heap(rb_objspace_t *objspace)
00598 {
00599 size_t min_size = initial_heap_min_slots / HEAP_OBJ_LIMIT;
00600
00601 if (min_size > heaps_used) {
00602 add_heap_slots(objspace, min_size - heaps_used);
00603 }
00604 }
00605
00606 static void
00607 set_heaps_increment(rb_objspace_t *objspace)
00608 {
00609 size_t next_heaps_length = (size_t)(heaps_used * 1.8);
00610
00611 if (next_heaps_length == heaps_used) {
00612 next_heaps_length++;
00613 }
00614
00615 heaps_inc = next_heaps_length - heaps_used;
00616
00617 if (next_heaps_length > heaps_length) {
00618 allocate_sorted_heaps(objspace, next_heaps_length);
00619 heaps_length = next_heaps_length;
00620 }
00621 }
00622
00623 static int
00624 heaps_increment(rb_objspace_t *objspace)
00625 {
00626 if (heaps_inc > 0) {
00627 assign_heap_slot(objspace);
00628 heaps_inc--;
00629 return TRUE;
00630 }
00631 return FALSE;
00632 }
00633
00634 static VALUE
00635 newobj(VALUE klass, VALUE flags)
00636 {
00637 rb_objspace_t *objspace = &rb_objspace;
00638 VALUE obj;
00639
00640 if (UNLIKELY(during_gc)) {
00641 dont_gc = 1;
00642 during_gc = 0;
00643 rb_bug("object allocation during garbage collection phase");
00644 }
00645
00646 if (UNLIKELY(ruby_gc_stress && !ruby_disable_gc_stress)) {
00647 if (!garbage_collect(objspace)) {
00648 during_gc = 0;
00649 rb_memerror();
00650 }
00651 }
00652
00653 if (UNLIKELY(!has_free_object)) {
00654 if (!gc_prepare_free_objects(objspace)) {
00655 during_gc = 0;
00656 rb_memerror();
00657 }
00658 }
00659
00660 obj = (VALUE)objspace->heap.free_slots->freelist;
00661 objspace->heap.free_slots->freelist = RANY(obj)->as.free.next;
00662 if (objspace->heap.free_slots->freelist == NULL) {
00663 unlink_free_heap_slot(objspace, objspace->heap.free_slots);
00664 }
00665
00666 MEMZERO((void*)obj, RVALUE, 1);
00667 #ifdef GC_DEBUG
00668 RANY(obj)->file = rb_sourcefile();
00669 RANY(obj)->line = rb_sourceline();
00670 #endif
00671 objspace->total_allocated_object_num++;
00672
00673 return obj;
00674 }
00675
00676 VALUE
00677 rb_newobj(void)
00678 {
00679 return newobj(0, T_NONE);
00680 }
00681
00682 VALUE
00683 rb_newobj_of(VALUE klass, VALUE flags)
00684 {
00685 VALUE obj;
00686
00687 obj = newobj(klass, flags);
00688 OBJSETUP(obj, klass, flags);
00689
00690 return obj;
00691 }
00692
00693 NODE*
00694 rb_node_newnode(enum node_type type, VALUE a0, VALUE a1, VALUE a2)
00695 {
00696 NODE *n = (NODE*)rb_newobj();
00697
00698 n->flags |= T_NODE;
00699 nd_set_type(n, type);
00700
00701 n->u1.value = a0;
00702 n->u2.value = a1;
00703 n->u3.value = a2;
00704
00705 return n;
00706 }
00707
00708 VALUE
00709 rb_data_object_alloc(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
00710 {
00711 NEWOBJ(data, struct RData);
00712 if (klass) Check_Type(klass, T_CLASS);
00713 OBJSETUP(data, klass, T_DATA);
00714 data->data = datap;
00715 data->dfree = dfree;
00716 data->dmark = dmark;
00717
00718 return (VALUE)data;
00719 }
00720
00721 VALUE
00722 rb_data_typed_object_alloc(VALUE klass, void *datap, const rb_data_type_t *type)
00723 {
00724 NEWOBJ(data, struct RTypedData);
00725
00726 if (klass) Check_Type(klass, T_CLASS);
00727
00728 OBJSETUP(data, klass, T_DATA);
00729
00730 data->data = datap;
00731 data->typed_flag = 1;
00732 data->type = type;
00733
00734 return (VALUE)data;
00735 }
00736
00737 size_t
00738 rb_objspace_data_type_memsize(VALUE obj)
00739 {
00740 if (RTYPEDDATA_P(obj) && RTYPEDDATA_TYPE(obj)->function.dsize) {
00741 return RTYPEDDATA_TYPE(obj)->function.dsize(RTYPEDDATA_DATA(obj));
00742 }
00743 else {
00744 return 0;
00745 }
00746 }
00747
00748 const char *
00749 rb_objspace_data_type_name(VALUE obj)
00750 {
00751 if (RTYPEDDATA_P(obj)) {
00752 return RTYPEDDATA_TYPE(obj)->wrap_struct_name;
00753 }
00754 else {
00755 return 0;
00756 }
00757 }
00758
00759 static void gc_mark(rb_objspace_t *objspace, VALUE ptr);
00760 static void gc_mark_children(rb_objspace_t *objspace, VALUE ptr);
00761
00762 static inline int
00763 is_pointer_to_heap(rb_objspace_t *objspace, void *ptr)
00764 {
00765 register RVALUE *p = RANY(ptr);
00766 register struct heaps_header *heap;
00767 register size_t hi, lo, mid;
00768
00769 if (p < lomem || p > himem) return FALSE;
00770 if ((VALUE)p % sizeof(RVALUE) != 0) return FALSE;
00771
00772
00773 lo = 0;
00774 hi = heaps_used;
00775 while (lo < hi) {
00776 mid = (lo + hi) / 2;
00777 heap = objspace->heap.sorted[mid];
00778 if (heap->start <= p) {
00779 if (p < heap->end)
00780 return TRUE;
00781 lo = mid + 1;
00782 }
00783 else {
00784 hi = mid;
00785 }
00786 }
00787 return FALSE;
00788 }
00789
00790 static int
00791 free_method_entry_i(ID key, rb_method_entry_t *me, st_data_t data)
00792 {
00793 if (!me->mark) {
00794 rb_free_method_entry(me);
00795 }
00796 return ST_CONTINUE;
00797 }
00798
00799 void
00800 rb_free_m_table(st_table *tbl)
00801 {
00802 st_foreach(tbl, free_method_entry_i, 0);
00803 st_free_table(tbl);
00804 }
00805
00806 static int
00807 free_const_entry_i(ID key, rb_const_entry_t *ce, st_data_t data)
00808 {
00809 xfree(ce);
00810 return ST_CONTINUE;
00811 }
00812
00813 void
00814 rb_free_const_table(st_table *tbl)
00815 {
00816 st_foreach(tbl, free_const_entry_i, 0);
00817 st_free_table(tbl);
00818 }
00819
00820 static int obj_free(rb_objspace_t *, VALUE);
00821
00822 static inline struct heaps_slot *
00823 add_slot_local_freelist(rb_objspace_t *objspace, RVALUE *p)
00824 {
00825 struct heaps_slot *slot;
00826
00827 (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)p, sizeof(RVALUE));
00828 p->as.free.flags = 0;
00829 slot = GET_HEAP_SLOT(p);
00830 p->as.free.next = slot->freelist;
00831 slot->freelist = p;
00832
00833 return slot;
00834 }
00835
00836 static void
00837 unlink_heap_slot(rb_objspace_t *objspace, struct heaps_slot *slot)
00838 {
00839 if (slot->prev)
00840 slot->prev->next = slot->next;
00841 if (slot->next)
00842 slot->next->prev = slot->prev;
00843 if (heaps == slot)
00844 heaps = slot->next;
00845 if (objspace->heap.sweep_slots == slot)
00846 objspace->heap.sweep_slots = slot->next;
00847 slot->prev = NULL;
00848 slot->next = NULL;
00849 }
00850
00851 static void
00852 free_unused_heaps(rb_objspace_t *objspace)
00853 {
00854 size_t i, j;
00855 struct heaps_header *last = 0;
00856
00857 for (i = j = 1; j < heaps_used; i++) {
00858 if (objspace->heap.sorted[i]->limit == 0) {
00859 struct heaps_header* h = objspace->heap.sorted[i];
00860 ((struct heaps_free_bitmap *)(h->bits))->next =
00861 objspace->heap.free_bitmap;
00862 objspace->heap.free_bitmap = (struct heaps_free_bitmap *)h->bits;
00863 if (!last) {
00864 last = objspace->heap.sorted[i];
00865 }
00866 else {
00867 aligned_free(objspace->heap.sorted[i]);
00868 }
00869 heaps_used--;
00870 }
00871 else {
00872 if (i != j) {
00873 objspace->heap.sorted[j] = objspace->heap.sorted[i];
00874 }
00875 j++;
00876 }
00877 }
00878 if (last) {
00879 if (last < heaps_freed) {
00880 aligned_free(heaps_freed);
00881 heaps_freed = last;
00882 }
00883 else {
00884 aligned_free(last);
00885 }
00886 }
00887 }
00888 static inline void
00889 make_deferred(RVALUE *p)
00890 {
00891 p->as.basic.flags = (p->as.basic.flags & ~T_MASK) | T_ZOMBIE;
00892 }
00893
00894 static inline void
00895 make_io_deferred(RVALUE *p)
00896 {
00897 rb_io_t *fptr = p->as.file.fptr;
00898 make_deferred(p);
00899 p->as.data.dfree = (void (*)(void*))rb_io_fptr_finalize;
00900 p->as.data.data = fptr;
00901 }
00902
00903 static int
00904 obj_free(rb_objspace_t *objspace, VALUE obj)
00905 {
00906 switch (BUILTIN_TYPE(obj)) {
00907 case T_NIL:
00908 case T_FIXNUM:
00909 case T_TRUE:
00910 case T_FALSE:
00911 rb_bug("obj_free() called for broken object");
00912 break;
00913 }
00914
00915 if (FL_TEST(obj, FL_EXIVAR)) {
00916 rb_free_generic_ivar((VALUE)obj);
00917 FL_UNSET(obj, FL_EXIVAR);
00918 }
00919
00920 switch (BUILTIN_TYPE(obj)) {
00921 case T_OBJECT:
00922 if (!(RANY(obj)->as.basic.flags & ROBJECT_EMBED) &&
00923 RANY(obj)->as.object.as.heap.ivptr) {
00924 xfree(RANY(obj)->as.object.as.heap.ivptr);
00925 }
00926 break;
00927 case T_MODULE:
00928 case T_CLASS:
00929 rb_clear_cache_by_class((VALUE)obj);
00930 if (RCLASS_M_TBL(obj)) {
00931 rb_free_m_table(RCLASS_M_TBL(obj));
00932 }
00933 if (RCLASS_IV_TBL(obj)) {
00934 st_free_table(RCLASS_IV_TBL(obj));
00935 }
00936 if (RCLASS_CONST_TBL(obj)) {
00937 rb_free_const_table(RCLASS_CONST_TBL(obj));
00938 }
00939 if (RCLASS_IV_INDEX_TBL(obj)) {
00940 st_free_table(RCLASS_IV_INDEX_TBL(obj));
00941 }
00942 xfree(RANY(obj)->as.klass.ptr);
00943 break;
00944 case T_STRING:
00945 rb_str_free(obj);
00946 break;
00947 case T_ARRAY:
00948 rb_ary_free(obj);
00949 break;
00950 case T_HASH:
00951 if (RANY(obj)->as.hash.ntbl) {
00952 st_free_table(RANY(obj)->as.hash.ntbl);
00953 }
00954 break;
00955 case T_REGEXP:
00956 if (RANY(obj)->as.regexp.ptr) {
00957 onig_free(RANY(obj)->as.regexp.ptr);
00958 }
00959 break;
00960 case T_DATA:
00961 if (DATA_PTR(obj)) {
00962 if (RTYPEDDATA_P(obj)) {
00963 RDATA(obj)->dfree = RANY(obj)->as.typeddata.type->function.dfree;
00964 }
00965 if (RANY(obj)->as.data.dfree == (RUBY_DATA_FUNC)-1) {
00966 xfree(DATA_PTR(obj));
00967 }
00968 else if (RANY(obj)->as.data.dfree) {
00969 make_deferred(RANY(obj));
00970 return 1;
00971 }
00972 }
00973 break;
00974 case T_MATCH:
00975 if (RANY(obj)->as.match.rmatch) {
00976 struct rmatch *rm = RANY(obj)->as.match.rmatch;
00977 onig_region_free(&rm->regs, 0);
00978 if (rm->char_offset)
00979 xfree(rm->char_offset);
00980 xfree(rm);
00981 }
00982 break;
00983 case T_FILE:
00984 if (RANY(obj)->as.file.fptr) {
00985 make_io_deferred(RANY(obj));
00986 return 1;
00987 }
00988 break;
00989 case T_RATIONAL:
00990 case T_COMPLEX:
00991 break;
00992 case T_ICLASS:
00993
00994 xfree(RANY(obj)->as.klass.ptr);
00995 break;
00996
00997 case T_FLOAT:
00998 break;
00999
01000 case T_BIGNUM:
01001 if (!(RBASIC(obj)->flags & RBIGNUM_EMBED_FLAG) && RBIGNUM_DIGITS(obj)) {
01002 xfree(RBIGNUM_DIGITS(obj));
01003 }
01004 break;
01005 case T_NODE:
01006 switch (nd_type(obj)) {
01007 case NODE_SCOPE:
01008 if (RANY(obj)->as.node.u1.tbl) {
01009 xfree(RANY(obj)->as.node.u1.tbl);
01010 }
01011 break;
01012 case NODE_ARGS:
01013 if (RANY(obj)->as.node.u3.args) {
01014 xfree(RANY(obj)->as.node.u3.args);
01015 }
01016 break;
01017 case NODE_ALLOCA:
01018 xfree(RANY(obj)->as.node.u1.node);
01019 break;
01020 }
01021 break;
01022
01023 case T_STRUCT:
01024 if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 &&
01025 RANY(obj)->as.rstruct.as.heap.ptr) {
01026 xfree(RANY(obj)->as.rstruct.as.heap.ptr);
01027 }
01028 break;
01029
01030 default:
01031 rb_bug("gc_sweep(): unknown data type 0x%x(%p) 0x%"PRIxVALUE,
01032 BUILTIN_TYPE(obj), (void*)obj, RBASIC(obj)->flags);
01033 }
01034
01035 return 0;
01036 }
01037
01038 void
01039 Init_heap(void)
01040 {
01041 init_heap(&rb_objspace);
01042 }
01043
01044 typedef int each_obj_callback(void *, void *, size_t, void *);
01045
01046 struct each_obj_args {
01047 each_obj_callback *callback;
01048 void *data;
01049 };
01050
01051 static VALUE
01052 objspace_each_objects(VALUE arg)
01053 {
01054 size_t i;
01055 RVALUE *membase = 0;
01056 RVALUE *pstart, *pend;
01057 rb_objspace_t *objspace = &rb_objspace;
01058 struct each_obj_args *args = (struct each_obj_args *)arg;
01059 volatile VALUE v;
01060
01061 i = 0;
01062 while (i < heaps_used) {
01063 while (0 < i && (uintptr_t)membase < (uintptr_t)objspace->heap.sorted[i-1])
01064 i--;
01065 while (i < heaps_used && (uintptr_t)objspace->heap.sorted[i] <= (uintptr_t)membase)
01066 i++;
01067 if (heaps_used <= i)
01068 break;
01069 membase = (RVALUE *)objspace->heap.sorted[i];
01070
01071 pstart = objspace->heap.sorted[i]->start;
01072 pend = pstart + objspace->heap.sorted[i]->limit;
01073
01074 for (; pstart != pend; pstart++) {
01075 if (pstart->as.basic.flags) {
01076 v = (VALUE)pstart;
01077 break;
01078 }
01079 }
01080 if (pstart != pend) {
01081 if ((*args->callback)(pstart, pend, sizeof(RVALUE), args->data)) {
01082 break;
01083 }
01084 }
01085 }
01086 RB_GC_GUARD(v);
01087
01088 return Qnil;
01089 }
01090
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114
01115
01116
01117
01118
01119
01120
01121
01122
01123
01124
01125
01126
01127 void
01128 rb_objspace_each_objects(each_obj_callback *callback, void *data)
01129 {
01130 struct each_obj_args args;
01131 rb_objspace_t *objspace = &rb_objspace;
01132
01133 rest_sweep(objspace);
01134 objspace->flags.dont_lazy_sweep = TRUE;
01135
01136 args.callback = callback;
01137 args.data = data;
01138 rb_ensure(objspace_each_objects, (VALUE)&args, lazy_sweep_enable, Qnil);
01139 }
01140
01141 struct os_each_struct {
01142 size_t num;
01143 VALUE of;
01144 };
01145
01146 static int
01147 internal_object_p(VALUE obj)
01148 {
01149 RVALUE *p = (RVALUE *)obj;
01150
01151 if (p->as.basic.flags) {
01152 switch (BUILTIN_TYPE(p)) {
01153 case T_NONE:
01154 case T_ICLASS:
01155 case T_NODE:
01156 case T_ZOMBIE:
01157 break;
01158 case T_CLASS:
01159 if (FL_TEST(p, FL_SINGLETON))
01160 break;
01161 default:
01162 if (!p->as.basic.klass) break;
01163 return 0;
01164 }
01165 }
01166 return 1;
01167 }
01168
01169 int
01170 rb_objspace_internal_object_p(VALUE obj)
01171 {
01172 return internal_object_p(obj);
01173 }
01174
01175 static int
01176 os_obj_of_i(void *vstart, void *vend, size_t stride, void *data)
01177 {
01178 struct os_each_struct *oes = (struct os_each_struct *)data;
01179 RVALUE *p = (RVALUE *)vstart, *pend = (RVALUE *)vend;
01180
01181 for (; p != pend; p++) {
01182 volatile VALUE v = (VALUE)p;
01183 if (!internal_object_p(v)) {
01184 if (!oes->of || rb_obj_is_kind_of(v, oes->of)) {
01185 rb_yield(v);
01186 oes->num++;
01187 }
01188 }
01189 }
01190
01191 return 0;
01192 }
01193
01194 static VALUE
01195 os_obj_of(VALUE of)
01196 {
01197 struct os_each_struct oes;
01198
01199 oes.num = 0;
01200 oes.of = of;
01201 rb_objspace_each_objects(os_obj_of_i, &oes);
01202 return SIZET2NUM(oes.num);
01203 }
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01219
01220
01221
01222
01223
01224
01225
01226
01227
01228
01229
01230
01231
01232
01233
01234
01235
01236
01237
01238
01239
01240
01241 static VALUE
01242 os_each_obj(int argc, VALUE *argv, VALUE os)
01243 {
01244 VALUE of;
01245
01246 rb_secure(4);
01247 if (argc == 0) {
01248 of = 0;
01249 }
01250 else {
01251 rb_scan_args(argc, argv, "01", &of);
01252 }
01253 RETURN_ENUMERATOR(os, 1, &of);
01254 return os_obj_of(of);
01255 }
01256
01257
01258
01259
01260
01261
01262
01263
01264
01265 static VALUE
01266 undefine_final(VALUE os, VALUE obj)
01267 {
01268 return rb_undefine_final(obj);
01269 }
01270
01271 VALUE
01272 rb_undefine_final(VALUE obj)
01273 {
01274 rb_objspace_t *objspace = &rb_objspace;
01275 st_data_t data = obj;
01276 rb_check_frozen(obj);
01277 st_delete(finalizer_table, &data, 0);
01278 FL_UNSET(obj, FL_FINALIZE);
01279 return obj;
01280 }
01281
01282
01283
01284
01285
01286
01287
01288
01289
01290
01291 static VALUE
01292 define_final(int argc, VALUE *argv, VALUE os)
01293 {
01294 VALUE obj, block;
01295
01296 rb_scan_args(argc, argv, "11", &obj, &block);
01297 rb_check_frozen(obj);
01298 if (argc == 1) {
01299 block = rb_block_proc();
01300 }
01301 else if (!rb_respond_to(block, rb_intern("call"))) {
01302 rb_raise(rb_eArgError, "wrong type argument %s (should be callable)",
01303 rb_obj_classname(block));
01304 }
01305
01306 return define_final0(obj, block);
01307 }
01308
01309 static VALUE
01310 define_final0(VALUE obj, VALUE block)
01311 {
01312 rb_objspace_t *objspace = &rb_objspace;
01313 VALUE table;
01314 st_data_t data;
01315
01316 if (!FL_ABLE(obj)) {
01317 rb_raise(rb_eArgError, "cannot define finalizer for %s",
01318 rb_obj_classname(obj));
01319 }
01320 RBASIC(obj)->flags |= FL_FINALIZE;
01321
01322 block = rb_ary_new3(2, INT2FIX(rb_safe_level()), block);
01323 OBJ_FREEZE(block);
01324
01325 if (st_lookup(finalizer_table, obj, &data)) {
01326 table = (VALUE)data;
01327 rb_ary_push(table, block);
01328 }
01329 else {
01330 table = rb_ary_new3(1, block);
01331 RBASIC(table)->klass = 0;
01332 st_add_direct(finalizer_table, obj, table);
01333 }
01334 return block;
01335 }
01336
01337 VALUE
01338 rb_define_final(VALUE obj, VALUE block)
01339 {
01340 rb_check_frozen(obj);
01341 if (!rb_respond_to(block, rb_intern("call"))) {
01342 rb_raise(rb_eArgError, "wrong type argument %s (should be callable)",
01343 rb_obj_classname(block));
01344 }
01345 return define_final0(obj, block);
01346 }
01347
01348 void
01349 rb_gc_copy_finalizer(VALUE dest, VALUE obj)
01350 {
01351 rb_objspace_t *objspace = &rb_objspace;
01352 VALUE table;
01353 st_data_t data;
01354
01355 if (!FL_TEST(obj, FL_FINALIZE)) return;
01356 if (st_lookup(finalizer_table, obj, &data)) {
01357 table = (VALUE)data;
01358 st_insert(finalizer_table, dest, table);
01359 }
01360 FL_SET(dest, FL_FINALIZE);
01361 }
01362
01363 static VALUE
01364 run_single_final(VALUE arg)
01365 {
01366 VALUE *args = (VALUE *)arg;
01367 rb_eval_cmd(args[0], args[1], (int)args[2]);
01368 return Qnil;
01369 }
01370
01371 static void
01372 run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table)
01373 {
01374 long i;
01375 int status;
01376 VALUE args[3];
01377 VALUE objid = nonspecial_obj_id(obj);
01378 VALUE saved_errinfo = rb_errinfo();
01379
01380 if (RARRAY_LEN(table) > 0) {
01381 args[1] = rb_obj_freeze(rb_ary_new3(1, objid));
01382 }
01383 else {
01384 args[1] = 0;
01385 }
01386
01387 args[2] = (VALUE)rb_safe_level();
01388 rb_set_errinfo(Qnil);
01389 for (i=0; i<RARRAY_LEN(table); i++) {
01390 VALUE final = RARRAY_PTR(table)[i];
01391 args[0] = RARRAY_PTR(final)[1];
01392 args[2] = FIX2INT(RARRAY_PTR(final)[0]);
01393 status = 0;
01394 rb_protect(run_single_final, (VALUE)args, &status);
01395 if (status)
01396 rb_set_errinfo(Qnil);
01397 }
01398 GET_THREAD()->errinfo = saved_errinfo;
01399 }
01400
01401 static void
01402 run_final(rb_objspace_t *objspace, VALUE obj)
01403 {
01404 RUBY_DATA_FUNC free_func = 0;
01405 st_data_t key, table;
01406
01407 objspace->heap.final_num--;
01408
01409 RBASIC(obj)->klass = 0;
01410
01411 if (RTYPEDDATA_P(obj)) {
01412 free_func = RTYPEDDATA_TYPE(obj)->function.dfree;
01413 }
01414 else {
01415 free_func = RDATA(obj)->dfree;
01416 }
01417 if (free_func) {
01418 (*free_func)(DATA_PTR(obj));
01419 }
01420
01421 key = (st_data_t)obj;
01422 if (st_delete(finalizer_table, &key, &table)) {
01423 run_finalizer(objspace, obj, (VALUE)table);
01424 }
01425 }
01426
01427 static void
01428 finalize_list(rb_objspace_t *objspace, RVALUE *p)
01429 {
01430 while (p) {
01431 RVALUE *tmp = p->as.free.next;
01432 run_final(objspace, (VALUE)p);
01433 objspace->total_freed_object_num++;
01434 if (!FL_TEST(p, FL_SINGLETON)) {
01435 add_slot_local_freelist(objspace, p);
01436 objspace->heap.free_num++;
01437 }
01438 else {
01439 struct heaps_slot *slot = (struct heaps_slot *)(VALUE)RDATA(p)->dmark;
01440 slot->header->limit--;
01441 }
01442 p = tmp;
01443 }
01444 }
01445
01446 static void
01447 finalize_deferred(rb_objspace_t *objspace)
01448 {
01449 RVALUE *p;
01450
01451 while ((p = ATOMIC_PTR_EXCHANGE(deferred_final_list, 0)) != 0) {
01452 finalize_list(objspace, p);
01453 }
01454 }
01455
01456 void
01457 rb_gc_finalize_deferred(void)
01458 {
01459 rb_objspace_t *objspace = &rb_objspace;
01460 if (ATOMIC_EXCHANGE(finalizing, 1)) return;
01461 finalize_deferred(objspace);
01462 ATOMIC_SET(finalizing, 0);
01463 }
01464
01465 struct force_finalize_list {
01466 VALUE obj;
01467 VALUE table;
01468 struct force_finalize_list *next;
01469 };
01470
01471 static int
01472 force_chain_object(st_data_t key, st_data_t val, st_data_t arg)
01473 {
01474 struct force_finalize_list **prev = (struct force_finalize_list **)arg;
01475 struct force_finalize_list *curr = ALLOC(struct force_finalize_list);
01476 curr->obj = key;
01477 curr->table = val;
01478 curr->next = *prev;
01479 *prev = curr;
01480 return ST_CONTINUE;
01481 }
01482
01483 void
01484 rb_gc_call_finalizer_at_exit(void)
01485 {
01486 rb_objspace_call_finalizer(&rb_objspace);
01487 }
01488
01489 static void
01490 rb_objspace_call_finalizer(rb_objspace_t *objspace)
01491 {
01492 RVALUE *p, *pend;
01493 RVALUE *final_list = 0;
01494 size_t i;
01495
01496 rest_sweep(objspace);
01497
01498 if (ATOMIC_EXCHANGE(finalizing, 1)) return;
01499
01500
01501 finalize_deferred(objspace);
01502 assert(deferred_final_list == 0);
01503
01504
01505 while (finalizer_table->num_entries) {
01506 struct force_finalize_list *list = 0;
01507 st_foreach(finalizer_table, force_chain_object, (st_data_t)&list);
01508 while (list) {
01509 struct force_finalize_list *curr = list;
01510 st_data_t obj = (st_data_t)curr->obj;
01511 run_finalizer(objspace, curr->obj, curr->table);
01512 st_delete(finalizer_table, &obj, 0);
01513 list = curr->next;
01514 xfree(curr);
01515 }
01516 }
01517
01518
01519 during_gc++;
01520
01521
01522 for (i = 0; i < heaps_used; i++) {
01523 p = objspace->heap.sorted[i]->start; pend = p + objspace->heap.sorted[i]->limit;
01524 while (p < pend) {
01525 if (BUILTIN_TYPE(p) == T_DATA &&
01526 DATA_PTR(p) && RANY(p)->as.data.dfree &&
01527 !rb_obj_is_thread((VALUE)p) && !rb_obj_is_mutex((VALUE)p) &&
01528 !rb_obj_is_fiber((VALUE)p)) {
01529 p->as.free.flags = 0;
01530 if (RTYPEDDATA_P(p)) {
01531 RDATA(p)->dfree = RANY(p)->as.typeddata.type->function.dfree;
01532 }
01533 if (RANY(p)->as.data.dfree == (RUBY_DATA_FUNC)-1) {
01534 xfree(DATA_PTR(p));
01535 }
01536 else if (RANY(p)->as.data.dfree) {
01537 make_deferred(RANY(p));
01538 RANY(p)->as.free.next = final_list;
01539 final_list = p;
01540 }
01541 }
01542 else if (BUILTIN_TYPE(p) == T_FILE) {
01543 if (RANY(p)->as.file.fptr) {
01544 make_io_deferred(RANY(p));
01545 RANY(p)->as.free.next = final_list;
01546 final_list = p;
01547 }
01548 }
01549 p++;
01550 }
01551 }
01552 during_gc = 0;
01553 if (final_list) {
01554 finalize_list(objspace, final_list);
01555 }
01556
01557 st_free_table(finalizer_table);
01558 finalizer_table = 0;
01559 ATOMIC_SET(finalizing, 0);
01560 }
01561
01562 static inline int
01563 is_id_value(rb_objspace_t *objspace, VALUE ptr)
01564 {
01565 if (!is_pointer_to_heap(objspace, (void *)ptr)) return FALSE;
01566 if (BUILTIN_TYPE(ptr) > T_FIXNUM) return FALSE;
01567 if (BUILTIN_TYPE(ptr) == T_ICLASS) return FALSE;
01568 return TRUE;
01569 }
01570
01571 static inline int
01572 is_swept_object(rb_objspace_t *objspace, VALUE ptr)
01573 {
01574 struct heaps_slot *slot = objspace->heap.sweep_slots;
01575
01576 while (slot) {
01577 if ((VALUE)slot->header->start <= ptr && ptr < (VALUE)(slot->header->end))
01578 return FALSE;
01579 slot = slot->next;
01580 }
01581 return TRUE;
01582 }
01583
01584 static inline int
01585 is_dead_object(rb_objspace_t *objspace, VALUE ptr)
01586 {
01587 if (!is_lazy_sweeping(objspace) || MARKED_IN_BITMAP(GET_HEAP_BITMAP(ptr), ptr))
01588 return FALSE;
01589 if (!is_swept_object(objspace, ptr))
01590 return TRUE;
01591 return FALSE;
01592 }
01593
01594 static inline int
01595 is_live_object(rb_objspace_t *objspace, VALUE ptr)
01596 {
01597 if (BUILTIN_TYPE(ptr) == 0) return FALSE;
01598 if (RBASIC(ptr)->klass == 0) return FALSE;
01599 if (is_dead_object(objspace, ptr)) return FALSE;
01600 return TRUE;
01601 }
01602
01603
01604
01605
01606
01607
01608
01609
01610
01611
01612
01613
01614
01615
01616 static VALUE
01617 id2ref(VALUE obj, VALUE objid)
01618 {
01619 #if SIZEOF_LONG == SIZEOF_VOIDP
01620 #define NUM2PTR(x) NUM2ULONG(x)
01621 #elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
01622 #define NUM2PTR(x) NUM2ULL(x)
01623 #endif
01624 rb_objspace_t *objspace = &rb_objspace;
01625 VALUE ptr;
01626 void *p0;
01627
01628 rb_secure(4);
01629 ptr = NUM2PTR(objid);
01630 p0 = (void *)ptr;
01631
01632 if (ptr == Qtrue) return Qtrue;
01633 if (ptr == Qfalse) return Qfalse;
01634 if (ptr == Qnil) return Qnil;
01635 if (FIXNUM_P(ptr)) return (VALUE)ptr;
01636 if (FLONUM_P(ptr)) return (VALUE)ptr;
01637 ptr = obj_id_to_ref(objid);
01638
01639 if ((ptr % sizeof(RVALUE)) == (4 << 2)) {
01640 ID symid = ptr / sizeof(RVALUE);
01641 if (rb_id2name(symid) == 0)
01642 rb_raise(rb_eRangeError, "%p is not symbol id value", p0);
01643 return ID2SYM(symid);
01644 }
01645
01646 if (!is_id_value(objspace, ptr)) {
01647 rb_raise(rb_eRangeError, "%p is not id value", p0);
01648 }
01649 if (!is_live_object(objspace, ptr)) {
01650 rb_raise(rb_eRangeError, "%p is recycled object", p0);
01651 }
01652 return (VALUE)ptr;
01653 }
01654
01655
01656
01657
01658
01659
01660
01661
01662
01663
01664
01665
01666
01667
01668
01669
01670
01671
01672
01673
01674
01675
01676
01677
01678
01679
01680
01681
01682
01683
01684
01685
01686
01687
01688
01689 VALUE
01690 rb_obj_id(VALUE obj)
01691 {
01692
01693
01694
01695
01696
01697
01698
01699
01700
01701
01702
01703
01704
01705
01706
01707
01708
01709
01710
01711
01712
01713
01714
01715
01716
01717
01718
01719
01720 if (SYMBOL_P(obj)) {
01721 return (SYM2ID(obj) * sizeof(RVALUE) + (4 << 2)) | FIXNUM_FLAG;
01722 }
01723 else if (FLONUM_P(obj)) {
01724 #if SIZEOF_LONG == SIZEOF_VOIDP
01725 return LONG2NUM((SIGNED_VALUE)obj);
01726 #else
01727 return LL2NUM((SIGNED_VALUE)obj);
01728 #endif
01729 }
01730 else if (SPECIAL_CONST_P(obj)) {
01731 return LONG2NUM((SIGNED_VALUE)obj);
01732 }
01733 return nonspecial_obj_id(obj);
01734 }
01735
01736 static int
01737 set_zero(st_data_t key, st_data_t val, st_data_t arg)
01738 {
01739 VALUE k = (VALUE)key;
01740 VALUE hash = (VALUE)arg;
01741 rb_hash_aset(hash, k, INT2FIX(0));
01742 return ST_CONTINUE;
01743 }
01744
01745
01746
01747
01748
01749
01750
01751
01752
01753
01754
01755
01756
01757
01758
01759
01760
01761
01762
01763
01764
01765
01766
01767
01768
01769
01770 static VALUE
01771 count_objects(int argc, VALUE *argv, VALUE os)
01772 {
01773 rb_objspace_t *objspace = &rb_objspace;
01774 size_t counts[T_MASK+1];
01775 size_t freed = 0;
01776 size_t total = 0;
01777 size_t i;
01778 VALUE hash;
01779
01780 if (rb_scan_args(argc, argv, "01", &hash) == 1) {
01781 if (!RB_TYPE_P(hash, T_HASH))
01782 rb_raise(rb_eTypeError, "non-hash given");
01783 }
01784
01785 for (i = 0; i <= T_MASK; i++) {
01786 counts[i] = 0;
01787 }
01788
01789 for (i = 0; i < heaps_used; i++) {
01790 RVALUE *p, *pend;
01791
01792 p = objspace->heap.sorted[i]->start; pend = p + objspace->heap.sorted[i]->limit;
01793 for (;p < pend; p++) {
01794 if (p->as.basic.flags) {
01795 counts[BUILTIN_TYPE(p)]++;
01796 }
01797 else {
01798 freed++;
01799 }
01800 }
01801 total += objspace->heap.sorted[i]->limit;
01802 }
01803
01804 if (hash == Qnil) {
01805 hash = rb_hash_new();
01806 }
01807 else if (!RHASH_EMPTY_P(hash)) {
01808 st_foreach(RHASH_TBL(hash), set_zero, hash);
01809 }
01810 rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
01811 rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(freed));
01812
01813 for (i = 0; i <= T_MASK; i++) {
01814 VALUE type;
01815 switch (i) {
01816 #define COUNT_TYPE(t) case (t): type = ID2SYM(rb_intern(#t)); break;
01817 COUNT_TYPE(T_NONE);
01818 COUNT_TYPE(T_OBJECT);
01819 COUNT_TYPE(T_CLASS);
01820 COUNT_TYPE(T_MODULE);
01821 COUNT_TYPE(T_FLOAT);
01822 COUNT_TYPE(T_STRING);
01823 COUNT_TYPE(T_REGEXP);
01824 COUNT_TYPE(T_ARRAY);
01825 COUNT_TYPE(T_HASH);
01826 COUNT_TYPE(T_STRUCT);
01827 COUNT_TYPE(T_BIGNUM);
01828 COUNT_TYPE(T_FILE);
01829 COUNT_TYPE(T_DATA);
01830 COUNT_TYPE(T_MATCH);
01831 COUNT_TYPE(T_COMPLEX);
01832 COUNT_TYPE(T_RATIONAL);
01833 COUNT_TYPE(T_NIL);
01834 COUNT_TYPE(T_TRUE);
01835 COUNT_TYPE(T_FALSE);
01836 COUNT_TYPE(T_SYMBOL);
01837 COUNT_TYPE(T_FIXNUM);
01838 COUNT_TYPE(T_UNDEF);
01839 COUNT_TYPE(T_NODE);
01840 COUNT_TYPE(T_ICLASS);
01841 COUNT_TYPE(T_ZOMBIE);
01842 #undef COUNT_TYPE
01843 default: type = INT2NUM(i); break;
01844 }
01845 if (counts[i])
01846 rb_hash_aset(hash, type, SIZET2NUM(counts[i]));
01847 }
01848
01849 return hash;
01850 }
01851
01852
01853
01854
01855
01856
01857
01858
01859
01860 static VALUE
01861 lazy_sweep_enable(void)
01862 {
01863 rb_objspace_t *objspace = &rb_objspace;
01864
01865 objspace->flags.dont_lazy_sweep = FALSE;
01866 return Qnil;
01867 }
01868
01869 static void
01870 gc_clear_slot_bits(struct heaps_slot *slot)
01871 {
01872 memset(slot->bits, 0, HEAP_BITMAP_LIMIT * sizeof(uintptr_t));
01873 }
01874
01875 static size_t
01876 objspace_live_num(rb_objspace_t *objspace)
01877 {
01878 return objspace->total_allocated_object_num - objspace->total_freed_object_num;
01879 }
01880
01881 static void
01882 slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
01883 {
01884 size_t empty_num = 0, freed_num = 0, final_num = 0;
01885 RVALUE *p, *pend;
01886 RVALUE *final = deferred_final_list;
01887 int deferred;
01888 uintptr_t *bits;
01889
01890 p = sweep_slot->header->start; pend = p + sweep_slot->header->limit;
01891 bits = GET_HEAP_BITMAP(p);
01892 while (p < pend) {
01893 if ((!(MARKED_IN_BITMAP(bits, p))) && BUILTIN_TYPE(p) != T_ZOMBIE) {
01894 if (p->as.basic.flags) {
01895 if ((deferred = obj_free(objspace, (VALUE)p)) ||
01896 (FL_TEST(p, FL_FINALIZE))) {
01897 if (!deferred) {
01898 p->as.free.flags = T_ZOMBIE;
01899 RDATA(p)->dfree = 0;
01900 }
01901 p->as.free.next = deferred_final_list;
01902 deferred_final_list = p;
01903 assert(BUILTIN_TYPE(p) == T_ZOMBIE);
01904 final_num++;
01905 }
01906 else {
01907 (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)p, sizeof(RVALUE));
01908 p->as.free.flags = 0;
01909 p->as.free.next = sweep_slot->freelist;
01910 sweep_slot->freelist = p;
01911 freed_num++;
01912 }
01913 }
01914 else {
01915 empty_num++;
01916 }
01917 }
01918 p++;
01919 }
01920 gc_clear_slot_bits(sweep_slot);
01921 if (final_num + freed_num + empty_num == sweep_slot->header->limit &&
01922 objspace->heap.free_num > objspace->heap.do_heap_free) {
01923 RVALUE *pp;
01924
01925 for (pp = deferred_final_list; pp != final; pp = pp->as.free.next) {
01926 RDATA(pp)->dmark = (void (*)(void *))(VALUE)sweep_slot;
01927 pp->as.free.flags |= FL_SINGLETON;
01928 }
01929 sweep_slot->header->limit = final_num;
01930 unlink_heap_slot(objspace, sweep_slot);
01931 }
01932 else {
01933 if (freed_num + empty_num > 0) {
01934 link_free_heap_slot(objspace, sweep_slot);
01935 }
01936 else {
01937 sweep_slot->free_next = NULL;
01938 }
01939 objspace->heap.free_num += freed_num + empty_num;
01940 }
01941 objspace->total_freed_object_num += freed_num;
01942 objspace->heap.final_num += final_num;
01943
01944 if (deferred_final_list && !finalizing) {
01945 rb_thread_t *th = GET_THREAD();
01946 if (th) {
01947 RUBY_VM_SET_FINALIZER_INTERRUPT(th);
01948 }
01949 }
01950 }
01951
01952 static int
01953 ready_to_gc(rb_objspace_t *objspace)
01954 {
01955 if (dont_gc || during_gc) {
01956 if (!has_free_object) {
01957 if (!heaps_increment(objspace)) {
01958 set_heaps_increment(objspace);
01959 heaps_increment(objspace);
01960 }
01961 }
01962 return FALSE;
01963 }
01964 return TRUE;
01965 }
01966
01967 static void
01968 before_gc_sweep(rb_objspace_t *objspace)
01969 {
01970 objspace->heap.do_heap_free = (size_t)((heaps_used * HEAP_OBJ_LIMIT) * 0.65);
01971 objspace->heap.free_min = (size_t)((heaps_used * HEAP_OBJ_LIMIT) * 0.2);
01972 if (objspace->heap.free_min < initial_free_min) {
01973 objspace->heap.free_min = initial_free_min;
01974 if (objspace->heap.do_heap_free < initial_free_min)
01975 objspace->heap.do_heap_free = initial_free_min;
01976 }
01977 objspace->heap.sweep_slots = heaps;
01978 objspace->heap.free_num = 0;
01979 objspace->heap.free_slots = NULL;
01980
01981
01982 if (GET_VM()->unlinked_method_entry_list) {
01983 rb_sweep_method_entry(GET_VM());
01984 }
01985 }
01986
01987 static void
01988 after_gc_sweep(rb_objspace_t *objspace)
01989 {
01990 size_t inc;
01991
01992 gc_prof_set_malloc_info(objspace);
01993 if (objspace->heap.free_num < objspace->heap.free_min) {
01994 set_heaps_increment(objspace);
01995 heaps_increment(objspace);
01996 }
01997
01998 inc = ATOMIC_SIZE_EXCHANGE(malloc_increase, 0);
01999 if (inc > malloc_limit) {
02000 malloc_limit +=
02001 (size_t)((inc - malloc_limit) * (double)objspace->heap.marked_num / (heaps_used * HEAP_OBJ_LIMIT));
02002 if (malloc_limit < initial_malloc_limit) malloc_limit = initial_malloc_limit;
02003 }
02004
02005 free_unused_heaps(objspace);
02006 }
02007
02008 static int
02009 lazy_sweep(rb_objspace_t *objspace)
02010 {
02011 struct heaps_slot *next;
02012
02013 heaps_increment(objspace);
02014 while (objspace->heap.sweep_slots) {
02015 next = objspace->heap.sweep_slots->next;
02016 slot_sweep(objspace, objspace->heap.sweep_slots);
02017 objspace->heap.sweep_slots = next;
02018 if (has_free_object) {
02019 during_gc = 0;
02020 return TRUE;
02021 }
02022 }
02023 return FALSE;
02024 }
02025
02026 static void
02027 rest_sweep(rb_objspace_t *objspace)
02028 {
02029 if (objspace->heap.sweep_slots) {
02030 while (objspace->heap.sweep_slots) {
02031 lazy_sweep(objspace);
02032 }
02033 after_gc_sweep(objspace);
02034 }
02035 }
02036
02037 static void gc_marks(rb_objspace_t *objspace);
02038
02039 static int
02040 gc_prepare_free_objects(rb_objspace_t *objspace)
02041 {
02042 int res;
02043
02044 if (objspace->flags.dont_lazy_sweep)
02045 return garbage_collect(objspace);
02046
02047
02048 if (!ready_to_gc(objspace)) return TRUE;
02049
02050 during_gc++;
02051 gc_prof_timer_start(objspace);
02052 gc_prof_sweep_timer_start(objspace);
02053
02054 if (objspace->heap.sweep_slots) {
02055 res = lazy_sweep(objspace);
02056 if (res) {
02057 gc_prof_sweep_timer_stop(objspace);
02058 gc_prof_set_malloc_info(objspace);
02059 gc_prof_timer_stop(objspace, Qfalse);
02060 return res;
02061 }
02062 after_gc_sweep(objspace);
02063 }
02064 else {
02065 if (heaps_increment(objspace)) {
02066 during_gc = 0;
02067 return TRUE;
02068 }
02069 }
02070
02071 gc_marks(objspace);
02072
02073 before_gc_sweep(objspace);
02074 if (objspace->heap.free_min > (heaps_used * HEAP_OBJ_LIMIT - objspace->heap.marked_num)) {
02075 set_heaps_increment(objspace);
02076 }
02077
02078 gc_prof_sweep_timer_start(objspace);
02079 if (!(res = lazy_sweep(objspace))) {
02080 after_gc_sweep(objspace);
02081 if (has_free_object) {
02082 res = TRUE;
02083 during_gc = 0;
02084 }
02085 }
02086 gc_prof_sweep_timer_stop(objspace);
02087
02088 gc_prof_timer_stop(objspace, Qtrue);
02089 return res;
02090 }
02091
02092 static void
02093 gc_sweep(rb_objspace_t *objspace)
02094 {
02095 struct heaps_slot *next;
02096
02097 before_gc_sweep(objspace);
02098
02099 while (objspace->heap.sweep_slots) {
02100 next = objspace->heap.sweep_slots->next;
02101 slot_sweep(objspace, objspace->heap.sweep_slots);
02102 objspace->heap.sweep_slots = next;
02103 }
02104
02105 after_gc_sweep(objspace);
02106
02107 during_gc = 0;
02108 }
02109
02110
02111
02112 static void push_mark_stack(mark_stack_t *, VALUE);
02113 static int pop_mark_stack(mark_stack_t *, VALUE *);
02114 static void shrink_stack_chunk_cache(mark_stack_t *stack);
02115
02116 static stack_chunk_t *
02117 stack_chunk_alloc(void)
02118 {
02119 stack_chunk_t *res;
02120
02121 res = malloc(sizeof(stack_chunk_t));
02122 if (!res)
02123 rb_memerror();
02124
02125 return res;
02126 }
02127
02128 static inline int
02129 is_mark_stask_empty(mark_stack_t *stack)
02130 {
02131 return stack->chunk == NULL;
02132 }
02133
02134 static void
02135 add_stack_chunk_cache(mark_stack_t *stack, stack_chunk_t *chunk)
02136 {
02137 chunk->next = stack->cache;
02138 stack->cache = chunk;
02139 stack->cache_size++;
02140 }
02141
02142 static void
02143 shrink_stack_chunk_cache(mark_stack_t *stack)
02144 {
02145 stack_chunk_t *chunk;
02146
02147 if (stack->unused_cache_size > (stack->cache_size/2)) {
02148 chunk = stack->cache;
02149 stack->cache = stack->cache->next;
02150 stack->cache_size--;
02151 free(chunk);
02152 }
02153 stack->unused_cache_size = stack->cache_size;
02154 }
02155
02156 static void
02157 push_mark_stack_chunk(mark_stack_t *stack)
02158 {
02159 stack_chunk_t *next;
02160
02161 assert(stack->index == stack->limit);
02162 if (stack->cache_size > 0) {
02163 next = stack->cache;
02164 stack->cache = stack->cache->next;
02165 stack->cache_size--;
02166 if (stack->unused_cache_size > stack->cache_size)
02167 stack->unused_cache_size = stack->cache_size;
02168 }
02169 else {
02170 next = stack_chunk_alloc();
02171 }
02172 next->next = stack->chunk;
02173 stack->chunk = next;
02174 stack->index = 0;
02175 }
02176
02177 static void
02178 pop_mark_stack_chunk(mark_stack_t *stack)
02179 {
02180 stack_chunk_t *prev;
02181
02182 prev = stack->chunk->next;
02183 assert(stack->index == 0);
02184 add_stack_chunk_cache(stack, stack->chunk);
02185 stack->chunk = prev;
02186 stack->index = stack->limit;
02187 }
02188
02189 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
02190 static void
02191 free_stack_chunks(mark_stack_t *stack)
02192 {
02193 stack_chunk_t *chunk = stack->chunk;
02194 stack_chunk_t *next = NULL;
02195
02196 while (chunk != NULL) {
02197 next = chunk->next;
02198 free(chunk);
02199 chunk = next;
02200 }
02201 }
02202 #endif
02203
02204 static void
02205 push_mark_stack(mark_stack_t *stack, VALUE data)
02206 {
02207 if (stack->index == stack->limit) {
02208 push_mark_stack_chunk(stack);
02209 }
02210 stack->chunk->data[stack->index++] = data;
02211 }
02212
02213 static int
02214 pop_mark_stack(mark_stack_t *stack, VALUE *data)
02215 {
02216 if (is_mark_stask_empty(stack)) {
02217 return FALSE;
02218 }
02219 if (stack->index == 1) {
02220 *data = stack->chunk->data[--stack->index];
02221 pop_mark_stack_chunk(stack);
02222 return TRUE;
02223 }
02224 *data = stack->chunk->data[--stack->index];
02225 return TRUE;
02226 }
02227
02228 static void
02229 init_mark_stack(mark_stack_t *stack)
02230 {
02231 int i;
02232
02233 push_mark_stack_chunk(stack);
02234 stack->limit = STACK_CHUNK_SIZE;
02235
02236 for (i=0; i < 4; i++) {
02237 add_stack_chunk_cache(stack, stack_chunk_alloc());
02238 }
02239 stack->unused_cache_size = stack->cache_size;
02240 }
02241
02242
02243
02244
02245 #define MARK_IN_BITMAP(bits, p) (bits[BITMAP_INDEX(p)] = bits[BITMAP_INDEX(p)] | ((uintptr_t)1 << BITMAP_OFFSET(p)))
02246
02247
02248 #ifdef __ia64
02249 #define SET_STACK_END (SET_MACHINE_STACK_END(&th->machine_stack_end), th->machine_register_stack_end = rb_ia64_bsp())
02250 #else
02251 #define SET_STACK_END SET_MACHINE_STACK_END(&th->machine_stack_end)
02252 #endif
02253
02254 #define STACK_START (th->machine_stack_start)
02255 #define STACK_END (th->machine_stack_end)
02256 #define STACK_LEVEL_MAX (th->machine_stack_maxsize/sizeof(VALUE))
02257
02258 #if STACK_GROW_DIRECTION < 0
02259 # define STACK_LENGTH (size_t)(STACK_START - STACK_END)
02260 #elif STACK_GROW_DIRECTION > 0
02261 # define STACK_LENGTH (size_t)(STACK_END - STACK_START + 1)
02262 #else
02263 # define STACK_LENGTH ((STACK_END < STACK_START) ? (size_t)(STACK_START - STACK_END) \
02264 : (size_t)(STACK_END - STACK_START + 1))
02265 #endif
02266 #if !STACK_GROW_DIRECTION
02267 int ruby_stack_grow_direction;
02268 int
02269 ruby_get_stack_grow_direction(volatile VALUE *addr)
02270 {
02271 VALUE *end;
02272 SET_MACHINE_STACK_END(&end);
02273
02274 if (end > addr) return ruby_stack_grow_direction = 1;
02275 return ruby_stack_grow_direction = -1;
02276 }
02277 #endif
02278
02279 size_t
02280 ruby_stack_length(VALUE **p)
02281 {
02282 rb_thread_t *th = GET_THREAD();
02283 SET_STACK_END;
02284 if (p) *p = STACK_UPPER(STACK_END, STACK_START, STACK_END);
02285 return STACK_LENGTH;
02286 }
02287
02288 #if !(defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK))
02289 static int
02290 stack_check(int water_mark)
02291 {
02292 int ret;
02293 rb_thread_t *th = GET_THREAD();
02294 SET_STACK_END;
02295 ret = STACK_LENGTH > STACK_LEVEL_MAX - water_mark;
02296 #ifdef __ia64
02297 if (!ret) {
02298 ret = (VALUE*)rb_ia64_bsp() - th->machine_register_stack_start >
02299 th->machine_register_stack_maxsize/sizeof(VALUE) - water_mark;
02300 }
02301 #endif
02302 return ret;
02303 }
02304 #endif
02305
02306 #define STACKFRAME_FOR_CALL_CFUNC 512
02307
02308 int
02309 ruby_stack_check(void)
02310 {
02311 #if defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK)
02312 return 0;
02313 #else
02314 return stack_check(STACKFRAME_FOR_CALL_CFUNC);
02315 #endif
02316 }
02317
02318 static void
02319 mark_locations_array(rb_objspace_t *objspace, register VALUE *x, register long n)
02320 {
02321 VALUE v;
02322 while (n--) {
02323 v = *x;
02324 (void)VALGRIND_MAKE_MEM_DEFINED(&v, sizeof(v));
02325 if (is_pointer_to_heap(objspace, (void *)v)) {
02326 gc_mark(objspace, v);
02327 }
02328 x++;
02329 }
02330 }
02331
02332 static void
02333 gc_mark_locations(rb_objspace_t *objspace, VALUE *start, VALUE *end)
02334 {
02335 long n;
02336
02337 if (end <= start) return;
02338 n = end - start;
02339 mark_locations_array(objspace, start, n);
02340 }
02341
02342 void
02343 rb_gc_mark_locations(VALUE *start, VALUE *end)
02344 {
02345 gc_mark_locations(&rb_objspace, start, end);
02346 }
02347
02348 #define rb_gc_mark_locations(start, end) gc_mark_locations(objspace, (start), (end))
02349
02350 struct mark_tbl_arg {
02351 rb_objspace_t *objspace;
02352 };
02353
02354 static int
02355 mark_entry(st_data_t key, st_data_t value, st_data_t data)
02356 {
02357 struct mark_tbl_arg *arg = (void*)data;
02358 gc_mark(arg->objspace, (VALUE)value);
02359 return ST_CONTINUE;
02360 }
02361
02362 static void
02363 mark_tbl(rb_objspace_t *objspace, st_table *tbl)
02364 {
02365 struct mark_tbl_arg arg;
02366 if (!tbl || tbl->num_entries == 0) return;
02367 arg.objspace = objspace;
02368 st_foreach(tbl, mark_entry, (st_data_t)&arg);
02369 }
02370
02371 static int
02372 mark_key(st_data_t key, st_data_t value, st_data_t data)
02373 {
02374 struct mark_tbl_arg *arg = (void*)data;
02375 gc_mark(arg->objspace, (VALUE)key);
02376 return ST_CONTINUE;
02377 }
02378
02379 static void
02380 mark_set(rb_objspace_t *objspace, st_table *tbl)
02381 {
02382 struct mark_tbl_arg arg;
02383 if (!tbl) return;
02384 arg.objspace = objspace;
02385 st_foreach(tbl, mark_key, (st_data_t)&arg);
02386 }
02387
02388 void
02389 rb_mark_set(st_table *tbl)
02390 {
02391 mark_set(&rb_objspace, tbl);
02392 }
02393
02394 static int
02395 mark_keyvalue(st_data_t key, st_data_t value, st_data_t data)
02396 {
02397 struct mark_tbl_arg *arg = (void*)data;
02398 gc_mark(arg->objspace, (VALUE)key);
02399 gc_mark(arg->objspace, (VALUE)value);
02400 return ST_CONTINUE;
02401 }
02402
02403 static void
02404 mark_hash(rb_objspace_t *objspace, st_table *tbl)
02405 {
02406 struct mark_tbl_arg arg;
02407 if (!tbl) return;
02408 arg.objspace = objspace;
02409 st_foreach(tbl, mark_keyvalue, (st_data_t)&arg);
02410 }
02411
02412 void
02413 rb_mark_hash(st_table *tbl)
02414 {
02415 mark_hash(&rb_objspace, tbl);
02416 }
02417
02418 static void
02419 mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
02420 {
02421 const rb_method_definition_t *def = me->def;
02422
02423 gc_mark(objspace, me->klass);
02424 again:
02425 if (!def) return;
02426 switch (def->type) {
02427 case VM_METHOD_TYPE_ISEQ:
02428 gc_mark(objspace, def->body.iseq->self);
02429 break;
02430 case VM_METHOD_TYPE_BMETHOD:
02431 gc_mark(objspace, def->body.proc);
02432 break;
02433 case VM_METHOD_TYPE_ATTRSET:
02434 case VM_METHOD_TYPE_IVAR:
02435 gc_mark(objspace, def->body.attr.location);
02436 break;
02437 case VM_METHOD_TYPE_REFINED:
02438 if (def->body.orig_me) {
02439 def = def->body.orig_me->def;
02440 goto again;
02441 }
02442 break;
02443 default:
02444 break;
02445 }
02446 }
02447
02448 void
02449 rb_mark_method_entry(const rb_method_entry_t *me)
02450 {
02451 mark_method_entry(&rb_objspace, me);
02452 }
02453
02454 static int
02455 mark_method_entry_i(ID key, const rb_method_entry_t *me, st_data_t data)
02456 {
02457 struct mark_tbl_arg *arg = (void*)data;
02458 mark_method_entry(arg->objspace, me);
02459 return ST_CONTINUE;
02460 }
02461
02462 static void
02463 mark_m_tbl(rb_objspace_t *objspace, st_table *tbl)
02464 {
02465 struct mark_tbl_arg arg;
02466 if (!tbl) return;
02467 arg.objspace = objspace;
02468 st_foreach(tbl, mark_method_entry_i, (st_data_t)&arg);
02469 }
02470
02471 static int
02472 mark_const_entry_i(ID key, const rb_const_entry_t *ce, st_data_t data)
02473 {
02474 struct mark_tbl_arg *arg = (void*)data;
02475 gc_mark(arg->objspace, ce->value);
02476 gc_mark(arg->objspace, ce->file);
02477 return ST_CONTINUE;
02478 }
02479
02480 static void
02481 mark_const_tbl(rb_objspace_t *objspace, st_table *tbl)
02482 {
02483 struct mark_tbl_arg arg;
02484 if (!tbl) return;
02485 arg.objspace = objspace;
02486 st_foreach(tbl, mark_const_entry_i, (st_data_t)&arg);
02487 }
02488
02489 #if STACK_GROW_DIRECTION < 0
02490 #define GET_STACK_BOUNDS(start, end, appendix) ((start) = STACK_END, (end) = STACK_START)
02491 #elif STACK_GROW_DIRECTION > 0
02492 #define GET_STACK_BOUNDS(start, end, appendix) ((start) = STACK_START, (end) = STACK_END+(appendix))
02493 #else
02494 #define GET_STACK_BOUNDS(start, end, appendix) \
02495 ((STACK_END < STACK_START) ? \
02496 ((start) = STACK_END, (end) = STACK_START) : ((start) = STACK_START, (end) = STACK_END+(appendix)))
02497 #endif
02498
02499 #define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
02500
02501 static void
02502 mark_current_machine_context(rb_objspace_t *objspace, rb_thread_t *th)
02503 {
02504 union {
02505 rb_jmp_buf j;
02506 VALUE v[sizeof(rb_jmp_buf) / sizeof(VALUE)];
02507 } save_regs_gc_mark;
02508 VALUE *stack_start, *stack_end;
02509
02510 FLUSH_REGISTER_WINDOWS;
02511
02512 rb_setjmp(save_regs_gc_mark.j);
02513
02514 SET_STACK_END;
02515 GET_STACK_BOUNDS(stack_start, stack_end, 1);
02516
02517 mark_locations_array(objspace, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v));
02518
02519 rb_gc_mark_locations(stack_start, stack_end);
02520 #ifdef __ia64
02521 rb_gc_mark_locations(th->machine_register_stack_start, th->machine_register_stack_end);
02522 #endif
02523 #if defined(__mc68000__)
02524 mark_locations_array(objspace, (VALUE*)((char*)STACK_END + 2),
02525 (STACK_START - STACK_END));
02526 #endif
02527 }
02528
02529 void
02530 rb_gc_mark_machine_stack(rb_thread_t *th)
02531 {
02532 rb_objspace_t *objspace = &rb_objspace;
02533 VALUE *stack_start, *stack_end;
02534
02535 GET_STACK_BOUNDS(stack_start, stack_end, 0);
02536 rb_gc_mark_locations(stack_start, stack_end);
02537 #ifdef __ia64
02538 rb_gc_mark_locations(th->machine_register_stack_start, th->machine_register_stack_end);
02539 #endif
02540 }
02541
02542 void
02543 rb_mark_tbl(st_table *tbl)
02544 {
02545 mark_tbl(&rb_objspace, tbl);
02546 }
02547
02548 void
02549 rb_gc_mark_maybe(VALUE obj)
02550 {
02551 if (is_pointer_to_heap(&rb_objspace, (void *)obj)) {
02552 gc_mark(&rb_objspace, obj);
02553 }
02554 }
02555
02556 static int
02557 gc_mark_ptr(rb_objspace_t *objspace, VALUE ptr)
02558 {
02559 register uintptr_t *bits = GET_HEAP_BITMAP(ptr);
02560 if (MARKED_IN_BITMAP(bits, ptr)) return 0;
02561 MARK_IN_BITMAP(bits, ptr);
02562 objspace->heap.marked_num++;
02563 return 1;
02564 }
02565
02566 static int
02567 markable_object_p(rb_objspace_t *objspace, VALUE ptr)
02568 {
02569 register RVALUE *obj = RANY(ptr);
02570
02571 if (rb_special_const_p(ptr)) return 0;
02572 if (obj->as.basic.flags == 0) return 0 ;
02573
02574 return 1;
02575 }
02576
02577 int
02578 rb_objspace_markable_object_p(VALUE obj)
02579 {
02580 return markable_object_p( 0, obj);
02581 }
02582
02583 static void
02584 gc_mark(rb_objspace_t *objspace, VALUE ptr)
02585 {
02586 if (!markable_object_p(objspace, ptr)) {
02587 return;
02588 }
02589
02590 if (LIKELY(objspace->mark_func_data == 0)) {
02591 if (!gc_mark_ptr(objspace, ptr)) return;
02592 push_mark_stack(&objspace->mark_stack, ptr);
02593 }
02594 else {
02595 objspace->mark_func_data->mark_func(ptr, objspace->mark_func_data->data);
02596 }
02597 }
02598
02599 void
02600 rb_gc_mark(VALUE ptr)
02601 {
02602 gc_mark(&rb_objspace, ptr);
02603 }
02604
02605 static void
02606 gc_mark_children(rb_objspace_t *objspace, VALUE ptr)
02607 {
02608 register RVALUE *obj = RANY(ptr);
02609
02610 goto marking;
02611
02612 again:
02613 if (LIKELY(objspace->mark_func_data == 0)) {
02614 obj = RANY(ptr);
02615 if (!markable_object_p(objspace, ptr)) return;
02616 if (!gc_mark_ptr(objspace, ptr)) return;
02617 }
02618 else {
02619 gc_mark(objspace, ptr);
02620 return;
02621 }
02622
02623 marking:
02624 if (FL_TEST(obj, FL_EXIVAR)) {
02625 rb_mark_generic_ivar(ptr);
02626 }
02627
02628 switch (BUILTIN_TYPE(obj)) {
02629 case T_NIL:
02630 case T_FIXNUM:
02631 rb_bug("rb_gc_mark() called for broken object");
02632 break;
02633
02634 case T_NODE:
02635 switch (nd_type(obj)) {
02636 case NODE_IF:
02637 case NODE_FOR:
02638 case NODE_ITER:
02639 case NODE_WHEN:
02640 case NODE_MASGN:
02641 case NODE_RESCUE:
02642 case NODE_RESBODY:
02643 case NODE_CLASS:
02644 case NODE_BLOCK_PASS:
02645 gc_mark(objspace, (VALUE)obj->as.node.u2.node);
02646
02647 case NODE_BLOCK:
02648 case NODE_ARRAY:
02649 case NODE_DSTR:
02650 case NODE_DXSTR:
02651 case NODE_DREGX:
02652 case NODE_DREGX_ONCE:
02653 case NODE_ENSURE:
02654 case NODE_CALL:
02655 case NODE_DEFS:
02656 case NODE_OP_ASGN1:
02657 gc_mark(objspace, (VALUE)obj->as.node.u1.node);
02658
02659 case NODE_SUPER:
02660 case NODE_FCALL:
02661 case NODE_DEFN:
02662 case NODE_ARGS_AUX:
02663 ptr = (VALUE)obj->as.node.u3.node;
02664 goto again;
02665
02666 case NODE_WHILE:
02667 case NODE_UNTIL:
02668 case NODE_AND:
02669 case NODE_OR:
02670 case NODE_CASE:
02671 case NODE_SCLASS:
02672 case NODE_DOT2:
02673 case NODE_DOT3:
02674 case NODE_FLIP2:
02675 case NODE_FLIP3:
02676 case NODE_MATCH2:
02677 case NODE_MATCH3:
02678 case NODE_OP_ASGN_OR:
02679 case NODE_OP_ASGN_AND:
02680 case NODE_MODULE:
02681 case NODE_ALIAS:
02682 case NODE_VALIAS:
02683 case NODE_ARGSCAT:
02684 gc_mark(objspace, (VALUE)obj->as.node.u1.node);
02685
02686 case NODE_GASGN:
02687 case NODE_LASGN:
02688 case NODE_DASGN:
02689 case NODE_DASGN_CURR:
02690 case NODE_IASGN:
02691 case NODE_IASGN2:
02692 case NODE_CVASGN:
02693 case NODE_COLON3:
02694 case NODE_OPT_N:
02695 case NODE_EVSTR:
02696 case NODE_UNDEF:
02697 case NODE_POSTEXE:
02698 ptr = (VALUE)obj->as.node.u2.node;
02699 goto again;
02700
02701 case NODE_HASH:
02702 case NODE_LIT:
02703 case NODE_STR:
02704 case NODE_XSTR:
02705 case NODE_DEFINED:
02706 case NODE_MATCH:
02707 case NODE_RETURN:
02708 case NODE_BREAK:
02709 case NODE_NEXT:
02710 case NODE_YIELD:
02711 case NODE_COLON2:
02712 case NODE_SPLAT:
02713 case NODE_TO_ARY:
02714 ptr = (VALUE)obj->as.node.u1.node;
02715 goto again;
02716
02717 case NODE_SCOPE:
02718 case NODE_CDECL:
02719 case NODE_OPT_ARG:
02720 gc_mark(objspace, (VALUE)obj->as.node.u3.node);
02721 ptr = (VALUE)obj->as.node.u2.node;
02722 goto again;
02723
02724 case NODE_ARGS:
02725 {
02726 struct rb_args_info *args = obj->as.node.u3.args;
02727 if (args) {
02728 if (args->pre_init) gc_mark(objspace, (VALUE)args->pre_init);
02729 if (args->post_init) gc_mark(objspace, (VALUE)args->post_init);
02730 if (args->opt_args) gc_mark(objspace, (VALUE)args->opt_args);
02731 if (args->kw_args) gc_mark(objspace, (VALUE)args->kw_args);
02732 if (args->kw_rest_arg) gc_mark(objspace, (VALUE)args->kw_rest_arg);
02733 }
02734 }
02735 ptr = (VALUE)obj->as.node.u2.node;
02736 goto again;
02737
02738 case NODE_ZARRAY:
02739 case NODE_ZSUPER:
02740 case NODE_VCALL:
02741 case NODE_GVAR:
02742 case NODE_LVAR:
02743 case NODE_DVAR:
02744 case NODE_IVAR:
02745 case NODE_CVAR:
02746 case NODE_NTH_REF:
02747 case NODE_BACK_REF:
02748 case NODE_REDO:
02749 case NODE_RETRY:
02750 case NODE_SELF:
02751 case NODE_NIL:
02752 case NODE_TRUE:
02753 case NODE_FALSE:
02754 case NODE_ERRINFO:
02755 case NODE_BLOCK_ARG:
02756 break;
02757 case NODE_ALLOCA:
02758 mark_locations_array(objspace,
02759 (VALUE*)obj->as.node.u1.value,
02760 obj->as.node.u3.cnt);
02761 gc_mark(objspace, (VALUE)obj->as.node.u2.node);
02762 break;
02763
02764 case NODE_CREF:
02765 gc_mark(objspace, obj->as.node.nd_refinements);
02766 gc_mark(objspace, (VALUE)obj->as.node.u1.node);
02767 ptr = (VALUE)obj->as.node.u3.node;
02768 goto again;
02769
02770 default:
02771 if (is_pointer_to_heap(objspace, obj->as.node.u1.node)) {
02772 gc_mark(objspace, (VALUE)obj->as.node.u1.node);
02773 }
02774 if (is_pointer_to_heap(objspace, obj->as.node.u2.node)) {
02775 gc_mark(objspace, (VALUE)obj->as.node.u2.node);
02776 }
02777 if (is_pointer_to_heap(objspace, obj->as.node.u3.node)) {
02778 gc_mark(objspace, (VALUE)obj->as.node.u3.node);
02779 }
02780 }
02781 return;
02782 }
02783
02784 gc_mark(objspace, obj->as.basic.klass);
02785 switch (BUILTIN_TYPE(obj)) {
02786 case T_ICLASS:
02787 case T_CLASS:
02788 case T_MODULE:
02789 mark_m_tbl(objspace, RCLASS_M_TBL(obj));
02790 if (!RCLASS_EXT(obj)) break;
02791 mark_tbl(objspace, RCLASS_IV_TBL(obj));
02792 mark_const_tbl(objspace, RCLASS_CONST_TBL(obj));
02793 ptr = RCLASS_SUPER(obj);
02794 goto again;
02795
02796 case T_ARRAY:
02797 if (FL_TEST(obj, ELTS_SHARED)) {
02798 ptr = obj->as.array.as.heap.aux.shared;
02799 goto again;
02800 }
02801 else {
02802 long i, len = RARRAY_LEN(obj);
02803 VALUE *ptr = RARRAY_PTR(obj);
02804 for (i=0; i < len; i++) {
02805 gc_mark(objspace, *ptr++);
02806 }
02807 }
02808 break;
02809
02810 case T_HASH:
02811 mark_hash(objspace, obj->as.hash.ntbl);
02812 ptr = obj->as.hash.ifnone;
02813 goto again;
02814
02815 case T_STRING:
02816 #define STR_ASSOC FL_USER3
02817 if (FL_TEST(obj, RSTRING_NOEMBED) && FL_ANY(obj, ELTS_SHARED|STR_ASSOC)) {
02818 ptr = obj->as.string.as.heap.aux.shared;
02819 goto again;
02820 }
02821 break;
02822
02823 case T_DATA:
02824 if (RTYPEDDATA_P(obj)) {
02825 RUBY_DATA_FUNC mark_func = obj->as.typeddata.type->function.dmark;
02826 if (mark_func) (*mark_func)(DATA_PTR(obj));
02827 }
02828 else {
02829 if (obj->as.data.dmark) (*obj->as.data.dmark)(DATA_PTR(obj));
02830 }
02831 break;
02832
02833 case T_OBJECT:
02834 {
02835 long i, len = ROBJECT_NUMIV(obj);
02836 VALUE *ptr = ROBJECT_IVPTR(obj);
02837 for (i = 0; i < len; i++) {
02838 gc_mark(objspace, *ptr++);
02839 }
02840 }
02841 break;
02842
02843 case T_FILE:
02844 if (obj->as.file.fptr) {
02845 gc_mark(objspace, obj->as.file.fptr->pathv);
02846 gc_mark(objspace, obj->as.file.fptr->tied_io_for_writing);
02847 gc_mark(objspace, obj->as.file.fptr->writeconv_asciicompat);
02848 gc_mark(objspace, obj->as.file.fptr->writeconv_pre_ecopts);
02849 gc_mark(objspace, obj->as.file.fptr->encs.ecopts);
02850 gc_mark(objspace, obj->as.file.fptr->write_lock);
02851 }
02852 break;
02853
02854 case T_REGEXP:
02855 ptr = obj->as.regexp.src;
02856 goto again;
02857
02858 case T_FLOAT:
02859 case T_BIGNUM:
02860 case T_ZOMBIE:
02861 break;
02862
02863 case T_MATCH:
02864 gc_mark(objspace, obj->as.match.regexp);
02865 if (obj->as.match.str) {
02866 ptr = obj->as.match.str;
02867 goto again;
02868 }
02869 break;
02870
02871 case T_RATIONAL:
02872 gc_mark(objspace, obj->as.rational.num);
02873 ptr = obj->as.rational.den;
02874 goto again;
02875
02876 case T_COMPLEX:
02877 gc_mark(objspace, obj->as.complex.real);
02878 ptr = obj->as.complex.imag;
02879 goto again;
02880
02881 case T_STRUCT:
02882 {
02883 long len = RSTRUCT_LEN(obj);
02884 VALUE *ptr = RSTRUCT_PTR(obj);
02885
02886 while (len--) {
02887 gc_mark(objspace, *ptr++);
02888 }
02889 }
02890 break;
02891
02892 default:
02893 rb_bug("rb_gc_mark(): unknown data type 0x%x(%p) %s",
02894 BUILTIN_TYPE(obj), (void *)obj,
02895 is_pointer_to_heap(objspace, obj) ? "corrupted object" : "non object");
02896 }
02897 }
02898
02899 static void
02900 gc_mark_stacked_objects(rb_objspace_t *objspace)
02901 {
02902 mark_stack_t *mstack = &objspace->mark_stack;
02903 VALUE obj = 0;
02904
02905 if (!mstack->index) return;
02906 while (pop_mark_stack(mstack, &obj)) {
02907 gc_mark_children(objspace, obj);
02908 }
02909 shrink_stack_chunk_cache(mstack);
02910 }
02911
02912 static void
02913 gc_marks(rb_objspace_t *objspace)
02914 {
02915 struct gc_list *list;
02916 rb_thread_t *th = GET_THREAD();
02917 struct mark_func_data_struct *prev_mark_func_data;
02918
02919 prev_mark_func_data = objspace->mark_func_data;
02920 objspace->mark_func_data = 0;
02921
02922 gc_prof_mark_timer_start(objspace);
02923 objspace->heap.marked_num = 0;
02924 objspace->count++;
02925
02926 SET_STACK_END;
02927
02928 th->vm->self ? rb_gc_mark(th->vm->self) : rb_vm_mark(th->vm);
02929
02930 mark_tbl(objspace, finalizer_table);
02931 mark_current_machine_context(objspace, th);
02932
02933 rb_gc_mark_symbols();
02934 rb_gc_mark_encodings();
02935
02936
02937 for (list = global_List; list; list = list->next) {
02938 rb_gc_mark_maybe(*list->varptr);
02939 }
02940 rb_mark_end_proc();
02941 rb_gc_mark_global_tbl();
02942
02943 mark_tbl(objspace, rb_class_tbl);
02944
02945
02946 rb_mark_generic_ivar_tbl();
02947
02948 rb_gc_mark_parser();
02949
02950 rb_gc_mark_unlinked_live_method_entries(th->vm);
02951
02952
02953 gc_mark_stacked_objects(objspace);
02954
02955 gc_prof_mark_timer_stop(objspace);
02956
02957 objspace->mark_func_data = prev_mark_func_data;
02958 }
02959
02960
02961
02962 void
02963 rb_gc_force_recycle(VALUE p)
02964 {
02965 rb_objspace_t *objspace = &rb_objspace;
02966 struct heaps_slot *slot;
02967
02968 objspace->total_freed_object_num++;
02969 if (MARKED_IN_BITMAP(GET_HEAP_BITMAP(p), p)) {
02970 add_slot_local_freelist(objspace, (RVALUE *)p);
02971 }
02972 else {
02973 objspace->heap.free_num++;
02974 slot = add_slot_local_freelist(objspace, (RVALUE *)p);
02975 if (slot->free_next == NULL) {
02976 link_free_heap_slot(objspace, slot);
02977 }
02978 }
02979 }
02980
02981 void
02982 rb_gc_register_mark_object(VALUE obj)
02983 {
02984 VALUE ary = GET_THREAD()->vm->mark_object_ary;
02985 rb_ary_push(ary, obj);
02986 }
02987
02988 void
02989 rb_gc_register_address(VALUE *addr)
02990 {
02991 rb_objspace_t *objspace = &rb_objspace;
02992 struct gc_list *tmp;
02993
02994 tmp = ALLOC(struct gc_list);
02995 tmp->next = global_List;
02996 tmp->varptr = addr;
02997 global_List = tmp;
02998 }
02999
03000 void
03001 rb_gc_unregister_address(VALUE *addr)
03002 {
03003 rb_objspace_t *objspace = &rb_objspace;
03004 struct gc_list *tmp = global_List;
03005
03006 if (tmp->varptr == addr) {
03007 global_List = tmp->next;
03008 xfree(tmp);
03009 return;
03010 }
03011 while (tmp->next) {
03012 if (tmp->next->varptr == addr) {
03013 struct gc_list *t = tmp->next;
03014
03015 tmp->next = tmp->next->next;
03016 xfree(t);
03017 break;
03018 }
03019 tmp = tmp->next;
03020 }
03021 }
03022
03023 #define GC_NOTIFY 0
03024
03025 static int
03026 garbage_collect(rb_objspace_t *objspace)
03027 {
03028 if (GC_NOTIFY) printf("start garbage_collect()\n");
03029
03030 if (!heaps) {
03031 return FALSE;
03032 }
03033 if (!ready_to_gc(objspace)) {
03034 return TRUE;
03035 }
03036
03037 gc_prof_timer_start(objspace);
03038
03039 rest_sweep(objspace);
03040
03041 during_gc++;
03042 gc_marks(objspace);
03043
03044 gc_prof_sweep_timer_start(objspace);
03045 gc_sweep(objspace);
03046 gc_prof_sweep_timer_stop(objspace);
03047
03048 gc_prof_timer_stop(objspace, Qtrue);
03049 if (GC_NOTIFY) printf("end garbage_collect()\n");
03050 return TRUE;
03051 }
03052
03053 static void *
03054 gc_with_gvl(void *ptr)
03055 {
03056 return (void *)(VALUE)garbage_collect((rb_objspace_t *)ptr);
03057 }
03058
03059 static int
03060 garbage_collect_with_gvl(rb_objspace_t *objspace)
03061 {
03062 if (dont_gc) return TRUE;
03063 if (ruby_thread_has_gvl_p()) {
03064 return garbage_collect(objspace);
03065 }
03066 else {
03067 if (ruby_native_thread_p()) {
03068 return (int)(VALUE)rb_thread_call_with_gvl(gc_with_gvl, (void *)objspace);
03069 }
03070 else {
03071
03072 fprintf(stderr, "[FATAL] failed to allocate memory\n");
03073 exit(EXIT_FAILURE);
03074 }
03075 }
03076 }
03077
03078 int
03079 rb_garbage_collect(void)
03080 {
03081 return garbage_collect(&rb_objspace);
03082 }
03083
03084 #undef Init_stack
03085
03086 void
03087 Init_stack(volatile VALUE *addr)
03088 {
03089 ruby_init_stack(addr);
03090 }
03091
03092
03093
03094
03095
03096
03097
03098
03099
03100
03101
03102 VALUE
03103 rb_gc_start(void)
03104 {
03105 rb_gc();
03106 return Qnil;
03107 }
03108
03109 void
03110 rb_gc(void)
03111 {
03112 rb_objspace_t *objspace = &rb_objspace;
03113 garbage_collect(objspace);
03114 if (!finalizing) finalize_deferred(objspace);
03115 free_unused_heaps(objspace);
03116 }
03117
03118 int
03119 rb_during_gc(void)
03120 {
03121 rb_objspace_t *objspace = &rb_objspace;
03122 return during_gc;
03123 }
03124
03125
03126
03127
03128
03129
03130
03131
03132
03133
03134
03135 static VALUE
03136 gc_count(VALUE self)
03137 {
03138 return UINT2NUM(rb_objspace.count);
03139 }
03140
03141
03142
03143
03144
03145
03146
03147
03148
03149
03150
03151
03152
03153
03154
03155
03156
03157
03158
03159
03160
03161
03162
03163
03164
03165
03166
03167
03168 static VALUE
03169 gc_stat(int argc, VALUE *argv, VALUE self)
03170 {
03171 rb_objspace_t *objspace = &rb_objspace;
03172 VALUE hash;
03173 static VALUE sym_count;
03174 static VALUE sym_heap_used, sym_heap_length, sym_heap_increment;
03175 static VALUE sym_heap_live_num, sym_heap_free_num, sym_heap_final_num;
03176 static VALUE sym_total_allocated_object, sym_total_freed_object;
03177 if (sym_count == 0) {
03178 sym_count = ID2SYM(rb_intern_const("count"));
03179 sym_heap_used = ID2SYM(rb_intern_const("heap_used"));
03180 sym_heap_length = ID2SYM(rb_intern_const("heap_length"));
03181 sym_heap_increment = ID2SYM(rb_intern_const("heap_increment"));
03182 sym_heap_live_num = ID2SYM(rb_intern_const("heap_live_num"));
03183 sym_heap_free_num = ID2SYM(rb_intern_const("heap_free_num"));
03184 sym_heap_final_num = ID2SYM(rb_intern_const("heap_final_num"));
03185 sym_total_allocated_object = ID2SYM(rb_intern_const("total_allocated_object"));
03186 sym_total_freed_object = ID2SYM(rb_intern_const("total_freed_object"));
03187 }
03188
03189 if (rb_scan_args(argc, argv, "01", &hash) == 1) {
03190 if (!RB_TYPE_P(hash, T_HASH))
03191 rb_raise(rb_eTypeError, "non-hash given");
03192 }
03193
03194 if (hash == Qnil) {
03195 hash = rb_hash_new();
03196 }
03197
03198 rest_sweep(objspace);
03199
03200 rb_hash_aset(hash, sym_count, SIZET2NUM(objspace->count));
03201
03202 rb_hash_aset(hash, sym_heap_used, SIZET2NUM(objspace->heap.used));
03203 rb_hash_aset(hash, sym_heap_length, SIZET2NUM(objspace->heap.length));
03204 rb_hash_aset(hash, sym_heap_increment, SIZET2NUM(objspace->heap.increment));
03205 rb_hash_aset(hash, sym_heap_live_num, SIZET2NUM(objspace_live_num(objspace)));
03206 rb_hash_aset(hash, sym_heap_free_num, SIZET2NUM(objspace->heap.free_num));
03207 rb_hash_aset(hash, sym_heap_final_num, SIZET2NUM(objspace->heap.final_num));
03208 rb_hash_aset(hash, sym_total_allocated_object, SIZET2NUM(objspace->total_allocated_object_num));
03209 rb_hash_aset(hash, sym_total_freed_object, SIZET2NUM(objspace->total_freed_object_num));
03210
03211 return hash;
03212 }
03213
03214
03215
03216
03217
03218
03219
03220
03221 static VALUE
03222 gc_stress_get(VALUE self)
03223 {
03224 rb_objspace_t *objspace = &rb_objspace;
03225 return ruby_gc_stress ? Qtrue : Qfalse;
03226 }
03227
03228
03229
03230
03231
03232
03233
03234
03235
03236
03237
03238
03239
03240 static VALUE
03241 gc_stress_set(VALUE self, VALUE flag)
03242 {
03243 rb_objspace_t *objspace = &rb_objspace;
03244 rb_secure(2);
03245 ruby_gc_stress = RTEST(flag);
03246 return flag;
03247 }
03248
03249
03250
03251
03252
03253
03254
03255
03256
03257
03258
03259
03260
03261
03262 VALUE
03263 rb_gc_enable(void)
03264 {
03265 rb_objspace_t *objspace = &rb_objspace;
03266 int old = dont_gc;
03267
03268 dont_gc = FALSE;
03269 return old ? Qtrue : Qfalse;
03270 }
03271
03272
03273
03274
03275
03276
03277
03278
03279
03280
03281
03282
03283
03284 VALUE
03285 rb_gc_disable(void)
03286 {
03287 rb_objspace_t *objspace = &rb_objspace;
03288 int old = dont_gc;
03289
03290 rest_sweep(objspace);
03291
03292 dont_gc = TRUE;
03293 return old ? Qtrue : Qfalse;
03294 }
03295
03296 void
03297 rb_gc_set_params(void)
03298 {
03299 char *malloc_limit_ptr, *heap_min_slots_ptr, *free_min_ptr;
03300
03301 if (rb_safe_level() > 0) return;
03302
03303 malloc_limit_ptr = getenv("RUBY_GC_MALLOC_LIMIT");
03304 if (malloc_limit_ptr != NULL) {
03305 int malloc_limit_i = atoi(malloc_limit_ptr);
03306 if (RTEST(ruby_verbose))
03307 fprintf(stderr, "malloc_limit=%d (%d)\n",
03308 malloc_limit_i, initial_malloc_limit);
03309 if (malloc_limit_i > 0) {
03310 initial_malloc_limit = malloc_limit_i;
03311 }
03312 }
03313
03314 heap_min_slots_ptr = getenv("RUBY_HEAP_MIN_SLOTS");
03315 if (heap_min_slots_ptr != NULL) {
03316 int heap_min_slots_i = atoi(heap_min_slots_ptr);
03317 if (RTEST(ruby_verbose))
03318 fprintf(stderr, "heap_min_slots=%d (%d)\n",
03319 heap_min_slots_i, initial_heap_min_slots);
03320 if (heap_min_slots_i > 0) {
03321 initial_heap_min_slots = heap_min_slots_i;
03322 initial_expand_heap(&rb_objspace);
03323 }
03324 }
03325
03326 free_min_ptr = getenv("RUBY_FREE_MIN");
03327 if (free_min_ptr != NULL) {
03328 int free_min_i = atoi(free_min_ptr);
03329 if (RTEST(ruby_verbose))
03330 fprintf(stderr, "free_min=%d (%d)\n", free_min_i, initial_free_min);
03331 if (free_min_i > 0) {
03332 initial_free_min = free_min_i;
03333 }
03334 }
03335 }
03336
03337 void
03338 rb_objspace_reachable_objects_from(VALUE obj, void (func)(VALUE, void *), void *data)
03339 {
03340 rb_objspace_t *objspace = &rb_objspace;
03341
03342 if (markable_object_p(objspace, obj)) {
03343 struct mark_func_data_struct mfd;
03344 mfd.mark_func = func;
03345 mfd.data = data;
03346 objspace->mark_func_data = &mfd;
03347 gc_mark_children(objspace, obj);
03348 objspace->mark_func_data = 0;
03349 }
03350 }
03351
03352
03353
03354
03355
03356 static void vm_xfree(rb_objspace_t *objspace, void *ptr);
03357
03358 static void *
03359 negative_size_allocation_error_with_gvl(void *ptr)
03360 {
03361 rb_raise(rb_eNoMemError, "%s", (const char *)ptr);
03362 return 0;
03363 }
03364
03365 static void
03366 negative_size_allocation_error(const char *msg)
03367 {
03368 if (ruby_thread_has_gvl_p()) {
03369 rb_raise(rb_eNoMemError, "%s", msg);
03370 }
03371 else {
03372 if (ruby_native_thread_p()) {
03373 rb_thread_call_with_gvl(negative_size_allocation_error_with_gvl, (void *)msg);
03374 }
03375 else {
03376 fprintf(stderr, "[FATAL] %s\n", msg);
03377 exit(EXIT_FAILURE);
03378 }
03379 }
03380 }
03381
03382 static void *
03383 ruby_memerror_body(void *dummy)
03384 {
03385 rb_memerror();
03386 return 0;
03387 }
03388
03389 static void
03390 ruby_memerror(void)
03391 {
03392 if (ruby_thread_has_gvl_p()) {
03393 rb_memerror();
03394 }
03395 else {
03396 if (ruby_native_thread_p()) {
03397 rb_thread_call_with_gvl(ruby_memerror_body, 0);
03398 }
03399 else {
03400
03401 fprintf(stderr, "[FATAL] failed to allocate memory\n");
03402 exit(EXIT_FAILURE);
03403 }
03404 }
03405 }
03406
03407 void
03408 rb_memerror(void)
03409 {
03410 rb_thread_t *th = GET_THREAD();
03411 if (!nomem_error ||
03412 (rb_thread_raised_p(th, RAISED_NOMEMORY) && rb_safe_level() < 4)) {
03413 fprintf(stderr, "[FATAL] failed to allocate memory\n");
03414 exit(EXIT_FAILURE);
03415 }
03416 if (rb_thread_raised_p(th, RAISED_NOMEMORY)) {
03417 rb_thread_raised_clear(th);
03418 GET_THREAD()->errinfo = nomem_error;
03419 JUMP_TAG(TAG_RAISE);
03420 }
03421 rb_thread_raised_set(th, RAISED_NOMEMORY);
03422 rb_exc_raise(nomem_error);
03423 }
03424
03425 static void *
03426 aligned_malloc(size_t alignment, size_t size)
03427 {
03428 void *res;
03429
03430 #if defined __MINGW32__
03431 res = __mingw_aligned_malloc(size, alignment);
03432 #elif defined _WIN32 && !defined __CYGWIN__
03433 res = _aligned_malloc(size, alignment);
03434 #elif defined(HAVE_POSIX_MEMALIGN)
03435 if (posix_memalign(&res, alignment, size) == 0) {
03436 return res;
03437 }
03438 else {
03439 return NULL;
03440 }
03441 #elif defined(HAVE_MEMALIGN)
03442 res = memalign(alignment, size);
03443 #else
03444 char* aligned;
03445 res = malloc(alignment + size + sizeof(void*));
03446 aligned = (char*)res + alignment + sizeof(void*);
03447 aligned -= ((VALUE)aligned & (alignment - 1));
03448 ((void**)aligned)[-1] = res;
03449 res = (void*)aligned;
03450 #endif
03451
03452 #if defined(_DEBUG) || defined(GC_DEBUG)
03453
03454 assert((alignment - 1) & alignment == 0);
03455 assert(alignment % sizeof(void*) == 0);
03456 #endif
03457 return res;
03458 }
03459
03460 static void
03461 aligned_free(void *ptr)
03462 {
03463 #if defined __MINGW32__
03464 __mingw_aligned_free(ptr);
03465 #elif defined _WIN32 && !defined __CYGWIN__
03466 _aligned_free(ptr);
03467 #elif defined(HAVE_MEMALIGN) || defined(HAVE_POSIX_MEMALIGN)
03468 free(ptr);
03469 #else
03470 free(((void**)ptr)[-1]);
03471 #endif
03472 }
03473
03474 static inline size_t
03475 vm_malloc_prepare(rb_objspace_t *objspace, size_t size)
03476 {
03477 if ((ssize_t)size < 0) {
03478 negative_size_allocation_error("negative allocation size (or too big)");
03479 }
03480 if (size == 0) size = 1;
03481
03482 #if CALC_EXACT_MALLOC_SIZE
03483 size += sizeof(size_t);
03484 #endif
03485
03486 if ((ruby_gc_stress && !ruby_disable_gc_stress) ||
03487 (malloc_increase+size) > malloc_limit) {
03488 garbage_collect_with_gvl(objspace);
03489 }
03490
03491 return size;
03492 }
03493
03494 static inline void *
03495 vm_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
03496 {
03497 ATOMIC_SIZE_ADD(malloc_increase, size);
03498
03499 #if CALC_EXACT_MALLOC_SIZE
03500 ATOMIC_SIZE_ADD(objspace->malloc_params.allocated_size, size);
03501 ATOMIC_SIZE_INC(objspace->malloc_params.allocations);
03502 ((size_t *)mem)[0] = size;
03503 mem = (size_t *)mem + 1;
03504 #endif
03505
03506 return mem;
03507 }
03508
03509 #define TRY_WITH_GC(alloc) do { \
03510 if (!(alloc) && \
03511 (!garbage_collect_with_gvl(objspace) || \
03512 !(alloc))) { \
03513 ruby_memerror(); \
03514 } \
03515 } while (0)
03516
03517 static void *
03518 vm_xmalloc(rb_objspace_t *objspace, size_t size)
03519 {
03520 void *mem;
03521
03522 size = vm_malloc_prepare(objspace, size);
03523 TRY_WITH_GC(mem = malloc(size));
03524 return vm_malloc_fixup(objspace, mem, size);
03525 }
03526
03527 static void *
03528 vm_xrealloc(rb_objspace_t *objspace, void *ptr, size_t size)
03529 {
03530 void *mem;
03531 #if CALC_EXACT_MALLOC_SIZE
03532 size_t oldsize;
03533 #endif
03534
03535 if ((ssize_t)size < 0) {
03536 negative_size_allocation_error("negative re-allocation size");
03537 }
03538
03539 if (!ptr) return vm_xmalloc(objspace, size);
03540
03541
03542
03543
03544
03545
03546 if (size == 0) {
03547 vm_xfree(objspace, ptr);
03548 return 0;
03549 }
03550 if (ruby_gc_stress && !ruby_disable_gc_stress)
03551 garbage_collect_with_gvl(objspace);
03552
03553 #if CALC_EXACT_MALLOC_SIZE
03554 size += sizeof(size_t);
03555 ptr = (size_t *)ptr - 1;
03556 oldsize = ((size_t *)ptr)[0];
03557 #endif
03558
03559 mem = realloc(ptr, size);
03560 if (!mem) {
03561 if (garbage_collect_with_gvl(objspace)) {
03562 mem = realloc(ptr, size);
03563 }
03564 if (!mem) {
03565 ruby_memerror();
03566 }
03567 }
03568 ATOMIC_SIZE_ADD(malloc_increase, size);
03569
03570 #if CALC_EXACT_MALLOC_SIZE
03571 ATOMIC_SIZE_ADD(objspace->malloc_params.allocated_size, size - oldsize);
03572 ((size_t *)mem)[0] = size;
03573 mem = (size_t *)mem + 1;
03574 #endif
03575
03576 return mem;
03577 }
03578
03579 static void
03580 vm_xfree(rb_objspace_t *objspace, void *ptr)
03581 {
03582 #if CALC_EXACT_MALLOC_SIZE
03583 size_t size;
03584 ptr = ((size_t *)ptr) - 1;
03585 size = ((size_t*)ptr)[0];
03586 if (size) {
03587 ATOMIC_SIZE_SUB(objspace->malloc_params.allocated_size, size);
03588 ATOMIC_SIZE_DEC(objspace->malloc_params.allocations);
03589 }
03590 #endif
03591
03592 free(ptr);
03593 }
03594
03595 void *
03596 ruby_xmalloc(size_t size)
03597 {
03598 return vm_xmalloc(&rb_objspace, size);
03599 }
03600
03601 static inline size_t
03602 xmalloc2_size(size_t n, size_t size)
03603 {
03604 size_t len = size * n;
03605 if (n != 0 && size != len / n) {
03606 rb_raise(rb_eArgError, "malloc: possible integer overflow");
03607 }
03608 return len;
03609 }
03610
03611 void *
03612 ruby_xmalloc2(size_t n, size_t size)
03613 {
03614 return vm_xmalloc(&rb_objspace, xmalloc2_size(n, size));
03615 }
03616
03617 static void *
03618 vm_xcalloc(rb_objspace_t *objspace, size_t count, size_t elsize)
03619 {
03620 void *mem;
03621 size_t size;
03622
03623 size = xmalloc2_size(count, elsize);
03624 size = vm_malloc_prepare(objspace, size);
03625
03626 TRY_WITH_GC(mem = calloc(1, size));
03627 return vm_malloc_fixup(objspace, mem, size);
03628 }
03629
03630 void *
03631 ruby_xcalloc(size_t n, size_t size)
03632 {
03633 return vm_xcalloc(&rb_objspace, n, size);
03634 }
03635
03636 void *
03637 ruby_xrealloc(void *ptr, size_t size)
03638 {
03639 return vm_xrealloc(&rb_objspace, ptr, size);
03640 }
03641
03642 void *
03643 ruby_xrealloc2(void *ptr, size_t n, size_t size)
03644 {
03645 size_t len = size * n;
03646 if (n != 0 && size != len / n) {
03647 rb_raise(rb_eArgError, "realloc: possible integer overflow");
03648 }
03649 return ruby_xrealloc(ptr, len);
03650 }
03651
03652 void
03653 ruby_xfree(void *x)
03654 {
03655 if (x)
03656 vm_xfree(&rb_objspace, x);
03657 }
03658
03659
03660
03661
03662
03663 void *
03664 ruby_mimmalloc(size_t size)
03665 {
03666 void *mem;
03667 #if CALC_EXACT_MALLOC_SIZE
03668 size += sizeof(size_t);
03669 #endif
03670 mem = malloc(size);
03671 #if CALC_EXACT_MALLOC_SIZE
03672
03673 ((size_t *)mem)[0] = 0;
03674 mem = (size_t *)mem + 1;
03675 #endif
03676 return mem;
03677 }
03678
03679 #if CALC_EXACT_MALLOC_SIZE
03680
03681
03682
03683
03684
03685
03686
03687
03688
03689 static VALUE
03690 gc_malloc_allocated_size(VALUE self)
03691 {
03692 return UINT2NUM(rb_objspace.malloc_params.allocated_size);
03693 }
03694
03695
03696
03697
03698
03699
03700
03701
03702
03703
03704 static VALUE
03705 gc_malloc_allocations(VALUE self)
03706 {
03707 return UINT2NUM(rb_objspace.malloc_params.allocations);
03708 }
03709 #endif
03710
03711
03712
03713
03714
03715 struct weakmap {
03716 st_table *obj2wmap;
03717 st_table *wmap2obj;
03718 VALUE final;
03719 };
03720
03721 static int
03722 wmap_mark_map(st_data_t key, st_data_t val, st_data_t arg)
03723 {
03724 gc_mark_ptr((rb_objspace_t *)arg, (VALUE)val);
03725 return ST_CONTINUE;
03726 }
03727
03728 static void
03729 wmap_mark(void *ptr)
03730 {
03731 struct weakmap *w = ptr;
03732 st_foreach(w->obj2wmap, wmap_mark_map, (st_data_t)&rb_objspace);
03733 rb_gc_mark(w->final);
03734 }
03735
03736 static int
03737 wmap_free_map(st_data_t key, st_data_t val, st_data_t arg)
03738 {
03739 rb_ary_resize((VALUE)val, 0);
03740 return ST_CONTINUE;
03741 }
03742
03743 static void
03744 wmap_free(void *ptr)
03745 {
03746 struct weakmap *w = ptr;
03747 st_foreach(w->obj2wmap, wmap_free_map, 0);
03748 st_free_table(w->obj2wmap);
03749 st_free_table(w->wmap2obj);
03750 }
03751
03752 size_t rb_ary_memsize(VALUE ary);
03753 static int
03754 wmap_memsize_map(st_data_t key, st_data_t val, st_data_t arg)
03755 {
03756 *(size_t *)arg += rb_ary_memsize((VALUE)val);
03757 return ST_CONTINUE;
03758 }
03759
03760 static size_t
03761 wmap_memsize(const void *ptr)
03762 {
03763 size_t size;
03764 const struct weakmap *w = ptr;
03765 if (!w) return 0;
03766 size = sizeof(*w);
03767 size += st_memsize(w->obj2wmap);
03768 size += st_memsize(w->wmap2obj);
03769 st_foreach(w->obj2wmap, wmap_memsize_map, (st_data_t)&size);
03770 return size;
03771 }
03772
03773 static const rb_data_type_t weakmap_type = {
03774 "weakmap",
03775 {
03776 wmap_mark,
03777 wmap_free,
03778 wmap_memsize,
03779 }
03780 };
03781
03782 static VALUE
03783 wmap_allocate(VALUE klass)
03784 {
03785 struct weakmap *w;
03786 VALUE obj = TypedData_Make_Struct(klass, struct weakmap, &weakmap_type, w);
03787 w->obj2wmap = st_init_numtable();
03788 w->wmap2obj = st_init_numtable();
03789 w->final = rb_obj_method(obj, ID2SYM(rb_intern("finalize")));
03790 return obj;
03791 }
03792
03793 static int
03794 wmap_final_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
03795 {
03796 VALUE wmap, ary;
03797 if (!existing) return ST_STOP;
03798 wmap = (VALUE)arg, ary = (VALUE)*value;
03799 rb_ary_delete_same(ary, wmap);
03800 if (!RARRAY_LEN(ary)) return ST_DELETE;
03801 return ST_CONTINUE;
03802 }
03803
03804 static VALUE
03805 wmap_finalize(VALUE self, VALUE objid)
03806 {
03807 st_data_t orig, wmap, data;
03808 VALUE obj, rids;
03809 long i;
03810 struct weakmap *w;
03811
03812 TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
03813
03814 obj = obj_id_to_ref(objid);
03815
03816
03817 orig = (st_data_t)obj;
03818 if (st_delete(w->obj2wmap, &orig, &data)) {
03819 rids = (VALUE)data;
03820 for (i = 0; i < RARRAY_LEN(rids); ++i) {
03821 wmap = (st_data_t)RARRAY_PTR(rids)[i];
03822 st_delete(w->wmap2obj, &wmap, NULL);
03823 }
03824 }
03825
03826 wmap = (st_data_t)obj;
03827 if (st_delete(w->wmap2obj, &wmap, &orig)) {
03828 wmap = (st_data_t)obj;
03829 st_update(w->obj2wmap, orig, wmap_final_func, wmap);
03830 }
03831 return self;
03832 }
03833
03834
03835 static VALUE
03836 wmap_aset(VALUE self, VALUE wmap, VALUE orig)
03837 {
03838 st_data_t data;
03839 VALUE rids;
03840 struct weakmap *w;
03841
03842 TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
03843 rb_define_final(orig, w->final);
03844 rb_define_final(wmap, w->final);
03845 if (st_lookup(w->obj2wmap, (st_data_t)orig, &data)) {
03846 rids = (VALUE)data;
03847 }
03848 else {
03849 rids = rb_ary_tmp_new(1);
03850 st_insert(w->obj2wmap, (st_data_t)orig, (st_data_t)rids);
03851 }
03852 rb_ary_push(rids, wmap);
03853 st_insert(w->wmap2obj, (st_data_t)wmap, (st_data_t)orig);
03854 return nonspecial_obj_id(orig);
03855 }
03856
03857
03858 static VALUE
03859 wmap_aref(VALUE self, VALUE wmap)
03860 {
03861 st_data_t data;
03862 VALUE obj;
03863 struct weakmap *w;
03864 rb_objspace_t *objspace = &rb_objspace;
03865
03866 TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
03867 if (!st_lookup(w->wmap2obj, (st_data_t)wmap, &data)) return Qnil;
03868 obj = (VALUE)data;
03869 if (!is_id_value(objspace, obj)) return Qnil;
03870 if (!is_live_object(objspace, obj)) return Qnil;
03871 return obj;
03872 }
03873
03874
03875
03876
03877
03878
03879 static inline void gc_prof_set_heap_info(rb_objspace_t *, gc_profile_record *);
03880 #define GC_PROFILE_RECORD_DEFAULT_SIZE 100
03881
03882 static double
03883 getrusage_time(void)
03884 {
03885 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)
03886 {
03887 static int try_clock_gettime = 1;
03888 struct timespec ts;
03889 if (try_clock_gettime && clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == 0) {
03890 return ts.tv_sec + ts.tv_nsec * 1e-9;
03891 }
03892 else {
03893 try_clock_gettime = 0;
03894 }
03895 }
03896 #endif
03897
03898 #ifdef RUSAGE_SELF
03899 {
03900 struct rusage usage;
03901 struct timeval time;
03902 if (getrusage(RUSAGE_SELF, &usage) == 0) {
03903 time = usage.ru_utime;
03904 return time.tv_sec + time.tv_usec * 1e-6;
03905 }
03906 }
03907 #endif
03908
03909 #ifdef _WIN32
03910 {
03911 FILETIME creation_time, exit_time, kernel_time, user_time;
03912 ULARGE_INTEGER ui;
03913 LONG_LONG q;
03914 double t;
03915
03916 if (GetProcessTimes(GetCurrentProcess(),
03917 &creation_time, &exit_time, &kernel_time, &user_time) != 0) {
03918 memcpy(&ui, &user_time, sizeof(FILETIME));
03919 q = ui.QuadPart / 10L;
03920 t = (DWORD)(q % 1000000L) * 1e-6;
03921 q /= 1000000L;
03922 #ifdef __GNUC__
03923 t += q;
03924 #else
03925 t += (double)(DWORD)(q >> 16) * (1 << 16);
03926 t += (DWORD)q & ~(~0 << 16);
03927 #endif
03928 return t;
03929 }
03930 }
03931 #endif
03932
03933 return 0.0;
03934 }
03935
03936 static inline void
03937 gc_prof_timer_start(rb_objspace_t *objspace)
03938 {
03939 if (objspace->profile.run) {
03940 size_t count = objspace->profile.count;
03941
03942 if (!objspace->profile.record) {
03943 objspace->profile.size = GC_PROFILE_RECORD_DEFAULT_SIZE;
03944 objspace->profile.record = malloc(sizeof(gc_profile_record) * objspace->profile.size);
03945 }
03946 if (count >= objspace->profile.size) {
03947 objspace->profile.size += 1000;
03948 objspace->profile.record = realloc(objspace->profile.record, sizeof(gc_profile_record) * objspace->profile.size);
03949 }
03950 if (!objspace->profile.record) {
03951 rb_bug("gc_profile malloc or realloc miss");
03952 }
03953 MEMZERO(&objspace->profile.record[count], gc_profile_record, 1);
03954 objspace->profile.record[count].gc_time = getrusage_time();
03955 objspace->profile.record[objspace->profile.count].gc_invoke_time =
03956 objspace->profile.record[count].gc_time - objspace->profile.invoke_time;
03957 }
03958 }
03959
03960 static inline void
03961 gc_prof_timer_stop(rb_objspace_t *objspace, int marked)
03962 {
03963 if (objspace->profile.run) {
03964 double gc_time = 0;
03965 size_t count = objspace->profile.count;
03966 gc_profile_record *record = &objspace->profile.record[count];
03967
03968 gc_time = getrusage_time() - record->gc_time;
03969 if (gc_time < 0) gc_time = 0;
03970 record->gc_time = gc_time;
03971 record->is_marked = !!(marked);
03972 gc_prof_set_heap_info(objspace, record);
03973 objspace->profile.count++;
03974 }
03975 }
03976
03977 #if !GC_PROFILE_MORE_DETAIL
03978
03979 static inline void
03980 gc_prof_mark_timer_start(rb_objspace_t *objspace)
03981 {
03982 if (RUBY_DTRACE_GC_MARK_BEGIN_ENABLED()) {
03983 RUBY_DTRACE_GC_MARK_BEGIN();
03984 }
03985 }
03986
03987 static inline void
03988 gc_prof_mark_timer_stop(rb_objspace_t *objspace)
03989 {
03990 if (RUBY_DTRACE_GC_MARK_END_ENABLED()) {
03991 RUBY_DTRACE_GC_MARK_END();
03992 }
03993 }
03994
03995 static inline void
03996 gc_prof_sweep_timer_start(rb_objspace_t *objspace)
03997 {
03998 if (RUBY_DTRACE_GC_SWEEP_BEGIN_ENABLED()) {
03999 RUBY_DTRACE_GC_SWEEP_BEGIN();
04000 }
04001 }
04002
04003 static inline void
04004 gc_prof_sweep_timer_stop(rb_objspace_t *objspace)
04005 {
04006 if (RUBY_DTRACE_GC_SWEEP_END_ENABLED()) {
04007 RUBY_DTRACE_GC_SWEEP_END();
04008 }
04009 }
04010
04011 static inline void
04012 gc_prof_set_malloc_info(rb_objspace_t *objspace)
04013 {
04014 }
04015
04016 static inline void
04017 gc_prof_set_heap_info(rb_objspace_t *objspace, gc_profile_record *record)
04018 {
04019 size_t live = objspace_live_num(objspace);
04020 size_t total = heaps_used * HEAP_OBJ_LIMIT;
04021
04022 record->heap_total_objects = total;
04023 record->heap_use_size = live * sizeof(RVALUE);
04024 record->heap_total_size = total * sizeof(RVALUE);
04025 }
04026
04027 #else
04028
04029 static inline void
04030 gc_prof_mark_timer_start(rb_objspace_t *objspace)
04031 {
04032 if (RUBY_DTRACE_GC_MARK_BEGIN_ENABLED()) {
04033 RUBY_DTRACE_GC_MARK_BEGIN();
04034 }
04035 if (objspace->profile.run) {
04036 size_t count = objspace->profile.count;
04037
04038 objspace->profile.record[count].gc_mark_time = getrusage_time();
04039 }
04040 }
04041
04042 static inline void
04043 gc_prof_mark_timer_stop(rb_objspace_t *objspace)
04044 {
04045 if (RUBY_DTRACE_GC_MARK_END_ENABLED()) {
04046 RUBY_DTRACE_GC_MARK_END();
04047 }
04048 if (objspace->profile.run) {
04049 double mark_time = 0;
04050 size_t count = objspace->profile.count;
04051 gc_profile_record *record = &objspace->profile.record[count];
04052
04053 mark_time = getrusage_time() - record->gc_mark_time;
04054 if (mark_time < 0) mark_time = 0;
04055 record->gc_mark_time = mark_time;
04056 }
04057 }
04058
04059 static inline void
04060 gc_prof_sweep_timer_start(rb_objspace_t *objspace)
04061 {
04062 if (RUBY_DTRACE_GC_SWEEP_BEGIN_ENABLED()) {
04063 RUBY_DTRACE_GC_SWEEP_BEGIN();
04064 }
04065 if (objspace->profile.run) {
04066 size_t count = objspace->profile.count;
04067
04068 objspace->profile.record[count].gc_sweep_time = getrusage_time();
04069 }
04070 }
04071
04072 static inline void
04073 gc_prof_sweep_timer_stop(rb_objspace_t *objspace)
04074 {
04075 if (RUBY_DTRACE_GC_SWEEP_END_ENABLED()) {
04076 RUBY_DTRACE_GC_SWEEP_END();
04077 }
04078 if (objspace->profile.run) {
04079 double sweep_time = 0;
04080 size_t count = objspace->profile.count;
04081 gc_profile_record *record = &objspace->profile.record[count];
04082
04083 sweep_time = getrusage_time() - record->gc_sweep_time;\
04084 if (sweep_time < 0) sweep_time = 0;\
04085 record->gc_sweep_time = sweep_time;
04086 }
04087 }
04088
04089 static inline void
04090 gc_prof_set_malloc_info(rb_objspace_t *objspace)
04091 {
04092 if (objspace->profile.run) {
04093 gc_profile_record *record = &objspace->profile.record[objspace->profile.count];
04094 if (record) {
04095 record->allocate_increase = malloc_increase;
04096 record->allocate_limit = malloc_limit;
04097 }
04098 }
04099 }
04100
04101 static inline void
04102 gc_prof_set_heap_info(rb_objspace_t *objspace, gc_profile_record *record)
04103 {
04104 size_t live = objspace_live_num(objspace);
04105 size_t total = heaps_used * HEAP_OBJ_LIMIT;
04106
04107 record->heap_use_slots = heaps_used;
04108 record->heap_live_objects = live;
04109 record->heap_free_objects = total - live;
04110 record->heap_total_objects = total;
04111 record->have_finalize = deferred_final_list ? Qtrue : Qfalse;
04112 record->heap_use_size = live * sizeof(RVALUE);
04113 record->heap_total_size = total * sizeof(RVALUE);
04114 }
04115
04116 #endif
04117
04118
04119
04120
04121
04122
04123
04124
04125
04126
04127 static VALUE
04128 gc_profile_clear(void)
04129 {
04130 rb_objspace_t *objspace = &rb_objspace;
04131
04132 if (GC_PROFILE_RECORD_DEFAULT_SIZE * 2 < objspace->profile.size) {
04133 objspace->profile.size = GC_PROFILE_RECORD_DEFAULT_SIZE * 2;
04134 objspace->profile.record = realloc(objspace->profile.record, sizeof(gc_profile_record) * objspace->profile.size);
04135 if (!objspace->profile.record) {
04136 rb_memerror();
04137 }
04138 }
04139 MEMZERO(objspace->profile.record, gc_profile_record, objspace->profile.size);
04140 objspace->profile.count = 0;
04141 return Qnil;
04142 }
04143
04144
04145
04146
04147
04148
04149
04150
04151
04152
04153
04154
04155
04156
04157
04158
04159
04160
04161
04162
04163
04164
04165
04166
04167
04168
04169
04170
04171
04172
04173
04174
04175
04176
04177
04178
04179
04180
04181
04182
04183
04184
04185
04186
04187
04188
04189
04190
04191
04192
04193
04194 static VALUE
04195 gc_profile_record_get(void)
04196 {
04197 VALUE prof;
04198 VALUE gc_profile = rb_ary_new();
04199 size_t i;
04200 rb_objspace_t *objspace = (&rb_objspace);
04201
04202 if (!objspace->profile.run) {
04203 return Qnil;
04204 }
04205
04206 for (i =0; i < objspace->profile.count; i++) {
04207 prof = rb_hash_new();
04208 rb_hash_aset(prof, ID2SYM(rb_intern("GC_TIME")), DBL2NUM(objspace->profile.record[i].gc_time));
04209 rb_hash_aset(prof, ID2SYM(rb_intern("GC_INVOKE_TIME")), DBL2NUM(objspace->profile.record[i].gc_invoke_time));
04210 rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SIZE")), SIZET2NUM(objspace->profile.record[i].heap_use_size));
04211 rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_SIZE")), SIZET2NUM(objspace->profile.record[i].heap_total_size));
04212 rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_OBJECTS")), SIZET2NUM(objspace->profile.record[i].heap_total_objects));
04213 rb_hash_aset(prof, ID2SYM(rb_intern("GC_IS_MARKED")), objspace->profile.record[i].is_marked);
04214 #if GC_PROFILE_MORE_DETAIL
04215 rb_hash_aset(prof, ID2SYM(rb_intern("GC_MARK_TIME")), DBL2NUM(objspace->profile.record[i].gc_mark_time));
04216 rb_hash_aset(prof, ID2SYM(rb_intern("GC_SWEEP_TIME")), DBL2NUM(objspace->profile.record[i].gc_sweep_time));
04217 rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_INCREASE")), SIZET2NUM(objspace->profile.record[i].allocate_increase));
04218 rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_LIMIT")), SIZET2NUM(objspace->profile.record[i].allocate_limit));
04219 rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SLOTS")), SIZET2NUM(objspace->profile.record[i].heap_use_slots));
04220 rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_LIVE_OBJECTS")), SIZET2NUM(objspace->profile.record[i].heap_live_objects));
04221 rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_FREE_OBJECTS")), SIZET2NUM(objspace->profile.record[i].heap_free_objects));
04222 rb_hash_aset(prof, ID2SYM(rb_intern("HAVE_FINALIZE")), objspace->profile.record[i].have_finalize);
04223 #endif
04224 rb_ary_push(gc_profile, prof);
04225 }
04226
04227 return gc_profile;
04228 }
04229
04230 static void
04231 gc_profile_dump_on(VALUE out, VALUE (*append)(VALUE, VALUE))
04232 {
04233 rb_objspace_t *objspace = &rb_objspace;
04234 size_t count = objspace->profile.count;
04235
04236 if (objspace->profile.run && count) {
04237 int index = 1;
04238 size_t i;
04239 gc_profile_record r;
04240 append(out, rb_sprintf("GC %"PRIuSIZE" invokes.\n", objspace->count));
04241 append(out, rb_str_new_cstr("Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms)\n"));
04242 for (i = 0; i < count; i++) {
04243 r = objspace->profile.record[i];
04244 #if !GC_PROFILE_MORE_DETAIL
04245 if (r.is_marked) {
04246 #endif
04247 append(out, rb_sprintf("%5d %19.3f %20"PRIuSIZE" %20"PRIuSIZE" %20"PRIuSIZE" %30.20f\n",
04248 index++, r.gc_invoke_time, r.heap_use_size,
04249 r.heap_total_size, r.heap_total_objects, r.gc_time*1000));
04250 #if !GC_PROFILE_MORE_DETAIL
04251 }
04252 #endif
04253 }
04254 #if GC_PROFILE_MORE_DETAIL
04255 append(out, rb_str_new_cstr("\n\n" \
04256 "More detail.\n" \
04257 "Index Allocate Increase Allocate Limit Use Slot Have Finalize Mark Time(ms) Sweep Time(ms)\n"));
04258 index = 1;
04259 for (i = 0; i < count; i++) {
04260 r = objspace->profile.record[i];
04261 append(out, rb_sprintf("%5d %17"PRIuSIZE" %17"PRIuSIZE" %9"PRIuSIZE" %14s %25.20f %25.20f\n",
04262 index++, r.allocate_increase, r.allocate_limit,
04263 r.heap_use_slots, (r.have_finalize ? "true" : "false"),
04264 r.gc_mark_time*1000, r.gc_sweep_time*1000));
04265 }
04266 #endif
04267 }
04268 }
04269
04270
04271
04272
04273
04274
04275
04276
04277
04278
04279
04280
04281 static VALUE
04282 gc_profile_result(void)
04283 {
04284 VALUE str = rb_str_buf_new(0);
04285 gc_profile_dump_on(str, rb_str_buf_append);
04286 return str;
04287 }
04288
04289
04290
04291
04292
04293
04294
04295
04296
04297
04298 static VALUE
04299 gc_profile_report(int argc, VALUE *argv, VALUE self)
04300 {
04301 VALUE out;
04302
04303 if (argc == 0) {
04304 out = rb_stdout;
04305 }
04306 else {
04307 rb_scan_args(argc, argv, "01", &out);
04308 }
04309 gc_profile_dump_on(out, rb_io_write);
04310
04311 return Qnil;
04312 }
04313
04314
04315
04316
04317
04318
04319
04320
04321 static VALUE
04322 gc_profile_total_time(VALUE self)
04323 {
04324 double time = 0;
04325 rb_objspace_t *objspace = &rb_objspace;
04326 size_t i;
04327
04328 if (objspace->profile.run && objspace->profile.count) {
04329 for (i = 0; i < objspace->profile.count; i++) {
04330 time += objspace->profile.record[i].gc_time;
04331 }
04332 }
04333 return DBL2NUM(time);
04334 }
04335
04336
04337
04338
04339
04340
04341
04342
04343 static VALUE
04344 gc_profile_enable_get(VALUE self)
04345 {
04346 rb_objspace_t *objspace = &rb_objspace;
04347 return objspace->profile.run ? Qtrue : Qfalse;
04348 }
04349
04350
04351
04352
04353
04354
04355
04356
04357
04358 static VALUE
04359 gc_profile_enable(void)
04360 {
04361 rb_objspace_t *objspace = &rb_objspace;
04362
04363 objspace->profile.run = TRUE;
04364 return Qnil;
04365 }
04366
04367
04368
04369
04370
04371
04372
04373
04374
04375 static VALUE
04376 gc_profile_disable(void)
04377 {
04378 rb_objspace_t *objspace = &rb_objspace;
04379
04380 objspace->profile.run = FALSE;
04381 return Qnil;
04382 }
04383
04384 #ifdef GC_DEBUG
04385
04386
04387
04388
04389
04390 void
04391 rb_gcdebug_print_obj_condition(VALUE obj)
04392 {
04393 rb_objspace_t *objspace = &rb_objspace;
04394
04395 if (is_pointer_to_heap(objspace, (void *)obj)) {
04396 fprintf(stderr, "pointer to heap?: true\n");
04397 }
04398 else {
04399 fprintf(stderr, "pointer to heap?: false\n");
04400 return;
04401 }
04402 fprintf(stderr, "marked?: %s\n",
04403 MARKED_IN_BITMAP(GET_HEAP_BITMAP(obj), obj) ? "true" : "false");
04404 if (is_lazy_sweeping(objspace)) {
04405 fprintf(stderr, "lazy sweeping?: true\n");
04406 fprintf(stderr, "swept?: %s\n",
04407 is_swept_object(objspace, obj) ? "done" : "not yet");
04408 }
04409 else {
04410 fprintf(stderr, "lazy sweeping?: false\n");
04411 }
04412 }
04413
04414 static VALUE
04415 gcdebug_sential(VALUE obj, VALUE name)
04416 {
04417 fprintf(stderr, "WARNING: object %s(%p) is inadvertently collected\n", (char *)name, (void *)obj);
04418 return Qnil;
04419 }
04420
04421 void
04422 rb_gcdebug_sentinel(VALUE obj, const char *name)
04423 {
04424 rb_define_final(obj, rb_proc_new(gcdebug_sential, (VALUE)name));
04425 }
04426 #endif
04427
04428
04429
04430
04431
04432
04433
04434
04435
04436
04437
04438
04439
04440
04441
04442
04443
04444
04445
04446
04447
04448
04449
04450
04451
04452
04453
04454
04455
04456
04457
04458
04459
04460
04461
04462
04463
04464
04465
04466
04467
04468
04469
04470
04471
04472
04473
04474
04475
04476
04477
04478
04479
04480
04481
04482
04483
04484
04485
04486
04487
04488
04489
04490
04491
04492
04493
04494
04495
04496
04497 void
04498 Init_GC(void)
04499 {
04500 VALUE rb_mObSpace;
04501 VALUE rb_mProfiler;
04502
04503 rb_mGC = rb_define_module("GC");
04504 rb_define_singleton_method(rb_mGC, "start", rb_gc_start, 0);
04505 rb_define_singleton_method(rb_mGC, "enable", rb_gc_enable, 0);
04506 rb_define_singleton_method(rb_mGC, "disable", rb_gc_disable, 0);
04507 rb_define_singleton_method(rb_mGC, "stress", gc_stress_get, 0);
04508 rb_define_singleton_method(rb_mGC, "stress=", gc_stress_set, 1);
04509 rb_define_singleton_method(rb_mGC, "count", gc_count, 0);
04510 rb_define_singleton_method(rb_mGC, "stat", gc_stat, -1);
04511 rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0);
04512
04513 rb_mProfiler = rb_define_module_under(rb_mGC, "Profiler");
04514 rb_define_singleton_method(rb_mProfiler, "enabled?", gc_profile_enable_get, 0);
04515 rb_define_singleton_method(rb_mProfiler, "enable", gc_profile_enable, 0);
04516 rb_define_singleton_method(rb_mProfiler, "raw_data", gc_profile_record_get, 0);
04517 rb_define_singleton_method(rb_mProfiler, "disable", gc_profile_disable, 0);
04518 rb_define_singleton_method(rb_mProfiler, "clear", gc_profile_clear, 0);
04519 rb_define_singleton_method(rb_mProfiler, "result", gc_profile_result, 0);
04520 rb_define_singleton_method(rb_mProfiler, "report", gc_profile_report, -1);
04521 rb_define_singleton_method(rb_mProfiler, "total_time", gc_profile_total_time, 0);
04522
04523 rb_mObSpace = rb_define_module("ObjectSpace");
04524 rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1);
04525 rb_define_module_function(rb_mObSpace, "garbage_collect", rb_gc_start, 0);
04526
04527 rb_define_module_function(rb_mObSpace, "define_finalizer", define_final, -1);
04528 rb_define_module_function(rb_mObSpace, "undefine_finalizer", undefine_final, 1);
04529
04530 rb_define_module_function(rb_mObSpace, "_id2ref", id2ref, 1);
04531
04532 nomem_error = rb_exc_new3(rb_eNoMemError,
04533 rb_obj_freeze(rb_str_new2("failed to allocate memory")));
04534 OBJ_TAINT(nomem_error);
04535 OBJ_FREEZE(nomem_error);
04536
04537 rb_define_method(rb_cBasicObject, "__id__", rb_obj_id, 0);
04538 rb_define_method(rb_mKernel, "object_id", rb_obj_id, 0);
04539
04540 rb_define_module_function(rb_mObSpace, "count_objects", count_objects, -1);
04541
04542 {
04543 VALUE rb_cWeakMap = rb_define_class_under(rb_mObSpace, "WeakMap", rb_cObject);
04544 rb_define_alloc_func(rb_cWeakMap, wmap_allocate);
04545 rb_define_method(rb_cWeakMap, "[]=", wmap_aset, 2);
04546 rb_define_method(rb_cWeakMap, "[]", wmap_aref, 1);
04547 rb_define_private_method(rb_cWeakMap, "finalize", wmap_finalize, 1);
04548 }
04549
04550 #if CALC_EXACT_MALLOC_SIZE
04551 rb_define_singleton_method(rb_mGC, "malloc_allocated_size", gc_malloc_allocated_size, 0);
04552 rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0);
04553 #endif
04554 }
04555