mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
[Bug #22191] Report EXIT handler exceptions after END blocks
Keep exceptions raised by `EXIT` traps until `END` handlers finish. That leaves them available for reporting during cleanup. Print `END` handler exceptions without replaying causes already reported from earlier exit handlers. This keeps the previous handler exception visible while avoiding duplicate diagnostics.
This commit is contained in:
1 parent
eec3aba7a3
commit
0d7e50a174
4 files changed
+95
-12
Notes:
git
2026-07-12 19:22:47 +00:00
No files matched your search
@@ -147,15 +147,26 @@ rb_ec_fiber_scheduler_finalize(rb_execution_context_t *ec)
|
||||
static void
|
||||
rb_ec_teardown(rb_execution_context_t *ec)
|
||||
{
|
||||
enum ruby_tag_type state = TAG_NONE;
|
||||
volatile VALUE trap_errinfo = Qnil;
|
||||
|
||||
// If the user code defined a scheduler for the top level thread, run it:
|
||||
rb_ec_fiber_scheduler_finalize(ec);
|
||||
|
||||
EC_PUSH_TAG(ec);
|
||||
if (EC_EXEC_TAG() == TAG_NONE) {
|
||||
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
|
||||
rb_vm_trap_exit(rb_ec_vm_ptr(ec));
|
||||
}
|
||||
else {
|
||||
trap_errinfo = ec->errinfo;
|
||||
ec->errinfo = Qnil;
|
||||
}
|
||||
EC_POP_TAG();
|
||||
rb_ec_exec_end_proc(ec);
|
||||
if (!NIL_P(trap_errinfo)) {
|
||||
error_handle(ec, trap_errinfo, state);
|
||||
ec->errinfo = trap_errinfo;
|
||||
}
|
||||
rb_ec_clear_all_trace_func(ec);
|
||||
}
|
||||
|
||||
|
||||
+40
-9
@@ -276,6 +276,16 @@ shown_cause_p(VALUE cause, VALUE *shown_causes)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
add_shown_cause(VALUE cause, VALUE *shown_causes)
|
||||
{
|
||||
if (!NIL_OR_UNDEF_P(cause) &&
|
||||
!THROW_DATA_P(cause) &&
|
||||
rb_obj_is_kind_of(cause, rb_eException)) {
|
||||
shown_cause_p(cause, shown_causes);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
show_cause(VALUE errinfo, VALUE str, VALUE opt, VALUE highlight, VALUE reverse, long backtrace_limit, VALUE *shown_causes)
|
||||
{
|
||||
@@ -309,15 +319,18 @@ rb_exc_check_circular_cause(VALUE exc)
|
||||
} while (!NIL_P(cause = rb_attr_get(cause, id_cause)));
|
||||
}
|
||||
|
||||
void
|
||||
rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VALUE highlight, VALUE reverse)
|
||||
static void
|
||||
rb_error_write0(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VALUE highlight, VALUE reverse, VALUE *shown_causes)
|
||||
{
|
||||
volatile VALUE eclass;
|
||||
VALUE shown_causes = 0;
|
||||
VALUE local_shown_causes = 0;
|
||||
long backtrace_limit = rb_backtrace_length_limit;
|
||||
|
||||
if (NIL_P(errinfo))
|
||||
return;
|
||||
if (!shown_causes) {
|
||||
shown_causes = &local_shown_causes;
|
||||
}
|
||||
|
||||
if (UNDEF_P(errat)) {
|
||||
errat = Qnil;
|
||||
@@ -340,19 +353,25 @@ rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VA
|
||||
len = p - (msg = buff);
|
||||
}
|
||||
write_warn2(str, msg, len);
|
||||
show_cause(errinfo, str, opt, highlight, reverse, backtrace_limit, &shown_causes);
|
||||
show_cause(errinfo, str, opt, highlight, reverse, backtrace_limit, shown_causes);
|
||||
print_backtrace(eclass, errat, str, TRUE, backtrace_limit);
|
||||
print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
|
||||
}
|
||||
else {
|
||||
print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
|
||||
print_backtrace(eclass, errat, str, FALSE, backtrace_limit);
|
||||
show_cause(errinfo, str, opt, highlight, reverse, backtrace_limit, &shown_causes);
|
||||
show_cause(errinfo, str, opt, highlight, reverse, backtrace_limit, shown_causes);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VALUE highlight, VALUE reverse)
|
||||
{
|
||||
rb_error_write0(errinfo, emesg, errat, str, opt, highlight, reverse, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
rb_ec_error_print_detailed(rb_execution_context_t *const ec, const VALUE errinfo, const VALUE str, VALUE emesg0)
|
||||
rb_ec_error_print_detailed0(rb_execution_context_t *const ec, const VALUE errinfo, const VALUE str, VALUE emesg0, VALUE *shown_causes)
|
||||
{
|
||||
volatile uint8_t raised_flag = ec->raised_flag;
|
||||
volatile VALUE errat = Qundef;
|
||||
@@ -378,7 +397,7 @@ rb_ec_error_print_detailed(rb_execution_context_t *const ec, const VALUE errinfo
|
||||
|
||||
if (!written) {
|
||||
written = true;
|
||||
rb_error_write(errinfo, emesg, errat, str, opt, highlight, Qfalse);
|
||||
rb_error_write0(errinfo, emesg, errat, str, opt, highlight, Qfalse, shown_causes);
|
||||
}
|
||||
|
||||
EC_POP_TAG();
|
||||
@@ -386,6 +405,12 @@ rb_ec_error_print_detailed(rb_execution_context_t *const ec, const VALUE errinfo
|
||||
rb_ec_raised_set(ec, raised_flag);
|
||||
}
|
||||
|
||||
static void
|
||||
rb_ec_error_print_detailed(rb_execution_context_t *const ec, const VALUE errinfo, const VALUE str, VALUE emesg0)
|
||||
{
|
||||
rb_ec_error_print_detailed0(ec, errinfo, str, emesg0, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
rb_ec_error_print(rb_execution_context_t *volatile ec, volatile VALUE errinfo)
|
||||
{
|
||||
@@ -504,7 +529,7 @@ exiting_split(VALUE errinfo, volatile int *exitcode, volatile int *sigstatus)
|
||||
rb_bug("Unknown longjmp status %d", status)
|
||||
|
||||
static int
|
||||
error_handle(rb_execution_context_t *ec, VALUE errinfo, enum ruby_tag_type ex)
|
||||
error_handle_with_shown_causes(rb_execution_context_t *ec, VALUE errinfo, enum ruby_tag_type ex, VALUE *shown_causes)
|
||||
{
|
||||
int status = EXIT_FAILURE;
|
||||
|
||||
@@ -546,7 +571,7 @@ error_handle(rb_execution_context_t *ec, VALUE errinfo, enum ruby_tag_type ex)
|
||||
}
|
||||
/* fallthrough */
|
||||
case TAG_FATAL:
|
||||
rb_ec_error_print(ec, errinfo);
|
||||
rb_ec_error_print_detailed0(ec, errinfo, Qnil, Qundef, shown_causes);
|
||||
break;
|
||||
default:
|
||||
unknown_longjmp_status(ex);
|
||||
@@ -555,3 +580,9 @@ error_handle(rb_execution_context_t *ec, VALUE errinfo, enum ruby_tag_type ex)
|
||||
rb_ec_reset_raised(ec);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
error_handle(rb_execution_context_t *ec, VALUE errinfo, enum ruby_tag_type ex)
|
||||
{
|
||||
return error_handle_with_shown_causes(ec, errinfo, ex, NULL);
|
||||
}
|
||||
+6
-2
@@ -114,6 +114,8 @@ rb_ec_exec_end_proc(rb_execution_context_t * ec)
|
||||
{
|
||||
enum ruby_tag_type state;
|
||||
volatile VALUE errinfo = ec->errinfo;
|
||||
/* Share only among END/at_exit handlers to avoid repeated causes. */
|
||||
VALUE shown_causes = 0;
|
||||
volatile bool finished = false;
|
||||
|
||||
while (!finished) {
|
||||
@@ -125,8 +127,10 @@ rb_ec_exec_end_proc(rb_execution_context_t * ec)
|
||||
}
|
||||
EC_POP_TAG();
|
||||
if (state != TAG_NONE) {
|
||||
error_handle(ec, ec->errinfo, state);
|
||||
if (!NIL_P(ec->errinfo)) errinfo = ec->errinfo;
|
||||
VALUE err = ec->errinfo;
|
||||
add_shown_cause(errinfo, &shown_causes);
|
||||
error_handle_with_shown_causes(ec, err, state, &shown_causes);
|
||||
if (!NIL_P(err)) errinfo = err;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -826,6 +826,43 @@ end.join
|
||||
end;
|
||||
end
|
||||
|
||||
def test_multiple_error_handle
|
||||
errs = [
|
||||
/.*END3 \(RuntimeError\).*\n/,
|
||||
/.*END2 \(RuntimeError\).*\n/,
|
||||
/.*END1 \(RuntimeError\).*\n/,
|
||||
/.*EXIT \(RuntimeError\).*\n/,
|
||||
]
|
||||
assert_in_out_err([], <<-'end;', [], errs, success: false)
|
||||
Signal.trap(:EXIT) {raise "EXIT"}
|
||||
END{raise "END1"};
|
||||
END{raise "END2"};
|
||||
END{raise "END3"};
|
||||
end;
|
||||
end
|
||||
|
||||
def test_cause_in_exit_handler
|
||||
errs = [
|
||||
/.*outer \(RuntimeError\).*\n/,
|
||||
/.*inner \(RuntimeError\).*\n/,
|
||||
]
|
||||
assert_in_out_err([], <<-'end;', [], errs, success: false)
|
||||
END{begin; raise "inner"; rescue; raise "outer"; end}
|
||||
end;
|
||||
assert_in_out_err([], <<-'end;', [], errs, success: false)
|
||||
Signal.trap(:EXIT) {begin; raise "inner"; rescue; raise "outer"; end}
|
||||
end;
|
||||
errs = [
|
||||
/.*previous \(RuntimeError\).*\n/,
|
||||
/.*outer \(RuntimeError\).*\n/,
|
||||
/.*middle \(RuntimeError\).*\n/,
|
||||
]
|
||||
assert_in_out_err([], <<-'end;', [], errs, success: false)
|
||||
END{begin; raise "middle"; rescue => e; raise "outer", cause: e; end}
|
||||
END{raise "previous"}
|
||||
end;
|
||||
end
|
||||
|
||||
def test_raise_with_cause
|
||||
msg = "[Feature #8257]"
|
||||
cause = ArgumentError.new("foobar")
|
||||
|
||||
Reference in new issue
Block a user