From: matz@... (Yukihiro Matsumoto) Date: 1999-08-01T01:41:45+09:00 Subject: [ruby-dev:7515] Re: ERb について まつもと ゆきひろです [ruby-list:15799]の話題ですが、難しいので引越し。 In message "[ruby-list:15799] Re: ERb について" on 99/08/01, matsuo hisanori writes: |更にせきさんの検討によると、1.3でも変なことが起こるようです。 |例えば以下のスクリプトを僕の環境 | |ruby 1.3.6 (1999-07-28) [i386-cygwin] | |で動かしてみると。 | |(1..1000).each{|i| | begin | begin | raise 'out of eRuby' | rescue | Thread.start { true }.value | end | rescue | p "#{i}回目の例外が再発生? #{$!}" | end |} えーと、この問題は以下の二つの問題の組合せでした。 * rescue節でスタートされたThreadではその中では例外が発生し ていないのに $! がセットされている * joinは例外の再発生をするはずなのに、すでに終了したスレッ ドに対しては例外チェックをしていなかった で、Thread#valueの中で間接的に呼ばれているThread#joinが $! がセットされていると例外を再発生する挙動になっていましたが、 前者の問題のためにスレッド内部で例外が発生してなくても、再発 生させていたのでした。 ところが、後者のためにこの例ではほとんどのタイミングでjoinす るまえにスレッドの実行は終了しているので、例外再発生のコード に到達してなかったのでした。で、タイミングによって例外が発生 したりしなかったりすると。 後者は単なるチェック条件の変更だけなので、簡単に修正するとちゃ んと(?)毎回例外を再発生するようになります。で、スレッドにフ ラグを持たせて、自分の中で例外が発生したかどうかを記録するよ うにして、前者の問題も解決させました。 で、パッチです。[ruby-dev:7504]の対策も含んでます。 --- /tmp/eval.c Sun Aug 1 01:34:35 1999 +++ eval.c Sun Aug 1 01:23:30 1999 @@ -422,3 +422,3 @@ int vmode; - int d_scope; + int flags; struct RVarmap *d_vars; @@ -427,2 +427,6 @@ }; + +#define BLOCK_D_SCOPE 1 +#define BLOCK_DYNAMIC 2 + static struct BLOCK *ruby_block; @@ -443,3 +447,3 @@ _block.vmode = scope_vmode; \ - _block.d_scope = Qtrue; \ + _block.flags = BLOCK_D_SCOPE; \ _block.d_vars = ruby_dyna_vars; \ @@ -1161,3 +1165,3 @@ *state = status; - if (status == 0) { + if (status) { JUMP_TAG(status); @@ -1933,3 +1937,3 @@ - _block.d_scope = Qfalse; + _block.flags &= ~BLOCK_D_SCOPE; recv = rb_eval(self, node->nd_iter); @@ -3140,3 +3144,3 @@ ruby_block = block->prev; - if (block->d_scope) { + if (block->flags & BLOCK_D_SCOPE) { /* put place holder for dynamic (in-block) local variables */ @@ -4298,2 +4302,3 @@ +/* function to call func under the specified class/module context */ static VALUE @@ -4339,2 +4344,3 @@ +/* string eval under the class/module context */ static VALUE @@ -4359,2 +4365,27 @@ { + if (ruby_block->flags & BLOCK_DYNAMIC) { + struct BLOCK * volatile old_block = ruby_block; + struct BLOCK block = *ruby_block; + volatile VALUE cbase = ruby_block->frame.cbase; + /* cbase should be pointed from volatile local variable */ + /* to be protected from GC. */ + VALUE result; + int state; + + /* copy the block to avoid modifying global data. */ + block.frame.cbase = ruby_frame->cbase; + ruby_block = █ + + PUSH_TAG(PROT_NONE); + if ((state = EXEC_TAG()) == 0) { + result = rb_yield_0(self, self, ruby_class); + } + POP_TAG(); + ruby_block = old_block; + if (state) JUMP_TAG(state); + + return result; + } + /* static block, no need to restore */ + ruby_block->frame.cbase = ruby_frame->cbase; return rb_yield_0(self, self, ruby_class); @@ -4362,2 +4393,3 @@ +/* block eval under the class/module context */ static VALUE @@ -5416,2 +5448,3 @@ } + data->flags |= BLOCK_DYNAMIC; @@ -5873,3 +5906,3 @@ VALUE trace; - int misc; /* misc. states (vmode/rb_trap_immediate) */ + int flags; /* misc. states (vmode/rb_trap_immediate/raised) */ @@ -5898,2 +5931,4 @@ +#define THREAD_RAISED 0x200 + static thread_t curr_thread = 0; @@ -6027,3 +6062,3 @@ th->block = ruby_block; - th->misc = scope_vmode | (rb_trap_immediate<<8); + th->flags = scope_vmode | (rb_trap_immediate<<8); th->iter = ruby_iter; @@ -6095,4 +6130,4 @@ ruby_block = th->block; - scope_vmode = th->misc&SCOPE_MASK; - rb_trap_immediate = th->misc>>8; + scope_vmode = th->flags&SCOPE_MASK; + rb_trap_immediate = (th->flags&0x100)?1:0; ruby_iter = th->iter; @@ -6565,14 +6600,15 @@ - if (rb_thread_dead(th)) return thread; - if (th == curr_thread) - rb_raise(rb_eThreadError, "recursive join"); - if ((th->wait_for & WAIT_JOIN) && th->join == curr_thread) - rb_raise(rb_eThreadError, "Thread#join: deadlock"); - curr_thread->status = THREAD_STOPPED; - curr_thread->join = th; - num_waiting_on_join++; - curr_thread->wait_for |= WAIT_JOIN; - rb_thread_schedule(); + if (!rb_thread_dead(th)) { + if (th == curr_thread) + rb_raise(rb_eThreadError, "recursive join"); + if ((th->wait_for & WAIT_JOIN) && th->join == curr_thread) + rb_raise(rb_eThreadError, "Thread#join: deadlock - mutual join"); + curr_thread->status = THREAD_STOPPED; + curr_thread->join = th; + num_waiting_on_join++; + curr_thread->wait_for |= WAIT_JOIN; + rb_thread_schedule(); + } - if (!NIL_P(th->errinfo)) { + if (!NIL_P(th->errinfo) && (th->flags & THREAD_RAISED)) { VALUE oldbt = get_backtrace(th->errinfo); @@ -6897,2 +6933,3 @@ if (state && status != THREAD_TO_KILL && !NIL_P(ruby_errinfo)) { + th->flags |= THREAD_RAISED; if (state == TAG_FATAL) { @@ -6906,3 +6943,3 @@ } - else if (thread_abort || curr_thread->abort || RTEST(ruby_debug)) { + else if (thread_abort || th->abort || RTEST(ruby_debug)) { VALUE err = rb_exc_new(rb_eSystemExit, 0, 0); @@ -6913,3 +6950,3 @@ else { - curr_thread->errinfo = ruby_errinfo; + th->errinfo = ruby_errinfo; } @@ -6971,3 +7008,4 @@ if (rb_thread_dead(th)) { - if (NIL_P(th->errinfo)) return Qfalse; + if (NIL_P(th->errinfo) && (th->flags & THREAD_RAISED)) + return Qfalse; return Qnil;