Skip to content

Commit 9ba3aab

Browse files
committed
[GR-32325] Kernel#exit!, Fiber kill and internal errors should not run code in ensure
PullRequest: truffleruby/3638
2 parents f9d0a78 + 5410e96 commit 9ba3aab

File tree

24 files changed

+185
-79
lines changed

24 files changed

+185
-79
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Compatibility:
7575
* Fix `Array` methods `select!` and `keep_if` and handle a case when exception is raised in a passed block properly (@andrykonchin).
7676
* Fix `Enumerable` methods `each_cons` and `each_slice` to return receiver (#2733, @horakivo)
7777
* `Module` methods `#private`, `#public`, `#protected`, `#module_function` now returns their arguments like in CRuby 3.1 (#2733, @horakivo)
78+
* `Kernel#exit!`, killing Fibers and internal errors do not run code in `ensure` clauses anymore, the same as CRuby (@eregon).
7879

7980
Performance:
8081

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
ready = false
2+
t = Thread.new do
3+
f = Fiber.new do
4+
begin
5+
Fiber.yield
6+
ensure
7+
STDERR.puts "suspended fiber ensure"
8+
end
9+
end
10+
f.resume
11+
12+
begin
13+
ready = true
14+
sleep
15+
ensure
16+
STDERR.puts "current fiber ensure"
17+
end
18+
end
19+
20+
Thread.pass until ready && t.stop?
21+
22+
# let the program end, it's the same as #exit or an exception for this behavior
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
ready = false
2+
t = Thread.new do
3+
f = Fiber.new do
4+
begin
5+
Fiber.yield
6+
ensure
7+
STDERR.puts "suspended fiber ensure"
8+
end
9+
end
10+
f.resume
11+
12+
f2 = Fiber.new do
13+
begin
14+
ready = true
15+
sleep
16+
ensure
17+
STDERR.puts "current fiber ensure"
18+
end
19+
end
20+
f2.resume
21+
end
22+
23+
Thread.pass until ready && t.stop?
24+
25+
# let the program end, it's the same as #exit or an exception for this behavior

spec/ruby/core/exception/top_level_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,16 @@ def raise_wrapped
4242
EOS
4343
end
4444
end
45+
46+
describe "kills all threads and fibers, ensure clauses are only run for threads current fibers, not for suspended fibers" do
47+
it "with ensure on the root fiber" do
48+
file = fixture(__FILE__, "thread_fiber_ensure.rb")
49+
ruby_exe(file, args: "2>&1", exit_status: 0).should == "current fiber ensure\n"
50+
end
51+
52+
it "with ensure on non-root fiber" do
53+
file = fixture(__FILE__, "thread_fiber_ensure_non_root_fiber.rb")
54+
ruby_exe(file, args: "2>&1", exit_status: 0).should == "current fiber ensure\n"
55+
end
56+
end
4557
end

spec/ruby/core/kernel/exit_spec.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
it_behaves_like :process_exit, :exit, KernelSpecs::Method.new
1111
end
1212

13+
describe "Kernel.exit" do
14+
it_behaves_like :process_exit, :exit, Kernel
15+
end
16+
1317
describe "Kernel#exit!" do
1418
it "is a private method" do
1519
Kernel.should have_private_instance_method(:exit!)
@@ -18,10 +22,6 @@
1822
it_behaves_like :process_exit!, :exit!, "self"
1923
end
2024

21-
describe "Kernel.exit" do
22-
it_behaves_like :process_exit, :exit, Kernel
23-
end
24-
2525
describe "Kernel.exit!" do
26-
it_behaves_like :process_exit!, :exit!, Kernel
26+
it_behaves_like :process_exit!, :exit!, "Kernel"
2727
end

spec/ruby/core/process/exit_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
end
77

88
describe "Process.exit!" do
9-
it_behaves_like :process_exit!, :exit!, Process
9+
it_behaves_like :process_exit!, :exit!, "Process"
1010
end

spec/ruby/core/thread/kill_spec.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@
99
it_behaves_like :thread_exit, :kill
1010
end
1111

12-
describe "Thread#kill!" do
13-
it "needs to be reviewed for spec completeness"
14-
end
15-
1612
describe "Thread.kill" do
1713
it "causes the given thread to exit" do
1814
thread = Thread.new { sleep }

spec/ruby/core/thread/shared/exit.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,25 @@
113113
ScratchPad.recorded.should == nil
114114
end
115115

116+
it "kills other fibers of that thread without running their ensure clauses" do
117+
t = Thread.new do
118+
f = Fiber.new do
119+
ScratchPad.record :fiber_resumed
120+
begin
121+
Fiber.yield
122+
ensure
123+
ScratchPad.record :fiber_ensure
124+
end
125+
end
126+
f.resume
127+
sleep
128+
end
129+
Thread.pass until t.stop?
130+
t.send(@method)
131+
t.join
132+
ScratchPad.recorded.should == :fiber_resumed
133+
end
134+
116135
# This spec is a mess. It fails randomly, it hangs on MRI, it needs to be removed
117136
quarantine! do
118137
it "killing dying running does nothing" do

spec/ruby/shared/process/exit.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@
104104
$?.exitstatus.should == 21
105105
end
106106

107+
it "skips ensure clauses" do
108+
out = ruby_exe("begin; STDERR.puts 'before'; #{@object}.send(:exit!, 21); ensure; STDERR.puts 'ensure'; end", args: '2>&1', exit_status: 21)
109+
out.should == "before\n"
110+
$?.exitstatus.should == 21
111+
end
112+
107113
it "overrides the original exception and exit status when called from #at_exit" do
108114
code = <<-RUBY
109115
at_exit do
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
slow:An Exception reaching the top level is printed on STDERR
22
slow:An Exception reaching the top level with a custom backtrace is printed on STDERR
33
slow:An Exception reaching the top level the Exception#cause is printed to STDERR with backtraces
4+
slow:An Exception reaching the top level kills all threads and fibers, ensure clauses are only run for threads current fibers, not for suspended fibers with ensure on the root fiber
5+
slow:An Exception reaching the top level kills all threads and fibers, ensure clauses are only run for threads current fibers, not for suspended fibers with ensure on non-root fiber
6+
fails(GR-44211):An Exception reaching the top level kills all threads and fibers, ensure clauses are only run for threads current fibers, not for suspended fibers with ensure on non-root fiber

0 commit comments

Comments
 (0)