From: Nobuyoshi Nakada Date: 2009-11-02T15:36:51+09:00 Subject: [ruby-dev:39602] Re: infinite recursive call to C function なかだです。 At Mon, 2 Nov 2009 11:52:04 +0900, Yukihiro Matsumoto wrote in [ruby-dev:39601]: > |までsweepしようとして落ちている感じです。 > > scan? でした。GCは詳しくないもので。 > |たとえばrb_make_backtrace()からset_backtrace()までGCを禁止するこ > |とでも落ちなくなりますが、そもそもmallocの途中でシグナルを受けて > |いることもありうるわけで、無条件に復帰可能な例外を投げるのも危険 > |な気がします。 > > うーむ、とういうことは、 > > * 代替スタック中ではRubyコードを実行しない(r23720をリバー > トする) > * 代替スタック使用中はスタック先頭アドレスも書き換える > > のいずれかが必要ということですか。 前者のほうがいいんじゃないでしょうか。代替スタック使用中のGCでス タックオーバフローしたりとか考えると。 > それよりもむしろrb_longjmp でのbacktraceの割り当てをlazyにす > べきなのかなあ。でも、バックとレース情報を記録する場所はいず > れにせよ必要なわけだし...。 「バックトレース情報を記録する場所」というのは? とりあえずJUMP_TAGのときにチェックさせるようにしてみました。 Index: eval_error.c =================================================================== --- eval_error.c (revision 25629) +++ eval_error.c (working copy) @@ -273,2 +273,14 @@ error_handle(int ex) return status; } + +void +ruby_threadptr_handle_critical_exception(rb_thread_t *th) +{ + VALUE e = th->errinfo, bt; + if (!OBJ_FROZEN(e)) return; + if (ruby_threadptr_stack_check(th)) return; + bt = rb_threadptr_backtrace(th, -1); + th->errinfo = e = rb_obj_dup(e); + set_backtrace(e, bt); + rb_thread_raised_reset(th, RAISED_STACKOVERFLOW|RAISED_NOMEMORY); +} Index: eval_intern.h =================================================================== --- eval_intern.h (revision 25629) +++ eval_intern.h (working copy) @@ -131,5 +131,7 @@ NORETURN(void _longjmp(jmp_buf, int)); #define TH_JUMP_TAG(th, st) do { \ - ruby_longjmp(th->tag->buf,(st)); \ + if (rb_thread_raised_p(th, RAISED_STACKOVERFLOW|RAISED_NOMEMORY)) \ + ruby_threadptr_handle_critical_exception(th); \ + ruby_longjmp(th->tag->buf,(st)); \ } while (0) @@ -192,4 +194,6 @@ int rb_threadptr_reset_raised(rb_thread_ #define rb_thread_raised_p(th, f) (((th)->raised_flag & (f)) != 0) #define rb_thread_raised_clear(th) ((th)->raised_flag = 0) +void ruby_threadptr_handle_critical_exception(rb_thread_t *th); +int ruby_threadptr_stack_check(rb_thread_t *th); VALUE rb_f_eval(int argc, VALUE *argv, VALUE self); Index: gc.c =================================================================== --- gc.c (revision 25629) +++ gc.c (working copy) @@ -1205,9 +1205,8 @@ ruby_stack_length(VALUE **p) } -static int -stack_check(void) +int +ruby_threadptr_stack_check(rb_thread_t *th) { int ret; - rb_thread_t *th = GET_THREAD(); SET_STACK_END; ret = STACK_LENGTH > STACK_LEVEL_MAX - GC_WATER_MARK; @@ -1221,4 +1220,10 @@ stack_check(void) } +static inline int +stack_check(void) +{ + return ruby_threadptr_stack_check(GET_THREAD()); +} + int ruby_stack_check(void) Index: signal.c =================================================================== --- signal.c (revision 25629) +++ signal.c (working copy) @@ -599,6 +599,4 @@ sigsegv(int sig SIGINFO_ARG) { #ifdef USE_SIGALTSTACK - int ruby_stack_overflowed_p(const rb_thread_t *, const void *); - NORETURN(void ruby_thread_stack_overflow(rb_thread_t *th)); rb_thread_t *th = GET_THREAD(); if (ruby_stack_overflowed_p(th, info->si_addr)) { Index: thread.c =================================================================== --- thread.c (revision 25629) +++ thread.c (working copy) @@ -1347,12 +1347,7 @@ void ruby_thread_stack_overflow(rb_thread_t *th) { - th->raised_flag = 0; -#ifdef USE_SIGALTSTACK - th->raised_flag = 0; - rb_exc_raise(sysstack_error); -#else + th->raised_flag = RAISED_STACKOVERFLOW; th->errinfo = sysstack_error; - TH_JUMP_TAG(th, TAG_RAISE); -#endif + ruby_longjmp(th->tag->buf, TAG_RAISE); } Index: vm_core.h =================================================================== --- vm_core.h (revision 25629) +++ vm_core.h (working copy) @@ -616,6 +616,8 @@ void *rb_thread_call_with_gvl(void *(*fu int ruby_thread_has_gvl_p(void); VALUE rb_make_backtrace(void); +VALUE rb_threadptr_backtrace(rb_thread_t *th, int lev); typedef int rb_backtrace_iter_func(void *, VALUE, int, VALUE); int rb_backtrace_each(rb_backtrace_iter_func *iter, void *arg); +int rb_threadptr_backtrace_each(rb_thread_t *th, int lev, rb_backtrace_iter_func *iter, void *arg); rb_control_frame_t *rb_vm_get_ruby_level_next_cfp(rb_thread_t *th, rb_control_frame_t *cfp); VALUE rb_name_err_mesg_new(VALUE obj, VALUE mesg, VALUE recv, VALUE method); @@ -654,4 +656,6 @@ void rb_threadptr_signal_raise(rb_thread void rb_threadptr_signal_exit(rb_thread_t *th); void rb_threadptr_execute_interrupts(rb_thread_t *); +int ruby_stack_overflowed_p(const rb_thread_t *, const void *); +void ruby_thread_stack_overflow(rb_thread_t *th); #define RUBY_VM_CHECK_INTS_TH(th) do { \ Index: vm_eval.c =================================================================== --- vm_eval.c (revision 25629) +++ vm_eval.c (working copy) @@ -192,11 +192,8 @@ rb_call_super(int argc, const VALUE *arg static inline void -stack_check(void) +stack_check(rb_thread_t *th) { - rb_thread_t *th = GET_THREAD(); - if (!rb_thread_raised_p(th, RAISED_STACKOVERFLOW) && ruby_stack_check()) { - rb_thread_raised_set(th, RAISED_STACKOVERFLOW); - rb_exc_raise(sysstack_error); + ruby_thread_stack_overflow(th); } } @@ -231,5 +228,5 @@ rb_call0(VALUE recv, ID mid, int argc, c return method_missing(recv, mid, argc, argv, call_status); } - stack_check(); + stack_check(th); return vm_call0(th, recv, mid, argc, argv, me); } @@ -286,5 +283,5 @@ check_funcall(VALUE recv, ID mid, int ar } } - stack_check(); + stack_check(th); return vm_call0(th, recv, mid, argc, argv, me); } @@ -434,5 +431,5 @@ raise_method_missing(rb_thread_t *th, in } - stack_check(); + stack_check(th); id = SYM2ID(argv[0]); @@ -1524,4 +1521,16 @@ rb_thread_backtrace(VALUE thval) int +rb_threadptr_backtrace_each(rb_thread_t *th, int lev, rb_backtrace_iter_func *iter, void *arg) +{ + return vm_backtrace_each(th, lev, iter, arg); +} + +VALUE +rb_threadptr_backtrace(rb_thread_t *th, int lev) +{ + return vm_backtrace(th, lev); +} + +int rb_backtrace_each(rb_backtrace_iter_func *iter, void *arg) {