Issue #19542 has been reported by hanazuki (Kasumi Hanazuki).
----------------------------------------
Bug #19542: Operations on zero-sized IO::Buffer are raising
https://bugs.ruby-lang.org/issues/19542
* Author: hanazuki (Kasumi Hanazuki)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
I found that IO::Buffer of zero length is not cloneable.
```
% ruby -v
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
% ruby -e 'p IO::Buffer.for("").dup'
-e:1:in `initialize_copy': The buffer is not allocated! (IO::Buffer::AllocationError)
from -e:1:in `initialize_dup'
from -e:1:in `dup'
from -e:1:in `<main>'
% ruby -e 'p IO::Buffer.new(0).dup'
-e:1: warning: IO::Buffer is experimental and both the Ruby and C interface may change in the future!
-e:1:in `initialize_copy': The buffer is not allocated! (IO::Buffer::AllocationError)
from -e:1:in `initialize_dup'
from -e:1:in `dup'
from -e:1:in `<main>'
```
It seems `IO::Buffer.new(0)` allocates no memory for buffer on object creation and thus prohibits reading from or writing to it. So `#dup` method copying zero bytes into the new IO::Buffer raises the exception.
Empty buffers, however, often appear in corner cases of usual operations (encrypting an empty string, encoding an empty list of items into binary, etc.) and it would be easy if such cases could be handled consistently.
Other operations on NULL IO::Buffers are also useful but currently raising.
```
IO::Buffer.new(0) <=> IO::Buffer.new(1)
IO::Buffer.new(0).each(:U8).to_a
IO::Buffer.new(0).get_values([], 0)
IO::Buffer.new(0).set_values([], 0, [])
```
I'm not sure this is a bug or by design, but at least I don't want cloning and comparison to raise.
--
https://bugs.ruby-lang.org/
Issue #19461 has been reported by ioquatix (Samuel Williams).
----------------------------------------
Bug #19461: Time.local performance tanks in forked process (on macOS only?)
https://bugs.ruby-lang.org/issues/19461
* Author: ioquatix (Samuel Williams)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
The following program demonstrates a performance regression in forked child processes when invoking `Time.local`:
```ruby
require 'benchmark'
require 'time'
def sir_local_alot
result = Benchmark.measure do
10_000.times do
tm = ::Time.local(2023)
end
end
$stderr.puts result
end
sir_local_alot
pid = fork do
sir_local_alot
end
Process.wait(pid)
```
On Linux the performance is similar, but on macOS, the performance is over 100x worse on my M1 laptop.
--
https://bugs.ruby-lang.org/
Issue #19246 has been reported by thomthom (Thomas Thomassen).
----------------------------------------
Bug #19246: Rebuilding the loaded feature index much slower in Ruby 3.1
https://bugs.ruby-lang.org/issues/19246
* Author: thomthom (Thomas Thomassen)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
Some background to this issue: (This is a case that is unconventional usage of Ruby, but I hope you bear with me.)
We ship the Ruby interpreter with our desktop applications for plugin support in our application (SketchUp).
One feature we have had since, at least 2006 (maybe earlier-hard to track history beyond that) is that we had a custom alternate `require` method: `Sketchup.require`. This allows the users of our API to load encrypted Ruby files.
This originally used `rb_provide` to add the path to the encrypted file into the list of loaded feature. However, somewhere between Ruby 2.2 and 2.5 there was some string optimisations made and the function `rb_provide` would not use a copy of the string passed to it. Instead it just held on to a pointer reference. In our case that string came from user-land, being passed in from `Sketchup.require` and would eventually be garbage collected and cause access violation crashes.
To work around that we changed our custom `Sketchup.require` to push to `$LOADED_FEATURES` directly. There was a small penalty to the index being rebuilt after that, but it was negligible.
Recently we tried to upgrade the Ruby interpreter in our application from 2.7 to 3.1 and found a major performance reduction when using our `Sketchup.require. As in, a plugin that would load in half a second would now spend 30 seconds.
From https://bugs.ruby-lang.org/issues/18452 it sounds like there is _some_ expected extra penalty due to changes in how the index is built. But should it really be this much?
Example minimal repro to simulate the issue:
```
# frozen_string_literal: true
require 'benchmark'
iterations = 200
foo_files = iterations.times.map { |i| "#{__dir__}/tmp/foo-#{i}.rb" }
foo_files.each { |f| File.write(f, "") }
bar_files = iterations.times.map { |i| "#{__dir__}/tmp/bar-#{i}.rb" }
bar_files.each { |f| File.write(f, "") }
biz_files = iterations.times.map { |i| "#{__dir__}/tmp/biz-#{i}.rb" }
biz_files.each { |f| File.write(f, "") }
Benchmark.bm do |x|
x.report('normal') {
foo_files.each { |file|
require file
}
}
x.report('loaded_features') {
foo_files.each { |file|
require file
$LOADED_FEATURES << "#{file}-fake.rb"
}
}
x.report('normal again') {
biz_files.each { |file|
require file
}
}
end
```
```
C:\Users\Thomas\SourceTree\ruby-perf>ruby27.bat
ruby 2.7.4p191 (2021-07-07 revision a21a3b7d23) [x64-mingw32]
C:\Users\Thomas\SourceTree\ruby-perf>ruby test-require.rb
user system total real
normal 0.000000 0.031000 0.031000 ( 0.078483)
loaded_features 0.015000 0.000000 0.015000 ( 0.038759)
normal again 0.016000 0.032000 0.048000 ( 0.076940)
```
```
C:\Users\Thomas\SourceTree\ruby-perf>ruby30.bat
ruby 2.7.4p191 (2021-07-07 revision a21a3b7d23) [x64-mingw32]
C:\Users\Thomas\SourceTree\ruby-perf>ruby test-require.rb
user system total real
normal 0.000000 0.031000 0.031000 ( 0.074733)
loaded_features 0.032000 0.000000 0.032000 ( 0.038898)
normal again 0.000000 0.047000 0.047000 ( 0.076343)
```
```
C:\Users\Thomas\SourceTree\ruby-perf>ruby31.bat
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x64-mingw-ucrt]
C:\Users\Thomas\SourceTree\ruby-perf>ruby test-require.rb
user system total real
normal 0.016000 0.031000 0.047000 ( 0.132633)
loaded_features 1.969000 11.500000 13.469000 ( 18.395761)
normal again 0.031000 0.125000 0.156000 ( 0.249130)
```
Right now we're exploring options to deal with this. Because the performance degradation is a blocker for us upgrading. We also have 16 years of plugins created by third party developer that makes it impossible for us to drop this feature.
Some options as-is, none of which are ideal:
1. We revert to using `rb_provide` but ensure the string passed in is not owned by Ruby, instead building a list of strings that we keep around for the duration of the application process. The problem is that some of our plugin developers have on occasion released plugins that will touch `$LOADED_FEATURES`, and if such a plugin is installed on a user machine it might cause the application to become unresponsive for minutes. The other non-ideal issue with using `rb_provide` is that we're also using that in ways it wasn't really intended (from that I understand). And it's not an official API?
2. We create a separate way for our `Sketchup.require` to keep track of it's loaded features, but then that would diverge even more from the behaviour of `require`. Replicating `require` functionality is not trivial and would be prone to subtle errors and possible diverge. It also doesn't address our issue that there is code out there in existing plugins that touches `$LOADED_FEATURES`. (And it's not something we can just ask people to clean up. From previous experience old versions stick around for a long time and is very hard to purge from circulation.)
I have two questions for the Ruby mantainers:
1. Would it be reasonable to see an API for adding/removing/checking `$LOADED_FEATURE` that would allow for a more ideal implementation of a custom `require` functionality?
2. Is the performance difference in rebuilding the loaded feature index really expected to be as high as what we're seeing? An increase of nearly 100 times? Is there something there that might be addressed to make the rebuild to be less expensive against? (This would really help to address our challenges with third party plugins occasionally touching the global.)
--
https://bugs.ruby-lang.org/
Issue #19387 has been reported by luke-gru (Luke Gruber).
----------------------------------------
Bug #19387: Issue with ObjectSpace.each_objects not returning IO objects after starting a ractor
https://bugs.ruby-lang.org/issues/19387
* Author: luke-gru (Luke Gruber)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
```ruby
r = Ractor.new do
receive # block, the problem is not the termination of the ractor but the starting
end
ObjectSpace.each_object(IO) { |io|
p io # we get no objects
}
```
--
https://bugs.ruby-lang.org/
Issue #19717 has been reported by ioquatix (Samuel Williams).
----------------------------------------
Bug #19717: `ConditionVariable#signal` is not fair when the wakeup is consistently spurious.
https://bugs.ruby-lang.org/issues/19717
* Author: ioquatix (Samuel Williams)
* Status: Open
* Priority: Normal
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
For background, see this issue <https://github.com/socketry/async/issues/99>.
It looks like `ConditionVariable#signal` is not fair, if the calling thread immediately reacquires the resource.
I've given a detailed reproduction here as it's non-trivial: <https://github.com/ioquatix/ruby-condition-variable-timeout>.
Because the spurious wakeup occurs, the thread is pushed to the back of the waitq, which means any other waiting thread will acquire the resource, and that thread will perpetually be at the back of the queue.
I believe the solution is to change `ConditionVarialbe#signal` should only remove the thread from the waitq if it's possible to acquire the lock. Otherwise it should be left in place, so that the order is retained, this should result in fair scheduling.
--
https://bugs.ruby-lang.org/
Issue #19409 has been reported by luke-gru (Luke Gruber).
----------------------------------------
Bug #19409: Object's shape is reset after a ractor move
https://bugs.ruby-lang.org/issues/19409
* Author: luke-gru (Luke Gruber)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
I believe an object should have the same shape after being moved from 1 ractor to another.
```ruby
class Obj
attr_accessor :a, :b, :c, :d
def initialize
@a = 1
@b = 2
@c = 3
end
end
r = Ractor.new do
obj = receive
#p RubyVM::Shape.of(obj)
obj.d = 4
p obj.a, obj.b, obj.c, obj.d # gets wrong values due to object shape id being reset on object
end
obj = Obj.new
#p RubyVM::Shape.of(obj)
r.send(obj, move: true)
r.take
```
--
https://bugs.ruby-lang.org/
Issue #19411 has been reported by luke-gru (Luke Gruber).
----------------------------------------
Bug #19411: GC issue with moved objects
https://bugs.ruby-lang.org/issues/19411
* Author: luke-gru (Luke Gruber)
* Status: Open
* Priority: Normal
* ruby -v: 3.2.0
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
This crashes:
```ruby
class Obj
def initialize
@obj = 3
end
end
GC.stress = true
r = Ractor.new do
obj = receive
p obj
end
obj = Obj.new
r.send(obj, move: true)
r.take
```
It only crashes with nested objects, if you remove the ivar set in `initialize` it works fine. Maybe missing `RB_GC_GUARD`?
--
https://bugs.ruby-lang.org/
Issue #19375 has been reported by luke-gru (Luke Gruber).
----------------------------------------
Bug #19375: File objects are currently shareable
https://bugs.ruby-lang.org/issues/19375
* Author: luke-gru (Luke Gruber)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
I don't know the internals of file.c but I don't think files are thread-safe.
--
https://bugs.ruby-lang.org/
Issue #19333 has been reported by ioquatix (Samuel Williams).
----------------------------------------
Feature #19333: Setting (Fiber Local|Thread Local|Fiber Storage) to nil should delete value in order to avoid memory leaks.
https://bugs.ruby-lang.org/issues/19333
* Author: ioquatix (Samuel Williams)
* Status: Open
* Priority: Normal
----------------------------------------
As it stands, Fiber Locals, Thread Locals and Fiber Storage have no way of deleting key-value associations.
```ruby
100.times do |i|
name = :"variable-#{i}"
Thread.current[name] = 10
end
```
Because of this, dynamically generated associations can leak over time. This is worse for things like Threads that might be pooled (or maybe an argument against user-space pooling).
In any case, having a way to delete those associations would allow application code to at least delete the associations when they no longer make sense.
I propose that assigning `nil` to "locals" or "storage" should effectively delete them.
e.g.
```ruby
100.times do |i|
name = :"variable-#{i}"
Thread.current[name] = 10
Thread.current[name] = nil # delete association
end
```
A more invasive alternative would be to define new interfaces like `Thread::Local`, `Fiber::Local` and `Fiber::Storage::Local` (or something) which correctly clean up on GC.
--
https://bugs.ruby-lang.org/
Issue #19442 has been reported by eightbitraptor (Matthew Valentine-House).
----------------------------------------
Bug #19442: Remove USE_RINCGC flag
https://bugs.ruby-lang.org/issues/19442
* Author: eightbitraptor (Matthew Valentine-House)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
[GitHub PR #7317](https://github.com/ruby/ruby/pull/7313)
Ruby doesn't compile when USE_RINCGC is disabled. It's also not being tested in CI. As @nobu has pointed out in comments on the PR, fixing it is simple.
I think there are 2 approaches we could take:
1. Remove `USE_RINCGC` entirely and always run with incremental GC enabled. We have a precedent for this: `USE_RGENGC=0` was removed [in this commit](https://github.com/ruby/ruby/commit/62c2b8c74e47652fc5bbaf6150f4acd… almost 3 years ago. `USE_RINCGC=0` is in a similar state. It has been broken for almost a year (since [this commit](https://github.com/ruby/ruby/commit/dde164e968e382d50b07ad455946888…), and [disabled in CI for more than 2 years](https://github.com/ruby/ruby/commit/46d3ea2c2569e2e5a9ee3e7e206f07f0….
2. We could fix `USE_RINCGC`. If we do this we should re-enable the CI task so that we continue to keep it working in the future.
I am very much in favour of option 1, because I don't think this flag is being actively used, and removing it will simplify the code and reduce the cognitive overhead of working with the GC. However I am happy to defer to the team here.
--
https://bugs.ruby-lang.org/