mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
[Bug #19016] re-order error handling at cleanup
Build and store the error message with `#detailed_message` before terminating all Ractors, then show the message later.
This commit is contained in:
parent
1f0888ab3e
commit
5b959e238e
Notes:
git
2022-11-21 15:33:40 +00:00
4 changed files with 122 additions and 83 deletions
97
eval.c
97
eval.c
|
@ -100,8 +100,10 @@ ruby_init(void)
|
|||
{
|
||||
int state = ruby_setup();
|
||||
if (state) {
|
||||
if (RTEST(ruby_debug))
|
||||
error_print(GET_EC());
|
||||
if (RTEST(ruby_debug)) {
|
||||
rb_execution_context_t *ec = GET_EC();
|
||||
rb_ec_error_print(ec, ec->errinfo);
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
@ -120,9 +122,9 @@ ruby_options(int argc, char **argv)
|
|||
}
|
||||
else {
|
||||
rb_ec_clear_current_thread_trace_func(ec);
|
||||
state = error_handle(ec, state);
|
||||
int exitcode = error_handle(ec, ec->errinfo, state);
|
||||
ec->errinfo = Qnil; /* just been handled */
|
||||
iseq = (void *)INT2FIX(state);
|
||||
iseq = (void *)INT2FIX(exitcode);
|
||||
}
|
||||
EC_POP_TAG();
|
||||
return iseq;
|
||||
|
@ -138,7 +140,7 @@ rb_ec_fiber_scheduler_finalize(rb_execution_context_t *ec)
|
|||
rb_fiber_scheduler_set(Qnil);
|
||||
}
|
||||
else {
|
||||
state = error_handle(ec, state);
|
||||
state = error_handle(ec, ec->errinfo, state);
|
||||
}
|
||||
EC_POP_TAG();
|
||||
}
|
||||
|
@ -181,16 +183,17 @@ ruby_cleanup(int ex)
|
|||
}
|
||||
|
||||
static int
|
||||
rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex0)
|
||||
rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex)
|
||||
{
|
||||
int state;
|
||||
volatile VALUE errs[2] = { Qundef, Qundef };
|
||||
int nerr;
|
||||
volatile VALUE save_error = Qundef;
|
||||
volatile int sysex = EXIT_SUCCESS;
|
||||
volatile int signaled = 0;
|
||||
rb_thread_t *th = rb_ec_thread_ptr(ec);
|
||||
rb_thread_t *const volatile th0 = th;
|
||||
volatile int sysex = EXIT_SUCCESS;
|
||||
volatile int step = 0;
|
||||
volatile enum ruby_tag_type ex = ex0;
|
||||
volatile VALUE message = Qnil;
|
||||
VALUE buf;
|
||||
|
||||
rb_threadptr_interrupt(th);
|
||||
rb_threadptr_check_signal(th);
|
||||
|
@ -200,56 +203,57 @@ rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex0)
|
|||
SAVE_ROOT_JMPBUF(th, { RUBY_VM_CHECK_INTS(ec); });
|
||||
|
||||
step_0: step++;
|
||||
errs[1] = ec->errinfo;
|
||||
save_error = ec->errinfo;
|
||||
if (THROW_DATA_P(ec->errinfo)) ec->errinfo = Qnil;
|
||||
ruby_init_stack(&errs[STACK_UPPER(errs, 0, 1)]);
|
||||
ruby_init_stack(&message);
|
||||
|
||||
/* exits with failure but silently when an exception raised
|
||||
* here */
|
||||
SAVE_ROOT_JMPBUF(th, rb_ec_teardown(ec));
|
||||
|
||||
step_1: step++;
|
||||
VALUE err = ec->errinfo;
|
||||
int mode0 = 0, mode1 = 0;
|
||||
if (err != save_error && !NIL_P(err)) {
|
||||
mode0 = exiting_split(err, &sysex, &signaled);
|
||||
}
|
||||
|
||||
/* exceptions after here will be ignored */
|
||||
|
||||
/* build error message including causes */
|
||||
err = ATOMIC_VALUE_EXCHANGE(save_error, Qnil);
|
||||
|
||||
if (!NIL_P(err) && !THROW_DATA_P(err)) {
|
||||
mode1 = exiting_split(err, (mode0 & EXITING_WITH_STATUS) ? NULL : &sysex, &signaled);
|
||||
if (mode1 & EXITING_WITH_MESSAGE) {
|
||||
buf = rb_str_new(NULL, 0);
|
||||
SAVE_ROOT_JMPBUF(th, rb_ec_error_print_detailed(ec, err, buf, Qundef));
|
||||
message = buf;
|
||||
}
|
||||
}
|
||||
|
||||
step_2: step++;
|
||||
/* protect from Thread#raise */
|
||||
th->status = THREAD_KILLED;
|
||||
|
||||
errs[0] = ec->errinfo;
|
||||
SAVE_ROOT_JMPBUF(th, rb_ractor_terminate_all());
|
||||
|
||||
step_3: step++;
|
||||
if (!NIL_P(buf = message)) {
|
||||
warn_print_str(buf);
|
||||
}
|
||||
else if (!NIL_OR_UNDEF_P(err = save_error) ||
|
||||
(ex != TAG_NONE && !((mode0|mode1) & EXITING_WITH_STATUS))) {
|
||||
sysex = error_handle(ec, err, ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
th = th0;
|
||||
switch (step) {
|
||||
case 0: goto step_0;
|
||||
case 1: goto step_1;
|
||||
}
|
||||
if (ex == 0) ex = state;
|
||||
}
|
||||
ec->errinfo = errs[1];
|
||||
sysex = error_handle(ec, ex);
|
||||
|
||||
state = 0;
|
||||
for (nerr = 0; nerr < numberof(errs); ++nerr) {
|
||||
VALUE err = ATOMIC_VALUE_EXCHANGE(errs[nerr], Qnil);
|
||||
VALUE sig;
|
||||
|
||||
if (!RTEST(err)) continue;
|
||||
|
||||
/* ec->errinfo contains a NODE while break'ing */
|
||||
if (THROW_DATA_P(err)) continue;
|
||||
|
||||
if (rb_obj_is_kind_of(err, rb_eSystemExit)) {
|
||||
sysex = sysexit_status(err);
|
||||
break;
|
||||
}
|
||||
else if (rb_obj_is_kind_of(err, rb_eSignal)) {
|
||||
VALUE sig = rb_ivar_get(err, id_signo);
|
||||
state = NUM2INT(sig);
|
||||
break;
|
||||
}
|
||||
else if (rb_obj_is_kind_of(err, rb_eSystemCallError) &&
|
||||
FIXNUM_P(sig = rb_attr_get(err, id_signo))) {
|
||||
state = NUM2INT(sig);
|
||||
break;
|
||||
}
|
||||
else if (sysex == EXIT_SUCCESS) {
|
||||
sysex = EXIT_FAILURE;
|
||||
case 2: goto step_2;
|
||||
case 3: goto step_3;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,7 +270,8 @@ rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex0)
|
|||
ruby_vm_destruct(th->vm);
|
||||
// For YJIT, call this after ruby_vm_destruct() frees jit_cont for the root fiber.
|
||||
rb_jit_cont_finish();
|
||||
if (state) ruby_default_signal(state);
|
||||
|
||||
if (signaled) ruby_default_signal(signaled);
|
||||
|
||||
return sysex;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue