diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb index f7b33c3..2579fdf 100644 --- a/test/ruby/test_thread.rb +++ b/test/ruby/test_thread.rb @@ -708,4 +708,18 @@ class TestThreadGroup < Test::Unit::TestCase end assert_in_delta(t1 - t0, 1, 1) end + + def test_join + thgrp = ThreadGroup.new + + thgrp.add(t = Thread.new{ sleep }) + + t0 = Time.now.to_f + assert_nil(thgrp.join(1)) + t1 = Time.now.to_f + assert_in_delta(t1 - t0, 1, 1) + + ensure + t.kill if t + end end diff --git a/thread.c b/thread.c index 55ba49d..05a4f19 100644 --- a/thread.c +++ b/thread.c @@ -3139,6 +3139,61 @@ thgroup_list(VALUE group) return ary; } +struct thgroup_join_params { + double limit; + VALUE group; +}; + +static int +thgroup_join_i(st_data_t key, st_data_t value, st_data_t data) +{ + VALUE thread = (VALUE)key; + rb_thread_t *th; + struct thgroup_join_params *args = (struct thgroup_join_params *)data; + double delay = args->limit; + + GetThreadPtr(thread, th); + if (th->thgroup == args->group) { + if (delay != DELAY_INFTY) delay -= timeofday(); + if (NIL_P(thread_join(th, delay))) { + args->group = Qnil; + return ST_STOP; + } + } + return ST_CONTINUE; +} + +/* + * call-seq: + * thgrp.join -> thgrp + * thgrp.join(limit) -> thgrp or nil + * + * The calling thread will suspend execution until threads in the receiving + * ThreadGroup exit or until limit seconds have passed. + * If the time limit expires, nil will be returned, otherwise + * thgrp is returned. + * + */ + +static VALUE +thgroup_join(int argc, VALUE *argv, VALUE self) +{ + VALUE limit; + struct thgroup_join_params param; + + rb_scan_args(argc, argv, "01", &limit); + + if (!NIL_P(limit)) { + param.limit = timeofday() + rb_num2dbl(limit); + } else { + param.limit = DELAY_INFTY; + } + + param.group = self; + + st_foreach(GET_THREAD()->vm->living_threads, thgroup_join_i, (st_data_t) & param); + return param.group; +} /* * call-seq: @@ -4622,6 +4677,7 @@ Init_Thread(void) rb_define_method(cThGroup, "enclose", thgroup_enclose, 0); rb_define_method(cThGroup, "enclosed?", thgroup_enclosed_p, 0); rb_define_method(cThGroup, "add", thgroup_add, 1); + rb_define_method(cThGroup, "join", thgroup_join, -1); { th->thgroup = th->vm->thgroup_default = rb_obj_alloc(cThGroup);