Skip to content

Commit 19f0e6a

Browse files
committed
[GR-28930] Clear and restore errinfo on entry to and normal return from C.
PullRequest: truffleruby/2359
2 parents 7d387cd + 215cc0d commit 19f0e6a

File tree

4 files changed

+55
-1
lines changed

4 files changed

+55
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Bug fixes:
1010
* Show the interleaved host and guest stacktrace for host exceptions (#2226).
1111
* Fix the label of the first location reported by `Thread#backtrace_locations` (#2229).
1212
* Fix `Thread.handle_interrupt` to defer non-pure interrupts until the end of the `handle_interrupt` block (#2219).
13+
* Clear and restore errinfo on entry and normal return from methods in C extensions (#2227).
1314

1415
Compatibility:
1516

lib/truffle/truffle/cext_ruby.rb

+5-1
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,16 @@ def rb_define_method(mod, name, function, argc)
3131
args = [Primitive.cext_wrap(self), *args.map! { |arg| Primitive.cext_wrap(arg) }]
3232
end
3333

34+
exc = $!
35+
Primitive.thread_set_exception(nil)
3436
# Using raw execute instead of #call here to avoid argument conversion
3537

3638
# We must set block argument if given here so that the
3739
# `rb_block_*` functions will be able to find it by walking the
3840
# stack.
39-
Primitive.cext_unwrap(Primitive.call_with_c_mutex_and_frame(function, args, block))
41+
res = Primitive.cext_unwrap(Primitive.call_with_c_mutex_and_frame(function, args, block))
42+
Primitive.thread_set_exception(exc)
43+
res
4044
end
4145

4246
mod.define_method(name, method_body)

spec/ruby/optional/capi/exception_spec.rb

+44
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,50 @@
3636
runtime_error.set_backtrace []
3737
-> { @s.rb_exc_raise(runtime_error) }.should raise_error(RuntimeError, '42')
3838
end
39+
40+
it "sets $! to the raised exception when not rescuing from an another exception" do
41+
runtime_error = RuntimeError.new '42'
42+
runtime_error.set_backtrace []
43+
begin
44+
@s.rb_exc_raise(runtime_error)
45+
rescue
46+
$!.should == runtime_error
47+
end
48+
end
49+
50+
it "sets $! to the raised exception when $! when rescuing from an another exception" do
51+
runtime_error = RuntimeError.new '42'
52+
runtime_error.set_backtrace []
53+
begin
54+
begin
55+
raise StandardError
56+
rescue
57+
@s.rb_exc_raise(runtime_error)
58+
end
59+
rescue
60+
$!.should == runtime_error
61+
end
62+
end
63+
end
64+
65+
describe "rb_errinfo" do
66+
it "is cleared when entering a C method" do
67+
begin
68+
raise StandardError
69+
rescue
70+
$!.class.should == StandardError
71+
@s.rb_errinfo().should == nil
72+
end
73+
end
74+
75+
it "does not clear $! in the calling method" do
76+
begin
77+
raise StandardError
78+
rescue
79+
@s.rb_errinfo()
80+
$!.class.should == StandardError
81+
end
82+
end
3983
end
4084

4185
describe "rb_set_errinfo" do

spec/ruby/optional/capi/ext/exception_spec.c

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
extern "C" {
99
#endif
1010

11+
VALUE exception_spec_rb_errinfo(VALUE self) {
12+
return rb_errinfo();
13+
}
14+
1115
VALUE exception_spec_rb_exc_new(VALUE self, VALUE str) {
1216
char *cstr = StringValuePtr(str);
1317
return rb_exc_new(rb_eException, cstr, strlen(cstr));
@@ -41,6 +45,7 @@ VALUE exception_spec_rb_make_exception(VALUE self, VALUE ary) {
4145

4246
void Init_exception_spec(void) {
4347
VALUE cls = rb_define_class("CApiExceptionSpecs", rb_cObject);
48+
rb_define_method(cls, "rb_errinfo", exception_spec_rb_errinfo, 0);
4449
rb_define_method(cls, "rb_exc_new", exception_spec_rb_exc_new, 1);
4550
rb_define_method(cls, "rb_exc_new2", exception_spec_rb_exc_new2, 1);
4651
rb_define_method(cls, "rb_exc_new3", exception_spec_rb_exc_new3, 1);

0 commit comments

Comments
 (0)