summaryrefslogtreecommitdiff
path: root/thread.c
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-04-22 04:13:01 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-04-22 04:13:01 +0000
commit9c7707629608304a2eaf674a72af16c95ebc4f52 (patch)
tree31f8b0c15d6014203f1f7c0f4b37bae160b75381 /thread.c
parent62aa7aae51004bddf9e0c9c6bcdf2d6fe88840e8 (diff)
* 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
Diffstat (limited to 'thread.c')
-rw-r--r--thread.c64
1 files changed, 55 insertions, 9 deletions
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);