Ruby 1.9.3p362(2012-12-25revision38607)

gc.c

Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   gc.c -
00004 
00005   $Author: usa $
00006   created at: Tue Oct  5 09:44:46 JST 1993
00007 
00008   Copyright (C) 1993-2007 Yukihiro Matsumoto
00009   Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
00010   Copyright (C) 2000  Information-technology Promotion Agency, Japan
00011 
00012 **********************************************************************/
00013 
00014 #include "ruby/ruby.h"
00015 #include "ruby/st.h"
00016 #include "ruby/re.h"
00017 #include "ruby/io.h"
00018 #include "ruby/util.h"
00019 #include "eval_intern.h"
00020 #include "vm_core.h"
00021 #include "internal.h"
00022 #include "gc.h"
00023 #include "constant.h"
00024 #include "ruby_atomic.h"
00025 #include <stdio.h>
00026 #include <setjmp.h>
00027 #include <sys/types.h>
00028 
00029 #ifdef HAVE_SYS_TIME_H
00030 #include <sys/time.h>
00031 #endif
00032 
00033 #ifdef HAVE_SYS_RESOURCE_H
00034 #include <sys/resource.h>
00035 #endif
00036 
00037 #if defined _WIN32 || defined __CYGWIN__
00038 #include <windows.h>
00039 #endif
00040 
00041 #ifdef HAVE_VALGRIND_MEMCHECK_H
00042 # include <valgrind/memcheck.h>
00043 # ifndef VALGRIND_MAKE_MEM_DEFINED
00044 #  define VALGRIND_MAKE_MEM_DEFINED(p, n) VALGRIND_MAKE_READABLE((p), (n))
00045 # endif
00046 # ifndef VALGRIND_MAKE_MEM_UNDEFINED
00047 #  define VALGRIND_MAKE_MEM_UNDEFINED(p, n) VALGRIND_MAKE_WRITABLE((p), (n))
00048 # endif
00049 #else
00050 # define VALGRIND_MAKE_MEM_DEFINED(p, n) /* empty */
00051 # define VALGRIND_MAKE_MEM_UNDEFINED(p, n) /* empty */
00052 #endif
00053 
00054 #define rb_setjmp(env) RUBY_SETJMP(env)
00055 #define rb_jmp_buf rb_jmpbuf_t
00056 
00057 /* Make alloca work the best possible way.  */
00058 #ifdef __GNUC__
00059 # ifndef atarist
00060 #  ifndef alloca
00061 #   define alloca __builtin_alloca
00062 #  endif
00063 # endif /* atarist */
00064 #else
00065 # ifdef HAVE_ALLOCA_H
00066 #  include <alloca.h>
00067 # else
00068 #  ifdef _AIX
00069  #pragma alloca
00070 #  else
00071 #   ifndef alloca /* predefined by HP cc +Olibcalls */
00072 void *alloca ();
00073 #   endif
00074 #  endif /* AIX */
00075 # endif /* HAVE_ALLOCA_H */
00076 #endif /* __GNUC__ */
00077 
00078 #ifndef GC_MALLOC_LIMIT
00079 #define GC_MALLOC_LIMIT 8000000
00080 #endif
00081 #define HEAP_MIN_SLOTS 10000
00082 #define FREE_MIN  4096
00083 
00084 typedef struct {
00085     unsigned int initial_malloc_limit;
00086     unsigned int initial_heap_min_slots;
00087     unsigned int initial_free_min;
00088     int gc_stress;
00089 } ruby_gc_params_t;
00090 
00091 ruby_gc_params_t initial_params = {
00092     GC_MALLOC_LIMIT,
00093     HEAP_MIN_SLOTS,
00094     FREE_MIN,
00095 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00096     FALSE,
00097 #endif
00098 };
00099 
00100 #define nomem_error GET_VM()->special_exceptions[ruby_error_nomemory]
00101 
00102 #if SIZEOF_LONG == SIZEOF_VOIDP
00103 # define nonspecial_obj_id(obj) (VALUE)((SIGNED_VALUE)(obj)|FIXNUM_FLAG)
00104 # define obj_id_to_ref(objid) ((objid) ^ FIXNUM_FLAG) /* unset FIXNUM_FLAG */
00105 #elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
00106 # define nonspecial_obj_id(obj) LL2NUM((SIGNED_VALUE)(obj) / 2)
00107 # define obj_id_to_ref(objid) (FIXNUM_P(objid) ? \
00108    ((objid) ^ FIXNUM_FLAG) : (NUM2PTR(objid) << 1))
00109 #else
00110 # error not supported
00111 #endif
00112 
00113 int ruby_gc_debug_indent = 0;
00114 
00115 /* for GC profile */
00116 #define GC_PROFILE_MORE_DETAIL 0
00117 typedef struct gc_profile_record {
00118     double gc_time;
00119     double gc_mark_time;
00120     double gc_sweep_time;
00121     double gc_invoke_time;
00122 
00123     size_t heap_use_slots;
00124     size_t heap_live_objects;
00125     size_t heap_free_objects;
00126     size_t heap_total_objects;
00127     size_t heap_use_size;
00128     size_t heap_total_size;
00129 
00130     int have_finalize;
00131     int is_marked;
00132 
00133     size_t allocate_increase;
00134     size_t allocate_limit;
00135 } gc_profile_record;
00136 
00137 static double
00138 getrusage_time(void)
00139 {
00140 #ifdef RUSAGE_SELF
00141     struct rusage usage;
00142     struct timeval time;
00143     getrusage(RUSAGE_SELF, &usage);
00144     time = usage.ru_utime;
00145     return time.tv_sec + time.tv_usec * 1e-6;
00146 #elif defined _WIN32
00147     FILETIME creation_time, exit_time, kernel_time, user_time;
00148     ULARGE_INTEGER ui;
00149     LONG_LONG q;
00150     double t;
00151 
00152     if (GetProcessTimes(GetCurrentProcess(),
00153                         &creation_time, &exit_time, &kernel_time, &user_time) == 0)
00154     {
00155         return 0.0;
00156     }
00157     memcpy(&ui, &user_time, sizeof(FILETIME));
00158     q = ui.QuadPart / 10L;
00159     t = (DWORD)(q % 1000000L) * 1e-6;
00160     q /= 1000000L;
00161 #ifdef __GNUC__
00162     t += q;
00163 #else
00164     t += (double)(DWORD)(q >> 16) * (1 << 16);
00165     t += (DWORD)q & ~(~0 << 16);
00166 #endif
00167     return t;
00168 #else
00169     return 0.0;
00170 #endif
00171 }
00172 
00173 #define GC_PROF_TIMER_START do {\
00174         if (objspace->profile.run) {\
00175             if (!objspace->profile.record) {\
00176                 objspace->profile.size = 1000;\
00177                 objspace->profile.record = malloc(sizeof(gc_profile_record) * objspace->profile.size);\
00178             }\
00179             if (count >= objspace->profile.size) {\
00180                 objspace->profile.size += 1000;\
00181                 objspace->profile.record = realloc(objspace->profile.record, sizeof(gc_profile_record) * objspace->profile.size);\
00182             }\
00183             if (!objspace->profile.record) {\
00184                 rb_bug("gc_profile malloc or realloc miss");\
00185             }\
00186             MEMZERO(&objspace->profile.record[count], gc_profile_record, 1);\
00187             gc_time = getrusage_time();\
00188             objspace->profile.record[count].gc_invoke_time = gc_time - objspace->profile.invoke_time;\
00189         }\
00190     } while(0)
00191 
00192 #define GC_PROF_TIMER_STOP(marked) do {\
00193         if (objspace->profile.run) {\
00194             gc_time = getrusage_time() - gc_time;\
00195             if (gc_time < 0) gc_time = 0;\
00196             objspace->profile.record[count].gc_time = gc_time;\
00197             objspace->profile.record[count].is_marked = !!(marked);\
00198             GC_PROF_SET_HEAP_INFO(objspace->profile.record[count]);\
00199             objspace->profile.count++;\
00200         }\
00201     } while(0)
00202 
00203 #if GC_PROFILE_MORE_DETAIL
00204 #define INIT_GC_PROF_PARAMS double gc_time = 0, sweep_time = 0;\
00205     size_t count = objspace->profile.count, total = 0, live = 0
00206 
00207 #define GC_PROF_MARK_TIMER_START double mark_time = 0;\
00208     do {\
00209         if (objspace->profile.run) {\
00210             mark_time = getrusage_time();\
00211         }\
00212     } while(0)
00213 
00214 #define GC_PROF_MARK_TIMER_STOP do {\
00215         if (objspace->profile.run) {\
00216             mark_time = getrusage_time() - mark_time;\
00217             if (mark_time < 0) mark_time = 0;\
00218             objspace->profile.record[objspace->profile.count].gc_mark_time = mark_time;\
00219         }\
00220     } while(0)
00221 
00222 #define GC_PROF_SWEEP_TIMER_START do {\
00223         if (objspace->profile.run) {\
00224             sweep_time = getrusage_time();\
00225         }\
00226     } while(0)
00227 
00228 #define GC_PROF_SWEEP_TIMER_STOP do {\
00229         if (objspace->profile.run) {\
00230             sweep_time = getrusage_time() - sweep_time;\
00231             if (sweep_time < 0) sweep_time = 0;\
00232             objspace->profile.record[count].gc_sweep_time = sweep_time;\
00233         }\
00234     } while(0)
00235 #define GC_PROF_SET_MALLOC_INFO do {\
00236         if (objspace->profile.run) {\
00237             gc_profile_record *record = &objspace->profile.record[objspace->profile.count];\
00238             record->allocate_increase = malloc_increase;\
00239             record->allocate_limit = malloc_limit; \
00240         }\
00241     } while(0)
00242 #define GC_PROF_SET_HEAP_INFO(record) do {\
00243         live = objspace->heap.live_num;\
00244         total = heaps_used * HEAP_OBJ_LIMIT;\
00245         (record).heap_use_slots = heaps_used;\
00246         (record).heap_live_objects = live;\
00247         (record).heap_free_objects = total - live;\
00248         (record).heap_total_objects = total;\
00249         (record).have_finalize = deferred_final_list ? Qtrue : Qfalse;\
00250         (record).heap_use_size = live * sizeof(RVALUE);\
00251         (record).heap_total_size = total * sizeof(RVALUE);\
00252     } while(0)
00253 #define GC_PROF_INC_LIVE_NUM objspace->heap.live_num++
00254 #define GC_PROF_DEC_LIVE_NUM objspace->heap.live_num--
00255 #else
00256 #define INIT_GC_PROF_PARAMS double gc_time = 0;\
00257     size_t count = objspace->profile.count, total = 0, live = 0
00258 #define GC_PROF_MARK_TIMER_START
00259 #define GC_PROF_MARK_TIMER_STOP
00260 #define GC_PROF_SWEEP_TIMER_START
00261 #define GC_PROF_SWEEP_TIMER_STOP
00262 #define GC_PROF_SET_MALLOC_INFO
00263 #define GC_PROF_SET_HEAP_INFO(record) do {\
00264         live = objspace->heap.live_num;\
00265         total = heaps_used * HEAP_OBJ_LIMIT;\
00266         (record).heap_total_objects = total;\
00267         (record).heap_use_size = live * sizeof(RVALUE);\
00268         (record).heap_total_size = total * sizeof(RVALUE);\
00269     } while(0)
00270 #define GC_PROF_INC_LIVE_NUM
00271 #define GC_PROF_DEC_LIVE_NUM
00272 #endif
00273 
00274 
00275 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
00276 #pragma pack(push, 1) /* magic for reducing sizeof(RVALUE): 24 -> 20 */
00277 #endif
00278 
00279 typedef struct RVALUE {
00280     union {
00281         struct {
00282             VALUE flags;                /* always 0 for freed obj */
00283             struct RVALUE *next;
00284         } free;
00285         struct RBasic  basic;
00286         struct RObject object;
00287         struct RClass  klass;
00288         struct RFloat  flonum;
00289         struct RString string;
00290         struct RArray  array;
00291         struct RRegexp regexp;
00292         struct RHash   hash;
00293         struct RData   data;
00294         struct RTypedData   typeddata;
00295         struct RStruct rstruct;
00296         struct RBignum bignum;
00297         struct RFile   file;
00298         struct RNode   node;
00299         struct RMatch  match;
00300         struct RRational rational;
00301         struct RComplex complex;
00302     } as;
00303 #ifdef GC_DEBUG
00304     const char *file;
00305     int   line;
00306 #endif
00307 } RVALUE;
00308 
00309 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
00310 #pragma pack(pop)
00311 #endif
00312 
00313 struct heaps_slot {
00314     void *membase;
00315     RVALUE *slot;
00316     size_t limit;
00317     struct heaps_slot *next;
00318     struct heaps_slot *prev;
00319 };
00320 
00321 struct sorted_heaps_slot {
00322     RVALUE *start;
00323     RVALUE *end;
00324     struct heaps_slot *slot;
00325 };
00326 
00327 struct gc_list {
00328     VALUE *varptr;
00329     struct gc_list *next;
00330 };
00331 
00332 #define STACK_CHUNK_SIZE 500
00333 
00334 typedef struct stack_chunk {
00335     VALUE data[STACK_CHUNK_SIZE];
00336     struct stack_chunk *next;
00337 } stack_chunk_t;
00338 
00339 typedef struct mark_stack {
00340     stack_chunk_t *chunk;
00341     stack_chunk_t *cache;
00342     size_t index;
00343     size_t limit;
00344     size_t cache_size;
00345     size_t unused_cache_size;
00346 } mark_stack_t;
00347 
00348 #define CALC_EXACT_MALLOC_SIZE 0
00349 
00350 typedef struct rb_objspace {
00351     struct {
00352         size_t limit;
00353         size_t increase;
00354 #if CALC_EXACT_MALLOC_SIZE
00355         size_t allocated_size;
00356         size_t allocations;
00357 #endif
00358     } malloc_params;
00359     struct {
00360         size_t increment;
00361         struct heaps_slot *ptr;
00362         struct heaps_slot *sweep_slots;
00363         struct sorted_heaps_slot *sorted;
00364         size_t length;
00365         size_t used;
00366         RVALUE *freelist;
00367         RVALUE *range[2];
00368         RVALUE *freed;
00369         size_t live_num;
00370         size_t free_num;
00371         size_t free_min;
00372         size_t final_num;
00373         size_t do_heap_free;
00374     } heap;
00375     struct {
00376         int dont_gc;
00377         int dont_lazy_sweep;
00378         int during_gc;
00379     } flags;
00380     struct {
00381         st_table *table;
00382         RVALUE *deferred;
00383     } final;
00384     mark_stack_t mark_stack;
00385     struct {
00386         int run;
00387         gc_profile_record *record;
00388         size_t count;
00389         size_t size;
00390         double invoke_time;
00391     } profile;
00392     struct gc_list *global_list;
00393     size_t count;
00394     int gc_stress;
00395 } rb_objspace_t;
00396 
00397 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00398 #define rb_objspace (*GET_VM()->objspace)
00399 #define ruby_initial_gc_stress  initial_params.gc_stress
00400 int *ruby_initial_gc_stress_ptr = &ruby_initial_gc_stress;
00401 #else
00402 static rb_objspace_t rb_objspace = {{GC_MALLOC_LIMIT}, {HEAP_MIN_SLOTS}};
00403 int *ruby_initial_gc_stress_ptr = &rb_objspace.gc_stress;
00404 #endif
00405 #define malloc_limit            objspace->malloc_params.limit
00406 #define malloc_increase         objspace->malloc_params.increase
00407 #define heaps                   objspace->heap.ptr
00408 #define heaps_length            objspace->heap.length
00409 #define heaps_used              objspace->heap.used
00410 #define freelist                objspace->heap.freelist
00411 #define lomem                   objspace->heap.range[0]
00412 #define himem                   objspace->heap.range[1]
00413 #define heaps_inc               objspace->heap.increment
00414 #define heaps_freed             objspace->heap.freed
00415 #define dont_gc                 objspace->flags.dont_gc
00416 #define during_gc               objspace->flags.during_gc
00417 #define finalizer_table         objspace->final.table
00418 #define deferred_final_list     objspace->final.deferred
00419 #define global_List             objspace->global_list
00420 #define ruby_gc_stress          objspace->gc_stress
00421 #define initial_malloc_limit    initial_params.initial_malloc_limit
00422 #define initial_heap_min_slots  initial_params.initial_heap_min_slots
00423 #define initial_free_min        initial_params.initial_free_min
00424 
00425 static void rb_objspace_call_finalizer(rb_objspace_t *objspace);
00426 
00427 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00428 rb_objspace_t *
00429 rb_objspace_alloc(void)
00430 {
00431     rb_objspace_t *objspace = malloc(sizeof(rb_objspace_t));
00432     memset(objspace, 0, sizeof(*objspace));
00433     malloc_limit = initial_malloc_limit;
00434     ruby_gc_stress = ruby_initial_gc_stress;
00435 
00436     return objspace;
00437 }
00438 #endif
00439 
00440 static void initial_expand_heap(rb_objspace_t *objspace);
00441 static void init_mark_stack(mark_stack_t *stack);
00442 
00443 void
00444 rb_gc_set_params(void)
00445 {
00446     char *malloc_limit_ptr, *heap_min_slots_ptr, *free_min_ptr;
00447 
00448     if (rb_safe_level() > 0) return;
00449 
00450     malloc_limit_ptr = getenv("RUBY_GC_MALLOC_LIMIT");
00451     if (malloc_limit_ptr != NULL) {
00452         int malloc_limit_i = atoi(malloc_limit_ptr);
00453         if (RTEST(ruby_verbose))
00454             fprintf(stderr, "malloc_limit=%d (%d)\n",
00455                     malloc_limit_i, initial_malloc_limit);
00456         if (malloc_limit_i > 0) {
00457             initial_malloc_limit = malloc_limit_i;
00458         }
00459     }
00460 
00461     heap_min_slots_ptr = getenv("RUBY_HEAP_MIN_SLOTS");
00462     if (heap_min_slots_ptr != NULL) {
00463         int heap_min_slots_i = atoi(heap_min_slots_ptr);
00464         if (RTEST(ruby_verbose))
00465             fprintf(stderr, "heap_min_slots=%d (%d)\n",
00466                     heap_min_slots_i, initial_heap_min_slots);
00467         if (heap_min_slots_i > 0) {
00468             initial_heap_min_slots = heap_min_slots_i;
00469             initial_expand_heap(&rb_objspace);
00470         }
00471     }
00472 
00473     free_min_ptr = getenv("RUBY_FREE_MIN");
00474     if (free_min_ptr != NULL) {
00475         int free_min_i = atoi(free_min_ptr);
00476         if (RTEST(ruby_verbose))
00477             fprintf(stderr, "free_min=%d (%d)\n", free_min_i, initial_free_min);
00478         if (free_min_i > 0) {
00479             initial_free_min = free_min_i;
00480         }
00481     }
00482 }
00483 
00484 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
00485 static void gc_sweep(rb_objspace_t *);
00486 static void slot_sweep(rb_objspace_t *, struct heaps_slot *);
00487 static void rest_sweep(rb_objspace_t *);
00488 static void free_stack_chunks(mark_stack_t *);
00489 
00490 void
00491 rb_objspace_free(rb_objspace_t *objspace)
00492 {
00493     rest_sweep(objspace);
00494     if (objspace->profile.record) {
00495         free(objspace->profile.record);
00496         objspace->profile.record = 0;
00497     }
00498     if (global_List) {
00499         struct gc_list *list, *next;
00500         for (list = global_List; list; list = next) {
00501             next = list->next;
00502             free(list);
00503         }
00504     }
00505     if (objspace->heap.sorted) {
00506         size_t i;
00507         for (i = 0; i < heaps_used; ++i) {
00508             free(objspace->heap.sorted[i].slot->membase);
00509             free(objspace->heap.sorted[i].slot);
00510         }
00511         free(objspace->heap.sorted);
00512         heaps_used = 0;
00513         heaps = 0;
00514     }
00515     free_stack_chunks(&objspace->mark_stack);
00516     free(objspace);
00517 }
00518 #endif
00519 
00520 /* tiny heap size */
00521 /* 32KB */
00522 /*#define HEAP_SIZE 0x8000 */
00523 /* 128KB */
00524 /*#define HEAP_SIZE 0x20000 */
00525 /* 64KB */
00526 /*#define HEAP_SIZE 0x10000 */
00527 /* 16KB */
00528 #define HEAP_SIZE 0x4000
00529 /* 8KB */
00530 /*#define HEAP_SIZE 0x2000 */
00531 /* 4KB */
00532 /*#define HEAP_SIZE 0x1000 */
00533 /* 2KB */
00534 /*#define HEAP_SIZE 0x800 */
00535 
00536 #define HEAP_OBJ_LIMIT (unsigned int)(HEAP_SIZE / sizeof(struct RVALUE))
00537 
00538 extern st_table *rb_class_tbl;
00539 
00540 int ruby_disable_gc_stress = 0;
00541 
00542 static void run_final(rb_objspace_t *objspace, VALUE obj);
00543 static int garbage_collect(rb_objspace_t *objspace);
00544 static int gc_lazy_sweep(rb_objspace_t *objspace);
00545 
00546 void
00547 rb_global_variable(VALUE *var)
00548 {
00549     rb_gc_register_address(var);
00550 }
00551 
00552 static void *
00553 ruby_memerror_body(void *dummy)
00554 {
00555     rb_memerror();
00556     return 0;
00557 }
00558 
00559 static void
00560 ruby_memerror(void)
00561 {
00562     if (ruby_thread_has_gvl_p()) {
00563         rb_memerror();
00564     }
00565     else {
00566         if (ruby_native_thread_p()) {
00567             rb_thread_call_with_gvl(ruby_memerror_body, 0);
00568         }
00569         else {
00570             /* no ruby thread */
00571             fprintf(stderr, "[FATAL] failed to allocate memory\n");
00572             exit(EXIT_FAILURE);
00573         }
00574     }
00575 }
00576 
00577 void
00578 rb_memerror(void)
00579 {
00580     rb_thread_t *th = GET_THREAD();
00581     if (!nomem_error ||
00582         (rb_thread_raised_p(th, RAISED_NOMEMORY) && rb_safe_level() < 4)) {
00583         fprintf(stderr, "[FATAL] failed to allocate memory\n");
00584         exit(EXIT_FAILURE);
00585     }
00586     if (rb_thread_raised_p(th, RAISED_NOMEMORY)) {
00587         rb_thread_raised_clear(th);
00588         GET_THREAD()->errinfo = nomem_error;
00589         JUMP_TAG(TAG_RAISE);
00590     }
00591     rb_thread_raised_set(th, RAISED_NOMEMORY);
00592     rb_exc_raise(nomem_error);
00593 }
00594 
00595 /*
00596  *  call-seq:
00597  *    GC.stress                 -> true or false
00598  *
00599  *  returns current status of GC stress mode.
00600  */
00601 
00602 static VALUE
00603 gc_stress_get(VALUE self)
00604 {
00605     rb_objspace_t *objspace = &rb_objspace;
00606     return ruby_gc_stress ? Qtrue : Qfalse;
00607 }
00608 
00609 /*
00610  *  call-seq:
00611  *    GC.stress = bool          -> bool
00612  *
00613  *  Updates the GC stress mode.
00614  *
00615  *  When stress mode is enabled the GC is invoked at every GC opportunity:
00616  *  all memory and object allocations.
00617  *
00618  *  Enabling stress mode makes Ruby very slow, it is only for debugging.
00619  */
00620 
00621 static VALUE
00622 gc_stress_set(VALUE self, VALUE flag)
00623 {
00624     rb_objspace_t *objspace = &rb_objspace;
00625     rb_secure(2);
00626     ruby_gc_stress = RTEST(flag);
00627     return flag;
00628 }
00629 
00630 /*
00631  *  call-seq:
00632  *    GC::Profiler.enable?                 -> true or false
00633  *
00634  *  The current status of GC profile mode.
00635  */
00636 
00637 static VALUE
00638 gc_profile_enable_get(VALUE self)
00639 {
00640     rb_objspace_t *objspace = &rb_objspace;
00641     return objspace->profile.run ? Qtrue : Qfalse;
00642 }
00643 
00644 /*
00645  *  call-seq:
00646  *    GC::Profiler.enable          -> nil
00647  *
00648  *  Starts the GC profiler.
00649  *
00650  */
00651 
00652 static VALUE
00653 gc_profile_enable(void)
00654 {
00655     rb_objspace_t *objspace = &rb_objspace;
00656 
00657     objspace->profile.run = TRUE;
00658     return Qnil;
00659 }
00660 
00661 /*
00662  *  call-seq:
00663  *    GC::Profiler.disable          -> nil
00664  *
00665  *  Stops the GC profiler.
00666  *
00667  */
00668 
00669 static VALUE
00670 gc_profile_disable(void)
00671 {
00672     rb_objspace_t *objspace = &rb_objspace;
00673 
00674     objspace->profile.run = FALSE;
00675     return Qnil;
00676 }
00677 
00678 /*
00679  *  call-seq:
00680  *    GC::Profiler.clear          -> nil
00681  *
00682  *  Clears the GC profiler data.
00683  *
00684  */
00685 
00686 static VALUE
00687 gc_profile_clear(void)
00688 {
00689     rb_objspace_t *objspace = &rb_objspace;
00690     MEMZERO(objspace->profile.record, gc_profile_record, objspace->profile.size);
00691     objspace->profile.count = 0;
00692     return Qnil;
00693 }
00694 
00695 static void *
00696 negative_size_allocation_error_with_gvl(void *ptr)
00697 {
00698     rb_raise(rb_eNoMemError, "%s", (const char *)ptr);
00699     return 0; /* should not be reached */
00700 }
00701 
00702 static void
00703 negative_size_allocation_error(const char *msg)
00704 {
00705     if (ruby_thread_has_gvl_p()) {
00706         rb_raise(rb_eNoMemError, "%s", msg);
00707     }
00708     else {
00709         if (ruby_native_thread_p()) {
00710             rb_thread_call_with_gvl(negative_size_allocation_error_with_gvl, (void *)msg);
00711         }
00712         else {
00713             fprintf(stderr, "[FATAL] %s\n", msg);
00714             exit(EXIT_FAILURE);
00715         }
00716     }
00717 }
00718 
00719 static void *
00720 gc_with_gvl(void *ptr)
00721 {
00722     return (void *)(VALUE)garbage_collect((rb_objspace_t *)ptr);
00723 }
00724 
00725 static int
00726 garbage_collect_with_gvl(rb_objspace_t *objspace)
00727 {
00728     if (dont_gc) return TRUE;
00729     if (ruby_thread_has_gvl_p()) {
00730         return garbage_collect(objspace);
00731     }
00732     else {
00733         if (ruby_native_thread_p()) {
00734             return (int)(VALUE)rb_thread_call_with_gvl(gc_with_gvl, (void *)objspace);
00735         }
00736         else {
00737             /* no ruby thread */
00738             fprintf(stderr, "[FATAL] failed to allocate memory\n");
00739             exit(EXIT_FAILURE);
00740         }
00741     }
00742 }
00743 
00744 static void vm_xfree(rb_objspace_t *objspace, void *ptr);
00745 
00746 static inline size_t
00747 vm_malloc_prepare(rb_objspace_t *objspace, size_t size)
00748 {
00749     if ((ssize_t)size < 0) {
00750         negative_size_allocation_error("negative allocation size (or too big)");
00751     }
00752     if (size == 0) size = 1;
00753 
00754 #if CALC_EXACT_MALLOC_SIZE
00755     size += sizeof(size_t);
00756 #endif
00757 
00758     if ((ruby_gc_stress && !ruby_disable_gc_stress) ||
00759         (malloc_increase+size) > malloc_limit) {
00760         garbage_collect_with_gvl(objspace);
00761     }
00762 
00763     return size;
00764 }
00765 
00766 static inline void *
00767 vm_malloc_fixup(rb_objspace_t *objspace, void *mem, size_t size)
00768 {
00769     malloc_increase += size;
00770 
00771 #if CALC_EXACT_MALLOC_SIZE
00772     objspace->malloc_params.allocated_size += size;
00773     objspace->malloc_params.allocations++;
00774     ((size_t *)mem)[0] = size;
00775     mem = (size_t *)mem + 1;
00776 #endif
00777 
00778     return mem;
00779 }
00780 
00781 #define TRY_WITH_GC(alloc) do { \
00782         if (!(alloc) && \
00783             (!garbage_collect_with_gvl(objspace) || \
00784              !(alloc))) { \
00785             ruby_memerror(); \
00786         } \
00787     } while (0)
00788 
00789 static void *
00790 vm_xmalloc(rb_objspace_t *objspace, size_t size)
00791 {
00792     void *mem;
00793 
00794     size = vm_malloc_prepare(objspace, size);
00795     TRY_WITH_GC(mem = malloc(size));
00796     return vm_malloc_fixup(objspace, mem, size);
00797 }
00798 
00799 static void *
00800 vm_xrealloc(rb_objspace_t *objspace, void *ptr, size_t size)
00801 {
00802     void *mem;
00803 
00804     if ((ssize_t)size < 0) {
00805         negative_size_allocation_error("negative re-allocation size");
00806     }
00807     if (!ptr) return vm_xmalloc(objspace, size);
00808     if (size == 0) {
00809         vm_xfree(objspace, ptr);
00810         return 0;
00811     }
00812     if (ruby_gc_stress && !ruby_disable_gc_stress)
00813         garbage_collect_with_gvl(objspace);
00814 
00815 #if CALC_EXACT_MALLOC_SIZE
00816     size += sizeof(size_t);
00817     objspace->malloc_params.allocated_size -= size;
00818     ptr = (size_t *)ptr - 1;
00819 #endif
00820 
00821     mem = realloc(ptr, size);
00822     if (!mem) {
00823         if (garbage_collect_with_gvl(objspace)) {
00824             mem = realloc(ptr, size);
00825         }
00826         if (!mem) {
00827             ruby_memerror();
00828         }
00829     }
00830     malloc_increase += size;
00831 
00832 #if CALC_EXACT_MALLOC_SIZE
00833     objspace->malloc_params.allocated_size += size;
00834     ((size_t *)mem)[0] = size;
00835     mem = (size_t *)mem + 1;
00836 #endif
00837 
00838     return mem;
00839 }
00840 
00841 static void
00842 vm_xfree(rb_objspace_t *objspace, void *ptr)
00843 {
00844 #if CALC_EXACT_MALLOC_SIZE
00845     size_t size;
00846     ptr = ((size_t *)ptr) - 1;
00847     size = ((size_t*)ptr)[0];
00848     objspace->malloc_params.allocated_size -= size;
00849     objspace->malloc_params.allocations--;
00850 #endif
00851 
00852     free(ptr);
00853 }
00854 
00855 void *
00856 ruby_xmalloc(size_t size)
00857 {
00858     return vm_xmalloc(&rb_objspace, size);
00859 }
00860 
00861 static inline size_t
00862 xmalloc2_size(size_t n, size_t size)
00863 {
00864     size_t len = size * n;
00865     if (n != 0 && size != len / n) {
00866         rb_raise(rb_eArgError, "malloc: possible integer overflow");
00867     }
00868     return len;
00869 }
00870 
00871 void *
00872 ruby_xmalloc2(size_t n, size_t size)
00873 {
00874     return vm_xmalloc(&rb_objspace, xmalloc2_size(n, size));
00875 }
00876 
00877 static void *
00878 vm_xcalloc(rb_objspace_t *objspace, size_t count, size_t elsize)
00879 {
00880     void *mem;
00881     size_t size;
00882 
00883     size = xmalloc2_size(count, elsize);
00884     size = vm_malloc_prepare(objspace, size);
00885 
00886     TRY_WITH_GC(mem = calloc(1, size));
00887     return vm_malloc_fixup(objspace, mem, size);
00888 }
00889 
00890 void *
00891 ruby_xcalloc(size_t n, size_t size)
00892 {
00893     return vm_xcalloc(&rb_objspace, n, size);
00894 }
00895 
00896 void *
00897 ruby_xrealloc(void *ptr, size_t size)
00898 {
00899     return vm_xrealloc(&rb_objspace, ptr, size);
00900 }
00901 
00902 void *
00903 ruby_xrealloc2(void *ptr, size_t n, size_t size)
00904 {
00905     size_t len = size * n;
00906     if (n != 0 && size != len / n) {
00907         rb_raise(rb_eArgError, "realloc: possible integer overflow");
00908     }
00909     return ruby_xrealloc(ptr, len);
00910 }
00911 
00912 void
00913 ruby_xfree(void *x)
00914 {
00915     if (x)
00916         vm_xfree(&rb_objspace, x);
00917 }
00918 
00919 
00920 /*
00921  *  call-seq:
00922  *     GC.enable    -> true or false
00923  *
00924  *  Enables garbage collection, returning <code>true</code> if garbage
00925  *  collection was previously disabled.
00926  *
00927  *     GC.disable   #=> false
00928  *     GC.enable    #=> true
00929  *     GC.enable    #=> false
00930  *
00931  */
00932 
00933 VALUE
00934 rb_gc_enable(void)
00935 {
00936     rb_objspace_t *objspace = &rb_objspace;
00937     int old = dont_gc;
00938 
00939     dont_gc = FALSE;
00940     return old ? Qtrue : Qfalse;
00941 }
00942 
00943 /*
00944  *  call-seq:
00945  *     GC.disable    -> true or false
00946  *
00947  *  Disables garbage collection, returning <code>true</code> if garbage
00948  *  collection was already disabled.
00949  *
00950  *     GC.disable   #=> false
00951  *     GC.disable   #=> true
00952  *
00953  */
00954 
00955 VALUE
00956 rb_gc_disable(void)
00957 {
00958     rb_objspace_t *objspace = &rb_objspace;
00959     int old = dont_gc;
00960 
00961     dont_gc = TRUE;
00962     return old ? Qtrue : Qfalse;
00963 }
00964 
00965 VALUE rb_mGC;
00966 
00967 void
00968 rb_gc_register_mark_object(VALUE obj)
00969 {
00970     VALUE ary = GET_THREAD()->vm->mark_object_ary;
00971     rb_ary_push(ary, obj);
00972 }
00973 
00974 void
00975 rb_gc_register_address(VALUE *addr)
00976 {
00977     rb_objspace_t *objspace = &rb_objspace;
00978     struct gc_list *tmp;
00979 
00980     tmp = ALLOC(struct gc_list);
00981     tmp->next = global_List;
00982     tmp->varptr = addr;
00983     global_List = tmp;
00984 }
00985 
00986 void
00987 rb_gc_unregister_address(VALUE *addr)
00988 {
00989     rb_objspace_t *objspace = &rb_objspace;
00990     struct gc_list *tmp = global_List;
00991 
00992     if (tmp->varptr == addr) {
00993         global_List = tmp->next;
00994         xfree(tmp);
00995         return;
00996     }
00997     while (tmp->next) {
00998         if (tmp->next->varptr == addr) {
00999             struct gc_list *t = tmp->next;
01000 
01001             tmp->next = tmp->next->next;
01002             xfree(t);
01003             break;
01004         }
01005         tmp = tmp->next;
01006     }
01007 }
01008 
01009 
01010 static void
01011 allocate_sorted_heaps(rb_objspace_t *objspace, size_t next_heaps_length)
01012 {
01013     struct sorted_heaps_slot *p;
01014     size_t size;
01015 
01016     size = next_heaps_length*sizeof(struct sorted_heaps_slot);
01017 
01018     if (heaps_used > 0) {
01019         p = (struct sorted_heaps_slot *)realloc(objspace->heap.sorted, size);
01020         if (p) objspace->heap.sorted = p;
01021     }
01022     else {
01023         p = objspace->heap.sorted = (struct sorted_heaps_slot *)malloc(size);
01024     }
01025 
01026     if (p == 0) {
01027         during_gc = 0;
01028         rb_memerror();
01029     }
01030     heaps_length = next_heaps_length;
01031 }
01032 
01033 static void
01034 assign_heap_slot(rb_objspace_t *objspace)
01035 {
01036     RVALUE *p, *pend, *membase;
01037     struct heaps_slot *slot;
01038     size_t hi, lo, mid;
01039     size_t objs;
01040 
01041     objs = HEAP_OBJ_LIMIT;
01042     p = (RVALUE*)malloc(HEAP_SIZE);
01043     if (p == 0) {
01044         during_gc = 0;
01045         rb_memerror();
01046     }
01047     slot = (struct heaps_slot *)malloc(sizeof(struct heaps_slot));
01048     if (slot == 0) {
01049         xfree(p);
01050         during_gc = 0;
01051         rb_memerror();
01052     }
01053     MEMZERO((void*)slot, struct heaps_slot, 1);
01054 
01055     slot->next = heaps;
01056     if (heaps) heaps->prev = slot;
01057     heaps = slot;
01058 
01059     membase = p;
01060     if ((VALUE)p % sizeof(RVALUE) != 0) {
01061         p = (RVALUE*)((VALUE)p + sizeof(RVALUE) - ((VALUE)p % sizeof(RVALUE)));
01062         if ((HEAP_SIZE - HEAP_OBJ_LIMIT * sizeof(RVALUE)) < (size_t)((char*)p - (char*)membase)) {
01063             objs--;
01064         }
01065     }
01066 
01067     lo = 0;
01068     hi = heaps_used;
01069     while (lo < hi) {
01070         register RVALUE *mid_membase;
01071         mid = (lo + hi) / 2;
01072         mid_membase = objspace->heap.sorted[mid].slot->membase;
01073         if (mid_membase < membase) {
01074             lo = mid + 1;
01075         }
01076         else if (mid_membase > membase) {
01077             hi = mid;
01078         }
01079         else {
01080             rb_bug("same heap slot is allocated: %p at %"PRIuVALUE, (void *)membase, (VALUE)mid);
01081         }
01082     }
01083     if (hi < heaps_used) {
01084         MEMMOVE(&objspace->heap.sorted[hi+1], &objspace->heap.sorted[hi], struct sorted_heaps_slot, heaps_used - hi);
01085     }
01086     objspace->heap.sorted[hi].slot = slot;
01087     objspace->heap.sorted[hi].start = p;
01088     objspace->heap.sorted[hi].end = (p + objs);
01089     heaps->membase = membase;
01090     heaps->slot = p;
01091     heaps->limit = objs;
01092     objspace->heap.free_num += objs;
01093     pend = p + objs;
01094     if (lomem == 0 || lomem > p) lomem = p;
01095     if (himem < pend) himem = pend;
01096     heaps_used++;
01097 
01098     while (p < pend) {
01099         p->as.free.flags = 0;
01100         p->as.free.next = freelist;
01101         freelist = p;
01102         p++;
01103     }
01104 }
01105 
01106 static void
01107 add_heap_slots(rb_objspace_t *objspace, size_t add)
01108 {
01109     size_t i;
01110 
01111     if ((heaps_used + add) > heaps_length) {
01112         allocate_sorted_heaps(objspace, heaps_used + add);
01113     }
01114 
01115     for (i = 0; i < add; i++) {
01116         assign_heap_slot(objspace);
01117     }
01118     heaps_inc = 0;
01119 }
01120 
01121 static void
01122 init_heap(rb_objspace_t *objspace)
01123 {
01124     add_heap_slots(objspace, HEAP_MIN_SLOTS / HEAP_OBJ_LIMIT);
01125     init_mark_stack(&objspace->mark_stack);
01126 #ifdef USE_SIGALTSTACK
01127     {
01128         /* altstack of another threads are allocated in another place */
01129         rb_thread_t *th = GET_THREAD();
01130         void *tmp = th->altstack;
01131         th->altstack = malloc(ALT_STACK_SIZE);
01132         free(tmp); /* free previously allocated area */
01133     }
01134 #endif
01135 
01136     objspace->profile.invoke_time = getrusage_time();
01137     finalizer_table = st_init_numtable();
01138 }
01139 
01140 static void
01141 initial_expand_heap(rb_objspace_t *objspace)
01142 {
01143     size_t min_size = initial_heap_min_slots / HEAP_OBJ_LIMIT;
01144 
01145     if (min_size > heaps_used) {
01146         add_heap_slots(objspace, min_size - heaps_used);
01147     }
01148 }
01149 
01150 static void
01151 set_heaps_increment(rb_objspace_t *objspace)
01152 {
01153     size_t next_heaps_length = (size_t)(heaps_used * 1.8);
01154 
01155     if (next_heaps_length == heaps_used) {
01156         next_heaps_length++;
01157     }
01158 
01159     heaps_inc = next_heaps_length - heaps_used;
01160 
01161     if (next_heaps_length > heaps_length) {
01162         allocate_sorted_heaps(objspace, next_heaps_length);
01163     }
01164 }
01165 
01166 static int
01167 heaps_increment(rb_objspace_t *objspace)
01168 {
01169     if (heaps_inc > 0) {
01170         assign_heap_slot(objspace);
01171         heaps_inc--;
01172         return TRUE;
01173     }
01174     return FALSE;
01175 }
01176 
01177 int
01178 rb_during_gc(void)
01179 {
01180     rb_objspace_t *objspace = &rb_objspace;
01181     return during_gc;
01182 }
01183 
01184 #define RANY(o) ((RVALUE*)(o))
01185 
01186 VALUE
01187 rb_newobj(void)
01188 {
01189     rb_objspace_t *objspace = &rb_objspace;
01190     VALUE obj;
01191 
01192     if (UNLIKELY(during_gc)) {
01193         dont_gc = 1;
01194         during_gc = 0;
01195         rb_bug("object allocation during garbage collection phase");
01196     }
01197 
01198     if (UNLIKELY(ruby_gc_stress && !ruby_disable_gc_stress)) {
01199         if (!garbage_collect(objspace)) {
01200             during_gc = 0;
01201             rb_memerror();
01202         }
01203     }
01204 
01205     if (UNLIKELY(!freelist)) {
01206         if (!gc_lazy_sweep(objspace)) {
01207             during_gc = 0;
01208             rb_memerror();
01209         }
01210     }
01211 
01212     obj = (VALUE)freelist;
01213     freelist = freelist->as.free.next;
01214 
01215     MEMZERO((void*)obj, RVALUE, 1);
01216 #ifdef GC_DEBUG
01217     RANY(obj)->file = rb_sourcefile();
01218     RANY(obj)->line = rb_sourceline();
01219 #endif
01220     GC_PROF_INC_LIVE_NUM;
01221 
01222     return obj;
01223 }
01224 
01225 NODE*
01226 rb_node_newnode(enum node_type type, VALUE a0, VALUE a1, VALUE a2)
01227 {
01228     NODE *n = (NODE*)rb_newobj();
01229 
01230     n->flags |= T_NODE;
01231     nd_set_type(n, type);
01232 
01233     n->u1.value = a0;
01234     n->u2.value = a1;
01235     n->u3.value = a2;
01236 
01237     return n;
01238 }
01239 
01240 VALUE
01241 rb_data_object_alloc(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree)
01242 {
01243     NEWOBJ(data, struct RData);
01244     if (klass) Check_Type(klass, T_CLASS);
01245     OBJSETUP(data, klass, T_DATA);
01246     data->data = datap;
01247     data->dfree = dfree;
01248     data->dmark = dmark;
01249 
01250     return (VALUE)data;
01251 }
01252 
01253 VALUE
01254 rb_data_typed_object_alloc(VALUE klass, void *datap, const rb_data_type_t *type)
01255 {
01256     NEWOBJ(data, struct RTypedData);
01257 
01258     if (klass) Check_Type(klass, T_CLASS);
01259 
01260     OBJSETUP(data, klass, T_DATA);
01261 
01262     data->data = datap;
01263     data->typed_flag = 1;
01264     data->type = type;
01265 
01266     return (VALUE)data;
01267 }
01268 
01269 size_t
01270 rb_objspace_data_type_memsize(VALUE obj)
01271 {
01272     if (RTYPEDDATA_P(obj) && RTYPEDDATA_TYPE(obj)->function.dsize) {
01273         return RTYPEDDATA_TYPE(obj)->function.dsize(RTYPEDDATA_DATA(obj));
01274     }
01275     else {
01276         return 0;
01277     }
01278 }
01279 
01280 const char *
01281 rb_objspace_data_type_name(VALUE obj)
01282 {
01283     if (RTYPEDDATA_P(obj)) {
01284         return RTYPEDDATA_TYPE(obj)->wrap_struct_name;
01285     }
01286     else {
01287         return 0;
01288     }
01289 }
01290 
01291 #ifdef __ia64
01292 #define SET_STACK_END (SET_MACHINE_STACK_END(&th->machine_stack_end), th->machine_register_stack_end = rb_ia64_bsp())
01293 #else
01294 #define SET_STACK_END SET_MACHINE_STACK_END(&th->machine_stack_end)
01295 #endif
01296 
01297 #define STACK_START (th->machine_stack_start)
01298 #define STACK_END (th->machine_stack_end)
01299 #define STACK_LEVEL_MAX (th->machine_stack_maxsize/sizeof(VALUE))
01300 
01301 #if STACK_GROW_DIRECTION < 0
01302 # define STACK_LENGTH  (size_t)(STACK_START - STACK_END)
01303 #elif STACK_GROW_DIRECTION > 0
01304 # define STACK_LENGTH  (size_t)(STACK_END - STACK_START + 1)
01305 #else
01306 # define STACK_LENGTH  ((STACK_END < STACK_START) ? (size_t)(STACK_START - STACK_END) \
01307                         : (size_t)(STACK_END - STACK_START + 1))
01308 #endif
01309 #if !STACK_GROW_DIRECTION
01310 int ruby_stack_grow_direction;
01311 int
01312 ruby_get_stack_grow_direction(volatile VALUE *addr)
01313 {
01314     VALUE *end;
01315     SET_MACHINE_STACK_END(&end);
01316 
01317     if (end > addr) return ruby_stack_grow_direction = 1;
01318     return ruby_stack_grow_direction = -1;
01319 }
01320 #endif
01321 
01322 /* Marking stack */
01323 
01324 static void push_mark_stack(mark_stack_t *, VALUE);
01325 static int pop_mark_stack(mark_stack_t *, VALUE *);
01326 static void shrink_stack_chunk_cache(mark_stack_t *stack);
01327 
01328 static stack_chunk_t *
01329 stack_chunk_alloc(void)
01330 {
01331     stack_chunk_t *res;
01332 
01333     res = malloc(sizeof(stack_chunk_t));
01334     if (!res)
01335         rb_memerror();
01336 
01337     return res;
01338 }
01339 
01340 static inline int
01341 is_mark_stask_empty(mark_stack_t *stack)
01342 {
01343     return stack->chunk == NULL;
01344 }
01345 
01346 static void
01347 add_stack_chunk_cache(mark_stack_t *stack, stack_chunk_t *chunk)
01348 {
01349     chunk->next = stack->cache;
01350     stack->cache = chunk;
01351     stack->cache_size++;
01352 }
01353 
01354 static void
01355 shrink_stack_chunk_cache(mark_stack_t *stack)
01356 {
01357     stack_chunk_t *chunk;
01358 
01359     if (stack->unused_cache_size > (stack->cache_size/2)) {
01360         chunk = stack->cache;
01361         stack->cache = stack->cache->next;
01362         stack->cache_size--;
01363         free(chunk);
01364     }
01365     stack->unused_cache_size = stack->cache_size;
01366 }
01367 
01368 static void
01369 push_mark_stack_chunk(mark_stack_t *stack)
01370 {
01371     stack_chunk_t *next;
01372 
01373     if (stack->cache_size > 0) {
01374         next = stack->cache;
01375         stack->cache = stack->cache->next;
01376         stack->cache_size--;
01377         if (stack->unused_cache_size > stack->cache_size)
01378             stack->unused_cache_size = stack->cache_size;
01379     }
01380     else {
01381         next = stack_chunk_alloc();
01382     }
01383     next->next = stack->chunk;
01384     stack->chunk = next;
01385     stack->index = 0;
01386 }
01387 
01388 static void
01389 pop_mark_stack_chunk(mark_stack_t *stack)
01390 {
01391     stack_chunk_t *prev;
01392 
01393     prev = stack->chunk->next;
01394     add_stack_chunk_cache(stack, stack->chunk);
01395     stack->chunk = prev;
01396     stack->index = stack->limit;
01397 }
01398 
01399 #if defined(ENABLE_VM_OBJSPACE) && ENABLE_VM_OBJSPACE
01400 static void
01401 free_stack_chunks(mark_stack_t *stack)
01402 {
01403     stack_chunk_t *chunk = stack->chunk;
01404     stack_chunk_t *next = NULL;
01405 
01406     while (chunk != NULL) {
01407         next = chunk->next;
01408         free(chunk);
01409         chunk = next;
01410     }
01411 }
01412 #endif
01413 
01414 static void
01415 push_mark_stack(mark_stack_t *stack, VALUE data)
01416 {
01417     if (stack->index == stack->limit) {
01418         push_mark_stack_chunk(stack);
01419     }
01420     stack->chunk->data[stack->index++] = data;
01421 }
01422 
01423 static int
01424 pop_mark_stack(mark_stack_t *stack, VALUE *data)
01425 {
01426     if (is_mark_stask_empty(stack)) {
01427         return FALSE;
01428     }
01429     if (stack->index == 1) {
01430         *data = stack->chunk->data[--stack->index];
01431         pop_mark_stack_chunk(stack);
01432         return TRUE;
01433     }
01434     *data = stack->chunk->data[--stack->index];
01435     return TRUE;
01436 }
01437 
01438 static void
01439 init_mark_stack(mark_stack_t *stack)
01440 {
01441     int i;
01442 
01443     push_mark_stack_chunk(stack);
01444     stack->limit = STACK_CHUNK_SIZE;
01445 
01446     for(i=0; i < 4; i++) {
01447         add_stack_chunk_cache(stack, stack_chunk_alloc());
01448     }
01449     stack->unused_cache_size = stack->cache_size;
01450 }
01451 
01452 
01453 size_t
01454 ruby_stack_length(VALUE **p)
01455 {
01456     rb_thread_t *th = GET_THREAD();
01457     SET_STACK_END;
01458     if (p) *p = STACK_UPPER(STACK_END, STACK_START, STACK_END);
01459     return STACK_LENGTH;
01460 }
01461 
01462 #if !(defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK))
01463 static int
01464 stack_check(int water_mark)
01465 {
01466     int ret;
01467     rb_thread_t *th = GET_THREAD();
01468     SET_STACK_END;
01469     ret = STACK_LENGTH > STACK_LEVEL_MAX - water_mark;
01470 #ifdef __ia64
01471     if (!ret) {
01472         ret = (VALUE*)rb_ia64_bsp() - th->machine_register_stack_start >
01473               th->machine_register_stack_maxsize/sizeof(VALUE) - water_mark;
01474     }
01475 #endif
01476     return ret;
01477 }
01478 #endif
01479 
01480 #define STACKFRAME_FOR_CALL_CFUNC 512
01481 
01482 int
01483 ruby_stack_check(void)
01484 {
01485 #if defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK)
01486     return 0;
01487 #else
01488     return stack_check(STACKFRAME_FOR_CALL_CFUNC);
01489 #endif
01490 }
01491 
01492 #define MARK_STACK_EMPTY (mark_stack_ptr == mark_stack)
01493 
01494 static void gc_mark(rb_objspace_t *objspace, VALUE ptr);
01495 static void gc_mark_children(rb_objspace_t *objspace, VALUE ptr);
01496 
01497 static void
01498 gc_mark_stacked_objects(rb_objspace_t *objspace)
01499 {
01500     mark_stack_t *mstack = &objspace->mark_stack;
01501     VALUE obj = 0;
01502 
01503     if (!mstack->index) return;
01504     while (pop_mark_stack(mstack, &obj)) {
01505         gc_mark_children(objspace, obj);
01506     }
01507     shrink_stack_chunk_cache(mstack);
01508 }
01509 
01510 static inline int
01511 is_pointer_to_heap(rb_objspace_t *objspace, void *ptr)
01512 {
01513     register RVALUE *p = RANY(ptr);
01514     register struct sorted_heaps_slot *heap;
01515     register size_t hi, lo, mid;
01516 
01517     if (p < lomem || p > himem) return FALSE;
01518     if ((VALUE)p % sizeof(RVALUE) != 0) return FALSE;
01519 
01520     /* check if p looks like a pointer using bsearch*/
01521     lo = 0;
01522     hi = heaps_used;
01523     while (lo < hi) {
01524         mid = (lo + hi) / 2;
01525         heap = &objspace->heap.sorted[mid];
01526         if (heap->start <= p) {
01527             if (p < heap->end)
01528                 return TRUE;
01529             lo = mid + 1;
01530         }
01531         else {
01532             hi = mid;
01533         }
01534     }
01535     return FALSE;
01536 }
01537 
01538 static void
01539 mark_locations_array(rb_objspace_t *objspace, register VALUE *x, register long n)
01540 {
01541     VALUE v;
01542     while (n--) {
01543         v = *x;
01544         VALGRIND_MAKE_MEM_DEFINED(&v, sizeof(v));
01545         if (is_pointer_to_heap(objspace, (void *)v)) {
01546             gc_mark(objspace, v);
01547         }
01548         x++;
01549     }
01550 }
01551 
01552 static void
01553 gc_mark_locations(rb_objspace_t *objspace, VALUE *start, VALUE *end)
01554 {
01555     long n;
01556 
01557     if (end <= start) return;
01558     n = end - start;
01559     mark_locations_array(objspace, start, n);
01560 }
01561 
01562 void
01563 rb_gc_mark_locations(VALUE *start, VALUE *end)
01564 {
01565     gc_mark_locations(&rb_objspace, start, end);
01566 }
01567 
01568 #define rb_gc_mark_locations(start, end) gc_mark_locations(objspace, (start), (end))
01569 
01570 struct mark_tbl_arg {
01571     rb_objspace_t *objspace;
01572 };
01573 
01574 static int
01575 mark_entry(ID key, VALUE value, st_data_t data)
01576 {
01577     struct mark_tbl_arg *arg = (void*)data;
01578     gc_mark(arg->objspace, value);
01579     return ST_CONTINUE;
01580 }
01581 
01582 static void
01583 mark_tbl(rb_objspace_t *objspace, st_table *tbl)
01584 {
01585     struct mark_tbl_arg arg;
01586     if (!tbl || tbl->num_entries == 0) return;
01587     arg.objspace = objspace;
01588     st_foreach(tbl, mark_entry, (st_data_t)&arg);
01589 }
01590 
01591 static int
01592 mark_key(VALUE key, VALUE value, st_data_t data)
01593 {
01594     struct mark_tbl_arg *arg = (void*)data;
01595     gc_mark(arg->objspace, key);
01596     return ST_CONTINUE;
01597 }
01598 
01599 static void
01600 mark_set(rb_objspace_t *objspace, st_table *tbl)
01601 {
01602     struct mark_tbl_arg arg;
01603     if (!tbl) return;
01604     arg.objspace = objspace;
01605     st_foreach(tbl, mark_key, (st_data_t)&arg);
01606 }
01607 
01608 void
01609 rb_mark_set(st_table *tbl)
01610 {
01611     mark_set(&rb_objspace, tbl);
01612 }
01613 
01614 static int
01615 mark_keyvalue(VALUE key, VALUE value, st_data_t data)
01616 {
01617     struct mark_tbl_arg *arg = (void*)data;
01618     gc_mark(arg->objspace, key);
01619     gc_mark(arg->objspace, value);
01620     return ST_CONTINUE;
01621 }
01622 
01623 static void
01624 mark_hash(rb_objspace_t *objspace, st_table *tbl)
01625 {
01626     struct mark_tbl_arg arg;
01627     if (!tbl) return;
01628     arg.objspace = objspace;
01629     st_foreach(tbl, mark_keyvalue, (st_data_t)&arg);
01630 }
01631 
01632 void
01633 rb_mark_hash(st_table *tbl)
01634 {
01635     mark_hash(&rb_objspace, tbl);
01636 }
01637 
01638 static void
01639 mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
01640 {
01641     const rb_method_definition_t *def = me->def;
01642 
01643     gc_mark(objspace, me->klass);
01644     if (!def) return;
01645     switch (def->type) {
01646       case VM_METHOD_TYPE_ISEQ:
01647         gc_mark(objspace, def->body.iseq->self);
01648         break;
01649       case VM_METHOD_TYPE_BMETHOD:
01650         gc_mark(objspace, def->body.proc);
01651         break;
01652       case VM_METHOD_TYPE_ATTRSET:
01653       case VM_METHOD_TYPE_IVAR:
01654         gc_mark(objspace, def->body.attr.location);
01655         break;
01656       default:
01657         break; /* ignore */
01658     }
01659 }
01660 
01661 void
01662 rb_mark_method_entry(const rb_method_entry_t *me)
01663 {
01664     mark_method_entry(&rb_objspace, me);
01665 }
01666 
01667 static int
01668 mark_method_entry_i(ID key, const rb_method_entry_t *me, st_data_t data)
01669 {
01670     struct mark_tbl_arg *arg = (void*)data;
01671     mark_method_entry(arg->objspace, me);
01672     return ST_CONTINUE;
01673 }
01674 
01675 static void
01676 mark_m_tbl(rb_objspace_t *objspace, st_table *tbl)
01677 {
01678     struct mark_tbl_arg arg;
01679     if (!tbl) return;
01680     arg.objspace = objspace;
01681     st_foreach(tbl, mark_method_entry_i, (st_data_t)&arg);
01682 }
01683 
01684 static int
01685 free_method_entry_i(ID key, rb_method_entry_t *me, st_data_t data)
01686 {
01687     if (!me->mark) {
01688         rb_free_method_entry(me);
01689     }
01690     return ST_CONTINUE;
01691 }
01692 
01693 void
01694 rb_free_m_table(st_table *tbl)
01695 {
01696     st_foreach(tbl, free_method_entry_i, 0);
01697     st_free_table(tbl);
01698 }
01699 
01700 static int
01701 mark_const_entry_i(ID key, const rb_const_entry_t *ce, st_data_t data)
01702 {
01703     struct mark_tbl_arg *arg = (void*)data;
01704     gc_mark(arg->objspace, ce->value);
01705     return ST_CONTINUE;
01706 }
01707 
01708 static void
01709 mark_const_tbl(rb_objspace_t *objspace, st_table *tbl)
01710 {
01711     struct mark_tbl_arg arg;
01712     if (!tbl) return;
01713     arg.objspace = objspace;
01714     st_foreach(tbl, mark_const_entry_i, (st_data_t)&arg);
01715 }
01716 
01717 static int
01718 free_const_entry_i(ID key, rb_const_entry_t *ce, st_data_t data)
01719 {
01720     xfree(ce);
01721     return ST_CONTINUE;
01722 }
01723 
01724 void
01725 rb_free_const_table(st_table *tbl)
01726 {
01727     st_foreach(tbl, free_const_entry_i, 0);
01728     st_free_table(tbl);
01729 }
01730 
01731 void
01732 rb_mark_tbl(st_table *tbl)
01733 {
01734     mark_tbl(&rb_objspace, tbl);
01735 }
01736 
01737 void
01738 rb_gc_mark_maybe(VALUE obj)
01739 {
01740     if (is_pointer_to_heap(&rb_objspace, (void *)obj)) {
01741         gc_mark(&rb_objspace, obj);
01742     }
01743 }
01744 
01745 static void
01746 gc_mark(rb_objspace_t *objspace, VALUE ptr)
01747 {
01748     register RVALUE *obj;
01749 
01750     obj = RANY(ptr);
01751     if (rb_special_const_p(ptr)) return; /* special const not marked */
01752     if (obj->as.basic.flags == 0) return;       /* free cell */
01753     if (obj->as.basic.flags & FL_MARK) return;  /* already marked */
01754     obj->as.basic.flags |= FL_MARK;
01755     objspace->heap.live_num++;
01756 
01757     push_mark_stack(&objspace->mark_stack, ptr);
01758 }
01759 
01760 void
01761 rb_gc_mark(VALUE ptr)
01762 {
01763     gc_mark(&rb_objspace, ptr);
01764 }
01765 
01766 static void
01767 gc_mark_children(rb_objspace_t *objspace, VALUE ptr)
01768 {
01769     register RVALUE *obj = RANY(ptr);
01770 
01771     goto marking;               /* skip */
01772 
01773   again:
01774     obj = RANY(ptr);
01775     if (rb_special_const_p(ptr)) return; /* special const not marked */
01776     if (obj->as.basic.flags == 0) return;       /* free cell */
01777     if (obj->as.basic.flags & FL_MARK) return;  /* already marked */
01778     obj->as.basic.flags |= FL_MARK;
01779     objspace->heap.live_num++;
01780 
01781   marking:
01782     if (FL_TEST(obj, FL_EXIVAR)) {
01783         rb_mark_generic_ivar(ptr);
01784     }
01785 
01786     switch (BUILTIN_TYPE(obj)) {
01787       case T_NIL:
01788       case T_FIXNUM:
01789         rb_bug("rb_gc_mark() called for broken object");
01790         break;
01791 
01792       case T_NODE:
01793         switch (nd_type(obj)) {
01794           case NODE_IF:         /* 1,2,3 */
01795           case NODE_FOR:
01796           case NODE_ITER:
01797           case NODE_WHEN:
01798           case NODE_MASGN:
01799           case NODE_RESCUE:
01800           case NODE_RESBODY:
01801           case NODE_CLASS:
01802           case NODE_BLOCK_PASS:
01803             gc_mark(objspace, (VALUE)obj->as.node.u2.node);
01804             /* fall through */
01805           case NODE_BLOCK:      /* 1,3 */
01806           case NODE_OPTBLOCK:
01807           case NODE_ARRAY:
01808           case NODE_DSTR:
01809           case NODE_DXSTR:
01810           case NODE_DREGX:
01811           case NODE_DREGX_ONCE:
01812           case NODE_ENSURE:
01813           case NODE_CALL:
01814           case NODE_DEFS:
01815           case NODE_OP_ASGN1:
01816           case NODE_ARGS:
01817             gc_mark(objspace, (VALUE)obj->as.node.u1.node);
01818             /* fall through */
01819           case NODE_SUPER:      /* 3 */
01820           case NODE_FCALL:
01821           case NODE_DEFN:
01822           case NODE_ARGS_AUX:
01823             ptr = (VALUE)obj->as.node.u3.node;
01824             goto again;
01825 
01826           case NODE_WHILE:      /* 1,2 */
01827           case NODE_UNTIL:
01828           case NODE_AND:
01829           case NODE_OR:
01830           case NODE_CASE:
01831           case NODE_SCLASS:
01832           case NODE_DOT2:
01833           case NODE_DOT3:
01834           case NODE_FLIP2:
01835           case NODE_FLIP3:
01836           case NODE_MATCH2:
01837           case NODE_MATCH3:
01838           case NODE_OP_ASGN_OR:
01839           case NODE_OP_ASGN_AND:
01840           case NODE_MODULE:
01841           case NODE_ALIAS:
01842           case NODE_VALIAS:
01843           case NODE_ARGSCAT:
01844             gc_mark(objspace, (VALUE)obj->as.node.u1.node);
01845             /* fall through */
01846           case NODE_GASGN:      /* 2 */
01847           case NODE_LASGN:
01848           case NODE_DASGN:
01849           case NODE_DASGN_CURR:
01850           case NODE_IASGN:
01851           case NODE_IASGN2:
01852           case NODE_CVASGN:
01853           case NODE_COLON3:
01854           case NODE_OPT_N:
01855           case NODE_EVSTR:
01856           case NODE_UNDEF:
01857           case NODE_POSTEXE:
01858             ptr = (VALUE)obj->as.node.u2.node;
01859             goto again;
01860 
01861           case NODE_HASH:       /* 1 */
01862           case NODE_LIT:
01863           case NODE_STR:
01864           case NODE_XSTR:
01865           case NODE_DEFINED:
01866           case NODE_MATCH:
01867           case NODE_RETURN:
01868           case NODE_BREAK:
01869           case NODE_NEXT:
01870           case NODE_YIELD:
01871           case NODE_COLON2:
01872           case NODE_SPLAT:
01873           case NODE_TO_ARY:
01874             ptr = (VALUE)obj->as.node.u1.node;
01875             goto again;
01876 
01877           case NODE_SCOPE:      /* 2,3 */
01878           case NODE_CDECL:
01879           case NODE_OPT_ARG:
01880             gc_mark(objspace, (VALUE)obj->as.node.u3.node);
01881             ptr = (VALUE)obj->as.node.u2.node;
01882             goto again;
01883 
01884           case NODE_ZARRAY:     /* - */
01885           case NODE_ZSUPER:
01886           case NODE_VCALL:
01887           case NODE_GVAR:
01888           case NODE_LVAR:
01889           case NODE_DVAR:
01890           case NODE_IVAR:
01891           case NODE_CVAR:
01892           case NODE_NTH_REF:
01893           case NODE_BACK_REF:
01894           case NODE_REDO:
01895           case NODE_RETRY:
01896           case NODE_SELF:
01897           case NODE_NIL:
01898           case NODE_TRUE:
01899           case NODE_FALSE:
01900           case NODE_ERRINFO:
01901           case NODE_BLOCK_ARG:
01902             break;
01903           case NODE_ALLOCA:
01904             mark_locations_array(objspace,
01905                                  (VALUE*)obj->as.node.u1.value,
01906                                  obj->as.node.u3.cnt);
01907             ptr = (VALUE)obj->as.node.u2.node;
01908             goto again;
01909 
01910           default:              /* unlisted NODE */
01911             if (is_pointer_to_heap(objspace, obj->as.node.u1.node)) {
01912                 gc_mark(objspace, (VALUE)obj->as.node.u1.node);
01913             }
01914             if (is_pointer_to_heap(objspace, obj->as.node.u2.node)) {
01915                 gc_mark(objspace, (VALUE)obj->as.node.u2.node);
01916             }
01917             if (is_pointer_to_heap(objspace, obj->as.node.u3.node)) {
01918                 gc_mark(objspace, (VALUE)obj->as.node.u3.node);
01919             }
01920         }
01921         return;                 /* no need to mark class. */
01922     }
01923 
01924     gc_mark(objspace, obj->as.basic.klass);
01925     switch (BUILTIN_TYPE(obj)) {
01926       case T_ICLASS:
01927       case T_CLASS:
01928       case T_MODULE:
01929         mark_m_tbl(objspace, RCLASS_M_TBL(obj));
01930         mark_tbl(objspace, RCLASS_IV_TBL(obj));
01931         mark_const_tbl(objspace, RCLASS_CONST_TBL(obj));
01932         ptr = RCLASS_SUPER(obj);
01933         goto again;
01934 
01935       case T_ARRAY:
01936         if (FL_TEST(obj, ELTS_SHARED)) {
01937             ptr = obj->as.array.as.heap.aux.shared;
01938             goto again;
01939         }
01940         else {
01941             long i, len = RARRAY_LEN(obj);
01942             VALUE *ptr = RARRAY_PTR(obj);
01943             for (i=0; i < len; i++) {
01944                 gc_mark(objspace, *ptr++);
01945             }
01946         }
01947         break;
01948 
01949       case T_HASH:
01950         mark_hash(objspace, obj->as.hash.ntbl);
01951         ptr = obj->as.hash.ifnone;
01952         goto again;
01953 
01954       case T_STRING:
01955 #define STR_ASSOC FL_USER3   /* copied from string.c */
01956         if (FL_TEST(obj, RSTRING_NOEMBED) && FL_ANY(obj, ELTS_SHARED|STR_ASSOC)) {
01957             ptr = obj->as.string.as.heap.aux.shared;
01958             goto again;
01959         }
01960         break;
01961 
01962       case T_DATA:
01963         if (RTYPEDDATA_P(obj)) {
01964             RUBY_DATA_FUNC mark_func = obj->as.typeddata.type->function.dmark;
01965             if (mark_func) (*mark_func)(DATA_PTR(obj));
01966         }
01967         else {
01968             if (obj->as.data.dmark) (*obj->as.data.dmark)(DATA_PTR(obj));
01969         }
01970         break;
01971 
01972       case T_OBJECT:
01973         {
01974             long i, len = ROBJECT_NUMIV(obj);
01975             VALUE *ptr = ROBJECT_IVPTR(obj);
01976             for (i  = 0; i < len; i++) {
01977                 gc_mark(objspace, *ptr++);
01978             }
01979         }
01980         break;
01981 
01982       case T_FILE:
01983         if (obj->as.file.fptr) {
01984             gc_mark(objspace, obj->as.file.fptr->pathv);
01985             gc_mark(objspace, obj->as.file.fptr->tied_io_for_writing);
01986             gc_mark(objspace, obj->as.file.fptr->writeconv_asciicompat);
01987             gc_mark(objspace, obj->as.file.fptr->writeconv_pre_ecopts);
01988             gc_mark(objspace, obj->as.file.fptr->encs.ecopts);
01989             gc_mark(objspace, obj->as.file.fptr->write_lock);
01990         }
01991         break;
01992 
01993       case T_REGEXP:
01994         ptr = obj->as.regexp.src;
01995         goto again;
01996 
01997       case T_FLOAT:
01998       case T_BIGNUM:
01999       case T_ZOMBIE:
02000         break;
02001 
02002       case T_MATCH:
02003         gc_mark(objspace, obj->as.match.regexp);
02004         if (obj->as.match.str) {
02005             ptr = obj->as.match.str;
02006             goto again;
02007         }
02008         break;
02009 
02010       case T_RATIONAL:
02011         gc_mark(objspace, obj->as.rational.num);
02012         ptr = obj->as.rational.den;
02013         goto again;
02014 
02015       case T_COMPLEX:
02016         gc_mark(objspace, obj->as.complex.real);
02017         ptr = obj->as.complex.imag;
02018         goto again;
02019 
02020       case T_STRUCT:
02021         {
02022             long len = RSTRUCT_LEN(obj);
02023             VALUE *ptr = RSTRUCT_PTR(obj);
02024 
02025             while (len--) {
02026                 gc_mark(objspace, *ptr++);
02027             }
02028         }
02029         break;
02030 
02031       default:
02032         rb_bug("rb_gc_mark(): unknown data type 0x%x(%p) %s",
02033                BUILTIN_TYPE(obj), (void *)obj,
02034                is_pointer_to_heap(objspace, obj) ? "corrupted object" : "non object");
02035     }
02036 }
02037 
02038 static int obj_free(rb_objspace_t *, VALUE);
02039 
02040 static inline void
02041 add_freelist(rb_objspace_t *objspace, RVALUE *p)
02042 {
02043     VALGRIND_MAKE_MEM_UNDEFINED((void*)p, sizeof(RVALUE));
02044     p->as.free.flags = 0;
02045     p->as.free.next = freelist;
02046     freelist = p;
02047 }
02048 
02049 static void
02050 finalize_list(rb_objspace_t *objspace, RVALUE *p)
02051 {
02052     while (p) {
02053         RVALUE *tmp = p->as.free.next;
02054         run_final(objspace, (VALUE)p);
02055         if (!FL_TEST(p, FL_SINGLETON)) { /* not freeing page */
02056             if (objspace->heap.sweep_slots) {
02057                 p->as.free.flags = 0;
02058             }
02059             else {
02060                 GC_PROF_DEC_LIVE_NUM;
02061                 add_freelist(objspace, p);
02062             }
02063         }
02064         else {
02065             struct heaps_slot *slot = (struct heaps_slot *)(VALUE)RDATA(p)->dmark;
02066             slot->limit--;
02067         }
02068         p = tmp;
02069     }
02070 }
02071 
02072 static void
02073 unlink_heap_slot(rb_objspace_t *objspace, struct heaps_slot *slot)
02074 {
02075     if (slot->prev)
02076         slot->prev->next = slot->next;
02077     if (slot->next)
02078         slot->next->prev = slot->prev;
02079     if (heaps == slot)
02080         heaps = slot->next;
02081     if (objspace->heap.sweep_slots == slot)
02082         objspace->heap.sweep_slots = slot->next;
02083     slot->prev = NULL;
02084     slot->next = NULL;
02085 }
02086 
02087 
02088 static void
02089 free_unused_heaps(rb_objspace_t *objspace)
02090 {
02091     size_t i, j;
02092     RVALUE *last = 0;
02093 
02094     for (i = j = 1; j < heaps_used; i++) {
02095         if (objspace->heap.sorted[i].slot->limit == 0) {
02096             if (!last) {
02097                 last = objspace->heap.sorted[i].slot->membase;
02098             }
02099             else {
02100                 free(objspace->heap.sorted[i].slot->membase);
02101             }
02102             free(objspace->heap.sorted[i].slot);
02103             heaps_used--;
02104         }
02105         else {
02106             if (i != j) {
02107                 objspace->heap.sorted[j] = objspace->heap.sorted[i];
02108             }
02109             j++;
02110         }
02111     }
02112     if (last) {
02113         if (last < heaps_freed) {
02114             free(heaps_freed);
02115             heaps_freed = last;
02116         }
02117         else {
02118             free(last);
02119         }
02120     }
02121 }
02122 
02123 static void
02124 slot_sweep(rb_objspace_t *objspace, struct heaps_slot *sweep_slot)
02125 {
02126     size_t free_num = 0, final_num = 0;
02127     RVALUE *p, *pend;
02128     RVALUE *free = freelist, *final = deferred_final_list;
02129     int deferred;
02130 
02131     p = sweep_slot->slot; pend = p + sweep_slot->limit;
02132     while (p < pend) {
02133         if (!(p->as.basic.flags & FL_MARK)) {
02134             if (p->as.basic.flags &&
02135                 ((deferred = obj_free(objspace, (VALUE)p)) ||
02136                  (FL_TEST(p, FL_FINALIZE)))) {
02137                 if (!deferred) {
02138                     p->as.free.flags = T_ZOMBIE;
02139                     RDATA(p)->dfree = 0;
02140                 }
02141                 p->as.free.flags |= FL_MARK;
02142                 p->as.free.next = deferred_final_list;
02143                 deferred_final_list = p;
02144                 final_num++;
02145             }
02146             else {
02147                 add_freelist(objspace, p);
02148                 free_num++;
02149             }
02150         }
02151         else if (BUILTIN_TYPE(p) == T_ZOMBIE) {
02152             /* objects to be finalized */
02153             /* do nothing remain marked */
02154         }
02155         else {
02156             RBASIC(p)->flags &= ~FL_MARK;
02157         }
02158         p++;
02159     }
02160     if (final_num + free_num == sweep_slot->limit &&
02161         objspace->heap.free_num > objspace->heap.do_heap_free) {
02162         RVALUE *pp;
02163 
02164         for (pp = deferred_final_list; pp != final; pp = pp->as.free.next) {
02165             RDATA(pp)->dmark = (void (*)(void *))(VALUE)sweep_slot;
02166             pp->as.free.flags |= FL_SINGLETON; /* freeing page mark */
02167         }
02168         sweep_slot->limit = final_num;
02169         freelist = free;        /* cancel this page from freelist */
02170         unlink_heap_slot(objspace, sweep_slot);
02171     }
02172     else {
02173         objspace->heap.free_num += free_num;
02174     }
02175     objspace->heap.final_num += final_num;
02176 
02177     if (deferred_final_list) {
02178         rb_thread_t *th = GET_THREAD();
02179         if (th) {
02180             RUBY_VM_SET_FINALIZER_INTERRUPT(th);
02181         }
02182     }
02183 }
02184 
02185 static int
02186 ready_to_gc(rb_objspace_t *objspace)
02187 {
02188     if (dont_gc || during_gc) {
02189         if (!freelist) {
02190             if (!heaps_increment(objspace)) {
02191                 set_heaps_increment(objspace);
02192                 heaps_increment(objspace);
02193             }
02194         }
02195         return FALSE;
02196     }
02197     return TRUE;
02198 }
02199 
02200 static void
02201 before_gc_sweep(rb_objspace_t *objspace)
02202 {
02203     freelist = 0;
02204     objspace->heap.do_heap_free = (size_t)((heaps_used * HEAP_OBJ_LIMIT) * 0.65);
02205     objspace->heap.free_min = (size_t)((heaps_used * HEAP_OBJ_LIMIT)  * 0.2);
02206     if (objspace->heap.free_min < initial_free_min) {
02207         objspace->heap.do_heap_free = heaps_used * HEAP_OBJ_LIMIT;
02208         objspace->heap.free_min = initial_free_min;
02209     }
02210     objspace->heap.sweep_slots = heaps;
02211     objspace->heap.free_num = 0;
02212 
02213     /* sweep unlinked method entries */
02214     if (GET_VM()->unlinked_method_entry_list) {
02215         rb_sweep_method_entry(GET_VM());
02216     }
02217 }
02218 
02219 static void
02220 after_gc_sweep(rb_objspace_t *objspace)
02221 {
02222     GC_PROF_SET_MALLOC_INFO;
02223 
02224     if (objspace->heap.free_num < objspace->heap.free_min) {
02225         set_heaps_increment(objspace);
02226         heaps_increment(objspace);
02227     }
02228 
02229     if (malloc_increase > malloc_limit) {
02230         malloc_limit += (size_t)((malloc_increase - malloc_limit) * (double)objspace->heap.live_num / (heaps_used * HEAP_OBJ_LIMIT));
02231         if (malloc_limit < initial_malloc_limit) malloc_limit = initial_malloc_limit;
02232     }
02233     malloc_increase = 0;
02234 
02235     free_unused_heaps(objspace);
02236 }
02237 
02238 static int
02239 lazy_sweep(rb_objspace_t *objspace)
02240 {
02241     struct heaps_slot *next;
02242 
02243     heaps_increment(objspace);
02244     while (objspace->heap.sweep_slots) {
02245         next = objspace->heap.sweep_slots->next;
02246         slot_sweep(objspace, objspace->heap.sweep_slots);
02247         objspace->heap.sweep_slots = next;
02248         if (freelist) {
02249             during_gc = 0;
02250             return TRUE;
02251         }
02252     }
02253     return FALSE;
02254 }
02255 
02256 static void
02257 rest_sweep(rb_objspace_t *objspace)
02258 {
02259     if (objspace->heap.sweep_slots) {
02260        while (objspace->heap.sweep_slots) {
02261            lazy_sweep(objspace);
02262        }
02263        after_gc_sweep(objspace);
02264     }
02265 }
02266 
02267 static void gc_marks(rb_objspace_t *objspace);
02268 
02269 static int
02270 gc_lazy_sweep(rb_objspace_t *objspace)
02271 {
02272     int res;
02273     INIT_GC_PROF_PARAMS;
02274 
02275     if (objspace->flags.dont_lazy_sweep)
02276         return garbage_collect(objspace);
02277 
02278 
02279     if (!ready_to_gc(objspace)) return TRUE;
02280 
02281     during_gc++;
02282     GC_PROF_TIMER_START;
02283     GC_PROF_SWEEP_TIMER_START;
02284 
02285     if (objspace->heap.sweep_slots) {
02286         res = lazy_sweep(objspace);
02287         if (res) {
02288             GC_PROF_SWEEP_TIMER_STOP;
02289             GC_PROF_SET_MALLOC_INFO;
02290             GC_PROF_TIMER_STOP(Qfalse);
02291             return res;
02292         }
02293         after_gc_sweep(objspace);
02294     }
02295     else {
02296         if (heaps_increment(objspace)) {
02297             during_gc = 0;
02298             return TRUE;
02299         }
02300     }
02301 
02302     gc_marks(objspace);
02303 
02304     before_gc_sweep(objspace);
02305     if (objspace->heap.free_min > (heaps_used * HEAP_OBJ_LIMIT - objspace->heap.live_num)) {
02306         set_heaps_increment(objspace);
02307     }
02308 
02309     GC_PROF_SWEEP_TIMER_START;
02310     if(!(res = lazy_sweep(objspace))) {
02311         after_gc_sweep(objspace);
02312         if(freelist) {
02313             res = TRUE;
02314             during_gc = 0;
02315         }
02316     }
02317     GC_PROF_SWEEP_TIMER_STOP;
02318 
02319     GC_PROF_TIMER_STOP(Qtrue);
02320     return res;
02321 }
02322 
02323 static void
02324 gc_sweep(rb_objspace_t *objspace)
02325 {
02326     struct heaps_slot *next;
02327 
02328     before_gc_sweep(objspace);
02329 
02330     while (objspace->heap.sweep_slots) {
02331         next = objspace->heap.sweep_slots->next;
02332         slot_sweep(objspace, objspace->heap.sweep_slots);
02333         objspace->heap.sweep_slots = next;
02334     }
02335 
02336     after_gc_sweep(objspace);
02337 
02338     during_gc = 0;
02339 }
02340 
02341 void
02342 rb_gc_force_recycle(VALUE p)
02343 {
02344     rb_objspace_t *objspace = &rb_objspace;
02345     GC_PROF_DEC_LIVE_NUM;
02346     if (RBASIC(p)->flags & FL_MARK) {
02347         RANY(p)->as.free.flags = 0;
02348     }
02349     else {
02350         add_freelist(objspace, (RVALUE *)p);
02351     }
02352 }
02353 
02354 static inline void
02355 make_deferred(RVALUE *p)
02356 {
02357     p->as.basic.flags = (p->as.basic.flags & ~T_MASK) | T_ZOMBIE;
02358 }
02359 
02360 static inline void
02361 make_io_deferred(RVALUE *p)
02362 {
02363     rb_io_t *fptr = p->as.file.fptr;
02364     make_deferred(p);
02365     p->as.data.dfree = (void (*)(void*))rb_io_fptr_finalize;
02366     p->as.data.data = fptr;
02367 }
02368 
02369 static int
02370 obj_free(rb_objspace_t *objspace, VALUE obj)
02371 {
02372     switch (BUILTIN_TYPE(obj)) {
02373       case T_NIL:
02374       case T_FIXNUM:
02375       case T_TRUE:
02376       case T_FALSE:
02377         rb_bug("obj_free() called for broken object");
02378         break;
02379     }
02380 
02381     if (FL_TEST(obj, FL_EXIVAR)) {
02382         rb_free_generic_ivar((VALUE)obj);
02383         FL_UNSET(obj, FL_EXIVAR);
02384     }
02385 
02386     switch (BUILTIN_TYPE(obj)) {
02387       case T_OBJECT:
02388         if (!(RANY(obj)->as.basic.flags & ROBJECT_EMBED) &&
02389             RANY(obj)->as.object.as.heap.ivptr) {
02390             xfree(RANY(obj)->as.object.as.heap.ivptr);
02391         }
02392         break;
02393       case T_MODULE:
02394       case T_CLASS:
02395         rb_clear_cache_by_class((VALUE)obj);
02396         rb_free_m_table(RCLASS_M_TBL(obj));
02397         if (RCLASS_IV_TBL(obj)) {
02398             st_free_table(RCLASS_IV_TBL(obj));
02399         }
02400         if (RCLASS_CONST_TBL(obj)) {
02401             rb_free_const_table(RCLASS_CONST_TBL(obj));
02402         }
02403         if (RCLASS_IV_INDEX_TBL(obj)) {
02404             st_free_table(RCLASS_IV_INDEX_TBL(obj));
02405         }
02406         xfree(RANY(obj)->as.klass.ptr);
02407         break;
02408       case T_STRING:
02409         rb_str_free(obj);
02410         break;
02411       case T_ARRAY:
02412         rb_ary_free(obj);
02413         break;
02414       case T_HASH:
02415         if (RANY(obj)->as.hash.ntbl) {
02416             st_free_table(RANY(obj)->as.hash.ntbl);
02417         }
02418         break;
02419       case T_REGEXP:
02420         if (RANY(obj)->as.regexp.ptr) {
02421             onig_free(RANY(obj)->as.regexp.ptr);
02422         }
02423         break;
02424       case T_DATA:
02425         if (DATA_PTR(obj)) {
02426             if (RTYPEDDATA_P(obj)) {
02427                 RDATA(obj)->dfree = RANY(obj)->as.typeddata.type->function.dfree;
02428             }
02429             if (RANY(obj)->as.data.dfree == (RUBY_DATA_FUNC)-1) {
02430                 xfree(DATA_PTR(obj));
02431             }
02432             else if (RANY(obj)->as.data.dfree) {
02433                 make_deferred(RANY(obj));
02434                 return 1;
02435             }
02436         }
02437         break;
02438       case T_MATCH:
02439         if (RANY(obj)->as.match.rmatch) {
02440             struct rmatch *rm = RANY(obj)->as.match.rmatch;
02441             onig_region_free(&rm->regs, 0);
02442             if (rm->char_offset)
02443                 xfree(rm->char_offset);
02444             xfree(rm);
02445         }
02446         break;
02447       case T_FILE:
02448         if (RANY(obj)->as.file.fptr) {
02449             make_io_deferred(RANY(obj));
02450             return 1;
02451         }
02452         break;
02453       case T_RATIONAL:
02454       case T_COMPLEX:
02455         break;
02456       case T_ICLASS:
02457         /* iClass shares table with the module */
02458         xfree(RANY(obj)->as.klass.ptr);
02459         break;
02460 
02461       case T_FLOAT:
02462         break;
02463 
02464       case T_BIGNUM:
02465         if (!(RBASIC(obj)->flags & RBIGNUM_EMBED_FLAG) && RBIGNUM_DIGITS(obj)) {
02466             xfree(RBIGNUM_DIGITS(obj));
02467         }
02468         break;
02469       case T_NODE:
02470         switch (nd_type(obj)) {
02471           case NODE_SCOPE:
02472             if (RANY(obj)->as.node.u1.tbl) {
02473                 xfree(RANY(obj)->as.node.u1.tbl);
02474             }
02475             break;
02476           case NODE_ALLOCA:
02477             xfree(RANY(obj)->as.node.u1.node);
02478             break;
02479         }
02480         break;                  /* no need to free iv_tbl */
02481 
02482       case T_STRUCT:
02483         if ((RBASIC(obj)->flags & RSTRUCT_EMBED_LEN_MASK) == 0 &&
02484             RANY(obj)->as.rstruct.as.heap.ptr) {
02485             xfree(RANY(obj)->as.rstruct.as.heap.ptr);
02486         }
02487         break;
02488 
02489       default:
02490         rb_bug("gc_sweep(): unknown data type 0x%x(%p)",
02491                BUILTIN_TYPE(obj), (void*)obj);
02492     }
02493 
02494     return 0;
02495 }
02496 
02497 #define GC_NOTIFY 0
02498 
02499 #if STACK_GROW_DIRECTION < 0
02500 #define GET_STACK_BOUNDS(start, end, appendix) ((start) = STACK_END, (end) = STACK_START)
02501 #elif STACK_GROW_DIRECTION > 0
02502 #define GET_STACK_BOUNDS(start, end, appendix) ((start) = STACK_START, (end) = STACK_END+(appendix))
02503 #else
02504 #define GET_STACK_BOUNDS(start, end, appendix) \
02505     ((STACK_END < STACK_START) ? \
02506      ((start) = STACK_END, (end) = STACK_START) : ((start) = STACK_START, (end) = STACK_END+(appendix)))
02507 #endif
02508 
02509 #define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
02510 
02511 static void
02512 mark_current_machine_context(rb_objspace_t *objspace, rb_thread_t *th)
02513 {
02514     union {
02515         rb_jmp_buf j;
02516         VALUE v[sizeof(rb_jmp_buf) / sizeof(VALUE)];
02517     } save_regs_gc_mark;
02518     VALUE *stack_start, *stack_end;
02519 
02520     FLUSH_REGISTER_WINDOWS;
02521     /* This assumes that all registers are saved into the jmp_buf (and stack) */
02522     rb_setjmp(save_regs_gc_mark.j);
02523 
02524     SET_STACK_END;
02525     GET_STACK_BOUNDS(stack_start, stack_end, 1);
02526 
02527     mark_locations_array(objspace, save_regs_gc_mark.v, numberof(save_regs_gc_mark.v));
02528 
02529     rb_gc_mark_locations(stack_start, stack_end);
02530 #ifdef __ia64
02531     rb_gc_mark_locations(th->machine_register_stack_start, th->machine_register_stack_end);
02532 #endif
02533 #if defined(__mc68000__)
02534     mark_locations_array(objspace, (VALUE*)((char*)STACK_END + 2),
02535                          (STACK_START - STACK_END));
02536 #endif
02537 }
02538 
02539 static void
02540 gc_marks(rb_objspace_t *objspace)
02541 {
02542     struct gc_list *list;
02543     rb_thread_t *th = GET_THREAD();
02544     GC_PROF_MARK_TIMER_START;
02545 
02546     objspace->heap.live_num = 0;
02547     objspace->count++;
02548 
02549 
02550     SET_STACK_END;
02551 
02552     th->vm->self ? rb_gc_mark(th->vm->self) : rb_vm_mark(th->vm);
02553 
02554     mark_tbl(objspace, finalizer_table);
02555     mark_current_machine_context(objspace, th);
02556 
02557     rb_gc_mark_symbols();
02558     rb_gc_mark_encodings();
02559 
02560     /* mark protected global variables */
02561     for (list = global_List; list; list = list->next) {
02562         rb_gc_mark_maybe(*list->varptr);
02563     }
02564     rb_mark_end_proc();
02565     rb_gc_mark_global_tbl();
02566 
02567     mark_tbl(objspace, rb_class_tbl);
02568 
02569     /* mark generic instance variables for special constants */
02570     rb_mark_generic_ivar_tbl();
02571 
02572     rb_gc_mark_parser();
02573 
02574     rb_gc_mark_unlinked_live_method_entries(th->vm);
02575 
02576     /* marking-loop */
02577     gc_mark_stacked_objects(objspace);
02578 
02579     GC_PROF_MARK_TIMER_STOP;
02580 }
02581 
02582 static int
02583 garbage_collect(rb_objspace_t *objspace)
02584 {
02585     INIT_GC_PROF_PARAMS;
02586 
02587     if (GC_NOTIFY) printf("start garbage_collect()\n");
02588 
02589     if (!heaps) {
02590         return FALSE;
02591     }
02592     if (!ready_to_gc(objspace)) {
02593         return TRUE;
02594     }
02595 
02596     GC_PROF_TIMER_START;
02597 
02598     rest_sweep(objspace);
02599 
02600     during_gc++;
02601     gc_marks(objspace);
02602 
02603     GC_PROF_SWEEP_TIMER_START;
02604     gc_sweep(objspace);
02605     GC_PROF_SWEEP_TIMER_STOP;
02606 
02607     GC_PROF_TIMER_STOP(Qtrue);
02608     if (GC_NOTIFY) printf("end garbage_collect()\n");
02609     return TRUE;
02610 }
02611 
02612 int
02613 rb_garbage_collect(void)
02614 {
02615     return garbage_collect(&rb_objspace);
02616 }
02617 
02618 void
02619 rb_gc_mark_machine_stack(rb_thread_t *th)
02620 {
02621     rb_objspace_t *objspace = &rb_objspace;
02622     VALUE *stack_start, *stack_end;
02623 
02624     GET_STACK_BOUNDS(stack_start, stack_end, 0);
02625     rb_gc_mark_locations(stack_start, stack_end);
02626 #ifdef __ia64
02627     rb_gc_mark_locations(th->machine_register_stack_start, th->machine_register_stack_end);
02628 #endif
02629 }
02630 
02631 
02632 /*
02633  *  call-seq:
02634  *     GC.start                     -> nil
02635  *     gc.garbage_collect           -> nil
02636  *     ObjectSpace.garbage_collect  -> nil
02637  *
02638  *  Initiates garbage collection, unless manually disabled.
02639  *
02640  */
02641 
02642 VALUE
02643 rb_gc_start(void)
02644 {
02645     rb_gc();
02646     return Qnil;
02647 }
02648 
02649 #undef Init_stack
02650 
02651 void
02652 Init_stack(volatile VALUE *addr)
02653 {
02654     ruby_init_stack(addr);
02655 }
02656 
02657 /*
02658  * Document-class: ObjectSpace
02659  *
02660  *  The <code>ObjectSpace</code> module contains a number of routines
02661  *  that interact with the garbage collection facility and allow you to
02662  *  traverse all living objects with an iterator.
02663  *
02664  *  <code>ObjectSpace</code> also provides support for object
02665  *  finalizers, procs that will be called when a specific object is
02666  *  about to be destroyed by garbage collection.
02667  *
02668  *     include ObjectSpace
02669  *
02670  *
02671  *     a = "A"
02672  *     b = "B"
02673  *     c = "C"
02674  *
02675  *
02676  *     define_finalizer(a, proc {|id| puts "Finalizer one on #{id}" })
02677  *     define_finalizer(a, proc {|id| puts "Finalizer two on #{id}" })
02678  *     define_finalizer(b, proc {|id| puts "Finalizer three on #{id}" })
02679  *
02680  *  <em>produces:</em>
02681  *
02682  *     Finalizer three on 537763470
02683  *     Finalizer one on 537763480
02684  *     Finalizer two on 537763480
02685  *
02686  */
02687 
02688 void
02689 Init_heap(void)
02690 {
02691     init_heap(&rb_objspace);
02692 }
02693 
02694 static VALUE
02695 lazy_sweep_enable(void)
02696 {
02697     rb_objspace_t *objspace = &rb_objspace;
02698 
02699     objspace->flags.dont_lazy_sweep = FALSE;
02700     return Qnil;
02701 }
02702 
02703 typedef int each_obj_callback(void *, void *, size_t, void *);
02704 
02705 struct each_obj_args {
02706     each_obj_callback *callback;
02707     void *data;
02708 };
02709 
02710 static VALUE
02711 objspace_each_objects(VALUE arg)
02712 {
02713     size_t i;
02714     RVALUE *membase = 0;
02715     RVALUE *pstart, *pend;
02716     rb_objspace_t *objspace = &rb_objspace;
02717     struct each_obj_args *args = (struct each_obj_args *)arg;
02718     volatile VALUE v;
02719 
02720     i = 0;
02721     while (i < heaps_used) {
02722         while (0 < i && (uintptr_t)membase < (uintptr_t)objspace->heap.sorted[i-1].slot->membase)
02723             i--;
02724         while (i < heaps_used && (uintptr_t)objspace->heap.sorted[i].slot->membase <= (uintptr_t)membase)
02725             i++;
02726         if (heaps_used <= i)
02727           break;
02728         membase = objspace->heap.sorted[i].slot->membase;
02729 
02730         pstart = objspace->heap.sorted[i].slot->slot;
02731         pend = pstart + objspace->heap.sorted[i].slot->limit;
02732 
02733         for (; pstart != pend; pstart++) {
02734             if (pstart->as.basic.flags) {
02735                 v = (VALUE)pstart; /* acquire to save this object */
02736                 break;
02737             }
02738         }
02739         if (pstart != pend) {
02740             if ((*args->callback)(pstart, pend, sizeof(RVALUE), args->data)) {
02741                 break;
02742             }
02743         }
02744     }
02745 
02746     return Qnil;
02747 }
02748 
02749 /*
02750  * rb_objspace_each_objects() is special C API to walk through
02751  * Ruby object space.  This C API is too difficult to use it.
02752  * To be frank, you should not use it. Or you need to read the
02753  * source code of this function and understand what this function does.
02754  *
02755  * 'callback' will be called several times (the number of heap slot,
02756  * at current implementation) with:
02757  *   vstart: a pointer to the first living object of the heap_slot.
02758  *   vend: a pointer to next to the valid heap_slot area.
02759  *   stride: a distance to next VALUE.
02760  *
02761  * If callback() returns non-zero, the iteration will be stopped.
02762  *
02763  * This is a sample callback code to iterate liveness objects:
02764  *
02765  *   int
02766  *   sample_callback(void *vstart, void *vend, int stride, void *data) {
02767  *     VALUE v = (VALUE)vstart;
02768  *     for (; v != (VALUE)vend; v += stride) {
02769  *       if (RBASIC(v)->flags) { // liveness check
02770  *       // do something with live object 'v'
02771  *     }
02772  *     return 0; // continue to iteration
02773  *   }
02774  *
02775  * Note: 'vstart' is not a top of heap_slot.  This point the first
02776  *       living object to grasp at least one object to avoid GC issue.
02777  *       This means that you can not walk through all Ruby object slot
02778  *       including freed object slot.
02779  *
02780  * Note: On this implementation, 'stride' is same as sizeof(RVALUE).
02781  *       However, there are possibilities to pass variable values with
02782  *       'stride' with some reasons.  You must use stride instead of
02783  *       use some constant value in the iteration.
02784  */
02785 void
02786 rb_objspace_each_objects(each_obj_callback *callback, void *data)
02787 {
02788     struct each_obj_args args;
02789     rb_objspace_t *objspace = &rb_objspace;
02790 
02791     rest_sweep(objspace);
02792     objspace->flags.dont_lazy_sweep = TRUE;
02793 
02794     args.callback = callback;
02795     args.data = data;
02796     rb_ensure(objspace_each_objects, (VALUE)&args, lazy_sweep_enable, Qnil);
02797 }
02798 
02799 struct os_each_struct {
02800     size_t num;
02801     VALUE of;
02802 };
02803 
02804 static int
02805 os_obj_of_i(void *vstart, void *vend, size_t stride, void *data)
02806 {
02807     struct os_each_struct *oes = (struct os_each_struct *)data;
02808     RVALUE *p = (RVALUE *)vstart, *pend = (RVALUE *)vend;
02809     volatile VALUE v;
02810 
02811     for (; p != pend; p++) {
02812         if (p->as.basic.flags) {
02813             switch (BUILTIN_TYPE(p)) {
02814               case T_NONE:
02815               case T_ICLASS:
02816               case T_NODE:
02817               case T_ZOMBIE:
02818                 continue;
02819               case T_CLASS:
02820                 if (FL_TEST(p, FL_SINGLETON))
02821                   continue;
02822               default:
02823                 if (!p->as.basic.klass) continue;
02824                 v = (VALUE)p;
02825                 if (!oes->of || rb_obj_is_kind_of(v, oes->of)) {
02826                     rb_yield(v);
02827                     oes->num++;
02828                 }
02829             }
02830         }
02831     }
02832 
02833     return 0;
02834 }
02835 
02836 static VALUE
02837 os_obj_of(VALUE of)
02838 {
02839     struct os_each_struct oes;
02840 
02841     oes.num = 0;
02842     oes.of = of;
02843     rb_objspace_each_objects(os_obj_of_i, &oes);
02844     return SIZET2NUM(oes.num);
02845 }
02846 
02847 /*
02848  *  call-seq:
02849  *     ObjectSpace.each_object([module]) {|obj| ... } -> fixnum
02850  *     ObjectSpace.each_object([module])              -> an_enumerator
02851  *
02852  *  Calls the block once for each living, nonimmediate object in this
02853  *  Ruby process. If <i>module</i> is specified, calls the block
02854  *  for only those classes or modules that match (or are a subclass of)
02855  *  <i>module</i>. Returns the number of objects found. Immediate
02856  *  objects (<code>Fixnum</code>s, <code>Symbol</code>s
02857  *  <code>true</code>, <code>false</code>, and <code>nil</code>) are
02858  *  never returned. In the example below, <code>each_object</code>
02859  *  returns both the numbers we defined and several constants defined in
02860  *  the <code>Math</code> module.
02861  *
02862  *  If no block is given, an enumerator is returned instead.
02863  *
02864  *     a = 102.7
02865  *     b = 95       # Won't be returned
02866  *     c = 12345678987654321
02867  *     count = ObjectSpace.each_object(Numeric) {|x| p x }
02868  *     puts "Total count: #{count}"
02869  *
02870  *  <em>produces:</em>
02871  *
02872  *     12345678987654321
02873  *     102.7
02874  *     2.71828182845905
02875  *     3.14159265358979
02876  *     2.22044604925031e-16
02877  *     1.7976931348623157e+308
02878  *     2.2250738585072e-308
02879  *     Total count: 7
02880  *
02881  */
02882 
02883 static VALUE
02884 os_each_obj(int argc, VALUE *argv, VALUE os)
02885 {
02886     VALUE of;
02887 
02888     rb_secure(4);
02889     if (argc == 0) {
02890         of = 0;
02891     }
02892     else {
02893         rb_scan_args(argc, argv, "01", &of);
02894     }
02895     RETURN_ENUMERATOR(os, 1, &of);
02896     return os_obj_of(of);
02897 }
02898 
02899 /*
02900  *  call-seq:
02901  *     ObjectSpace.undefine_finalizer(obj)
02902  *
02903  *  Removes all finalizers for <i>obj</i>.
02904  *
02905  */
02906 
02907 static VALUE
02908 undefine_final(VALUE os, VALUE obj)
02909 {
02910     rb_objspace_t *objspace = &rb_objspace;
02911     st_data_t data = obj;
02912     rb_check_frozen(obj);
02913     st_delete(finalizer_table, &data, 0);
02914     FL_UNSET(obj, FL_FINALIZE);
02915     return obj;
02916 }
02917 
02918 /*
02919  *  call-seq:
02920  *     ObjectSpace.define_finalizer(obj, aProc=proc())
02921  *
02922  *  Adds <i>aProc</i> as a finalizer, to be called after <i>obj</i>
02923  *  was destroyed.
02924  *
02925  */
02926 
02927 static VALUE
02928 define_final(int argc, VALUE *argv, VALUE os)
02929 {
02930     rb_objspace_t *objspace = &rb_objspace;
02931     VALUE obj, block, table;
02932     st_data_t data;
02933 
02934     rb_scan_args(argc, argv, "11", &obj, &block);
02935     rb_check_frozen(obj);
02936     if (argc == 1) {
02937         block = rb_block_proc();
02938     }
02939     else if (!rb_respond_to(block, rb_intern("call"))) {
02940         rb_raise(rb_eArgError, "wrong type argument %s (should be callable)",
02941                  rb_obj_classname(block));
02942     }
02943     if (!FL_ABLE(obj)) {
02944         rb_raise(rb_eArgError, "cannot define finalizer for %s",
02945                  rb_obj_classname(obj));
02946     }
02947     RBASIC(obj)->flags |= FL_FINALIZE;
02948 
02949     block = rb_ary_new3(2, INT2FIX(rb_safe_level()), block);
02950     OBJ_FREEZE(block);
02951 
02952     if (st_lookup(finalizer_table, obj, &data)) {
02953         table = (VALUE)data;
02954         rb_ary_push(table, block);
02955     }
02956     else {
02957         table = rb_ary_new3(1, block);
02958         RBASIC(table)->klass = 0;
02959         st_add_direct(finalizer_table, obj, table);
02960     }
02961     return block;
02962 }
02963 
02964 void
02965 rb_gc_copy_finalizer(VALUE dest, VALUE obj)
02966 {
02967     rb_objspace_t *objspace = &rb_objspace;
02968     VALUE table;
02969     st_data_t data;
02970 
02971     if (!FL_TEST(obj, FL_FINALIZE)) return;
02972     if (st_lookup(finalizer_table, obj, &data)) {
02973         table = (VALUE)data;
02974         st_insert(finalizer_table, dest, table);
02975     }
02976     FL_SET(dest, FL_FINALIZE);
02977 }
02978 
02979 static VALUE
02980 run_single_final(VALUE arg)
02981 {
02982     VALUE *args = (VALUE *)arg;
02983     rb_eval_cmd(args[0], args[1], (int)args[2]);
02984     return Qnil;
02985 }
02986 
02987 static void
02988 run_finalizer(rb_objspace_t *objspace, VALUE objid, VALUE table)
02989 {
02990     long i;
02991     int status;
02992     VALUE args[3];
02993 
02994     if (RARRAY_LEN(table) > 0) {
02995         args[1] = rb_obj_freeze(rb_ary_new3(1, objid));
02996     }
02997     else {
02998         args[1] = 0;
02999     }
03000 
03001     args[2] = (VALUE)rb_safe_level();
03002     for (i=0; i<RARRAY_LEN(table); i++) {
03003         VALUE final = RARRAY_PTR(table)[i];
03004         args[0] = RARRAY_PTR(final)[1];
03005         args[2] = FIX2INT(RARRAY_PTR(final)[0]);
03006         status = 0;
03007         rb_protect(run_single_final, (VALUE)args, &status);
03008         if (status)
03009             rb_set_errinfo(Qnil);
03010     }
03011 }
03012 
03013 static void
03014 run_final(rb_objspace_t *objspace, VALUE obj)
03015 {
03016     VALUE objid;
03017     RUBY_DATA_FUNC free_func = 0;
03018     st_data_t key, table;
03019 
03020     objspace->heap.final_num--;
03021 
03022     objid = rb_obj_id(obj);     /* make obj into id */
03023     RBASIC(obj)->klass = 0;
03024 
03025     if (RTYPEDDATA_P(obj)) {
03026         free_func = RTYPEDDATA_TYPE(obj)->function.dfree;
03027     }
03028     else {
03029         free_func = RDATA(obj)->dfree;
03030     }
03031     if (free_func) {
03032         (*free_func)(DATA_PTR(obj));
03033     }
03034 
03035     key = (st_data_t)obj;
03036     if (st_delete(finalizer_table, &key, &table)) {
03037         run_finalizer(objspace, objid, (VALUE)table);
03038     }
03039 }
03040 
03041 static void
03042 finalize_deferred(rb_objspace_t *objspace)
03043 {
03044     RVALUE *p = deferred_final_list;
03045     deferred_final_list = 0;
03046 
03047     if (p) {
03048         finalize_list(objspace, p);
03049     }
03050 }
03051 
03052 void
03053 rb_gc_finalize_deferred(void)
03054 {
03055     finalize_deferred(&rb_objspace);
03056 }
03057 
03058 static int
03059 chain_finalized_object(st_data_t key, st_data_t val, st_data_t arg)
03060 {
03061     RVALUE *p = (RVALUE *)key, **final_list = (RVALUE **)arg;
03062     if ((p->as.basic.flags & (FL_FINALIZE|FL_MARK)) == FL_FINALIZE) {
03063         if (BUILTIN_TYPE(p) != T_ZOMBIE) {
03064             p->as.free.flags = FL_MARK | T_ZOMBIE; /* remain marked */
03065             RDATA(p)->dfree = 0;
03066         }
03067         p->as.free.next = *final_list;
03068         *final_list = p;
03069     }
03070     return ST_CONTINUE;
03071 }
03072 
03073 struct force_finalize_list {
03074     VALUE obj;
03075     VALUE table;
03076     struct force_finalize_list *next;
03077 };
03078 
03079 static int
03080 force_chain_object(st_data_t key, st_data_t val, st_data_t arg)
03081 {
03082     struct force_finalize_list **prev = (struct force_finalize_list **)arg;
03083     struct force_finalize_list *curr = ALLOC(struct force_finalize_list);
03084     curr->obj = key;
03085     curr->table = val;
03086     curr->next = *prev;
03087     *prev = curr;
03088     return ST_CONTINUE;
03089 }
03090 
03091 void
03092 rb_gc_call_finalizer_at_exit(void)
03093 {
03094     rb_objspace_call_finalizer(&rb_objspace);
03095 }
03096 
03097 static void
03098 rb_objspace_call_finalizer(rb_objspace_t *objspace)
03099 {
03100     RVALUE *p, *pend;
03101     RVALUE *final_list = 0;
03102     size_t i;
03103 
03104     /* run finalizers */
03105     rest_sweep(objspace);
03106 
03107     do {
03108         /* XXX: this loop will make no sense */
03109         /* because mark will not be removed */
03110         finalize_deferred(objspace);
03111         mark_tbl(objspace, finalizer_table);
03112         gc_mark_stacked_objects(objspace);
03113         st_foreach(finalizer_table, chain_finalized_object,
03114                    (st_data_t)&deferred_final_list);
03115     } while (deferred_final_list);
03116     /* force to run finalizer */
03117     while (finalizer_table->num_entries) {
03118         struct force_finalize_list *list = 0;
03119         st_foreach(finalizer_table, force_chain_object, (st_data_t)&list);
03120         while (list) {
03121             struct force_finalize_list *curr = list;
03122             run_finalizer(objspace, rb_obj_id(curr->obj), curr->table);
03123             st_delete(finalizer_table, (st_data_t*)&curr->obj, 0);
03124             list = curr->next;
03125             xfree(curr);
03126         }
03127     }
03128 
03129     /* finalizers are part of garbage collection */
03130     during_gc++;
03131 
03132     /* run data object's finalizers */
03133     for (i = 0; i < heaps_used; i++) {
03134         p = objspace->heap.sorted[i].start; pend = objspace->heap.sorted[i].end;
03135         while (p < pend) {
03136             if (BUILTIN_TYPE(p) == T_DATA &&
03137                 DATA_PTR(p) && RANY(p)->as.data.dfree &&
03138                 !rb_obj_is_thread((VALUE)p) && !rb_obj_is_mutex((VALUE)p) &&
03139                 !rb_obj_is_fiber((VALUE)p)) {
03140                 p->as.free.flags = 0;
03141                 if (RTYPEDDATA_P(p)) {
03142                     RDATA(p)->dfree = RANY(p)->as.typeddata.type->function.dfree;
03143                 }
03144                 if (RANY(p)->as.data.dfree == (RUBY_DATA_FUNC)-1) {
03145                     xfree(DATA_PTR(p));
03146                 }
03147                 else if (RANY(p)->as.data.dfree) {
03148                     make_deferred(RANY(p));
03149                     RANY(p)->as.free.next = final_list;
03150                     final_list = p;
03151                 }
03152             }
03153             else if (BUILTIN_TYPE(p) == T_FILE) {
03154                 if (RANY(p)->as.file.fptr) {
03155                     make_io_deferred(RANY(p));
03156                     RANY(p)->as.free.next = final_list;
03157                     final_list = p;
03158                 }
03159             }
03160             p++;
03161         }
03162     }
03163     during_gc = 0;
03164     if (final_list) {
03165         finalize_list(objspace, final_list);
03166     }
03167 
03168     st_free_table(finalizer_table);
03169     finalizer_table = 0;
03170 }
03171 
03172 void
03173 rb_gc(void)
03174 {
03175     rb_objspace_t *objspace = &rb_objspace;
03176     garbage_collect(objspace);
03177     finalize_deferred(objspace);
03178     free_unused_heaps(objspace);
03179 }
03180 
03181 /*
03182  *  call-seq:
03183  *     ObjectSpace._id2ref(object_id) -> an_object
03184  *
03185  *  Converts an object id to a reference to the object. May not be
03186  *  called on an object id passed as a parameter to a finalizer.
03187  *
03188  *     s = "I am a string"                    #=> "I am a string"
03189  *     r = ObjectSpace._id2ref(s.object_id)   #=> "I am a string"
03190  *     r == s                                 #=> true
03191  *
03192  */
03193 
03194 static VALUE
03195 id2ref(VALUE obj, VALUE objid)
03196 {
03197 #if SIZEOF_LONG == SIZEOF_VOIDP
03198 #define NUM2PTR(x) NUM2ULONG(x)
03199 #elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
03200 #define NUM2PTR(x) NUM2ULL(x)
03201 #endif
03202     rb_objspace_t *objspace = &rb_objspace;
03203     VALUE ptr;
03204     void *p0;
03205 
03206     rb_secure(4);
03207     ptr = NUM2PTR(objid);
03208     p0 = (void *)ptr;
03209 
03210     if (ptr == Qtrue) return Qtrue;
03211     if (ptr == Qfalse) return Qfalse;
03212     if (ptr == Qnil) return Qnil;
03213     if (FIXNUM_P(ptr)) return (VALUE)ptr;
03214     ptr = obj_id_to_ref(objid);
03215 
03216     if ((ptr % sizeof(RVALUE)) == (4 << 2)) {
03217         ID symid = ptr / sizeof(RVALUE);
03218         if (rb_id2name(symid) == 0)
03219             rb_raise(rb_eRangeError, "%p is not symbol id value", p0);
03220         return ID2SYM(symid);
03221     }
03222 
03223     if (!is_pointer_to_heap(objspace, (void *)ptr) ||
03224         BUILTIN_TYPE(ptr) > T_FIXNUM || BUILTIN_TYPE(ptr) == T_ICLASS) {
03225         rb_raise(rb_eRangeError, "%p is not id value", p0);
03226     }
03227     if (BUILTIN_TYPE(ptr) == 0 || RBASIC(ptr)->klass == 0) {
03228         rb_raise(rb_eRangeError, "%p is recycled object", p0);
03229     }
03230     return (VALUE)ptr;
03231 }
03232 
03233 /*
03234  *  Document-method: __id__
03235  *  Document-method: object_id
03236  *
03237  *  call-seq:
03238  *     obj.__id__       -> fixnum
03239  *     obj.object_id    -> fixnum
03240  *
03241  *  Returns an integer identifier for <i>obj</i>. The same number will
03242  *  be returned on all calls to <code>id</code> for a given object, and
03243  *  no two active objects will share an id.
03244  *  <code>Object#object_id</code> is a different concept from the
03245  *  <code>:name</code> notation, which returns the symbol id of
03246  *  <code>name</code>. Replaces the deprecated <code>Object#id</code>.
03247  */
03248 
03249 /*
03250  *  call-seq:
03251  *     obj.hash    -> fixnum
03252  *
03253  *  Generates a <code>Fixnum</code> hash value for this object. This
03254  *  function must have the property that <code>a.eql?(b)</code> implies
03255  *  <code>a.hash == b.hash</code>. The hash value is used by class
03256  *  <code>Hash</code>. Any hash value that exceeds the capacity of a
03257  *  <code>Fixnum</code> will be truncated before being used.
03258  */
03259 
03260 VALUE
03261 rb_obj_id(VALUE obj)
03262 {
03263     /*
03264      *                32-bit VALUE space
03265      *          MSB ------------------------ LSB
03266      *  false   00000000000000000000000000000000
03267      *  true    00000000000000000000000000000010
03268      *  nil     00000000000000000000000000000100
03269      *  undef   00000000000000000000000000000110
03270      *  symbol  ssssssssssssssssssssssss00001110
03271      *  object  oooooooooooooooooooooooooooooo00        = 0 (mod sizeof(RVALUE))
03272      *  fixnum  fffffffffffffffffffffffffffffff1
03273      *
03274      *                    object_id space
03275      *                                       LSB
03276      *  false   00000000000000000000000000000000
03277      *  true    00000000000000000000000000000010
03278      *  nil     00000000000000000000000000000100
03279      *  undef   00000000000000000000000000000110
03280      *  symbol   000SSSSSSSSSSSSSSSSSSSSSSSSSSS0        S...S % A = 4 (S...S = s...s * A + 4)
03281      *  object   oooooooooooooooooooooooooooooo0        o...o % A = 0
03282      *  fixnum  fffffffffffffffffffffffffffffff1        bignum if required
03283      *
03284      *  where A = sizeof(RVALUE)/4
03285      *
03286      *  sizeof(RVALUE) is
03287      *  20 if 32-bit, double is 4-byte aligned
03288      *  24 if 32-bit, double is 8-byte aligned
03289      *  40 if 64-bit
03290      */
03291     if (SYMBOL_P(obj)) {
03292         return (SYM2ID(obj) * sizeof(RVALUE) + (4 << 2)) | FIXNUM_FLAG;
03293     }
03294     if (SPECIAL_CONST_P(obj)) {
03295         return LONG2NUM((SIGNED_VALUE)obj);
03296     }
03297     return nonspecial_obj_id(obj);
03298 }
03299 
03300 static int
03301 set_zero(st_data_t key, st_data_t val, st_data_t arg)
03302 {
03303     VALUE k = (VALUE)key;
03304     VALUE hash = (VALUE)arg;
03305     rb_hash_aset(hash, k, INT2FIX(0));
03306     return ST_CONTINUE;
03307 }
03308 
03309 /*
03310  *  call-seq:
03311  *     ObjectSpace.count_objects([result_hash]) -> hash
03312  *
03313  *  Counts objects for each type.
03314  *
03315  *  It returns a hash as:
03316  *  {:TOTAL=>10000, :FREE=>3011, :T_OBJECT=>6, :T_CLASS=>404, ...}
03317  *
03318  *  If the optional argument, result_hash, is given,
03319  *  it is overwritten and returned.
03320  *  This is intended to avoid probe effect.
03321  *
03322  *  The contents of the returned hash is implementation defined.
03323  *  It may be changed in future.
03324  *
03325  *  This method is not expected to work except C Ruby.
03326  *
03327  */
03328 
03329 static VALUE
03330 count_objects(int argc, VALUE *argv, VALUE os)
03331 {
03332     rb_objspace_t *objspace = &rb_objspace;
03333     size_t counts[T_MASK+1];
03334     size_t freed = 0;
03335     size_t total = 0;
03336     size_t i;
03337     VALUE hash;
03338 
03339     if (rb_scan_args(argc, argv, "01", &hash) == 1) {
03340         if (TYPE(hash) != T_HASH)
03341             rb_raise(rb_eTypeError, "non-hash given");
03342     }
03343 
03344     for (i = 0; i <= T_MASK; i++) {
03345         counts[i] = 0;
03346     }
03347 
03348     for (i = 0; i < heaps_used; i++) {
03349         RVALUE *p, *pend;
03350 
03351         p = objspace->heap.sorted[i].start; pend = objspace->heap.sorted[i].end;
03352         for (;p < pend; p++) {
03353             if (p->as.basic.flags) {
03354                 counts[BUILTIN_TYPE(p)]++;
03355             }
03356             else {
03357                 freed++;
03358             }
03359         }
03360         total += objspace->heap.sorted[i].slot->limit;
03361     }
03362 
03363     if (hash == Qnil) {
03364         hash = rb_hash_new();
03365     }
03366     else if (!RHASH_EMPTY_P(hash)) {
03367         st_foreach(RHASH_TBL(hash), set_zero, hash);
03368     }
03369     rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
03370     rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(freed));
03371 
03372     for (i = 0; i <= T_MASK; i++) {
03373         VALUE type;
03374         switch (i) {
03375 #define COUNT_TYPE(t) case (t): type = ID2SYM(rb_intern(#t)); break;
03376             COUNT_TYPE(T_NONE);
03377             COUNT_TYPE(T_OBJECT);
03378             COUNT_TYPE(T_CLASS);
03379             COUNT_TYPE(T_MODULE);
03380             COUNT_TYPE(T_FLOAT);
03381             COUNT_TYPE(T_STRING);
03382             COUNT_TYPE(T_REGEXP);
03383             COUNT_TYPE(T_ARRAY);
03384             COUNT_TYPE(T_HASH);
03385             COUNT_TYPE(T_STRUCT);
03386             COUNT_TYPE(T_BIGNUM);
03387             COUNT_TYPE(T_FILE);
03388             COUNT_TYPE(T_DATA);
03389             COUNT_TYPE(T_MATCH);
03390             COUNT_TYPE(T_COMPLEX);
03391             COUNT_TYPE(T_RATIONAL);
03392             COUNT_TYPE(T_NIL);
03393             COUNT_TYPE(T_TRUE);
03394             COUNT_TYPE(T_FALSE);
03395             COUNT_TYPE(T_SYMBOL);
03396             COUNT_TYPE(T_FIXNUM);
03397             COUNT_TYPE(T_UNDEF);
03398             COUNT_TYPE(T_NODE);
03399             COUNT_TYPE(T_ICLASS);
03400             COUNT_TYPE(T_ZOMBIE);
03401 #undef COUNT_TYPE
03402           default:              type = INT2NUM(i); break;
03403         }
03404         if (counts[i])
03405             rb_hash_aset(hash, type, SIZET2NUM(counts[i]));
03406     }
03407 
03408     return hash;
03409 }
03410 
03411 /*
03412  *  call-seq:
03413  *     GC.count -> Integer
03414  *
03415  *  The number of times GC occurred.
03416  *
03417  *  It returns the number of times GC occurred since the process started.
03418  *
03419  */
03420 
03421 static VALUE
03422 gc_count(VALUE self)
03423 {
03424     return UINT2NUM((&rb_objspace)->count);
03425 }
03426 
03427 /*
03428  *  call-seq:
03429  *     GC.stat -> Hash
03430  *
03431  *  Returns a Hash containing information about the GC.
03432  *
03433  *  The hash includes information about internal statistics about GC such as:
03434  *
03435  *    {
03436  *      :count          => 18,
03437  *      :heap_used      => 77,
03438  *      :heap_length    => 77,
03439  *      :heap_increment => 0,
03440  *      :heap_live_num  => 23287,
03441  *      :heap_free_num  => 8115,
03442  *      :heap_final_num => 0,
03443  *    }
03444  *
03445  *  The contents of the hash are implementation defined and may be changed in
03446  *  the future.
03447  *
03448  *  This method is only expected to work on C Ruby.
03449  *
03450  */
03451 
03452 static VALUE
03453 gc_stat(int argc, VALUE *argv, VALUE self)
03454 {
03455     rb_objspace_t *objspace = &rb_objspace;
03456     VALUE hash;
03457 
03458     if (rb_scan_args(argc, argv, "01", &hash) == 1) {
03459         if (TYPE(hash) != T_HASH)
03460             rb_raise(rb_eTypeError, "non-hash given");
03461     }
03462 
03463     if (hash == Qnil) {
03464         hash = rb_hash_new();
03465     }
03466 
03467     rest_sweep(objspace);
03468 
03469     rb_hash_aset(hash, ID2SYM(rb_intern("count")), SIZET2NUM(objspace->count));
03470 
03471     /* implementation dependent counters */
03472     rb_hash_aset(hash, ID2SYM(rb_intern("heap_used")), SIZET2NUM(objspace->heap.used));
03473     rb_hash_aset(hash, ID2SYM(rb_intern("heap_length")), SIZET2NUM(objspace->heap.length));
03474     rb_hash_aset(hash, ID2SYM(rb_intern("heap_increment")), SIZET2NUM(objspace->heap.increment));
03475     rb_hash_aset(hash, ID2SYM(rb_intern("heap_live_num")), SIZET2NUM(objspace->heap.live_num));
03476     rb_hash_aset(hash, ID2SYM(rb_intern("heap_free_num")), SIZET2NUM(objspace->heap.free_num));
03477     rb_hash_aset(hash, ID2SYM(rb_intern("heap_final_num")), SIZET2NUM(objspace->heap.final_num));
03478     return hash;
03479 }
03480 
03481 
03482 #if CALC_EXACT_MALLOC_SIZE
03483 /*
03484  *  call-seq:
03485  *     GC.malloc_allocated_size -> Integer
03486  *
03487  *  The allocated size by malloc().
03488  *
03489  *  It returns the allocated size by malloc().
03490  */
03491 
03492 static VALUE
03493 gc_malloc_allocated_size(VALUE self)
03494 {
03495     return UINT2NUM((&rb_objspace)->malloc_params.allocated_size);
03496 }
03497 
03498 /*
03499  *  call-seq:
03500  *     GC.malloc_allocations -> Integer
03501  *
03502  *  The number of allocated memory object by malloc().
03503  *
03504  *  It returns the number of allocated memory object by malloc().
03505  */
03506 
03507 static VALUE
03508 gc_malloc_allocations(VALUE self)
03509 {
03510     return UINT2NUM((&rb_objspace)->malloc_params.allocations);
03511 }
03512 #endif
03513 
03514 static VALUE
03515 gc_profile_record_get(void)
03516 {
03517     VALUE prof;
03518     VALUE gc_profile = rb_ary_new();
03519     size_t i;
03520     rb_objspace_t *objspace = (&rb_objspace);
03521 
03522     if (!objspace->profile.run) {
03523         return Qnil;
03524     }
03525 
03526     for (i =0; i < objspace->profile.count; i++) {
03527         prof = rb_hash_new();
03528         rb_hash_aset(prof, ID2SYM(rb_intern("GC_TIME")), DBL2NUM(objspace->profile.record[i].gc_time));
03529         rb_hash_aset(prof, ID2SYM(rb_intern("GC_INVOKE_TIME")), DBL2NUM(objspace->profile.record[i].gc_invoke_time));
03530         rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SIZE")), SIZET2NUM(objspace->profile.record[i].heap_use_size));
03531         rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_SIZE")), SIZET2NUM(objspace->profile.record[i].heap_total_size));
03532         rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_TOTAL_OBJECTS")), SIZET2NUM(objspace->profile.record[i].heap_total_objects));
03533         rb_hash_aset(prof, ID2SYM(rb_intern("GC_IS_MARKED")), objspace->profile.record[i].is_marked);
03534 #if GC_PROFILE_MORE_DETAIL
03535         rb_hash_aset(prof, ID2SYM(rb_intern("GC_MARK_TIME")), DBL2NUM(objspace->profile.record[i].gc_mark_time));
03536         rb_hash_aset(prof, ID2SYM(rb_intern("GC_SWEEP_TIME")), DBL2NUM(objspace->profile.record[i].gc_sweep_time));
03537         rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_INCREASE")), SIZET2NUM(objspace->profile.record[i].allocate_increase));
03538         rb_hash_aset(prof, ID2SYM(rb_intern("ALLOCATE_LIMIT")), SIZET2NUM(objspace->profile.record[i].allocate_limit));
03539         rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_USE_SLOTS")), SIZET2NUM(objspace->profile.record[i].heap_use_slots));
03540         rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_LIVE_OBJECTS")), SIZET2NUM(objspace->profile.record[i].heap_live_objects));
03541         rb_hash_aset(prof, ID2SYM(rb_intern("HEAP_FREE_OBJECTS")), SIZET2NUM(objspace->profile.record[i].heap_free_objects));
03542         rb_hash_aset(prof, ID2SYM(rb_intern("HAVE_FINALIZE")), objspace->profile.record[i].have_finalize);
03543 #endif
03544         rb_ary_push(gc_profile, prof);
03545     }
03546 
03547     return gc_profile;
03548 }
03549 
03550 /*
03551  *  call-seq:
03552  *     GC::Profiler.result -> String
03553  *
03554  *  Returns a profile data report such as:
03555  *
03556  *    GC 1 invokes.
03557  *    Index    Invoke Time(sec)       Use Size(byte)     Total Size(byte)         Total Object                    GC time(ms)
03558  *        1               0.012               159240               212940                10647         0.00000000000001530000
03559  */
03560 
03561 static VALUE
03562 gc_profile_result(void)
03563 {
03564     rb_objspace_t *objspace = &rb_objspace;
03565     VALUE record;
03566     VALUE result;
03567     int i, index;
03568 
03569     record = gc_profile_record_get();
03570     if (objspace->profile.run && objspace->profile.count) {
03571         result = rb_sprintf("GC %d invokes.\n", NUM2INT(gc_count(0)));
03572         index = 1;
03573         rb_str_cat2(result, "Index    Invoke Time(sec)       Use Size(byte)     Total Size(byte)         Total Object                    GC Time(ms)\n");
03574         for (i = 0; i < (int)RARRAY_LEN(record); i++) {
03575             VALUE r = RARRAY_PTR(record)[i];
03576 #if !GC_PROFILE_MORE_DETAIL
03577             if (rb_hash_aref(r, ID2SYM(rb_intern("GC_IS_MARKED")))) {
03578 #endif
03579             rb_str_catf(result, "%5d %19.3f %20"PRIuSIZE" %20"PRIuSIZE" %20"PRIuSIZE" %30.20f\n",
03580                         index++, NUM2DBL(rb_hash_aref(r, ID2SYM(rb_intern("GC_INVOKE_TIME")))),
03581                         (size_t)NUM2SIZET(rb_hash_aref(r, ID2SYM(rb_intern("HEAP_USE_SIZE")))),
03582                         (size_t)NUM2SIZET(rb_hash_aref(r, ID2SYM(rb_intern("HEAP_TOTAL_SIZE")))),
03583                         (size_t)NUM2SIZET(rb_hash_aref(r, ID2SYM(rb_intern("HEAP_TOTAL_OBJECTS")))),
03584                         NUM2DBL(rb_hash_aref(r, ID2SYM(rb_intern("GC_TIME"))))*1000);
03585 #if !GC_PROFILE_MORE_DETAIL
03586             }
03587 #endif
03588         }
03589 #if GC_PROFILE_MORE_DETAIL
03590         rb_str_cat2(result, "\n\n");
03591         rb_str_cat2(result, "More detail.\n");
03592         rb_str_cat2(result, "Index Allocate Increase    Allocate Limit  Use Slot  Have Finalize             Mark Time(ms)            Sweep Time(ms)\n");
03593         index = 1;
03594         for (i = 0; i < (int)RARRAY_LEN(record); i++) {
03595             VALUE r = RARRAY_PTR(record)[i];
03596             rb_str_catf(result, "%5d %17"PRIuSIZE" %17"PRIuSIZE" %9"PRIuSIZE" %14s %25.20f %25.20f\n",
03597                         index++, (size_t)NUM2SIZET(rb_hash_aref(r, ID2SYM(rb_intern("ALLOCATE_INCREASE")))),
03598                         (size_t)NUM2SIZET(rb_hash_aref(r, ID2SYM(rb_intern("ALLOCATE_LIMIT")))),
03599                         (size_t)NUM2SIZET(rb_hash_aref(r, ID2SYM(rb_intern("HEAP_USE_SLOTS")))),
03600                         rb_hash_aref(r, ID2SYM(rb_intern("HAVE_FINALIZE")))? "true" : "false",
03601                         NUM2DBL(rb_hash_aref(r, ID2SYM(rb_intern("GC_MARK_TIME"))))*1000,
03602                         NUM2DBL(rb_hash_aref(r, ID2SYM(rb_intern("GC_SWEEP_TIME"))))*1000);
03603         }
03604 #endif
03605     }
03606     else {
03607         result = rb_str_new2("");
03608     }
03609     return result;
03610 }
03611 
03612 
03613 /*
03614  *  call-seq:
03615  *     GC::Profiler.report
03616  *     GC::Profiler.report io
03617  *
03618  *  Writes the GC::Profiler#result to <tt>$stdout</tt> or the given IO object.
03619  *
03620  */
03621 
03622 static VALUE
03623 gc_profile_report(int argc, VALUE *argv, VALUE self)
03624 {
03625     VALUE out;
03626 
03627     if (argc == 0) {
03628         out = rb_stdout;
03629     }
03630     else {
03631         rb_scan_args(argc, argv, "01", &out);
03632     }
03633     rb_io_write(out, gc_profile_result());
03634 
03635     return Qnil;
03636 }
03637 
03638 /*
03639  *  call-seq:
03640  *     GC::Profiler.total_time -> float
03641  *
03642  *  The total time used for garbage collection in milliseconds
03643  */
03644 
03645 static VALUE
03646 gc_profile_total_time(VALUE self)
03647 {
03648     double time = 0;
03649     rb_objspace_t *objspace = &rb_objspace;
03650     size_t i;
03651 
03652     if (objspace->profile.run && objspace->profile.count) {
03653         for (i = 0; i < objspace->profile.count; i++) {
03654             time += objspace->profile.record[i].gc_time;
03655         }
03656     }
03657     return DBL2NUM(time);
03658 }
03659 
03660 /*  Document-class: GC::Profiler
03661  *
03662  *  The GC profiler provides access to information on GC runs including time,
03663  *  length and object space size.
03664  *
03665  *  Example:
03666  *
03667  *    GC::Profiler.enable
03668  *
03669  *    require 'rdoc/rdoc'
03670  *
03671  *    puts GC::Profiler.result
03672  *
03673  *    GC::Profiler.disable
03674  *
03675  *  See also GC.count, GC.malloc_allocated_size and GC.malloc_allocations
03676  */
03677 
03678 /*
03679  *  The <code>GC</code> module provides an interface to Ruby's mark and
03680  *  sweep garbage collection mechanism. Some of the underlying methods
03681  *  are also available via the ObjectSpace module.
03682  *
03683  *  You may obtain information about the operation of the GC through
03684  *  GC::Profiler.
03685  */
03686 
03687 void
03688 Init_GC(void)
03689 {
03690     VALUE rb_mObSpace;
03691     VALUE rb_mProfiler;
03692 
03693     rb_mGC = rb_define_module("GC");
03694     rb_define_singleton_method(rb_mGC, "start", rb_gc_start, 0);
03695     rb_define_singleton_method(rb_mGC, "enable", rb_gc_enable, 0);
03696     rb_define_singleton_method(rb_mGC, "disable", rb_gc_disable, 0);
03697     rb_define_singleton_method(rb_mGC, "stress", gc_stress_get, 0);
03698     rb_define_singleton_method(rb_mGC, "stress=", gc_stress_set, 1);
03699     rb_define_singleton_method(rb_mGC, "count", gc_count, 0);
03700     rb_define_singleton_method(rb_mGC, "stat", gc_stat, -1);
03701     rb_define_method(rb_mGC, "garbage_collect", rb_gc_start, 0);
03702 
03703     rb_mProfiler = rb_define_module_under(rb_mGC, "Profiler");
03704     rb_define_singleton_method(rb_mProfiler, "enabled?", gc_profile_enable_get, 0);
03705     rb_define_singleton_method(rb_mProfiler, "enable", gc_profile_enable, 0);
03706     rb_define_singleton_method(rb_mProfiler, "disable", gc_profile_disable, 0);
03707     rb_define_singleton_method(rb_mProfiler, "clear", gc_profile_clear, 0);
03708     rb_define_singleton_method(rb_mProfiler, "result", gc_profile_result, 0);
03709     rb_define_singleton_method(rb_mProfiler, "report", gc_profile_report, -1);
03710     rb_define_singleton_method(rb_mProfiler, "total_time", gc_profile_total_time, 0);
03711 
03712     rb_mObSpace = rb_define_module("ObjectSpace");
03713     rb_define_module_function(rb_mObSpace, "each_object", os_each_obj, -1);
03714     rb_define_module_function(rb_mObSpace, "garbage_collect", rb_gc_start, 0);
03715 
03716     rb_define_module_function(rb_mObSpace, "define_finalizer", define_final, -1);
03717     rb_define_module_function(rb_mObSpace, "undefine_finalizer", undefine_final, 1);
03718 
03719     rb_define_module_function(rb_mObSpace, "_id2ref", id2ref, 1);
03720 
03721     nomem_error = rb_exc_new3(rb_eNoMemError,
03722                               rb_obj_freeze(rb_str_new2("failed to allocate memory")));
03723     OBJ_TAINT(nomem_error);
03724     OBJ_FREEZE(nomem_error);
03725 
03726     rb_define_method(rb_cBasicObject, "__id__", rb_obj_id, 0);
03727     rb_define_method(rb_mKernel, "object_id", rb_obj_id, 0);
03728 
03729     rb_define_module_function(rb_mObSpace, "count_objects", count_objects, -1);
03730 
03731 #if CALC_EXACT_MALLOC_SIZE
03732     rb_define_singleton_method(rb_mGC, "malloc_allocated_size", gc_malloc_allocated_size, 0);
03733     rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0);
03734 #endif
03735 }
03736