https://redmine.ruby-lang.org/https://redmine.ruby-lang.org/favicon.ico?17113305112021-05-05T01:41:26ZRuby Issue Tracking SystemRuby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=918192021-05-05T01:41:26Zduerst (Martin Dürst)duerst@it.aoyama.ac.jp
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-5 priority-4 priority-default closed" href="/issues/17837">Feature #17837</a>: Add support for Regexp timeouts</i> added</li></ul> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=918242021-05-05T07:54:21Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><blockquote>
<p>We could automatically mask Thread#raise within ensure so it only happens after the ensure body completes.</p>
</blockquote>
<p>As said on the other issue, I don't think it would fix <em>all</em> issues, but it would certainly be a huge improvement.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=919142021-05-12T15:06:09Zschneems (Richard Schneeman)
<ul></ul><p>I've had a conversation with Matz about this and I've been thinking about this issue for a LONG time. The blocker to doing something like this before is that due to the halting problem we can never know if an ensure block will exit or not. In effect, if someone were to use <code>Thread#safe_raise</code> (or something similar) where the exception didn't fire inside an ensure block: It might accidentally end up with your program stuck in an infinite loop.</p>
<p>My proposal to avoid this would be to add an additional timer (yes, timers all the way down) to fire an "overtime" block. For example, you could specify the program should wait 1 second to clear all ensure blocks, if it doesn't then execute some code (such as raising an error or emitting a warning). Here's my stab at an API:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Timeout</span><span class="p">.</span><span class="nf">safe_timeout</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="ss">overtime: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">on_overtime: :warn</span> <span class="o">||</span> <span class="ss">:error</span><span class="p">)</span> <span class="k">do</span>
<span class="c1"># ...</span>
<span class="k">end</span>
<span class="no">Timeout</span><span class="p">.</span><span class="nf">safe_timeout</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="ss">overtime: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">on_overtime: </span><span class="o">-></span> <span class="p">{</span> <span class="nb">puts</span> <span class="s2">"Overtime reached"</span><span class="p">}</span> <span class="p">)</span> <span class="k">do</span>
<span class="c1"># ...</span>
<span class="k">end</span>
</code></pre>
<p>Here's the twitter thread that's referenced in the prior conversation <a href="https://mobile.twitter.com/schneems/status/1377340755878965248" class="external">https://mobile.twitter.com/schneems/status/1377340755878965248</a>.</p>
<p>I've written at length about the problem here <a href="https://www.schneems.com/2017/02/21/the-oldest-bug-in-ruby-why-racktimeout-might-hose-your-server/" class="external">https://www.schneems.com/2017/02/21/the-oldest-bug-in-ruby-why-racktimeout-might-hose-your-server/</a></p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=919492021-05-13T10:02:27Zduerst (Martin Dürst)duerst@it.aoyama.ac.jp
<ul><li><strong>Assignee</strong> set to <i>matz (Yukihiro Matsumoto)</i></li></ul><p>Assigning to Matz because this may involve a new method.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=919712021-05-14T18:15:23ZEregon (Benoit Daloze)
<ul></ul><p>Another way to express the idea is that <code>ensure; code(); end</code> would automatically behave like <code>Thread.handle_interrupt(Object => :never) { code }</code>.<br>
Since that would be directly in the interpreter and not actually call <code>Thread.handle_interrupt</code>, there is no worry that the interrupt happens during the call to <code>Thread.handle_interrupt</code> (which would be an issue when writing <code>ensure; Thread.handle_interrupt { ... }; end</code>).</p>
<p>byroot (Jean Boussier) wrote in <a href="#note-12">#note-12</a>:</p>
<blockquote>
<p>That would fix most issues, but not all. It can also trigger in places where exception are entirely unexpected, so there's just no <code>ensure</code>.</p>
</blockquote>
<p>Do you have an example?</p>
<blockquote>
<p>Also I'm not clear on the details, but some C extensions (often various clients) can't be interrupted by <code>Thread#raise</code>.</p>
</blockquote>
<p>If that's the case I would argue it's a bug of the C extension (for blocking without an unblocking function).<br>
It seems very much expected that Ctrl+C always works in Ruby.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=919732021-05-14T18:16:03ZEregon (Benoit Daloze)
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-2 priority-4 priority-default" href="/issues/17363">Feature #17363</a>: Timeouts</i> added</li></ul> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=919742021-05-14T18:22:09ZEregon (Benoit Daloze)
<ul></ul><p>This is also a bit related to the proposed <code>Timeout.wake</code> in <a class="issue tracker-2 status-2 priority-4 priority-default" title="Feature: Timeouts (Assigned)" href="https://redmine.ruby-lang.org/issues/17363">#17363</a>, but that would only interrupt blocking methods and it's unclear if that includes Regexp matching (probably not since it's CPU work, not waiting on anything external), and it would definitely not include code that does not block but runs for too long (e.g. a very long loop).<br>
Long story short, this proposal would be general, <code>Timeout.wake</code> would not and would only address blocking methods.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=919752021-05-14T18:28:26Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><blockquote>
<p>Do you have an example?</p>
</blockquote>
<p>I don't have a specific example in mind, but for instance things like:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">module</span> <span class="nn">Namespace</span>
<span class="kp">extend</span> <span class="nb">self</span>
<span class="k">def</span> <span class="nf">clear_something</span>
<span class="vi">@something</span><span class="p">,</span> <span class="n">old_something</span> <span class="o">=</span> <span class="no">Something</span><span class="p">.</span><span class="nf">new</span><span class="p">,</span> <span class="vi">@something</span>
<span class="vi">@something</span><span class="p">.</span><span class="nf">clear</span> <span class="c1"># if we raise here we might leak or whatever</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>The <code>setup / yield / ensure / clean</code> pattern is extremely frequent and just fixing this one would be a huge improvement.<br>
I just want to make it clear that it won't be sufficient to eliminate all possible woes.</p>
<blockquote>
<p>If that's the case I would argue it's a bug of the C extension</p>
</blockquote>
<p>Sure. But it can similarly be argued (and I heard it before) that a request that doesn't complete in a reasonable time is a bug in the application.</p>
<p>But in my opinion web server timeouts are exactly that, a safety net against bugs in the application. That's exactly the kind of cases I want a timeout for.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=920182021-05-19T18:13:46Zschneems (Richard Schneeman)
<ul></ul><blockquote>
<p>Assignee set to matz (Yukihiro Matsumoto)</p>
</blockquote>
<p>Maybe we want to tag in Koichi. Last I talked to Matz (EuRuKo in 2019) I believe he suggested having Koichi take a look at the issue. I'm not familiar with the interface here I don't know if we can @ mention someone or if we need to set them to the owner.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=920202021-05-19T19:34:02ZEregon (Benoit Daloze)
<ul></ul><p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/7941">@byroot (Jean Boussier)</a> For the blocking C extension code without an unblocking function case, I think there is no other way to unstuck it than to kill the entire process.<br>
That's probably a good last measure to have, but obviously it's very costly so should only be used when there is no other way.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=920392021-05-20T08:44:24Zko1 (Koichi Sasada)
<ul></ul><p>There are two issues on masking interrupts in <code>ensure</code> clauses:</p>
<p>(1) performance: manipulating masks in each <code>ensure</code> clauses introduces overhead<br>
(2) Some applications can run their program body in <code>ensure</code> clauses.</p>
<p>Before we discussed about it, we can't decide to mask interrupts because of (2).<br>
Such applications should allow interrupt explicitly.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=920402021-05-20T08:56:51ZEregon (Benoit Daloze)
<ul></ul><p>I think (2) is rare, and IMHO such programs should be fixed.<br>
What would happen is then those programs can't be interrupted (only by killing the process), but then that also becomes a clear motivation to fix those programs so they can be interrutped safely.</p>
<p>Not sure about (1), I think it's loading the current thread + saving current value of the mask, writing the interrupt mask for every <code>ensure</code>, and restoring it once the <code>ensure</code> finishes.<br>
It needs to be done on the non-exceptional path too, so indeed it could be some overhead.<br>
I think we need to measure it in practice here to evaluate better the overhead.</p>
<p>The whole strategy here relies on code in <code>ensure</code> having code that only takes a small amount of time.<br>
If that doesn't hold, it will either hang (motivating those programs to be fixed),<br>
or we could resort to the old semantics after some extra time (e.g., 10s), similar to what <a href="https://bugs.ruby-lang.org/issues/17849#note-3" class="external">https://bugs.ruby-lang.org/issues/17849#note-3</a> proposes, and then print a big warning that the program misbehaved (and that state might be corrupted due to that as <code>ensure</code> likely has been interrupted).<br>
That could be useful notably for Ctrl+C to still work as it does currently in such situations.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=920662021-05-21T06:11:38Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>I doubt if there's a clear solution.</p>
<ul>
<li>
<code>Timeout</code> uses asynchronous exceptions and it <strong>is</strong> difficult to handle correctly.</li>
<li>for example, if some calls <code>Timeout.timeout</code> from within <code>ensure</code> clause directly/indirectly</li>
<li>or what if timeout interrupt breaks in finalizers</li>
</ul>
<p>The perfect global timeout (that relies on asynchronous exceptions) might be theoretically possible, but practically (nearly) impossible, I believe.</p>
<p>Matz.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=920862021-05-21T18:19:22Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p>Just FYI. According to <a class="user active user-mention" href="https://redmine.ruby-lang.org/users/17">@ko1 (Koichi Sasada)</a>, masking all exceptions in an ensure clause is possible in pure Ruby, as long as you use MRI.</p>
<pre><code>begin
...
ensure
Thread.handle_interrupt(Object => :never) do
..
end
end
</code></pre>
<p>No interrupt checking is executed outside of handle_interrupt block. This is an implementation detail of MRI, though.</p>
<p>Note: we don't insist that people should write such messy code for all ensure clauses. It may be just useful to perform a proof-of-concept of this proposal, whether it is practically possible to implement a global timeout.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=920882021-05-21T18:53:26Zschneems (Richard Schneeman)
<ul></ul><blockquote>
<p>(2) Some applications can run their program body in ensure clauses.</p>
</blockquote>
<p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/17">@ko1 (Koichi Sasada)</a> first, thank you for looking at this issue. I agree this is a problem, it is the pathological case to what I was describing by the inability to detect a halting condition. This case is the cause of my suggestion to add some sort of "overtime" timeout value. Somewhat like how you should send a process SIGTERM and let it gracefully exit before having the option of sending a SIGKILL. What do you think of that option?</p>
<p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/18">@mame (Yusuke Endoh)</a> "It may be possible" thank you for this idea. If exploration or PoC is possible to help move things forward that would be great. I can benchmark some real-world rails apps using <a href="https://github.com/schneems/derailed_benchmarks" class="external">https://github.com/schneems/derailed_benchmarks</a> if we are worried about performance concerns. (Though if I am not responsive, it's because ruby-lang emails stopped coming for me. I have asked HSBT but I have no solution now)</p>
<p>I think that this ability to timeout arbitrary Ruby code is very useful, even if it is not perfect. I agree with <a class="user active user-mention" href="https://redmine.ruby-lang.org/users/13">@matz (Yukihiro Matsumoto)</a> that such a truly "perfect" API and implementation may be impossible...I also believe that this feature is worth improving even if the final result has shortcomings.</p>
<p>Thank you all for your continued work with Ruby!</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=921192021-05-22T19:55:30ZEregon (Benoit Daloze)
<ul></ul><p>mame (Yusuke Endoh) wrote in <a href="#note-14">#note-14</a>:</p>
<blockquote>
<p>Just FYI. According to <a class="user active user-mention" href="https://redmine.ruby-lang.org/users/17">@ko1 (Koichi Sasada)</a>, masking all exceptions in an ensure clause is possible in pure Ruby, as long as you use MRI.</p>
</blockquote>
<p>It does not seem to be the case: <a href="https://gist.github.com/eregon/9022f5709fa054fc4e488d7de085b254" class="external">https://gist.github.com/eregon/9022f5709fa054fc4e488d7de085b254</a><br>
And I would be surprised if it was on any Ruby, there are two calls there: <code>handle_interrupt</code> on <code>Thread</code>, and <code>hash</code> on <code>Object</code>.<br>
Actually such a pattern is a typical pitfall of using <code>handle_interrupt</code>, and monitor.rb had such a bug.</p>
<p>The correct pattern is the first one documented here: <a href="https://www.rubydoc.info/stdlib/core/Thread.handle_interrupt" class="external">https://www.rubydoc.info/stdlib/core/Thread.handle_interrupt</a><br>
Of course that's very verbose and unrealistic to be used for every <code>ensure</code>. And additionally it's quite a large overhead when done that way (extra calls, blocks, Hash instances, etc).</p>
<p>In other words, we need VM changes to experiment with this.</p>
<p>I think masking/disabling interrupts in <code>ensure</code> and in <code>finalizers</code> (<a href="https://bugs.ruby-lang.org/issues/13876#change-91365" class="external">https://bugs.ruby-lang.org/issues/13876#change-91365</a>) would be a good improvement, and definitely worth experimenting with.<br>
That won't make <code>Thread#raise</code> completely safe indeed, but much more usable, and much safer.<br>
Of course, we will need to evaluate the performance and semantics impact of this change.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=921222021-05-22T20:00:22ZEregon (Benoit Daloze)
<ul><li><strong>Related to</strong> <i><a class="issue tracker-1 status-5 priority-4 priority-default closed" href="/issues/13876">Bug #13876</a>: Tempfile's finalizer can be interrupted by a Timeout exception which can cause the process to hang</i> added</li></ul> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=940562021-10-07T12:56:12Zheadius (Charles Nutter)headius@headius.com
<ul></ul><p>mame (Yusuke Endoh) wrote in <a href="#note-14">#note-14</a>:</p>
<blockquote>
<p>Just FYI. According to <a class="user active user-mention" href="https://redmine.ruby-lang.org/users/17">@ko1 (Koichi Sasada)</a>, masking all exceptions in an ensure clause is possible in pure Ruby, as long as you use MRI.</p>
<pre><code>begin
...
ensure
Thread.handle_interrupt(Object => :never) do
..
end
end
</code></pre>
</blockquote>
<p>This is incorrect code. The <code>handle_interrupt</code> code must go outside of the entire begin/ensure/end block or you still run the risk of being interrupted before the ensure runs.</p>
<p><code>handle_interrupt</code> is a band-aid over the real problem, which is that code can be interrupted at any time. Unfortunately the Ruby Way to fix this is that users have to <strong>opt in</strong> to get safe ensures, and almost nobody gets it right. Every library in existence is broken.</p>
<p>And for the record, both JRuby and TruffleRuby fully support <code>handle_interrupt</code>, so you don't have to use MRI.</p>
<pre><code>Thread.handle_interrupt(Object => :never) do
begin
Thread.handle_interrupt(Object => :immediate) do
...
end
ensure
...
end
end
</code></pre>
<p>See <a href="http://blog.headius.com/2008/02/ruby-threadraise-threadkill-timeoutrb.html" class="external">http://blog.headius.com/2008/02/ruby-threadraise-threadkill-timeoutrb.html</a></p>
<p><code>handle_interrupt</code> makes it possible to avoid some of these problems, but nobody is using it and the ones who are using it are using it wrong.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=940682021-10-07T20:04:42Zioquatix (Samuel Williams)samuel@oriontransfer.net
<ul></ul><p>The fiber scheduler redefines <code>Timeout</code> to only well defined wait points.</p>
<p>We could also do the same for other options likes <code>Thread#raise</code> etc.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=945062021-11-06T12:03:02ZEregon (Benoit Daloze)
<ul></ul><p>Another way would be to default to <code>Thread.handle_interrupt(Exception => :on_blocking) do</code> for all threads, which is more or less what happens with the Fiber scheduler.<br>
Then <code>Thread#raise</code> would only affect blocking calls.<br>
OTOH it means we wouldn't be able to interrupt code which doesn't do blocking calls but e.g. loops infinitely and does a very long computation (unless it explicitly checks for interrupt but basically no code does that currently).<br>
Regexp matching is not considered a blocking call/operation currently, but I think we could maybe change that.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=945112021-11-08T08:58:33Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/286">@headius (Charles Nutter)</a></p>
<p>headius (Charles Nutter) wrote in <a href="#note-18">#note-18</a>:</p>
<blockquote>
<p>This is incorrect code.</p>
</blockquote>
<p>I believe that my code is correct under the current implementation of MRI. You may think that the method invocation itself <code>Thread.handle_interrupt(...) do ... end</code> could be interrupted, but it is not true. There is no point of interruption checks during this method invocation, under the current implementation of MRI. This is very subtle implementation detail of MRI, but it is as I have already stated in <a href="#note-14">#note-14</a>.</p>
<p>Here is a test:</p>
<pre><code>def foo
begin
sleep
ensure
Thread.handle_interrupt(Object => :never) do
$finalized = true
end
end
end
Thread.report_on_exception = false
1000.times do
$finalized = false
th = Thread.new { foo }
sleep 0.1
3000.times { th.raise }
begin
th.join
rescue
end
raise "test failed" unless $finalized
end
</code></pre> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=945162021-11-08T17:33:57ZEregon (Benoit Daloze)
<ul></ul><p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/18">@mame (Yusuke Endoh)</a> How do you explain the test I linked above fails (still does with latest CRuby) then?<br>
<a href="https://bugs.ruby-lang.org/issues/17849#note-16" class="external">https://bugs.ruby-lang.org/issues/17849#note-16</a></p>
<p>Anyway, we all agree nobody should rely on that, and it's also unrealistic to ask people to explicitly add <code>Thread.handle_interrupt</code> in every <code>ensure</code>.</p> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=945252021-11-09T02:09:20Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p>Eregon (Benoit Daloze) wrote in <a href="#note-22">#note-22</a>:</p>
<blockquote>
<p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/18">@mame (Yusuke Endoh)</a> How do you explain the test I linked above fails (still does with latest CRuby) then?</p>
</blockquote>
<p>The exception is raised immediately after the block returns, and before <code>handle_interrupt</code> returns.</p>
<pre><code>$ready = false
thread = Thread.new do
loop do
$ready = true
begin
Thread.pass
rescue => e
p e
ensure
Thread.handle_interrupt(Object => :never) do # backtrace reports it happens here
begin
Thread.pass
rescue => e
abort "ensure saw #{e}"
end
$finalized = true
# the exception is raised immediately after this block returns to handle_interrupt
end
end
end
end
$finalized = false
1000.times {
Thread.pass until $ready
thread.raise "timeout"
Thread.pass
}
p $finalized #=> true
</code></pre> Ruby master - Feature #17849: Fix Timeout.timeout so that it can be used in threaded Web servershttps://redmine.ruby-lang.org/issues/17849?journal_id=971902022-04-10T06:26:54Zavit (Andrew Vit)andrew@avit.ca
<ul></ul><p>The handling of timeouts is <a href="https://ruby-doc.org/core-3.1.1/Thread.html#method-c-handle_interrupt-label-Guarding+from+Timeout-3A-3AError" class="external">documented</a>, but that only shows it with a <code>handle_interrupt</code> block set up around everything, not under <code>ensure</code>. In the examples shown here, can <code>ensure;begin</code> or <code>ensure;Thread.handle_interrupt</code> be considered "atomic", or is it possible for an interrupt to happen between these lines?</p>
<p>(Another rubyist once recommended a <a href="https://web.archive.org/web/20110113205935/http://blog.segment7.net:80/articles/2006/04/11/care-and-feeding-of-timeout-timeout" class="external">different approach using rescue/retry</a>, but in my testing it looks like <code>handle_interrupt</code> is more robust.)</p>
<p>I have a small thought on a possible API: if it might be an improvement to pass interrupt handling options to <code>ensure</code>, similar to <code>rescue</code>:</p>
<pre><code>begin
ensure Timeout::Error => :never
$finalized = true
end
</code></pre>
<p>To extend the similarity with <code>rescue</code>, could it make sense to allow multiple ensure blocks, if different options are needed in sequence?</p>
<p>EDIT: It looks like my idea was previously suggested:</p>
<p><a href="https://bugs.ruby-lang.org/issues/6762#note-3" class="external">https://bugs.ruby-lang.org/issues/6762#note-3</a></p>