From 9c7707629608304a2eaf674a72af16c95ebc4f52 Mon Sep 17 00:00:00 2001 From: nobu Date: Tue, 22 Apr 2008 04:13:01 +0000 Subject: * thread.c (thread_join): remove the current thread from the join list of the target thread. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@16135 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 5 ++++ bootstraptest/test_thread.rb | 16 +++++++++++ thread.c | 64 +++++++++++++++++++++++++++++++++++++------- 3 files changed, 76 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3269669c69..b5e0859ae2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Tue Apr 22 13:12:58 2008 Nobuyoshi Nakada + + * thread.c (thread_join): remove the current thread from the join list + of the target thread. + Tue Apr 22 12:03:50 2008 Nobuyoshi Nakada * vm_insnhelper.c (vm_get_ev_const): search from the base klass if it diff --git a/bootstraptest/test_thread.rb b/bootstraptest/test_thread.rb index 43bb2fe772..873e955291 100644 --- a/bootstraptest/test_thread.rb +++ b/bootstraptest/test_thread.rb @@ -213,3 +213,19 @@ assert_equal 'true', %{ true end } + +assert_finish 3, %{ + th = Thread.new {sleep 2} + th.join(1) + th.join +} + +assert_finish 3, %{ + require 'timeout' + th = Thread.new {sleep 2} + begin + Timeout.timeout(1) {th.join} + rescue Timeout::Error + end + th.join +} diff --git a/thread.c b/thread.c index d2c9adc306..bcabb59ec1 100644 --- a/thread.c +++ b/thread.c @@ -461,20 +461,42 @@ rb_thread_create(VALUE (*fn)(ANYARGS), void *arg) /* +infty, for this purpose */ #define DELAY_INFTY 1E30 +struct join_arg { + rb_thread_t *target, *waiting; + double limit; + int forever; +}; + static VALUE -thread_join(rb_thread_t *target_th, double delay) +remove_from_join_list(VALUE arg) { - rb_thread_t *th = GET_THREAD(); - double now, limit = timeofday() + delay; - - thread_debug("thread_join (thid: %p)\n", (void *)target_th->thread_id); + struct join_arg *p = (struct join_arg *)arg; + rb_thread_t *target_th = p->target, *th = p->waiting; if (target_th->status != THREAD_KILLED) { - th->join_list_next = target_th->join_list_head; - target_th->join_list_head = th; + rb_thread_t **pth = &target_th->join_list_head; + + while (*pth) { + if (*pth == th) { + *pth = th->join_list_next; + break; + } + pth = &(*pth)->join_list_next; + } } + + return Qnil; +} + +static VALUE +thread_join_sleep(VALUE arg) +{ + struct join_arg *p = (struct join_arg *)arg; + rb_thread_t *target_th = p->target, *th = p->waiting; + double now, limit = p->limit; + while (target_th->status != THREAD_KILLED) { - if (delay == DELAY_INFTY) { + if (p->forever) { sleep_forever(th); } else { @@ -482,13 +504,37 @@ thread_join(rb_thread_t *target_th, double delay) if (now > limit) { thread_debug("thread_join: timeout (thid: %p)\n", (void *)target_th->thread_id); - return Qnil; + return Qfalse; } sleep_wait_for_interrupt(th, limit - now); } thread_debug("thread_join: interrupted (thid: %p)\n", (void *)target_th->thread_id); } + return Qtrue; +} + +static VALUE +thread_join(rb_thread_t *target_th, double delay) +{ + rb_thread_t *th = GET_THREAD(); + struct join_arg arg; + + arg.target = target_th; + arg.waiting = th; + arg.limit = timeofday() + delay; + arg.forever = delay == DELAY_INFTY; + + thread_debug("thread_join (thid: %p)\n", (void *)target_th->thread_id); + + if (target_th->status != THREAD_KILLED) { + th->join_list_next = target_th->join_list_head; + target_th->join_list_head = th; + if (!rb_ensure(thread_join_sleep, (VALUE)&arg, + remove_from_join_list, (VALUE)&arg)) { + return Qnil; + } + } thread_debug("thread_join: success (thid: %p)\n", (void *)target_th->thread_id); -- cgit v1.2.3