From: eregontp@... Date: 2021-06-02T20:54:14+00:00 Subject: [ruby-core:104151] [Ruby master Feature#15567] Allow ensure to match specific situations Issue #15567 has been updated by Eregon (Benoit Daloze). > A timeout error in my mind is more similar to a TERM signal, and Ruby handles that using an exception. I think that's a good point. I agree for similar things like Ctrl+C/SIGINT which is an `Interrupt` exception (< SignalException < Exception), and `Kernel#exit` which is a `SystemExit` exception (< Exception). Nothing can guarantee to bubble through user code without stopping in between (it's always possible to hang/loop via `ensure`), except a fatal error that would immediately end the process or skip even `ensure` blocks and always reach the C main(). So proper exception handling (e.g. no `rescue Exception; # ignore`, `ensure` code must be bounded in time/steps) is always needed for correctness, and if Ctrl+C/exit work with an exception, so should Timeout.timeout. Maybe we should open a new issue so that Timeout.timeout uses an Exception and not `throw`? --- I'm not sure if `throw` is always used as non-local control flow, and not as some sort of exception (e.g., to indicate some 403). BTW I guess we could replace throw with a non-local `return` using `handle_request(proc { return })` instead of `catch`, but it wouldn't change anything related to this issue, both are considered non-local exits and non-distinguishable. That's an argument for treating `throw` as control flow much like non-local `return` and not as exceptional though. ---------------------------------------- Feature #15567: Allow ensure to match specific situations https://bugs.ruby-lang.org/issues/15567#change-92323 * Author: ioquatix (Samuel Williams) * Status: Rejected * Priority: Normal * Assignee: ioquatix (Samuel Williams) ---------------------------------------- There are some situations where `rescue Exception` or `ensure` are not sufficient to correctly, efficiently and easily handle abnormal flow control. Take the following program for example: ``` def doot yield ensure # Did the function run to completion? return "abnormal" if $! end puts doot{throw :foo} puts doot{raise "Boom"} puts doot{"Hello World"} catch(:foo) do puts doot{throw :foo} end ``` Using `rescue Exception` is not sufficient as it is not invoked by `throw`. Using `ensure` is inefficient because it's triggered every time, even though exceptional case might never happen or happen very infrequently. I propose some way to limit the scope of the ensure block: ``` def doot yield ensure when raise, throw return "abnormal" end ``` The scope should be one (or more) of `raise`, `throw`, `return`, `next`, `break`, `redo`, `retry` (everything in `enum ruby_tag_type` except all except for `RUBY_TAG_FATAL`). Additionally, it might be nice to support the inverted pattern, i.e. ``` def doot yield ensure when not return return "abnormal" end ``` Inverted patterns allow user to specify the behaviour without having problems if future scopes are introduced. `return` in this case matches both explicit and implicit. -- https://bugs.ruby-lang.org/ Unsubscribe: