From: nobu.nakada@... Date: 2003-10-09T12:03:28+09:00 Subject: [ruby-dev:21556] rewinding for Thread なかだです。 以前、スレッドの中でスレッドを作るというのを繰り返すとstack level too deepになる、という話があったような気がするので、Proc を作ってからトップレベルまで巻き戻してブランチするようにしてみ ました。 * eval.c (TAG_THREAD): to start a new thread. * eval.c (ruby_init, ruby_options, rb_thread_start_0): start the thread in upper environment. * eval.c (proc_alloc): clone proc object if klass is not Proc or created in different thread. * eval.c (rb_block_pass): call a function with a block. [new] * eval.c (rb_f_throw): raise NameError in main thread. * ruby.h: prototype; rb_block_pass. 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 02:28:12 -0000 @@ -876,4 +877,5 @@ static struct tag *prot_tag; #define TAG_THROW 0x7 #define TAG_FATAL 0x8 +#define TAG_THREAD 0x9 #define TAG_MASK 0xf @@ -1135,4 +1137,6 @@ void Init_heap _((void)); void Init_ext _((void)); +NORETURN(static void rb_thread_start_1 _((void))); + void ruby_init() @@ -1165,5 +1169,5 @@ ruby_init() SCOPE_SET(SCOPE_PRIVATE); - PUSH_TAG(PROT_NONE); + PUSH_TAG(PROT_THREAD); if ((state = EXEC_TAG()) == 0) { rb_call_inits(); @@ -1179,4 +1183,7 @@ ruby_init() ALLOW_INTS; } + else if (state == TAG_THREAD) { + rb_thread_start_1(); + } POP_TAG(); if (state) { @@ -1291,8 +1298,11 @@ ruby_options(argc, argv) Init_stack((void*)&state); - PUSH_TAG(PROT_NONE); + PUSH_TAG(PROT_THREAD); if ((state = EXEC_TAG()) == 0) { ruby_process_options(argc, argv); } + else if (state == TAG_THREAD) { + rb_thread_start_1(); + } if (state) { trace_func = 0; @@ -1362,5 +1372,5 @@ ruby_exec() Init_stack((void*)&tmp); - PUSH_TAG(PROT_NONE); + PUSH_TAG(PROT_THREAD); PUSH_ITER(ITER_NOT); /* default visibility is private at toplevel */ @@ -1369,4 +1379,7 @@ ruby_exec() eval_node(ruby_top_self, ruby_eval_tree); } + else if (state == TAG_THREAD) { + rb_thread_start_1(); + } POP_ITER(); POP_TAG(); @@ -6930,6 +6987,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 +7267,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 +7284,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 +7305,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 +7329,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 +7358,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 +9500,28 @@ rb_thread_stop_timer() #endif +static struct { + rb_thread_t thread; + VALUE proc; + void *arg; +} new_thread_param; + +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) @@ -9411,4 +9533,5 @@ rb_thread_start_0(fn, arg, th) struct BLOCK *volatile saved_block = 0, *block; enum thread_status status; + struct tag *tt = 0; int state; @@ -9431,7 +9554,19 @@ rb_thread_start_0(fn, arg, th) #endif + if (fn == rb_thread_yield) { + for (tt = prot_tag; tt; tt = tt->prev) { + if (tt->tag == PROT_THREAD) { + new_thread_param.thread = th; + new_thread_param.proc = rb_block_proc(); + new_thread_param.arg = arg; + break; + } + } + } + if (THREAD_SAVE_CONTEXT(curr_thread)) { - return th_save->thread; + return th->thread; } + if (tt) longjmp(tt->buf, TAG_THREAD); if (ruby_block) { /* should nail down higher blocks */ @@ -9444,13 +9579,5 @@ 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); @@ -9480,4 +9608,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 +9647,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_param.thread; + VALUE proc = new_thread_param.proc; + struct thread_yield_arg arg; + enum thread_status status; + int state; + + arg.arg = (VALUE)new_thread_param.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 +11083,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 +11091,5 @@ rb_f_throw(argc, argv) } if (!tt) { + uncaught: rb_name_error(SYM2ID(tag), "uncaught throw `%s'", rb_id2name(SYM2ID(tag))); } Index: ruby.h =================================================================== RCS file: /cvs/ruby/src/ruby/ruby.h,v retrieving revision 1.89 diff -u -2 -p -r1.89 ruby.h --- ruby.h 22 Aug 2003 08:09:57 -0000 1.89 +++ ruby.h 12 Sep 2003 08:29:31 -0000 @@ -541,4 +541,5 @@ VALUE rb_yield_splat _((VALUE)); int rb_block_given_p _((void)); VALUE rb_iterate _((VALUE(*)(VALUE),VALUE,VALUE(*)(ANYARGS),VALUE)); +VALUE rb_block_pass _((VALUE(*)(VALUE),VALUE,VALUE)); VALUE rb_rescue _((VALUE(*)(ANYARGS),VALUE,VALUE(*)(ANYARGS),VALUE)); VALUE rb_rescue2 __((VALUE(*)(ANYARGS),VALUE,VALUE(*)(ANYARGS),VALUE,...)); -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦