From: Tanaka Akira Date: 2003-05-20T18:33:41+09:00 Subject: [ruby-dev:20248] Re: dyna_vars problem? In article <1053422521.786672.22712.nullmailer@picachu.netlab.jp>, matz@ruby-lang.org (Yukihiro Matsumoto) writes: > |たとえば、次のような問題が起きることがあります。 > |(起きないことも多々ありますし、他の問題が起きることもあります。) > > 再現しませんでした。rb_gc()を追加すると処理が全然進まないし。 あ、その進まないという奴も症状のひとつです。 たぶん、strace をかけると次のようにぜんぜん IO をしない状況になるんで すよね? sigreturn() = ? (mask now []) --- SIGVTALRM (Virtual timer expired) @ 0 (0) --- sigreturn() = ? (mask now []) --- SIGVTALRM (Virtual timer expired) @ 0 (0) --- sigreturn() = ? (mask now []) --- SIGVTALRM (Virtual timer expired) @ 0 (0) --- sigreturn() = ? (mask now []) --- SIGVTALRM (Virtual timer expired) @ 0 (0) --- sigreturn() = ? (mask now []) --- SIGVTALRM (Virtual timer expired) @ 0 (0) --- sigreturn() = ? (mask now []) --- SIGVTALRM (Virtual timer expired) @ 0 (0) --- sigreturn() = ? (mask now []) --- SIGVTALRM (Virtual timer expired) @ 0 (0) --- sigreturn() = ? (mask now []) --- SIGVTALRM (Virtual timer expired) @ 0 (0) --- これを gdb で attach してみると、(loop になる場所は何箇所かあるんです が、そのうちのひとつは) 次のような無限ループに陥っています。 dvar_asgn_internal (id=18985, value=4, curr=1) at eval.c:774 774 if (vars->id == id) { (gdb) p vars $1 = (struct RVarmap *) 0x403c9b58 (gdb) p *vars $2 = {super = {flags = 61, klass = 0}, id = 7729, val = 1077561272, next = 0x403 c9b58} (gdb) n 778 vars = vars->next; (gdb) 779 } (gdb) 769 if (curr && vars->id == 0) { (gdb) 774 if (vars->id == id) { (gdb) n 778 vars = vars->next; (gdb) p vars $3 = (struct RVarmap *) 0x403c9b58 (gdb) p *vars $4 = {super = {flags = 61, klass = 0}, id = 7729, val = 1077561272, next = 0x403 c9b58} (gdb) また、GC 自体が無限に呼び出されるループになることもあります。 サイクルができるのは、えーと、たしか dyna_vars 関係がいくつかと final_list だった覚えがあります。final_list は最初はサイクルがないのに 最初に run_final した後にはサイクルができているということを確認してい ます。 というようなわけで、かなりやりにくい状況なわけですが、私は次のように assert をいれまくって追い詰めようとしていました。まぁ、最初の質問のよ うに間違った assert をいれてるところもあるかも知れませんが。 あ、一回だけ、サイクルの大きさが 1 ではなくて 2 になったときもありまし た。これはここで入れているような assert では検査できないので止まらない はずです。 Index: eval.c =================================================================== RCS file: /src/ruby/eval.c,v retrieving revision 1.434 diff -u -r1.434 eval.c --- eval.c 19 May 2003 15:44:42 -0000 1.434 +++ eval.c 20 May 2003 01:58:10 -0000 @@ -12,6 +12,7 @@ **********************************************************************/ +#include #include "ruby.h" #include "node.h" #include "env.h" @@ -691,14 +692,29 @@ #define DVAR_DONT_RECYCLE FL_USER2 +#define live(v) (((struct RBasic *)(v))->flags != 0) +#define live_null(v) (!(v) || ((struct RBasic *)(v))->flags != 0) + static struct RVarmap* new_dvar(id, value, prev) ID id; VALUE value; struct RVarmap *prev; { - NEWOBJ(vars, struct RVarmap); + volatile struct RVarmap *v = prev; + struct RVarmap *vars; + assert(live_null(prev)); + assert(v == prev); + { + NEWOBJ(vars2, struct RVarmap); + assert(vars2 != prev); + vars = vars2; + } + assert(vars != prev); + assert(v == prev); OBJSETUP(vars, 0, T_VARMAP); + assert(v == prev); + assert(vars != prev); vars->id = id; vars->val = value; vars->next = prev; @@ -743,6 +759,7 @@ if (vars->id == id) { return vars->val; } + assert(vars != vars->next); vars = vars->next; } return Qnil; @@ -754,6 +771,7 @@ VALUE value; { ruby_dyna_vars = new_dvar(id, value, ruby_dyna_vars); + assert(ruby_dyna_vars != ruby_dyna_vars->next); } static void @@ -764,6 +782,7 @@ { int n = 0; struct RVarmap *vars = ruby_dyna_vars; + assert(live_null(ruby_dyna_vars)); while (vars) { if (curr && vars->id == 0) { @@ -775,13 +794,17 @@ vars->val = value; return; } + assert(vars != vars->next); vars = vars->next; } if (!ruby_dyna_vars) { ruby_dyna_vars = new_dvar(id, value, 0); } else { + assert(live(ruby_dyna_vars)); + assert(live_null(ruby_dyna_vars->next)); vars = new_dvar(id, value, ruby_dyna_vars->next); + assert(ruby_dyna_vars != vars); ruby_dyna_vars->next = vars; } } @@ -791,6 +814,7 @@ ID id; VALUE value; { + assert(live_null(ruby_dyna_vars)); dvar_asgn_internal(id, value, 0); } @@ -799,6 +823,7 @@ ID id; VALUE value; { + assert(live_null(ruby_dyna_vars)); dvar_asgn_internal(id, value, 1); } @@ -806,14 +831,28 @@ rb_svar(cnt) int cnt; { - struct RVarmap *vars = ruby_dyna_vars; + struct RVarmap * volatile vars = ruby_dyna_vars; ID id; + { + struct RVarmap * volatile v = ruby_dyna_vars; + while (v) { + assert(TYPE(v) == T_VARMAP); + v = v->next; + } + } + + assert(ruby_scope); if (!ruby_scope->local_tbl) return NULL; + assert(ruby_scope->local_tbl); if (cnt >= ruby_scope->local_tbl[0]) return NULL; id = ruby_scope->local_tbl[cnt+1]; while (vars) { - if (vars->id == id) return &vars->val; + assert(vars); + assert(TYPE(vars) == T_VARMAP); + assert(vars != (struct RVarmap *)0x200); + if (vars->id == id) + return &vars->val; vars = vars->next; } if (ruby_scope->local_vars == 0) return NULL; @@ -4099,9 +4138,11 @@ if (ruby_dyna_vars->id == 0) { vars = ruby_dyna_vars->next; rb_gc_force_recycle((VALUE)ruby_dyna_vars); + /*fprintf(stderr, "rb_gc_force_recycle ruby_dyna_vars: %08x\n", ruby_dyna_vars);*/ while (vars && vars->id != 0 && vars != block->dyna_vars) { struct RVarmap *tmp = vars->next; rb_gc_force_recycle((VALUE)vars); + /*fprintf(stderr, "rb_gc_force_recycle ruby_dyna_vars: %08x\n", vars);*/ vars = tmp; } } @@ -7929,6 +7970,8 @@ extern VALUE *rb_gc_stack_start; +volatile long thread_switch_count = 0; + static void rb_thread_save_context(th) rb_thread_t th; @@ -7942,6 +7985,7 @@ th->stk_pos = (rb_gc_stack_start th->stk_max) { + rb_gc(); REALLOC_N(th->stk_ptr, VALUE, len); th->stk_max = len; } @@ -8036,6 +8080,14 @@ static int ex; static VALUE tval; + { + struct RVarmap * volatile v = th->dyna_vars; + while (v) { + assert(TYPE(v) == T_VARMAP); + v = v->next; + } + } + if (!th->stk_ptr) rb_bug("unsaved context"); if (&v < rb_gc_stack_start) { @@ -8073,6 +8125,14 @@ FLUSH_REGISTER_WINDOWS; MEMCPY(tmp->stk_pos, tmp->stk_ptr, VALUE, tmp->stk_len); + { + struct RVarmap * volatile v = ruby_dyna_vars; + while (v) { + assert(TYPE(v) == T_VARMAP); + v = v->next; + } + } + tval = rb_lastline_get(); rb_lastline_set(tmp->last_line); tmp->last_line = tval; @@ -8080,6 +8140,7 @@ rb_backref_set(tmp->last_match); tmp->last_match = tval; + thread_switch_count++; longjmp(tmp->context, ex); } @@ -8412,6 +8473,15 @@ } } END_FOREACH_FROM(curr, th); + if (next) + { + struct RVarmap * volatile v = next->dyna_vars; + while (v) { + assert(TYPE(v) == T_VARMAP); + v = v->next; + } + } + if (next && next->status == THREAD_RUNNABLE) assert(next->stk_ptr); if (!next) { /* raise fatal error to main thread */ @@ -8444,10 +8514,24 @@ } rb_thread_deadlock(); } + { + struct RVarmap * volatile v = next->dyna_vars; + while (v) { + assert(TYPE(v) == T_VARMAP); + v = v->next; + } + } next->wait_for = 0; if (next->status == THREAD_RUNNABLE && next == curr_thread) { return; } + { + struct RVarmap * volatile v = next->dyna_vars; + while (v) { + assert(TYPE(v) == T_VARMAP); + v = v->next; + } + } /* context switch */ if (curr == curr_thread) { @@ -8455,6 +8539,14 @@ return; } } + assert(next->stk_ptr); + { + struct RVarmap * volatile v = next->dyna_vars; + while (v) { + assert(TYPE(v) == T_VARMAP); + v = v->next; + } + } curr_thread = next; if (next->status == THREAD_TO_KILL) { @@ -8463,6 +8555,15 @@ /* terminate; execute ensure-clause if any */ rb_thread_restore_context(next, RESTORE_FATAL); } + assert(next->stk_ptr); + } + assert(next->stk_ptr); + { + struct RVarmap * volatile v = next->dyna_vars; + while (v) { + assert(TYPE(v) == T_VARMAP); + v = v->next; + } } rb_thread_restore_context(next, RESTORE_NORMAL); } Index: gc.c =================================================================== RCS file: /src/ruby/gc.c,v retrieving revision 1.132 diff -u -r1.132 gc.c --- gc.c 8 May 2003 09:45:52 -0000 1.132 +++ gc.c 20 May 2003 01:58:10 -0000 @@ -12,6 +12,12 @@ **********************************************************************/ +volatile long finalizer_count = 0; +volatile long gc_count = 0; +volatile long gc_try_count = 0; +extern volatile long thread_switch_count; + +#include #include "ruby.h" #include "rubysig.h" #include "st.h" @@ -927,6 +933,7 @@ obj_free((VALUE)p); } if (need_call_final && FL_TEST(p, FL_FINALIZE)) { + assert(p != final_list); p->as.free.flags = FL_MARK; /* remain marked */ p->as.free.next = final_list; final_list = p; @@ -972,15 +979,52 @@ /* clear finalization list */ if (final_list) { RVALUE *tmp; + volatile int count = 0; if (rb_prohibit_interrupt || ruby_in_compile) { deferred_final_list = final_list; return; } + count = 0; for (p = final_list; p; p = tmp) { + tmp = p->as.free.next; + if (count == 0) assert(p != tmp); + if (count != 0) assert(p != tmp); + count++; + } + + count = 0; + for (p = final_list; p; p = tmp) { + RVALUE *q; + volatile long prev_finalizer_count; + volatile long prev_gc_try_count; + volatile long prev_gc_count; + volatile long prev_thread_switch_count; + for (q = p; q; q = q->as.free.next) { + assert(q != q->as.free.next); + } + tmp = p->as.free.next; + if (count == 0) assert(p != tmp); + if (count == 1) assert(p != tmp); + if (count >= 2) assert(p != tmp); + count++; + prev_finalizer_count = finalizer_count; + prev_gc_count = gc_count; + prev_gc_try_count = gc_try_count; + prev_thread_switch_count = thread_switch_count; + + for (q = p; q; q = q->as.free.next) { + assert(q != q->as.free.next); + } + run_final((VALUE)p); + + for (q = p; q; q = q->as.free.next) { + assert(q != q->as.free.next); + } + if (!FL_TEST(p, FL_SINGLETON)) { /* not freeing page */ p->as.free.flags = 0; p->as.free.next = freelist; @@ -1007,6 +1051,7 @@ rb_gc_force_recycle(p) VALUE p; { + assert(RANY(p)->as.free.flags != 0); RANY(p)->as.free.flags = 0; RANY(p)->as.free.next = freelist; freelist = RANY(p); @@ -1194,6 +1239,8 @@ jmp_buf save_regs_gc_mark; SET_STACK_END; + gc_try_count++; + if (dont_gc || during_gc) { if (!freelist) { add_heap(); @@ -1203,6 +1250,8 @@ if (during_gc) return; during_gc++; + gc_count++; + init_mark_stack(); /* mark frame stack */ @@ -1482,6 +1531,7 @@ finalizer_table = st_init_numtable(); } if (st_lookup(finalizer_table, obj, &table)) { + assert(0); rb_ary_push(table, proc); } else { @@ -1510,6 +1560,7 @@ run_single_final(args) VALUE *args; { + finalizer_count++; rb_eval_cmd(args[0], args[1], 0); return Qnil; } @@ -1523,11 +1574,13 @@ VALUE args[2], table; args[1] = rb_ary_new3(1, rb_obj_id(obj)); /* make obj into id */ + assert(RARRAY(finalizers)->len == 0); for (i=0; ilen; i++) { args[0] = RARRAY(finalizers)->ptr[i]; rb_protect((VALUE(*)_((VALUE)))run_single_final, (VALUE)args, &status); } if (finalizer_table && st_delete(finalizer_table, &obj, &table)) { + assert(RARRAY(table)->len == 1); for (i=0; ilen; i++) { args[0] = RARRAY(table)->ptr[i]; rb_protect((VALUE(*)_((VALUE)))run_single_final, (VALUE)args, &status); Index: io.c =================================================================== RCS file: /src/ruby/io.c,v retrieving revision 1.207 diff -u -r1.207 io.c --- io.c 19 May 2003 06:27:06 -0000 1.207 +++ io.c 20 May 2003 01:58:10 -0000 @@ -11,6 +11,7 @@ Copyright (C) 2000 Information-technology Promotion Agency, Japan **********************************************************************/ +#include #if defined(__VMS) #define _XOPEN_SOURCE @@ -400,6 +401,8 @@ return len - n; } +#define live(v) (((struct RBasic *)(v))->flags != 0) + static VALUE io_write(io, str) VALUE io, str; @@ -414,6 +417,7 @@ if (RSTRING(str)->len == 0) return INT2FIX(0); if (TYPE(io) != T_FILE) { + assert(live(io)); /* port is not IO, call write method for it. */ return rb_funcall(io, id_write, 1, str); } Index: parse.y =================================================================== RCS file: /src/ruby/parse.y,v retrieving revision 1.273 diff -u -r1.273 parse.y --- parse.y 26 Apr 2003 14:59:19 -0000 1.273 +++ parse.y 20 May 2003 01:58:13 -0000 @@ -14,6 +14,7 @@ #define YYDEBUG 1 +#include #include "ruby.h" #include "env.h" #include "intern.h" @@ -5968,10 +5969,22 @@ VALUE rb_lastline_get() { - VALUE *var = rb_svar(0); + volatile int pos = 0; + VALUE *var; + { + struct RVarmap * volatile v = ruby_dyna_vars; + while (v) { + assert(TYPE(v) == T_VARMAP); + v = v->next; + } + } + var = rb_svar(0); + pos = 1; if (var) { + pos = 2; return *var; } + pos = 3; return Qnil; } Index: signal.c =================================================================== RCS file: /src/ruby/signal.c,v retrieving revision 1.44 diff -u -r1.44 signal.c --- signal.c 19 May 2003 04:52:55 -0000 1.44 +++ signal.c 20 May 2003 01:58:13 -0000 @@ -730,7 +730,7 @@ install_sighandler(SIGBUS, sigbus); #endif #ifdef SIGSEGV - install_sighandler(SIGSEGV, sigsegv); + /*install_sighandler(SIGSEGV, sigsegv);*/ #endif #ifdef SIGPIPE install_sighandler(SIGPIPE, sigpipe); -- [田中 哲][たなか あきら][Tanaka Akira]