summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog12
-rw-r--r--eval.c60
-rw-r--r--version.h2
3 files changed, 61 insertions, 13 deletions
diff --git a/ChangeLog b/ChangeLog
index f11b311694..bfbce2fb3a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+Sun Feb 22 22:03:40 2009 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * eval.c (cc_mark): frees the continuation's stack if its thread
+ is dead to avoid recursive gc that segfaults. [ruby-core:13889]
+ a patch by Brent Roman <brent AT mbari.org>.
+
+ * eval.c (rb_cont_check): checks for valid continuation instance.
+
+ * eval.c (rb_callcc): assigns th->thread before scope_dup() to
+ avoid segfaults if this scope_dup() triggers a gc pass.
+ a patch by Brent Roman <brent AT mbari.org>.
+
Sun Feb 22 21:43:34 2009 Nobuyoshi Nakada <nobu@ruby-lang.org>
* numeric.c (ruby_float_step): extracted from num_step().
diff --git a/eval.c b/eval.c
index 34206702d9..e065bddd20 100644
--- a/eval.c
+++ b/eval.c
@@ -10513,8 +10513,8 @@ rb_gc_abort_threads()
} END_FOREACH_FROM(main_thread, th);
}
-static void
-thread_free(th)
+static inline void
+stack_free(th)
rb_thread_t th;
{
if (th->stk_ptr) free(th->stk_ptr);
@@ -10523,6 +10523,13 @@ thread_free(th)
if (th->bstr_ptr) free(th->bstr_ptr);
th->bstr_ptr = 0;
#endif
+}
+
+static void
+thread_free(th)
+ rb_thread_t th;
+{
+ stack_free(th);
if (th->locals) st_free_table(th->locals);
if (th->status != THREAD_KILLED) {
if (th->prev) th->prev->next = th->next;
@@ -10815,8 +10822,7 @@ rb_thread_die(th)
{
th->thgroup = 0;
th->status = THREAD_KILLED;
- if (th->stk_ptr) free(th->stk_ptr);
- th->stk_ptr = 0;
+ stack_free(th);
}
static void
@@ -13006,6 +13012,38 @@ rb_thread_atfork()
}
+static void
+cc_purge(cc)
+ rb_thread_t cc;
+{
+ /* free continuation's stack if it has just died */
+ if (NIL_P(cc->thread)) return;
+ if (rb_thread_check(cc->thread)->status == THREAD_KILLED) {
+ cc->thread = Qnil;
+ rb_thread_die(cc); /* can't possibly activate this stack */
+ }
+}
+
+static void
+cc_mark(cc)
+ rb_thread_t cc;
+{
+ /* mark this continuation's stack only if its parent thread is still alive */
+ cc_purge(cc);
+ thread_mark(cc);
+}
+
+static rb_thread_t
+rb_cont_check(data)
+ VALUE data;
+{
+ if (TYPE(data) != T_DATA || RDATA(data)->dmark != (RUBY_DATA_FUNC)cc_mark) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Continuation)",
+ rb_obj_classname(data));
+ }
+ return (rb_thread_t)RDATA(data)->data;
+}
+
/*
* Document-class: Continuation
*
@@ -13080,14 +13118,16 @@ rb_callcc(self)
struct RVarmap *vars;
THREAD_ALLOC(th);
- cont = Data_Wrap_Struct(rb_cCont, thread_mark, thread_free, th);
+ /* must finish th initialization before any possible gc.
+ * brent@mbari.org */
+ th->thread = curr_thread->thread;
+ th->thgroup = cont_protect;
+ cont = Data_Wrap_Struct(rb_cCont, cc_mark, thread_free, th);
scope_dup(ruby_scope);
for (tag=prot_tag; tag; tag=tag->prev) {
scope_dup(tag->scope);
}
- th->thread = curr_thread->thread;
- th->thgroup = cont_protect;
for (vars = ruby_dyna_vars; vars; vars = vars->next) {
if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
@@ -13124,7 +13164,7 @@ rb_cont_call(argc, argv, cont)
VALUE *argv;
VALUE cont;
{
- rb_thread_t th = rb_thread_check(cont);
+ rb_thread_t th = rb_cont_check(cont);
if (th->thread != curr_thread->thread) {
rb_raise(rb_eRuntimeError, "continuation called across threads");
@@ -13300,10 +13340,6 @@ thgroup_add(group, thread)
rb_secure(4);
th = rb_thread_check(thread);
- if (!th->next || !th->prev) {
- rb_raise(rb_eTypeError, "wrong argument type %s (expected Thread)",
- rb_obj_classname(thread));
- }
if (OBJ_FROZEN(group)) {
rb_raise(rb_eThreadError, "can't move to the frozen thread group");
diff --git a/version.h b/version.h
index 9c67e7aa48..f996ecad1e 100644
--- a/version.h
+++ b/version.h
@@ -2,7 +2,7 @@
#define RUBY_RELEASE_DATE "2009-02-22"
#define RUBY_VERSION_CODE 187
#define RUBY_RELEASE_CODE 20090222
-#define RUBY_PATCHLEVEL 137
+#define RUBY_PATCHLEVEL 138
#define RUBY_VERSION_MAJOR 1
#define RUBY_VERSION_MINOR 8