Issue #20165 has been reported by codekitchen (Brian Palmer).
----------------------------------------
Bug #20165: Ractors moving a Struct breaks beyond the first 3 fields
https://bugs.ruby-lang.org/issues/20165
* Author: codekitchen (Brian Palmer)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
Experimenting with Ractors on ruby 3.3.0, and I'm seeing a bug where if you move a struct between ractors, all but the first 3 fields are set to nil.
``` ruby
Foo = Struct.new(:a,:b,:c,:d,:e,:f)
r = Ractor.new {
foo = Foo[0,0,0,0,0,0]
p foo
Ractor.yield(foo, move: true)
}
p r.take
```
```
❯ ruby -v
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
❯ ruby rbug.rb
rbug.rb:3: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
#<struct Foo a=0, b=0, c=0, d=0, e=0, f=0>
#<struct Foo a=0, b=0, c=0, d=nil, e=nil, f=nil>
```
This seems specific to moving, if I set `move: false` the struct makes it across OK.
--
https://bugs.ruby-lang.org/
Issue #20176 has been reported by chucke (Tiago Cardoso).
----------------------------------------
Feature #20176: Array#pack: support offset kwarg
https://bugs.ruby-lang.org/issues/20176
* Author: chucke (Tiago Cardoso)
* Status: Open
* Priority: Normal
----------------------------------------
I was changing some code to use ruby 3.3's new `buffer` kwarg (great addition btw!) when using `Array#pack`. There are a few cases however, where I could perform the change, as not all my usages rely on appending; in some, I'm actually prepending it.
To solve this, I'd like to propose the `offset` kwarg, which declares where to add the resulting string. picking up on example from the docs:
[65, 66].pack('C*', buffer: 'foo') # => "fooAB"
[65, 66].pack('C*', buffer: 'foo', offset: 0) # => "ABfoo"
[65, 66].pack('C*', buffer: 'foo', offset: 1) # => "fABoo"
--
https://bugs.ruby-lang.org/
Issue #20175 has been reported by kiskoza (Zsolt Kozaroczy).
----------------------------------------
Bug #20175: Broken File.dirname(__FILE__) in eval blocks
https://bugs.ruby-lang.org/issues/20175
* Author: kiskoza (Zsolt Kozaroczy)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-linux]
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
Since #19755 eval use caller location by default, however, it broke `File.dirname(__FILE__)` in some cases.
```ruby
# eval.rb
eval('puts file: __FILE__, dirname: File.dirname(__FILE__)')
```
Up to Ruby 3.2.2 it gave back the same results, even if it wasn't technically correct in some cases
```bash
## Ruby 3.2.2
ruby eval.rb
# {:file=>"(eval)", :dirname=>"."}
ruby ./eval.rb
# {:file=>"(eval)", :dirname=>"."}
cd folder && ruby ../eval.rb
# {:file=>"(eval)", :dirname=>"."}
# This one is not pointing to the right directory, but still returns a valid path
ruby /Codes/eval.rb
# {:file=>"(eval)", :dirname=>"."}
# This one is not pointing to the right directory, but still returns a valid path
```
In Ruby 3.3.0 (introduced in commit:43a5c19135), it gives back different paths, trying to point to the right directory, but it has the `(eval at ` prefix which makes it broken for codes expecting a valid path.
```bash
## Ruby 3.3.0
ruby eval.rb
# {:file=>"(eval at eval.rb:1)", :dirname=>"."}
ruby ./eval.rb
# {:file=>"(eval at ./eval.rb:1)", :dirname=>"(eval at ."}
# Broken path
cd folder && ruby ../eval.rb
# {:file=>"(eval at ../eval.rb:1)", :dirname=>"(eval at .."}
# This one is trying to point to the right directory, but it has a broken syntax
ruby /Codes/eval.rb
# {:file=>"(eval at /Codes/eval.rb:1)", :dirname=>"(eval at /Codes"}
# This one is trying to point to the right directory, but it has a broken syntax
```
I was able to reproduce it on current master as well.
--
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 #20057 has been reported by kjtsanaktsidis (KJ Tsanaktsidis).
----------------------------------------
Feature #20057: Change behaviour of rb_register_postponed_job for Ruby 3.3
https://bugs.ruby-lang.org/issues/20057
* Author: kjtsanaktsidis (KJ Tsanaktsidis)
* Status: Open
* Priority: Normal
----------------------------------------
This ticket is to discuss some changes to `rb_register_postponed_job` that @ko1 and myself propose to make for Ruby 3.3. The motivation for this work is to fix a bug in the current implementation, which can cause the registered functions to be called with the wrong `data` argument (https://bugs.ruby-lang.org/issues/19991).
There's a long discussion on the associated PR (https://github.com/ruby/ruby/pull/9041) but in the end we came to the conclusion that the best way to fix this bug involved actually changing the current semantics of `rb_register_postponed_job`. I'm opening this issue to get feedback on this approach and to see if anybody knows of a reason why we should not release this for Ruby 3.3.
## Current behaviour in Ruby 3.2
Currently, Ruby has two functions for interacting with postponed jobs. These jobs can be enqueued from anywhere (including signal handlers), and will be executed next time Ruby checks for `RUBY_VM_CHECK_INTS()`.
* `rb_postponed_job_register(func, data)`: Schedules `func(data)` to be executed the next time `RUBY_VM_CHECK_INTS` is checked.
* `rb_postponed_job_register_once(func, data)`: Works like `rb_postponed_job_register`, _except_ if `func` is already scheduled to be executed (either with this `data` or with different `data`), in which case it does nothing.
The postponed jobs are stored in a fixed sized array (of length 1024), so it's possible that enqueuing them could fail if the buffer is full. In this case, they signal this by returning `0` (otherwise, they return `1` for successful enqueue or `2` because `rb_postponed_job_register_once` did nothing because `func` was already in the queue).
Unfortunately, as I mentioned before, the implementation of these functions are subject to a race condition because `func` and `data` are not written into the postponed job buffer together atomically (they are two separate variables and CPUs tend not to have double-word atomic instructions). Again, see https://bugs.ruby-lang.org/issues/19991 for the full details.
## What we have done
Whilst working on this issue, we had a look at all of the in-the-wild usages of these APIs on rubygems. The only real usage of these APIs is for profiling tools, and the following was true for essentially all of them:
* Each gem only is registering a single callback function,
* Almost all of the usages either make no use of the `data` argument at all, or pass some kind of never-changing global context into it.
* There are only a very small handful of gems using these APIs at all
Thus, we concluded that the current behaviour of allowing scheduling and execution of arbitrary `(func, data)` pairs is actually not really needed. Instead, we could offer a more limited API which would meet the needs of all current users, whilst making it easy to avoid the race conditions in the current implementation.
The new API is as follows:
* `rb_postponed_job_preregister(func, data)`: This function registers `func`/`data` into a small, fixed-size table, and return a handle to this registration. Subsequent calls to this function with the same `func` will return the same handle, and overwrite the `data` with new data if it is different. The size of the table is 32 entries on most systems, which is still enough to use literally every gem on rubygems that actually uses these APIs at the same time. The intention is that libraries would call this function in their initialization routines, storing the handle for later.
* `rb_postponed_job_trigger(handle)`: This function takes the handle from `rb_postponed_job_preregister` and schedules it for execution the next time `RUBY_VM_CHECK_INTS` is called. If the handle is already scheduled, this will not cause it to be scheduled twice; each `func` can only be called a maximum of one time for each call to `RUBY_VM_CHECK_INTS`, essentially.
All of the usages of the old `rb_postponed_job_register{,_once}` functions in the Ruby tree have been replaced by calls to the above two functions, and these two old functions have been marked with the deprecated attribute. They have also been re-implemented in terms of the new functions; both `rb_postponed_job_register` and `rb_postponed_job_register_once` are now both equivalent to `rb_postponed_job_trigger(rb_postponed_job_prereigster(func, data))`. This means that:
* `rb_postponed_job_register` now works like `rb_postponed_job_register_once` i.e. `func` can only be executed one time per `RUBY_VM_CHECK_INTS`, no matter how many times it is registered
* They are also called with the _last_ `data` to be registered, not the first (which is how `rb_postponed_job_register_once` previously worked)
I verified that stackprof still builds & works correctly with the new implementation of `rb_postponed_job_register`.
## What else we tried
I tried a couple of things to keep the current semantics of `rb_postponed_job_register{,_once}` intact, without introducing new APIs.
* First, I tried protecting postponed job buffer by masking signals around the critical section & using a POSIX semaphore instead of a pthread mutex: https://github.com/ruby/ruby/pull/8856. However, there was a concern that this would be too slow (since `RUBY_VM_CHECK_INTS` is called very often, and both the semaphore and the signal mask require calling into the kernel).
* Then, I implemented a lock-free ringbuffer to store the postponed job queue: https://github.com/ruby/ruby/compare/master...KJTsanaktsidis:ruby:old_circu…. However, the concern with this implementation was that it was too complex.
## Ruby 3.3
As of right now, we have merged these changes (from https://github.com/ruby/ruby/pull/9041), and @ko1 plans for them to go out in 3.3-rc1. The point of opening this issue is to ask: does anybody foresee any problem with our approach?
--
https://bugs.ruby-lang.org/
Issue #20101 has been reported by kjtsanaktsidis (KJ Tsanaktsidis).
----------------------------------------
Bug #20101: rb_file_open and rb_io_fdopen don't perform CRLF -> LF conversion when encoding is set
https://bugs.ruby-lang.org/issues/20101
* Author: kjtsanaktsidis (KJ Tsanaktsidis)
* Status: Open
* Priority: Normal
* Assignee: kjtsanaktsidis (KJ Tsanaktsidis)
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
When opening a file with `File.open`, as long as `'b'` is not set in the mode, Ruby will perform CRLF -> LF conversion on Windows when reading text files - i.e. CRLF line endings on disk get converted to Ruby strings with only "\n" in them. If you explicitly set the encoding with `IO#set_encoding`, this still works properly.
If you open the file in C with either the `rb_io_fdopen` or `rb_file_open` APIs in text mode, CRLF -> LF conversion also works. However, if you then call `IO#set_encoding` on this file, the CRLF -> LF conversion stops happening.
Concretely, this means that the conversion doesn't happen in the following circumstances:
* When loading ruby files with require (that calls `rb_io_fdopen`)
* When parsing ruuby files with RubyVM::AbstractSyntaxTree (that calls `rb_file_open`).
This then causes the ErrorHighlight tests to fail on windows if git has checked them out with CRLF line endings - the error messages it's testing wind up with literal \r\n sequences in them because the iseq text from the parser contains un-newline-converted strings.
This seems to happen because, in `File.open`, the file's encflags get the flag `ECONV_DEFAULT_NEWLINE_DECORATOR` in `rb_io_extract_modeenc`; however, this method isn't called for `rb_io_fdopen` or `rb_file_open`, so `encflags` doesn't get set to `ECONV_DEFAULT_NEWLINE_DECORATOR`. Without that flag, the underlying file descriptor's mode gets changed to binary mode by the `NEED_NEWLINE_DECORATOR_ON_READ_CHECK` macro.
--
https://bugs.ruby-lang.org/
Issue #20170 has been reported by kddnewton (Kevin Newton).
----------------------------------------
Misc #20170: Drop support for GCC < 11
https://bugs.ruby-lang.org/issues/20170
* Author: kddnewton (Kevin Newton)
* Status: Open
* Priority: Normal
----------------------------------------
Right now, CI compiles everything from GCC 7+. However, GCC 7-10 are all end-of-life and no longer supported. We should drop support for the end-of-life compilers.
--
https://bugs.ruby-lang.org/
Issue #20163 has been reported by garrison (Garrison Jensen).
----------------------------------------
Feature #20163: Introduce #bit_count method on Integer
https://bugs.ruby-lang.org/issues/20163
* Author: garrison (Garrison Jensen)
* Status: Open
* Priority: Normal
----------------------------------------
This feature request is to implement a method called #bit_count on Integer that returns the number of ones in the binary representation of the absolute value of the integer.
```
n = 19
n.bit_count #=> 3
(-n).bit_count #=> 3
```
This is often useful when you use an integer as a bitmask and want to count how many bits are set.
This would be equivalent to
```
n.to_s(2).count("1")
```
However, this can be outperformed by
```
def bit_count(n)
count = 0
while n > 0
n &= n - 1 # Flip the least significant 1 bit to 0
count += 1
end
count
end
```
I think this would be a useful addition because it would fit alongside the other bit-related methods defined on integer: `#bit_length,` `#allbits?`, `#anybits?`, `#nobits?`. Also, when working with bitmasks, a minor upgrade to performance often results in a significant improvement.
Similar methods from other languages:
https://docs.python.org/3/library/stdtypes.html#int.bit_counthttps://doc.rust-lang.org/std/primitive.i32.html#method.count_ones
--
https://bugs.ruby-lang.org/
Issue #19965 has been reported by mame (Yusuke Endoh).
----------------------------------------
Feature #19965: Make the name resolution interruptible
https://bugs.ruby-lang.org/issues/19965
* Author: mame (Yusuke Endoh)
* Status: Open
* Priority: Normal
----------------------------------------
## Problem
Currently, Ruby name resolution is not interruptible.
```
$ cat /etc/resolv.conf
nameserver 198.51.100.1
$ ./local/bin/ruby -rsocket -e 'Addrinfo.getaddrinfo("www.ruby-lang.org", 80)'
^C^C^C^C
```
If you set a non-responsive IP as the nameserver, you cannot stop `Addrinfo.getaddrinfo` by pressing Ctrl+C. Note that `Timeout.timeout` does not work either.
This is because there is no way to cancel `getaddrinfo(3)`.
## Proposal
I wrote a patch to make `getaddrinfo(3)` work in a separate pthread.
https://github.com/ruby/ruby/pull/8695
Whenever it needs name resolution, it creates a worker pthread, and executes `getaddrinfo(3)` in it.
The caller thread waits for the worker to complete.
When an interrupt occurs, the caller thread leaves stop waiting and leaves the worker pthread.
The detached worker pthread will exit after `getaddrinfo(3)` completes (or name resolution times out).
## Evaluation
By applying this patch, name resolution is now interruptible.
```
$ ./local/bin/ruby -rsocket -e 'pp Addrinfo.getaddrinfo("www.ruby-lang.org", 80)'
^C-e:1:in `getaddrinfo': Interrupt
from -e:1:in `<main>'
```
As a drawback, name resolution performance will be degraded.
```
10000.times { Addrinfo.getaddrinfo("www.ruby-lang.org", 80) }
# Before patch: 2.3 sec.
# After ptach: 3.0 sec.
```
However, I think that name resolution is typically short enough for the application's runtime. For example, the difference is small for the performance of `URI.open`.
```
100.times { URI.open("https://www.ruby-lang.org").read }
# Before patch: 3.36 sec.
# After ptach: 3.40 sec.
```
## Alternative approaches
I proposed using c-ares to resolve this issue (#19430). However, there was an opinion that it would be a problem that c-ares does not respect the platform-dependent own name resolution.
## Room for improvement
* Currently, this patch works only when pthread is available.
* It might be possible to force to stop the worker threads by using `pthread_cancel`. However, `pthread_cancel` with `getaddrinfo(3)` seems still premature; there seems to be a bug in glibc until recently: https://bugzilla.redhat.com/show_bug.cgi?id=1405071https://sourceware.org/bugzilla/show_bug.cgi?id=20975
* It would be more efficient to pool worker pthreads instead of creating them each time.
--
https://bugs.ruby-lang.org/
Issue #20171 has been reported by Mystorium (Josh Goldfarb).
----------------------------------------
Bug #20171: ri -i ruby interactive pressing tab does not autocomplete
https://bugs.ruby-lang.org/issues/20171
* Author: Mystorium (Josh Goldfarb)
* Status: Open
* Priority: Normal
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
When typing a Object or Object with a method, the tab button does not pull up autocomplete options.
This issue is not present in ruby 3.2.2
Ex
Type
- ri - i
- String.cap
Press.
- "Tab"
What is expected
- autocomplete options to populate
What is happening
- Nothing Happens when pressing tab.
--
https://bugs.ruby-lang.org/