summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--bootstraptest/test_thread.rb16
-rw-r--r--thread.c64
3 files changed, 76 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index 3269669c694..b5e0859ae2e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Tue Apr 22 13:12:58 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * 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 <nobu@ruby-lang.org>
* 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 43bb2fe7729..873e955291b 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 d2c9adc306b..bcabb59ec14 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);