diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c index 82adf67bd..946eb7166 100644 --- a/mrbgems/mruby-fiber/src/fiber.c +++ b/mrbgems/mruby-fiber/src/fiber.c @@ -223,7 +223,6 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr return fiber_error(mrb, "attempt to resume the current fiber"); } - fiber_check_cfunc(mrb, c); status = c->status; switch (status) { case MRB_FIBER_TRANSFERRED: @@ -241,6 +240,7 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr default: break; } + fiber_check_cfunc(mrb, c); if (resume) { old_c->status = MRB_FIBER_RESUMED; c->prev = mrb->c; @@ -394,8 +394,9 @@ fiber_to_s(mrb_state *mrb, mrb_value self) const char *file; int32_t line; - const struct RProc *p = f->cxt->cibase->proc; - if (f->cxt->status != MRB_FIBER_TERMINATED && !MRB_PROC_CFUNC_P(p) && !MRB_PROC_ALIAS_P(p) && + const struct RProc *p; + if (f->cxt->status != MRB_FIBER_TERMINATED && + !MRB_PROC_CFUNC_P(p = f->cxt->cibase->proc) && !MRB_PROC_ALIAS_P(p) && mrb_debug_get_position(mrb, p->body.irep, 0, &line, &file)) { mrb_str_cat_lit(mrb, s, " "); mrb_str_cat_cstr(mrb, s, file); diff --git a/src/vm.c b/src/vm.c index 20144419b..025fbc9e7 100644 --- a/src/vm.c +++ b/src/vm.c @@ -374,7 +374,45 @@ fiber_terminate(mrb_state *mrb, struct mrb_context *c, mrb_callinfo *ci) { mrb_assert(c != mrb->root_c); + struct REnv *env = CI_ENV(ci); + mrb_assert(env == NULL || MRB_ENV_LEN(env) <= c->stend - ci->stack); + c->status = MRB_FIBER_TERMINATED; + mrb_free(mrb, c->cibase); + c->cibase = c->ciend = c->ci = NULL; + mrb_value *stack = c->stbase; + c->stbase = c->stend = NULL; + + if (!env) { + mrb_free(mrb, stack); + } + else { + env->cxt = NULL; + + size_t len = (size_t)MRB_ENV_LEN(env); + if (len == 0) { + env->stack = NULL; + MRB_ENV_CLOSE(env); + mrb_free(mrb, stack); + } + else { + mrb_assert(stack == env->stack); + mrb_write_barrier(mrb, (struct RBasic*)env); + + // don't call MRB_ENV_CLOSE() before mrb_realloc(). + // the reason is that env->stack may be freed by mrb_realloc() if MRB_DEBUG + MRB_GC_STRESS are enabled. + // realloc() on a freed heap will cause double-free. + + stack = (mrb_value*)mrb_realloc(mrb, stack, len * sizeof(mrb_value)); + if (mrb_object_dead_p(mrb, (struct RBasic*)env)) { + mrb_free(mrb, stack); + } + else { + env->stack = stack; + MRB_ENV_CLOSE(env); + } + } + } /* fiber termination should automatic yield or transfer to root */ mrb->c = c->prev;