From: Nobuyoshi Nakada Date: 2011-09-29T20:20:05+09:00 Subject: [ruby-dev:44561] Re: [Ruby 1.9 - Bug #5368][Open] ensure節でsleepするようなThreadがあるとインタプリタが終了しない なかだです。 At Mon, 26 Sep 2011 23:52:19 +0900, SASADA Koichi wrote in [ruby-dev:44552]: > (2011/09/26 7:03), m_takao wrote: >> これは「ensure節の実行中に止めるのはNG」ということですよね? >> いまの実装 (rev.33339) では、ensure節の実行中であっても、terminateされたら >> (外側にさらなるensure節がなければ) ふつうに終了するので、辻褄が合わない気がします。 > >  ご指摘の通り,「最初の1回だけは必ず ensure 中でも強制的に止めてしま > う」という仕様になっていますね.現状では,ensure 中(後処理中)ではない > ことを期待しています.ファイナライザ実行中でも,強制的にキャンセルされる > ので,何か後処理をしている最中に止められるのは不可避です. ensureでsleepされたら止まるのは仕方がないにせよ、そこで割り込みを受けた らやはり抜けるほうがいいのではないでしょうか。 diff --git i/thread.c w/thread.c index d9d497a..0e3d096 100644 --- i/thread.c +++ w/thread.c @@ -333,6 +333,7 @@ typedef struct rb_mutex_struct static void rb_mutex_abandon_all(rb_mutex_t *mutexes); static const char* rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t volatile *th); +static int vm_living_thread_num(rb_vm_t *vm); void rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th) @@ -369,14 +370,20 @@ rb_thread_terminate_all(void) st_foreach(vm->living_threads, terminate_i, (st_data_t)th); vm->inhibit_thread_creation = 1; - while (!rb_thread_alone()) { + + if (th->vm->living_threads && vm_living_thread_num(th->vm) > 1) { + int state; + const double sleep_time = 3600.0; /* should not be too long to + * get rid of overflow */ PUSH_TAG(); - if (EXEC_TAG() == 0) { - rb_thread_schedule(); - } - else { - /* ignore exception */ + state = EXEC_TAG(); + while (state == 0 || !rb_thread_alone()) { + if (state) { + st_foreach(vm->living_threads, terminate_i, (st_data_t)th); + } + sleep_wait_for_interrupt(th, sleep_time); } + /* ignore exception */ POP_TAG(); } } @@ -445,6 +452,8 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_s gvl_acquire(th->vm, th); { + int terminating = 0; + thread_debug("thread start (get lock): %p\n", (void *)th); rb_thread_set_current(th); @@ -469,6 +478,7 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_s if (NIL_P(errinfo)) errinfo = rb_errinfo(); if (state == TAG_FATAL) { /* fatal error within this thread, need to stop whole script */ + terminating = (errinfo == eTerminateSignal); } else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) { if (th->safe_level >= 4) { @@ -509,13 +519,23 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_s /* delete self other than main thread from living_threads */ if (th != main_th) { st_delete_wrap(th->vm->living_threads, th->self); + if (terminating) { + if (vm_living_thread_num(th->vm) == 1) { + rb_threadptr_interrupt(main_th); + } + else { + terminating = 0; + } + } } /* wake up joining threads */ join_th = th->join_list_head; while (join_th) { - if (join_th == main_th) errinfo = Qnil; - rb_threadptr_interrupt(join_th); + if ((join_th != main_th) || (errinfo = Qnil, !terminating)) { + /* main thread is already interrupted when terminating */ + rb_threadptr_interrupt(join_th); + } switch (join_th->status) { case THREAD_STOPPED: case THREAD_STOPPED_FOREVER: join_th->status = THREAD_RUNNABLE; -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦