Issue #19473 has been reported by Eregon (Benoit Daloze).
----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473
* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
from -e:1:in `block in <main>'
from -e:1:in `kill'
from -e:1:in `<main>'
```
Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```
This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939
I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
However, that's just one case, not all, so we should not raise an exception early like that.
There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'
in trap
-e:1:in `wait'
-e:1:in `<main>'
in trap
-e:1:in `wait'
-e:1:in `<main>'
in trap
-e:1:in `wait'
-e:1:in `<main>'
in trap
-e:1:in `wait'
-e:1:in `<main>'
in trap
-e:1:in `wait'
-e:1:in `<main>'
```
And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.
--
https://bugs.ruby-lang.org/
Issue #20968 has been reported by koic (Koichi ITO).
----------------------------------------
Bug #20968: `Array#fetch_values` unexpected method name in stack trace
https://bugs.ruby-lang.org/issues/20968
* Author: koic (Koichi ITO)
* Status: Open
* ruby -v: ruby 3.4.0dev (2024-12-19T04:44:56Z master 2783868de2) +PRISM [x86_64-darwin23]
* Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
It seems that the current Ruby implementation is displaying unexpected method name in stack trace.
## Expected
Similar to `Hash#fetch_values`, the method name `Array#fetch_values` is expected to be displayed in the stack trace.
```console
$ ruby -e '{k: 42}.fetch_values(:unknown)'
-e:1:in 'Hash#fetch_values': key not found: :unknown (KeyError)
from -e:1:in '<main>'
$ ruby -e '[1].fetch_values(42)'
-e:1:in 'Array#fetch_values': index 42 outside of array bounds: -1...1 (IndexError)
from -e:1:in '<main>'
```
## Actual
The stack trace displays the `Array#fetch` method, which user is not aware of, along with the `<internal.array>` stack trace.
```console
$ ruby -e '[1].fetch_values(42)'
<internal:array>:211:in 'Array#fetch': index 42 outside of array bounds: -1...1 (IndexError)
from <internal:array>:211:in 'block in Array#fetch_values'
from <internal:array>:211:in 'Array#map!'
from <internal:array>:211:in 'Array#fetch_values'
from -e:1:in '<main>'
```
It likely requires an approach such as implementing it in C, as suggested in https://github.com/ruby/ruby/pull/11555.
--
https://bugs.ruby-lang.org/
Issue #20714 has been reported by Earlopain (A S).
----------------------------------------
Bug #20714: Handle optional dependencies in `bundled_gems.rb`
https://bugs.ruby-lang.org/issues/20714
* Author: Earlopain (A S)
* Status: Open
* ruby -v: 3.3.5
* Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
I've encountered a few places around bundled gems where the library doesn't care if the gem is available, but will still provide some functionallity if it is.
The way to accomplish that right now seems to be by setting `$VERBOSE = nil` and resetting it later again to not bother the user with the warning about the gem. However, this has the effect of silencing the warning about other gems as well, that may not be prepared about the bundling.
From `ruby/reline` for example: https://github.com/ruby/reline/blob/c90f08f7e308d2f1cdd7cfaf9939fe45ce546fd…
Or the `logging` gem: https://github.com/TwP/logging/blob/df41715364f7eb8c65098cd3c3316677ef1f378…
I propose to simply delay the warning to the next require. GitHub PR at ...
--
https://bugs.ruby-lang.org/
Issue #20998 has been reported by Eregon (Benoit Daloze).
----------------------------------------
Bug #20998: rb_str_locktmp() changes flags of frozen strings and string literals
https://bugs.ruby-lang.org/issues/20998
* Author: Eregon (Benoit Daloze)
* Status: Open
* ruby -v: ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [x86_64-linux]
* Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
```ruby
# frozen_string_literal: true
# BOILERPLATE START
require 'tmpdir'
require 'rbconfig'
def inline_c_extension(c_code)
Dir.mktmpdir('inline_c_extension') do |dir|
File.write("#{dir}/cext.c", c_code)
File.write("#{dir}/extconf.rb", <<~RUBY)
require 'mkmf'
create_makefile('cext')
RUBY
out = IO.popen([RbConfig.ruby, 'extconf.rb'], chdir: dir, &:read)
raise "ruby extconf.rb failed: #{$?.inspect}\n#{out}" unless $?.success?
out = IO.popen(['make'], chdir: dir, &:read)
raise "make failed: #{$?.inspect}\n#{out}" unless $?.success?
require "#{dir}/cext.#{RbConfig::CONFIG['DLEXT']}"
end
end
inline_c_extension <<~C
#include "ruby.h"
static VALUE foo(VALUE self, VALUE str) {
rb_str_locktmp(str);
return str;
}
void Init_cext(void) {
VALUE c = rb_define_class("Foo", rb_cObject);
rb_define_singleton_method(c, "foo", foo, 1);
}
C
# BOILERPLATE END
a = "str"
# imagine a million line of codes in between
b = "str"
Foo.foo(a)
b << "."
# can't modify string; temporarily locked (RuntimeError)
# What? Who "locked" that immutable frozen string literal?
# It should be: can't modify frozen String: "str" (FrozenError)
```
Same problem with:
```ruby
Foo.foo("abc")
# imagine a million line of codes in between
Foo.foo("abc") # emporal locking already locked string (RuntimeError)
```
Related: https://github.com/oracle/truffleruby/issues/3752
It seems a clear bug to mutate a frozen string (with visible side effects), even more so for shared frozen string literals.
I think rb_str_locktmp() should raise (a FrozenError, as it's effectively attempting to mutate it, same as calling `rb_str_modify()`) if called on a frozen string, because it makes little sense, I think "locking a string" only makes sense for mutable strings.
The alternative would be to noop on frozen strings for rb_str_locktmp() and rb_str_unlocktmp(), I think that's susprising, and potentially hiding more bugs, e.g. if one by mistake tries to mutate the RSTRING_PTR() or so.
Any attempt to mutate a frozen string should fail, so might as well fail early.
--
https://bugs.ruby-lang.org/
Issue #20452 has been reported by Earlopain (A S).
----------------------------------------
Bug #20452: Ruby 3.3 on Alpine Linux results in a relatively shallow SystemStackError exception
https://bugs.ruby-lang.org/issues/20452
* Author: Earlopain (A S)
* Status: Open
* ruby -v: 3.3.1
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
This is a redo of https://bugs.ruby-lang.org/issues/14387, reported against a non-eol version. The same issue still applies on recent rubies, though I personally only have tested 3.3 and 3.2:
```rb
n = 100000
res = {}
1.upto(n).to_a.inject(res) do |r, i|
r[i] = {}
end
def f(x)
x.each_value { |v| f(v) }
end
f(res)
```
The patch from https://bugs.ruby-lang.org/issues/14387#note-13 still works, though as per https://bugs.ruby-lang.org/issues/14387#note-16 it can't be accepted as is.
With the patch applied:
```
(irb):8:in `block in f': stack level too deep (SystemStackError)
from (irb):8:in `each_value'
from (irb):8:in `f'
from (irb):8:in `block in f'
from (irb):8:in `each_value'
from (irb):8:in `f'
from (irb):8:in `block in f'
from (irb):8:in `each_value'
from (irb):8:in `f'
... 11549 levels...
from /usr/local/lib/ruby/gems/3.3.0/gems/irb-1.11.0/exe/irb:9:in `<top (required)>'
from /usr/local/bin/irb:25:in `load'
from /usr/local/bin/irb:25:in `<main>'
```
Without the patch:
```
(irb):8:in `each_value': stack level too deep (SystemStackError)
from (irb):8:in `f'
from (irb):8:in `block in f'
from (irb):8:in `each_value'
from (irb):8:in `f'
from (irb):8:in `block in f'
from (irb):8:in `each_value'
from (irb):8:in `f'
from (irb):8:in `block in f'
... 286 levels...
from /usr/local/lib/ruby/gems/3.3.0/gems/irb-1.11.0/exe/irb:9:in `<top (required)>'
from /usr/local/bin/irb:25:in `load'
from /usr/local/bin/irb:25:in `<main>'
```
To reproduce, I have removed lines 102-105 from https://github.com/docker-library/ruby/blob/f5753434bb23041dd9913bb7b650e7b…. Check out the previous ticket for more information, there are a few conversations about possible solutions.
--
https://bugs.ruby-lang.org/
Issue #20009 has been reported by ippachi (Kazuya Hatanaka).
----------------------------------------
Bug #20009: Marshal.load raises exception when load dumped class include non-ASCII
https://bugs.ruby-lang.org/issues/20009
* Author: ippachi (Kazuya Hatanaka)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
## Reproduction code
```ruby
class Cクラス; end
Marshal.load(Marshal.dump(Cクラス))
```
## Actual result
```
<internal:marshal>:34:in `load': undefined class/module C\xE3\x82\xAF\xE3\x83\xA9\xE3\x82\xB9 (ArgumentError)
from marshal.rb:2:in `<main>'
```
## Expected result
Returns `Cクラス`
## Impacted area
An exception is raised in Rails under the following conditions
* minitest is used with default settings
* Parallel execution with parallelize
* test class names contain non-ASCII characters
The default parallelization uses DRb, and Marshal is used inside DRb.
## Other
After trying various things, I thought I could fix it by making `rb_path_to_class` support strings containing non-ASCII characters, but I couldn't find anything more than that.
--
https://bugs.ruby-lang.org/
Issue #21048 has been reported by Earlopain (Earlopain _).
----------------------------------------
Bug #21048: [Prism] rescue in modifier form with condition behaves differently
https://bugs.ruby-lang.org/issues/21048
* Author: Earlopain (Earlopain _)
* Status: Open
* ruby -v: 3.4.1
* Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
With the following code there is a discrepancy in how prism and parse.y consider precedence:
```rb
$called = false
def foo
$called = true
end
foo rescue nil if false
puts "Called: #{$called}"
```
Prism interprets it as `(foo rescue nil) if false`, not calling the method. `parse.y` does `foo rescue (nil if false)` since at least Ruby 2.0
```sh
$ ruby -v
ruby 3.5.0dev (2025-01-19T12:44:20Z master f27ed98eff) +PRISM [x86_64-linux]
$ ruby code.rb
Called: true
$ ruby --parser=parse.y code.rb
Called: false
```
--
https://bugs.ruby-lang.org/
Issue #20631 has been reported by hsbt (Hiroshi SHIBATA).
----------------------------------------
Bug #20631: Build failure with Xcode 16 beta and macOS 15 (Sequoia) Beta
https://bugs.ruby-lang.org/issues/20631
* Author: hsbt (Hiroshi SHIBATA)
* Status: Open
* Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
I found some issues with Xcode 16 beta and macOS 15 (Sequoia) Beta.
`TestRubyOptions` and `TestVMDump` are failed with them.
full log: https://gist.github.com/hsbt/378fe2ba47e734f2562ba9a154e1795b
I'm not sure why macOS require `sudo` command for that.
----
And I and @katei also found the regression of https://bugs.ruby-lang.org/issues/18912#note-15.
```
>> pid = fork { p File.realpath "/" }
>> Process.waitpid(pid)
objc[82573]: +[NSPlaceholderMutableString initialize] may have been in progress in another thread when fork() was called.
objc[82573]: +[NSPlaceholderMutableString initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.
=> 82573
```
--
https://bugs.ruby-lang.org/
Issue #20600 has been reported by shugo (Shugo Maeda).
----------------------------------------
Misc #20600: @ruby-lang.org has been migrated to Cloudflare
https://bugs.ruby-lang.org/issues/20600
* Author: shugo (Shugo Maeda)
* Status: Open
----------------------------------------
@ruby-lang.org has been migrated to Cloudflare, because ruby-lang.org was migrated from Google Domains to Squarespace, and Squarespace's e-mail forwarding doesn't work with custom MX records including subdomains:(
I and Shibata-san are adding e-mail routing rules on Cloudflare, so if you have an e-mail address on ruby-lang.org, please verify the e-mail routing address when receiving an email from Cloudflare.
--
https://bugs.ruby-lang.org/
Issue #20861 has been reported by tenderlovemaking (Aaron Patterson).
----------------------------------------
Feature #20861: Add an environment variable for tuning the default thread quantum
https://bugs.ruby-lang.org/issues/20861
* Author: tenderlovemaking (Aaron Patterson)
* Status: Open
----------------------------------------
The default thread quantum is currently [hard coded at 100ms](https://github.com/ruby/ruby/blob/c7708d22c33040a74ea7ac683bf7407d37…. This can impact multithreaded systems that are trying to process Ruby level CPU bound work at the same time as IO work.
I would like to add an environment variable `RUBY_THREAD_DEFAULT_QUANTUM_MS` that allows users to specify the default thread quantum (in milliseconds) via an environment variable. It defaults to our current default of 100ms. I've submitted the patch [here](https://github.com/ruby/ruby/pull/11981).
Here is a Ruby program to demonstrate the problem:
```ruby
def measure
x = Process.clock_gettime(Process::CLOCK_MONOTONIC)
yield
Process.clock_gettime(Process::CLOCK_MONOTONIC) - x
end
def fib(n)
if n < 2
n
else
fib(n-2) + fib(n-1)
end
end
# find fib that takes ~500ms
fib_i = 50.times.find { |i| measure { fib(i) } >= 0.05 }
sleep_i = measure { fib(fib_i) }
threads = [
Thread.new {
100.times {
sleep(sleep_i)
# sometimes stalled waiting for fib's quantum to finish
}
puts "done 1"
},
Thread.new { 100.times { fib(fib_i) }; puts "done 2" },
]
# We expect the total time to be about 100 * sleep_i (~5 seconds) because
# theoretically the sleep thread could be done nearly completely in parallel to
# the fib thread.
#
# But because the `sleep` thread is iterating over the sleep call, it must wait
# for the `fib` thread to complete its quantum, before it can start the next iteration.
#
# This means each sleep iteration could take up to `sleep_i + 100ms`
#
# We're calling that stalled time "waste"
total = measure { threads.each(&:join) }
waste = total - (sleep_i * 100)
p TOTAL: total, WASTE: waste
```
The program has two threads. One thread is using CPU time by computing `fib` in a loop. The other thread is simulating IO time by calling `sleep` in a loop. When the `sleep` call completes, it can stall, waiting for the quantum in the fib thread to expire. That means that each iteration on sleep can actually take `sleep time + thread quantum`, or in this case ~600ms when we expected it to only take ~500ms.
Ideally, the above program would take `500ms * 100` since all `sleep` calls should be able to execute in parallel with the `fib` calls. Of course this isn't true because the sleep thread must acquire the GVL before it can continue the next iteration, so there will always be _some_ overhead. This feature is for allowing people to tune that overhead.
If we run this program with the default quantum the output looks like this:
```
$ ./miniruby -v fibtest.rb
ruby 3.4.0dev (2024-11-01T14:49:50Z quantum-computing c7708d22c3) +PRISM [arm64-darwin24]
done 2
done 1
{TOTAL: 12.672821999993175, WASTE: 4.960721996147186}
```
The output shows that our program spent about 5 seconds stalled, waiting to acquire the GVL.
With this patch we can lower the default quantum, and the output is like this:
```
$ RUBY_THREAD_DEFAULT_QUANTUM_MS=10 ./miniruby -v fibtest.rb
ruby 3.4.0dev (2024-11-01T22:06:35Z quantum-computing 087500643d) +PRISM [arm64-darwin24]
done 2
done 1
{TOTAL: 8.898526000091806, WASTE: 1.4168260043952614}
```
Specifying the ENV to change the quantum to 10ms lowered our waste in the program to ~1.4 seconds.
It's common for web applications to do mixed CPU and IO bound tasks in threads (see the Puma webserver), so it would be great if there was a way to customize the thread quantum depending on your application's workload.
--
https://bugs.ruby-lang.org/