From: "ioquatix (Samuel Williams)" Date: 2022-10-15T00:22:29+00:00 Subject: [ruby-core:110294] [Ruby master Bug#19055] Regexp.new(regexp, timeout: nil) is intrupted by Regexp.timeout Issue #19055 has been updated by ioquatix (Samuel Williams). The model I've been following is this: ``` nil # default timeout 0 # would timeout immediately t > 0 # timeout after t seconds ``` The default timeout should have special meaning. By default, `timeout: nil` should mean no timeout, but if there is global timeout, e.g. `Regexp.timeout = t` (if such a thing exists) and `t` itself is non-nil, then `Regexp.new(..., timeout: nil)` should use that default, i.e. with a default timeout set, it's impossible to bypass the timeout. > I wonder what API is preferable here. I expect Regexp.new(str, timeout: nil) to be the same as Regexp.new(str). But it would be surprising if Regexp.new(str) ignores the global timeout configuration. I think I'm in agreement with this. I think it would be a good idea to document the standard model for how timeouts are handled and used, and the way a instance specific timeout is handled in relation to a global timeout. There are three layers, I'll use `Regexp` but it equally applies to `IO` and other "temporally relevant objects": ```ruby Regexp.timeout = nil | timeout # Global timeout, the default. regexp = Regexp.new(..., timeout: nil | timeout) # Per instance timeout, if nil, uses the global default. regexp.match(..., timeout: nil | timeout) # Per operation timeout, if nil, uses the instance timeout. ``` This is the most consistent and easy to understand model. What it does mean though, is that if a per instance or global timeout is set, you cannot bypass it. I actually think that's a good thing. If you set a global timeout, you don't want some poorly structured code to bypass it. That being said, if you do feel strongly about having a bypass feature, I propose using `false`. ``` regexp = Regexp.new(..., timeout: nil | false| timeout) # Per instance timeout, if nil, uses the global default. regexp.match(..., timeout: nil | false | timeout) # Per operation timeout, if nil, uses the instance timeout. ``` The timeout handling code should assume `RTEST(regexp->timeout)` implies there is no timeout (so false is handled correctly). If we agree on this, I suggest we document it clearly for all relevant systems, as I'll do my best to follow the same model for `IO` timeouts so that we are consistent. The counter point is, if you choose something inconsistent for `Regexp`, I might not be able to adopt it for `IO` if it's really different. So please let's try to make it consistent :) thanks. ---------------------------------------- Bug #19055: Regexp.new(regexp, timeout: nil) is intrupted by Regexp.timeout https://bugs.ruby-lang.org/issues/19055#change-99580 * Author: jaruga (Jun Aruga) * Status: Open * Priority: Normal * ruby -v: ruby 3.2.0preview2 (2022-09-09 master 35cfc9a3bb) [x86_64-linux] * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN ---------------------------------------- It seems that that a code for `Regexp timeout` written in the release page[1] is actually interrupted by the `Regexp.timeout` (global configuration) in Ruby 3.2.0 preview2 built from the source. Do you know what's wrong? ``` $ cat reg_timeout2.rb Regexp.timeout = 1.0 # This regexp has no timeout long_time_re = Regexp.new("^a*b?a*$", timeout: nil) long_time_re =~ "a" * 50000 + "x" # never interrupted ``` The error `Regexp::TimeoutError` appeared soon after around 1 second after running the script `reg_timeout2.rb`. ``` $ which ruby /usr/local/ruby-3.2.0-preview2/bin/ruby $ ruby -v ruby 3.2.0preview2 (2022-09-09 master 35cfc9a3bb) [x86_64-linux] $ ruby reg_timeout2.rb reg_timeout2.rb:4:in `
': regexp match timeout (Regexp::TimeoutError) $ time ruby reg_timeout2.rb reg_timeout2.rb:4:in `
': regexp match timeout (Regexp::TimeoutError) real 0m1.072s user 0m1.053s sys 0m0.014s $ echo $? 1 ``` ``` $ which irb /usr/local/ruby-3.2.0-preview2/bin/irb $ irb irb(main):001:0> Regexp.timeout = 1.0 => 1.0 irb(main):002:0> long_time_re = Regexp.new("^a*b?a*$", timeout: nil) => /^a*b?a*$/ irb(main):003:0> long_time_re =~ "a" * 50000 + "x" (irb):3:in `
': regexp match timeout (Regexp::TimeoutError) from /usr/local/ruby-3.2.0-preview2/lib/ruby/gems/3.2.0+2/gems/irb-1.4.1/exe/irb:11:in `' from /usr/local/ruby-3.2.0-preview2/bin/irb:25:in `load' from /usr/local/ruby-3.2.0-preview2/bin/irb:25:in `
' irb(main):004:0> ``` [1] https://www.ruby-lang.org/en/news/2022/09/09/ruby-3-2-0-preview2-released/ -- https://bugs.ruby-lang.org/ Unsubscribe: