From: nobu.nakada@... Date: 2003-10-09T13:53:29+09:00 Subject: [ruby-dev:21568] Re: rewinding for Thread なかだです。 At Thu, 9 Oct 2003 12:41:07 +0900, Tietew wrote: > > 以前、スレッドの中でスレッドを作るというのを繰り返すとstack > > level too deepになる、という話があったような気がするので、Proc > > を作ってからトップレベルまで巻き戻してブランチするようにしてみ > > ました。 > > パッチを見たのですが,ruby_exec を呼ばずに直接 rb_load や > rb_funcall を呼ぶような組み込み Ruby で困ります。 巻き戻すのはPROT_THREADがあるときだけなので、なければそのままス レッドを作るはずです。 > TAG_THREAD を捕まえて処理するような rb_protect_thread みたいな関 > 数を作ればいいのかな。それとも rb_protect が一括処理? rb_protect()は任意のコードを実行できるので、そこから呼ばれた関 数のローカル変数のポインタをrb_thread_create()に渡すこともでき ますから、かえって危険です。逆に、スタックを巻き戻さないように 上位のPROT_THREADを隠さないとまずいかも知れません。 Index: eval.c =================================================================== RCS file: /cvs/ruby/src/ruby/eval.c,v retrieving revision 1.554 diff -u -2 -p -r1.554 eval.c --- eval.c 9 Oct 2003 01:51:08 -0000 1.554 +++ eval.c 9 Oct 2003 04:36:53 -0000 @@ -854,4 +854,11 @@ static struct tag *prot_tag; #define PROT_PCALL INT2FIX(3) /* 7 */ +#define PUSH_THREAD_TAG() PUSH_TAG(PROT_THREAD); \ +do { \ + if (!new_thread.tag) new_thread.tag = prot_tag +#define POP_THREAD_TAG() \ + if (new_thread.tag == prot_tag) new_thread.tag = 0; \ +} while (0) + #define EXEC_TAG() (FLUSH_REGISTER_WINDOWS, setjmp(prot_tag->buf)) @@ -876,6 +883,14 @@ static struct tag *prot_tag; #define TAG_THROW 0x7 #define TAG_FATAL 0x8 +#define TAG_THREAD 0x9 #define TAG_MASK 0xf +static struct { + rb_thread_t thread; + VALUE proc; + void *arg; + struct tag *tag; +} new_thread; + VALUE ruby_class; static VALUE ruby_wrapper; /* security wrapper */ @@ -1135,4 +1150,6 @@ void Init_heap _((void)); void Init_ext _((void)); +NORETURN(static void rb_thread_start_1 _((void))); + void ruby_init() @@ -1165,5 +1182,5 @@ ruby_init() SCOPE_SET(SCOPE_PRIVATE); - PUSH_TAG(PROT_NONE); + PUSH_THREAD_TAG(); if ((state = EXEC_TAG()) == 0) { rb_call_inits(); @@ -1179,5 +1196,8 @@ ruby_init() ALLOW_INTS; } - POP_TAG(); + else if (state == TAG_THREAD) { + rb_thread_start_1(); + } + POP_THREAD_TAG(); if (state) { error_print(); @@ -1291,8 +1311,11 @@ ruby_options(argc, argv) Init_stack((void*)&state); - PUSH_TAG(PROT_NONE); + PUSH_THREAD_TAG(); if ((state = EXEC_TAG()) == 0) { ruby_process_options(argc, argv); } + else if (state == TAG_THREAD) { + rb_thread_start_1(); + } if (state) { trace_func = 0; @@ -1300,5 +1323,5 @@ ruby_options(argc, argv) exit(error_handle(state)); } - POP_TAG(); + POP_THREAD_TAG(); } @@ -1362,5 +1385,5 @@ ruby_exec() Init_stack((void*)&tmp); - PUSH_TAG(PROT_NONE); + PUSH_THREAD_TAG(); PUSH_ITER(ITER_NOT); /* default visibility is private at toplevel */ @@ -1369,6 +1392,9 @@ ruby_exec() eval_node(ruby_top_self, ruby_eval_tree); } + else if (state == TAG_THREAD) { + rb_thread_start_1(); + } POP_ITER(); - POP_TAG(); + POP_THREAD_TAG(); return state; } @@ -1435,13 +1461,16 @@ rb_eval_string_protect(str, state) int status; - PUSH_TAG(PROT_NONE); + PUSH_THREAD_TAG(); if ((status = EXEC_TAG()) == 0) { result = rb_eval_string(str); } - POP_TAG(); + POP_THREAD_TAG(); if (state) { *state = status; } if (status != 0) { + if (status == TAG_THREAD) { + rb_thread_start_1(); + } return Qnil; } @@ -4583,9 +4612,12 @@ rb_protect(proc, data, state) VALUE result = Qnil; /* OK */ int status; + struct tag *volatile thread_tag = new_thread.tag; PUSH_TAG(PROT_NONE); + new_thread.tag = 0; if ((status = EXEC_TAG()) == 0) { result = (*proc)(data); } + new_thread.tag = thread_tag; POP_TAG(); if (state) { @@ -5832,9 +5864,9 @@ rb_load_protect(fname, wrap, state) int status; - PUSH_TAG(PROT_NONE); + PUSH_THREAD_TAG(); if ((status = EXEC_TAG()) == 0) { rb_load(fname, wrap); } - POP_TAG(); + POP_THREAD_TAG(); if (state) *state = status; } @@ -6930,6 +6962,11 @@ proc_alloc(klass, proc) } - if (!proc && ruby_block->block_obj && CLASS_OF(ruby_block->block_obj) == klass) { - return ruby_block->block_obj; + if (!proc && ruby_block->block_obj) { + VALUE obj = ruby_block->block_obj; + if (CLASS_OF(obj) != klass) { + obj = proc_clone(obj); + RBASIC(obj)->klass = klass; + } + return obj; } block = Data_Make_Struct(klass, struct BLOCK, blk_mark, blk_free, data); @@ -7205,10 +7242,10 @@ proc_binding(proc) } -static VALUE -block_pass(self, node) - VALUE self; - NODE *node; +VALUE +rb_block_pass(func, arg, proc) + VALUE (*func) _((VALUE)); + VALUE arg; + VALUE proc; { - VALUE proc = rb_eval(self, node->nd_body); /* OK */ VALUE b; struct BLOCK * volatile old_block; @@ -7222,5 +7259,5 @@ block_pass(self, node) if (NIL_P(proc)) { PUSH_ITER(ITER_NOT); - result = rb_eval(self, node->nd_iter); + result = (*func)(arg); POP_ITER(); return result; @@ -7243,5 +7280,5 @@ block_pass(self, node) if (ruby_block && ruby_block->block_obj == proc) { PUSH_ITER(ITER_PRE); - result = rb_eval(self, node->nd_iter); + result = (*func)(arg); POP_ITER(); return result; @@ -7267,5 +7304,5 @@ block_pass(self, node) if (safe > ruby_safe_level) ruby_safe_level = safe; - result = rb_eval(self, node->nd_iter); + result = (*func)(arg); } else if (state == TAG_BREAK && TAG_DST()) { @@ -7296,4 +7333,28 @@ block_pass(self, node) } +struct block_arg { + VALUE self; + NODE *iter; +}; + +static VALUE +call_block(arg) + struct block_arg *arg; +{ + return rb_eval(arg->self, arg->iter); +} + +static VALUE +block_pass(self, node) + VALUE self; + NODE *node; +{ + struct block_arg arg; + arg.self = self; + arg.iter = node->nd_iter; + return rb_block_pass((VALUE (*)_((VALUE)))call_block, + (VALUE)&arg, rb_eval(self, node->nd_body)); +} + struct METHOD { VALUE klass, rklass; @@ -9402,4 +9463,22 @@ rb_thread_stop_timer() #endif +NORETURN(static void rb_thread_terminated _((rb_thread_t, int, enum thread_status))); +static VALUE rb_thread_yield _((VALUE, rb_thread_t)); + +static void +thread_inseart(th) + rb_thread_t th; +{ + if (!th->next) { + /* merge in thread list */ + th->prev = curr_thread; + curr_thread->next->prev = th; + th->next = curr_thread->next; + curr_thread->next = th; + th->priority = curr_thread->priority; + th->thgroup = curr_thread->thgroup; + } +} + static VALUE rb_thread_start_0(fn, arg, th) @@ -9432,5 +9511,11 @@ rb_thread_start_0(fn, arg, th) if (THREAD_SAVE_CONTEXT(curr_thread)) { - return th_save->thread; + return th->thread; + } + if (fn == rb_thread_yield && new_thread.tag) { + new_thread.thread = th; + new_thread.proc = rb_block_proc(); + new_thread.arg = arg; + longjmp(new_thread.tag->buf, TAG_THREAD); } @@ -9444,15 +9529,7 @@ rb_thread_start_0(fn, arg, th) scope_dup(ruby_scope); - if (!th->next) { - /* merge in thread list */ - th->prev = curr_thread; - curr_thread->next->prev = th; - th->next = curr_thread->next; - curr_thread->next = th; - th->priority = curr_thread->priority; - th->thgroup = curr_thread->thgroup; - } + thread_inseart(th); - PUSH_TAG(PROT_THREAD); + PUSH_THREAD_TAG(); if ((state = EXEC_TAG()) == 0) { if (THREAD_SAVE_CONTEXT(th) == 0) { @@ -9465,5 +9542,5 @@ rb_thread_start_0(fn, arg, th) } th = th_save; - POP_TAG(); + POP_THREAD_TAG(); status = th->status; @@ -9480,4 +9557,14 @@ rb_thread_start_0(fn, arg, th) } + rb_thread_terminated(th, state, status); + return 0; /* not reached */ +} + +static void +rb_thread_terminated(th, state, status) + rb_thread_t th; + int state; + enum thread_status status; +{ if (state && status != THREAD_TO_KILL && !NIL_P(ruby_errinfo)) { th->flags |= THREAD_RAISED; @@ -9509,5 +9596,49 @@ rb_thread_start_0(fn, arg, th) rb_thread_schedule(); ruby_stop(0); /* last thread termination */ - return 0; /* not reached */ +} + +struct thread_yield_arg { + VALUE arg; + rb_thread_t thread; +}; + +static VALUE +rb_thread_yield_0(arg) + struct thread_yield_arg *arg; +{ + return rb_thread_yield(arg->arg, arg->thread); +} + +static void +rb_thread_start_1() +{ + rb_thread_t th = new_thread.thread; + VALUE proc = new_thread.proc; + struct thread_yield_arg arg; + enum thread_status status; + int state; + + arg.arg = (VALUE)new_thread.arg; + arg.thread = th; + + thread_inseart(th); + + PUSH_TAG(PROT_NONE); + if ((state = EXEC_TAG()) == 0) { + if (THREAD_SAVE_CONTEXT(th) == 0) { + curr_thread = th; + th->result = rb_block_pass(rb_thread_yield_0, (VALUE)&arg, proc); + } + } + else if (TAG_DST()) { + th->result = prot_tag->retval; + } + th = arg.thread; + POP_TAG(); + status = th->status; + + if (th == main_thread) ruby_stop(state); + rb_thread_remove(th); + rb_thread_terminated(th, state, status); } @@ -10240,4 +10371,5 @@ rb_f_throw(argc, argv) } if (tt->tag == PROT_THREAD) { + if (curr_thread == main_thread) goto uncaught; rb_raise(rb_eThreadError, "uncaught throw `%s' in thread 0x%lx", rb_id2name(SYM2ID(tag)), @@ -10247,4 +10379,5 @@ rb_f_throw(argc, argv) } if (!tt) { + uncaught: rb_name_error(SYM2ID(tag), "uncaught throw `%s'", rb_id2name(SYM2ID(tag))); } -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦