diff options
author | Yukihiro Matsumoto <matz@ruby-lang.org> | 1994-08-10 15:54:46 +0900 |
---|---|---|
committer | Takashi Kokubun <takashikkbn@gmail.com> | 2019-08-17 22:09:30 +0900 |
commit | 6e3090413652b6592346556149fed1e9aec5495d (patch) | |
tree | bac97139bbeedc8cb67cb2e451a22ed4ddb2b2d4 /gc.c | |
parent | 200e0ee2fd3c1c006c528874a88f684447215524 (diff) |
version 0.50v0_50
http://cache.ruby-lang.org/pub/ruby/1.0/ruby-0.50.tar.gz
Wed Aug 10 15:54:46 1994 Yukihiro Matsumoto (matz@ix-02)
* variable.c: -vオプションが指定されている時は初期化されていない,
大域変数, インスタンス変数, ローカル変数を参照した時点でwarning
を出すようにした.
Tue Aug 9 11:50:48 1994 Yukihiro Matsumoto (matz@ix-02)
* bignum.c: 冪乗に関しても多倍長演算を行なうように. 特に浮動小数点
数の範囲を越えた時の処理を的確に行なうように.
* eval.c: メソッド定義後は構文木から, メソッド定義部分を外す. 無駄
な再定義が起こらないようにするためと2重にfree()されないため.
* array.c(Fary_aref): 引数が1つでFixnumの時, Range checkを行なわな
いように修正.
* eval.c: 引数の数をコンパイル時に計算して若干の高速化.
Mon Aug 8 13:06:24 1994 Yukihiro Matsumoto (matz@ix-02)
* object.c: nilによる比較連鎖をなくした.
* parse.y: bit演算子の優先順位を比較演算子よりも強くした. Cとは異
なることになるが, 直観には合致する.
* gc.c: クラスを解放する時, 個々のメソッド毎にキャッシュをクリアす
るのではなく, クラス単位でクリアするように.
Thu Aug 4 18:45:09 1994 Yukihiro Matsumoto (matz@ix-02)
* methods.c(method_free): 解放されたメソッドに関してキャッシュをク
リアしておく必要があった.
* gc.c: Dataクラスのデータ部分をfree()し忘れていた.
Wed Aug 3 09:58:14 1994 Yukihiro Matsumoto (matz@ix-02)
* parse.y: def func .. end形式による関数メソッドの定義はなくなった.
* methods.c: func形式のメソッドをなくした. あっても, あまり意味が
ないので.
* eval.c: $0への代入でps(1)の出力が変化するように.
* io.c(Fsyscall): syscall()を実現.
Mon Aug 1 13:41:11 1994 Yukihiro Matsumoto (matz@ix-02)
* parse.y: ダブルクォートで囲まれた文字列や正規表現中で"#{変数名}"
または"#変数名"という形式で変数の内容を埋め込むことができるよう
になった.
* io.c: 関数メソッドsystem2()はなくなった. 今はバッククォートがあ
るからね.
* parse.y: `cmd`によってコマンドを文字列に展開することができるよう
になった.
* parse.y: __FILE__, __LINE__を追加. それぞれファイル名(文字列),
行番号(整数)を値とする疑似変数.
Fri Jul 29 13:16:07 1994 Yukihiro Matsumoto (matz@ix-02)
* methods.h: メソッドをオブジェクトとして扱うのをやめる. メソッド
のメモリ管理にはリファレンスカウントを使うことにした. これでオブ
ジェクトの数が減ってほんの少しだけGCが速くなる(かな).
* purifyによってメモリ関係のバグを検査した(見つかる,見つかる…).
* gc.c: GCをプログラマが変数をマークする形式から, スタックとレジス
タからマークする方法に変更. 移植性が下がるような気もするが, siod
やscmでも採用されているから多分大丈夫だろう. Linux on i486でも動
作を確認した.
Wed Jul 27 16:13:13 1994 Yukihiro Matsumoto (matz@ix-02)
* eval.c(Eval): トップレベルでは構造木をfreeしないように. どうせ解
放されるから時間の無駄である.
* array.c, dict.c: "=="を構造一致に変更.
Fri Jul 22 10:14:09 1994 Yukihiro Matsumoto (matz@ix-02)
* error.c: 組み込みタイプの名前を登録し忘れていた.
Thu Jul 21 14:06:48 1994 Yukihiro Matsumoto (matz@ix-02)
* parse.y(freenode),eval.c(Eval): 解析木を解放し忘れていた.
Mon Jul 18 10:19:15 1994 Yukihiro Matsumoto (matz@ix-02)
* parse.y: 多重代入を処理するルールにバグがあって, 3要素以上の多重
代入に失敗していた.
* eval.c(rb_eval): 多重代入で, 右辺が配列でない時には`to_a'メソッ
ドで配列に変換して代入するようにした. 今までの仕様だと右辺値が第
1要素にそのまま代入されていたが, structなど配列に変換できるもの
は変換した方が嬉しい気がする.
* dbm.c,dict.c(delete_if): メソッド追加.
* process.c(wait,waitpid): システムコールwaitpidまたはwait4がある
時はそちらを使うように. configureもそれらをチェックするように変更.
* dbm.c, dict.c(clear): メソッド追加.
Diffstat (limited to 'gc.c')
-rw-r--r-- | gc.c | 522 |
1 files changed, 333 insertions, 189 deletions
@@ -14,16 +14,14 @@ #include "env.h" #include "st.h" #include <stdio.h> +#include <setjmp.h> void *malloc(); void *calloc(); void *realloc(); -struct gc_list *GC_List = Qnil; -static struct gc_list *Global_List = Qnil; -static unsigned long bytes_alloc = 0, gc_threshold = 1000000; - -static mark_tbl(); +void gc(); +void gc_mark(); void * xmalloc(size) @@ -31,12 +29,10 @@ xmalloc(size) { void *mem; - bytes_alloc += size; if (size == 0) size = 1; mem = malloc(size); if (mem == Qnil) { gc(); - bytes_alloc += size; mem = malloc(size); if (mem == Qnil) Fatal("failed to allocate memory"); @@ -75,23 +71,41 @@ xrealloc(ptr, size) return mem; } -void -rb_global_variable(var) - VALUE *var; -{ - struct gc_list *tmp; - - tmp = (struct gc_list*)xmalloc(sizeof(struct gc_list)); - tmp->next = Global_List; - tmp->varptr = var; - tmp->n = 1; - Global_List = tmp; -} - -static struct RBasic *object_list = Qnil; -static struct RBasic *literal_list = Qnil; -static unsigned long fl_current = FL_MARK; -static unsigned long fl_old = 0L; +/* The way of garbage collecting which allows use of the cstack is due to */ +/* Scheme In One Defun, but in C this time. + + * COPYRIGHT (c) 1989 BY * + * PARADIGM ASSOCIATES INCORPORATED, CAMBRIDGE, MASSACHUSETTS. * + * ALL RIGHTS RESERVED * + +Permission to use, copy, modify, distribute and sell this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all copies +and that both that copyright notice and this permission notice appear +in supporting documentation, and that the name of Paradigm Associates +Inc not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +PARADIGM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +PARADIGM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +gjc@paradigm.com + +Paradigm Associates Inc Phone: 617-492-6079 +29 Putnam Ave, Suite 6 +Cambridge, MA 02138 +*/ + +#ifdef sparc +#define FLUSH_REGISTER_WINDOWS asm("ta 3") +#else +#define FLUSH_REGISTER_WINDOWS /* empty */ +#endif static int dont_gc; @@ -113,23 +127,6 @@ Fgc_disable() return old; } -VALUE -Fgc_threshold(obj) - VALUE obj; -{ - return INT2FIX(gc_threshold); -} - -VALUE -Fgc_set_threshold(obj, val) - VALUE obj, val; -{ - int old = gc_threshold; - - gc_threshold = NUM2INT(val); - return INT2FIX(old); -} - #include <sys/types.h> #include <sys/times.h> @@ -145,140 +142,204 @@ static Fgc_end() VALUE M_GC; -static ID start_hook, end_hook; +static struct gc_list { + int n; + VALUE *varptr; + struct gc_list *next; +} *Global_List = Qnil; -struct RBasic * -newobj(size) - unsigned long size; +void +rb_global_variable(var) + VALUE *var; { - struct RBasic *obj = Qnil; + struct gc_list *tmp; - if (bytes_alloc + size > gc_threshold) { - gc(); - } - obj = (struct RBasic*)xmalloc(size); - obj->next = object_list; - object_list = obj; - obj->flags = fl_current; - obj->iv_tbl = Qnil; + tmp = (struct gc_list*)xmalloc(sizeof(struct gc_list)); + tmp->next = Global_List; + tmp->varptr = var; + tmp->n = 1; + Global_List = tmp; +} - return obj; +struct RVALUE { + union { + struct { + int flag; /* alway 0 for freed obj */ + struct RVALUE *next; + } free; + struct RObject object; + struct RClass class; + struct RFloat flonum; + struct RString string; + struct RArray array; + struct RRegexp regexp; + struct RDict dict; + struct RData data; + struct RStruct rstruct; + struct RBignum bignum; + } as; +} *freelist = Qnil; + +struct heap_block { + struct heap_block *next; + struct RVALUE *beg; + struct RVALUE *end; + struct RVALUE body[1]; +} *heap_link = Qnil; + +#define SEG_SLOTS 4000 +#define SEG_SIZE (SEG_SLOTS*sizeof(struct RVALUE)) + +static int heap_size; + +static void +add_heap() +{ + struct heap_block *block; + struct RVALUE *p, *pend; + + block = (struct heap_block*)malloc(sizeof(*block) + SEG_SIZE); + if (block == Qnil) Fatal("cant alloc memory"); + block->next = heap_link; + block->beg = &block->body[0]; + block->end = block->beg + SEG_SLOTS; + p = block->beg; pend = block->end; + while (p < pend) { + p->as.free.flag = 0; + p->as.free.next = freelist; + freelist = p; + p++; + } + heap_link = block; + heap_size += SEG_SLOTS; } -literalize(obj) - struct RBasic *obj; +struct RBasic * +newobj() { - struct RBasic *ptr = object_list; + struct RBasic *obj; + if (heap_link == Qnil) add_heap(); + if (freelist) { + retry: + obj = (struct RBasic*)freelist; + freelist = freelist->as.free.next; + obj->flags = 0; + obj->iv_tbl = Qnil; + return obj; + } + if (dont_gc) add_heap(); + else gc(); - if (NIL_P(obj) || FIXNUM_P(obj)) return; + goto retry; +} - FL_SET(obj, FL_LITERAL); - if (ptr == obj) { - object_list = ptr->next; - obj->next = literal_list; - literal_list = obj; +VALUE +newdata(size) + UINT size; +{ + extern VALUE C_Data; + struct RData *data = (struct RData*)newobj(); - return; - } + OBJSETUP(data, C_Data, T_DATA); + data->data = xmalloc(size); + return (VALUE)data; +} - while (ptr && ptr->next) { - if (ptr->next == obj) { - ptr->next = obj->next; - obj->next = literal_list; - literal_list = obj; +static struct literal_list { + VALUE val; + struct literal_list *next; +} *Literal_List = Qnil; - return; - } - ptr = ptr->next; - } - Bug("0x%x is not a object.", obj); +void +literalize(obj) + VALUE obj; +{ + struct literal_list *tmp; + + tmp = (struct literal_list*)xmalloc(sizeof(struct literal_list)); + tmp->next = Literal_List; + tmp->val = obj; + Literal_List = tmp; } void unliteralize(obj) - struct RBasic *obj; + VALUE obj; { - struct RBasic *ptr = literal_list; + struct literal_list *ptr = Literal_List, *tmp; if (NIL_P(obj) || FIXNUM_P(obj)) return; if (!FL_TEST(obj, FL_LITERAL)) return; FL_UNSET(obj, FL_LITERAL); - if (ptr == obj) { - literal_list = ptr->next; - goto unlit; + if (ptr->val == obj) { + Literal_List = ptr->next; + free(ptr); + return; } while (ptr->next) { - if (ptr->next == obj) { - ptr->next = obj->next; + if (ptr->next->val == obj) { + tmp = ptr->next; + ptr->next = ptr->next->next; + ptr = tmp; + free(tmp); + return; } ptr = ptr->next; - goto unlit; } Bug("0x%x is not a literal object.", obj); - - unlit: - obj->next = object_list; - object_list = obj; - obj->flags &= ~FL_MARK; - obj->flags |= fl_current; - return; } -extern st_table *rb_global_tbl; extern st_table *rb_class_tbl; +static VALUE *stack_start_ptr; -gc() +static long +looks_pointerp(p) + struct RVALUE *p; { - struct gc_list *list; - struct ENVIRON *env; - int i, max; - - rb_funcall(M_GC, start_hook, 0, Qnil); - - if (dont_gc) return; - dont_gc++; - fl_old = fl_current; - fl_current = ~fl_current & FL_MARK; - - /* mark env stack */ - for (env = the_env; env; env = env->prev) { - mark(env->self); - for (i=1, max=env->argc; i<max; i++) { - mark(env->argv[i]); - } - if (env->local_vars) { - for (i=0, max=env->local_tbl[0]; i<max; i++) - mark(env->local_vars[i]); - } - } - - /* mark protected C variables */ - for (list=GC_List; list; list=list->next) { - VALUE *v = list->varptr; - for (i=0, max = list->n; i<max; i++) { - mark(*v); - v++; - } - } - - /* mark protected global variables */ - for (list = Global_List; list; list = list->next) { - mark(*list->varptr); + struct heap_block *heap = heap_link; + + if (FIXNUM_P(p)) return FALSE; + while (heap) { + if (heap->beg <= p && p < heap->end + && ((((char*)p) - ((char*)heap->beg)) % sizeof(struct RVALUE)) == 0) + return TRUE; + heap = heap->next; } + return FALSE; +} - mark_global_tbl(); - mark_tbl(rb_class_tbl); - - mark_trap_list(); +static void +mark_locations_array(x, n) + VALUE *x; + long n; +{ + int j; + VALUE p; + for(j=0;j<n;++j) + {p = x[j]; + if (looks_pointerp(p)) { + gc_mark(p); + } + } +} - sweep(); - bytes_alloc = 0; - dont_gc--; +static void +mark_locations(start, end) + VALUE *start, *end; +{ + VALUE *tmp; + long n; - rb_funcall(M_GC, end_hook, 0, Qnil); + if (start > end) { + tmp = start; + start = end; + end = tmp; + } + n = end - start; + mark_locations_array(start,n); } static @@ -286,7 +347,7 @@ mark_entry(key, value) ID key; VALUE value; { - mark(value); + gc_mark(value); return ST_CONTINUE; } @@ -302,8 +363,8 @@ mark_dicentry(key, value) ID key; VALUE value; { - mark(key); - mark(value); + gc_mark(key); + gc_mark(value); return ST_CONTINUE; } @@ -314,39 +375,35 @@ mark_dict(tbl) st_foreach(tbl, mark_dicentry, 0); } -mark(obj) +void +gc_mark(obj) register struct RBasic *obj; { if (obj == Qnil) return; if (FIXNUM_P(obj)) return; - if ((obj->flags & FL_MARK) == fl_current) return; + if (obj->flags & FL_MARK) return; - obj->flags &= ~FL_MARK; - obj->flags |= fl_current; + obj->flags |= FL_MARK; switch (obj->flags & T_MASK) { case T_NIL: case T_FIXNUM: - Bug("mark() called for broken object"); + Bug("gc_mark() called for broken object"); break; } if (obj->iv_tbl) mark_tbl(obj->iv_tbl); + gc_mark(obj->class); switch (obj->flags & T_MASK) { - case T_OBJECT: - mark(obj->class); - break; case T_ICLASS: - mark(RCLASS(obj)->super); + gc_mark(RCLASS(obj)->super); if (RCLASS(obj)->c_tbl) mark_tbl(RCLASS(obj)->c_tbl); - mark_tbl(RCLASS(obj)->m_tbl); break; case T_CLASS: - mark(RCLASS(obj)->super); + gc_mark(RCLASS(obj)->super); case T_MODULE: if (RCLASS(obj)->c_tbl) mark_tbl(RCLASS(obj)->c_tbl); - mark_tbl(RCLASS(obj)->m_tbl); - mark(RBASIC(obj)->class); + gc_mark(RBASIC(obj)->class); break; case T_ARRAY: { @@ -354,21 +411,21 @@ mark(obj) VALUE *ptr = RARRAY(obj)->ptr; for (i=0; i < len; i++) - mark(ptr[i]); + gc_mark(ptr[i]); } break; case T_DICT: mark_dict(RDICT(obj)->tbl); break; case T_STRING: - if (RSTRING(obj)->orig) mark(RSTRING(obj)->orig); + if (RSTRING(obj)->orig) gc_mark(RSTRING(obj)->orig); break; case T_DATA: if (RDATA(obj)->dmark) (*RDATA(obj)->dmark)(DATA_PTR(obj)); break; + case T_OBJECT: case T_REGEXP: case T_FLOAT: - case T_METHOD: case T_BIGNUM: break; case T_STRUCT: @@ -377,42 +434,77 @@ mark(obj) struct kv_pair *ptr = RSTRUCT(obj)->tbl; for (i=0; i < len; i++) - mark(ptr[i].value); + gc_mark(ptr[i].value); } break; default: - Bug("mark(): unknown data type %d", obj->flags & T_MASK); + Bug("gc_mark(): unknown data type %d", obj->flags & T_MASK); } } -sweep() -{ - register struct RBasic *link = object_list; - register struct RBasic *next; - - if (link && (link->flags & FL_MARK) == fl_old) { - object_list = object_list->next; - obj_free(link); - link = object_list; - } +#define MIN_FREE_OBJ 512 - while (link && link->next) { - if ((link->next->flags & FL_MARK) == fl_old) { - next = link->next->next; - obj_free(link->next); - link->next = next; - continue; +static void +gc_sweep() +{ + struct heap_block *heap = heap_link; + int freed = 0; + + freelist = Qnil; + while (heap) { + struct RVALUE *p, *pend; + struct RVALUE *nfreelist; + int n = 0; + + nfreelist = freelist; + p = heap->beg; pend = heap->end; + while (p < pend) { + + if (!(RBASIC(p)->flags & FL_MARK)) { + if (RBASIC(p)->flags) obj_free(p); + p->as.free.flag = 0; + p->as.free.next = nfreelist; + nfreelist = p; + n++; + } + RBASIC(p)->flags &= ~FL_MARK; + p++; + } + if (n == SEG_SLOTS) { + struct heap_block *link = heap_link; + if (heap != link) { + while (link) { + if (link->next && link->next == heap) { + link->next = heap->next; + break; + } + link = link->next; + } + if (link == Qnil) { + Bug("non-existing heap at 0x%x", heap); + } + } + free(heap); + heap_size -= SEG_SLOTS; + heap = link; } - link = link->next; + else { + freed += n; + freelist = nfreelist; + } + heap = heap->next; + } + if (freed < heap_size/4) { + add_heap(); } } static freemethod(key, body) ID key; - char *body; + void *body; { - freenode(body); + method_free(body); return ST_CONTINUE; } @@ -432,6 +524,7 @@ obj_free(obj) break; case T_MODULE: case T_CLASS: + rb_clear_cache2(obj); st_foreach(RCLASS(obj)->m_tbl, freemethod); st_free_table(RCLASS(obj)->m_tbl); if (RCLASS(obj)->c_tbl) @@ -452,14 +545,12 @@ obj_free(obj) break; case T_DATA: if (RDATA(obj)->dfree) (*RDATA(obj)->dfree)(DATA_PTR(obj)); + free(DATA_PTR(obj)); break; case T_ICLASS: /* iClass shares table with the module */ case T_FLOAT: break; - case T_METHOD: - freenode(RMETHOD(obj)->node); - break; case T_STRUCT: free(RSTRUCT(obj)->name); free(RSTRUCT(obj)->tbl); @@ -468,9 +559,69 @@ obj_free(obj) free(RBIGNUM(obj)->digits); break; default: - Bug("sweep(): unknown data type %d", obj->flags & T_MASK); + Bug("gc_sweep(): unknown data type %d", obj->flags & T_MASK); + } +} + +void +gc() +{ + struct literal_list *lit; + struct gc_list *list; + struct ENVIRON *env; + int i, max; + jmp_buf save_regs_gc_mark; + VALUE stack_end; + + if (dont_gc) return; + dont_gc++; + + /* mark env stack */ + for (env = the_env; env; env = env->prev) { + gc_mark(env->self); + if (env->argv) + mark_locations_array(env->argv, env->argc); + if (env->local_vars) + mark_locations_array(env->local_vars, env->local_tbl[0]); } - free(obj); + + FLUSH_REGISTER_WINDOWS; + /* This assumes that all registers are saved into the jmp_buf */ + setjmp(save_regs_gc_mark); + mark_locations((VALUE*)save_regs_gc_mark, + (VALUE*)(((char*)save_regs_gc_mark)+sizeof(save_regs_gc_mark))); + mark_locations((VALUE*)save_regs_gc_mark, + sizeof save_regs_gc_mark/sizeof(VALUE)); + mark_locations(stack_start_ptr, (VALUE*) &stack_end); +#if defined(THINK_C) + mark_locations(((char*)stack_start_ptr + 2), + ((char*)&stack_end + 2)); +#endif + + /* mark protected global variables */ + for (list = Global_List; list; list = list->next) { + gc_mark(*list->varptr); + } + + /* mark literal objects */ + for (lit = Literal_List; lit; lit = lit->next) { + gc_mark(lit->val); + } + + gc_mark_global_tbl(); + mark_tbl(rb_class_tbl); + + gc_mark_trap_list(); + + gc_sweep(); + dont_gc--; +} + +Init_stack() +{ + VALUE start; + + stack_start_ptr = &start; } Init_GC() @@ -479,12 +630,5 @@ Init_GC() rb_define_single_method(M_GC, "start", gc, 0); rb_define_single_method(M_GC, "enable", Fgc_enable, 0); rb_define_single_method(M_GC, "disable", Fgc_disable, 0); - rb_define_single_method(M_GC, "threshold", Fgc_threshold, 0); - rb_define_single_method(M_GC, "threshold=", Fgc_set_threshold, 1); - rb_define_single_method(M_GC, "start_hook", Fgc_begin, 0); - rb_define_single_method(M_GC, "end_hook", Fgc_end, 0); - rb_define_func(M_GC, "garbage_collect", gc, 0); - - start_hook = rb_intern("start_hook"); - end_hook = rb_intern("end_hook"); + rb_define_method(M_GC, "garbage_collect", gc, 0); } |