Issue #19837 has been reported by kjtsanaktsidis (KJ Tsanaktsidis).
----------------------------------------
Bug #19837: Concurrent calls to Process.waitpid2 misbehave on Ruby 3.1 & 3.2
https://bugs.ruby-lang.org/issues/19837
* Author: kjtsanaktsidis (KJ Tsanaktsidis)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.1.4p236 (2023-07-26 revision a8670865c0) [arm64-darwin22]
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
On Ruby 3.1 & 3.2, if you have one thread blocked into a directed call to `Process.waitpid2` with a pid specified, a concurrent call to `Process.waitpid2 -1` will not be able to find & reap any other terminated child process, even one with a different pid that is not individually being waited on.
I've attached a Ruby program which should terminate but doesn't as a result of this bug, as well as a C program which demonstrates that the underlying syscalls (at least on Linux) do behave how you would expect. My reproduction creates two processes; a long-running process that does not exit, and a short one which does. There is a background thread calling `Process.waitpid2` on the long process. Then, a concurrent call to `Process.waitpid2 -1` does not notice that the short-running process has exited.
My `wait_bug.rb` program _does_ work properly and terminate on the current master branch of Ruby; I assume this is because all of the MJIT-related process management stuff with the waiting_pids & stuff has been cleaned up as part of the MJIT -> RJIT refactoring. Because of this, I'm not sure exactly how to make a patch; should I open a pair of PRs targeting the `ruby_3_2` and `ruby_3_1` branch?
Thanks!
---Files--------------------------------
wait_bug.c (1.55 KB)
wait_bug.rb (735 Bytes)
--
https://bugs.ruby-lang.org/
Issue #19869 has been reported by vo.x (Vit Ondruch).
----------------------------------------
Bug #19869: Mark skipped test somehow
https://bugs.ruby-lang.org/issues/19869
* Author: vo.x (Vit Ondruch)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.3.0dev (2023-09-05 master 7c8932365f) [x86_64-linux]
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
When Minitest was used to execute the Ruby test suite, skipped test were cleanly marked, e.g.:
~~~
[14036/25715] TestGemExtCmakeBuilder#test_self_build_has_makefile = s
~~~
However, with the test-unit the output is:
~~~
[14036/25715] TestGemExtCmakeBuilder#test_self_build_has_makefile = 0.01 s
~~~
This is problematic, because running test suite, I can see that some test were skipped:
~~~
25705 tests, 6108213 assertions, 0 failures, 0 errors, 99 skips
~~~
But I can't tell which. Is there a chance to fix this?
--
https://bugs.ruby-lang.org/
Issue #19868 has been reported by nobu (Nobuyoshi Nakada).
----------------------------------------
Bug #19868: `Process::Status` methods for compatibility with `Fixnum`
https://bugs.ruby-lang.org/issues/19868
* Author: nobu (Nobuyoshi Nakada)
* Status: Open
* Priority: Normal
* Assignee: matz (Yukihiro Matsumoto)
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Looking at commit:54274b8c65a0981f1c69055a1513ba3c614dd675, noticed that `pst_rshift` contains shift by a negative integer, an unspecified behavior in C99.
I think an exception would be ok for such argument, also we can suggest alternative methods to encourage the transition.
https://github.com/nobu/ruby/tree/process_status-fixnum-compatibility
--
https://bugs.ruby-lang.org/
Issue #19867 has been reported by iainbeeston (Iain Beeston).
----------------------------------------
Bug #19867: Unicode line and paragraph separator are not stripped
https://bugs.ruby-lang.org/issues/19867
* Author: iainbeeston (Iain Beeston)
* 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
----------------------------------------
Unicode newline and paragraph separators are not removed by any of the strip methods:
`"\u2028\u2029\u0000\t\n\v\f\r ".strip # => "\u2028\u2029"`
I would have expected `strip` (and `lstrip`, `rstrip`) to remove unicode whitespace as well. It looks like #7154 reported something similar but for regular expressions and way back In ruby 1.9.
I think that fixing this should be simple (just checking for `\x2028` and `\x2029` in ctype.h) but I'm not sure if it's supposed to behave this way or if changing it could introduce unexpected consequences.
--
https://bugs.ruby-lang.org/
Issue #19865 has been reported by ivoanjo (Ivo Anjo).
----------------------------------------
Bug #19865: Segfault when calling user signal handlers during VM shutdown
https://bugs.ruby-lang.org/issues/19865
* Author: ivoanjo (Ivo Anjo)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Howdy 👋! I work for Datadog [on the `ddtrace` gem](https://github.com/DataDog/dd-trace-rb) . I've found this issue while investigating a [customer crash report](https://github.com/DataDog/dd-trace-rb/issues/2980).
### Background
The original issue was found in a production app. A number of things need to be in play to cause it.
The [`ruby-odbc`](https://rubygems.org/gems/ruby-odbc) gem provides a way of accessing databases through the ODBC API. It wraps a database connection as a Data object, with a free function that, prior to freeing the native resources, disconnects from the database if the connection was still active.
Because disconnecting from the database is a blocking operation, the gem (reasonably, in my opinion), releases the global VM lock before disconnecting.
The trigger for the crash is:
1. The app in question used puma, and puma installs a `Signal.trap('TERM')`
2. The database object was still connected when the app started to shut down
3. A VM shutdown starts...
4. Half-way through shutdown, the VM received a SIGTERM signal, and queued it for processing
5. The VM calls the free function on all objects
6. The ruby-odbc gem sees there's an active database connection, and tries to release the GVL to call the blocking disconnect
7. Before releasing the GVL, the VM checks for pending interruptions
8. The VM tries to run the Ruby-level signal handler method half-way through VM shutdown, when you can no longer run Ruby code
9. Segfault
### How to reproduce (Ruby version & script)
I was able to reproduce on a minimal example on Ruby 3.2.2 (`ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]`) and recent master (`ruby 3.3.0dev (2023-08-17T07:30:01Z master d26b015e83) [x86_64-linux]`).
I've put the test-case up on github as well <https://github.com/DataDog/signal-bug-testcase>, but here's the important bits:
`signal-bug-testcase.rb`:
```ruby
require 'signal_bug_testcase'
Signal.trap("TERM") { puts "Hello, world" }
FOO = SignalBugTestcase.new
```
`signal_bug_testcase.c`:
```ruby
#include <ruby.h>
#include <ruby/thread.h>
#include <signal.h>
#include <unistd.h>
typedef struct { int dummy; } BugTestcase;
void *test_nogvl(void *unused) {
fprintf(stderr, "GVL released!\n");
return NULL;
}
static void bug_testcase_free(void* ptr) {
fprintf(stderr, "Free getting called! Sending signal...\n");
kill(getpid(), SIGTERM);
fprintf(stderr, "SIGTERM signal queued, trying to release GVL...\n");
rb_thread_call_without_gvl(test_nogvl, NULL, NULL, NULL);
fprintf(stderr, "After releasing GVL!\n");
free(ptr);
}
const rb_data_type_t bug_testcase_data_type = {
.wrap_struct_name = "SignalBugTestcase",
.function = { NULL, bug_testcase_free, NULL },
.flags = RUBY_TYPED_FREE_IMMEDIATELY
};
VALUE bug_testcase_alloc(VALUE klass) {
BugTestcase* obj = calloc(1, sizeof(BugTestcase));
return TypedData_Make_Struct(klass, BugTestcase, &bug_testcase_data_type, obj);
}
void Init_signal_bug_testcase(void) {
VALUE cBugTestcase = rb_define_class("SignalBugTestcase", rb_cObject);
rb_define_alloc_func(cBugTestcase, bug_testcase_alloc);
}
```
### Expectation and result
No segfault happens.
Interestingly, on Ruby 2.7, the VM exits half-way through but doesn't always segfault, but running it a few times always triggers the issue. On 3.2+ it crashes every time for me.
I suspect the right thing here is to no longer accept/try to run any Ruby-level signal handlers after VM shutdown starts.
Here's what I see with this test-case:
```
$ bundle exec ruby lib/signal-bug-testcase.rb
Free getting called! Sending signal...
SIGTERM signal queued, trying to release GVL...
lib/signal-bug-testcase.rb:3: [BUG] Segmentation fault at 0x0000000000000007
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]
-- Control frame information -----------------------------------------------
c:0003 p:---- s:0011 e:000010 CFUNC :puts
c:0002 p:0005 s:0006 e:000005 BLOCK lib/signal-bug-testcase.rb:3 [FINISH]
c:0001 p:0000 s:0003 E:0001a0 DUMMY [FINISH]
-- Ruby level backtrace information ----------------------------------------
lib/signal-bug-testcase.rb:3:in `block in <main>'
lib/signal-bug-testcase.rb:3:in `puts'
-- Machine register context ------------------------------------------------
RIP: 0x000070aa64cedbe7 RBP: 0x000070aa648e8fd0 RSP: 0x00007ffc5c057608
RAX: 0x0000000000004171 RBX: 0x00007ffc5c057630 RCX: 0x0000000000000001
RDX: 0x00007ffc5c057630 RDI: 0x0000000000000007 RSI: 0x0000000000004171
R8: 0x000000000000021b R9: 0x0000000000000000 R10: 0x000070aa63eff048
R11: 0x0000000000000000 R12: 0x000070aa648e8fd0 R13: 0x0000000000004171
R14: 0x0000000000000007 R15: 0x000070aa648e8ff0 EFL: 0x0000000000010202
-- C level backtrace information -------------------------------------------
ruby-3.2.2/lib/libruby.so.3.2(rb_print_backtrace+0xd) [0x70aa64d5bb5f] ruby-3.2.2/vm_dump.c:785
ruby-3.2.2/lib/libruby.so.3.2(rb_vm_bugreport) ruby-3.2.2/vm_dump.c:1080
ruby-3.2.2/lib/libruby.so.3.2(rb_bug_for_fatal_signal+0xf4) [0x70aa64b52164] ruby-3.2.2/error.c:813
ruby-3.2.2/lib/libruby.so.3.2(sigsegv+0x4d) [0x70aa64cab0fd] ruby-3.2.2/signal.c:964
/lib/x86_64-linux-gnu/libc.so.6(0x70aa64642520) [0x70aa64642520]
ruby-3.2.2/lib/libruby.so.3.2(hash_table_index+0x0) [0x70aa64cedbe7] ruby-3.2.2/symbol.h:72
ruby-3.2.2/lib/libruby.so.3.2(rb_id_table_lookup) ruby-3.2.2/id_table.c:230
ruby-3.2.2/lib/libruby.so.3.2(cached_callable_method_entry+0x24) [0x70aa64d356bb] ruby-3.2.2/vm_method.c:1295
ruby-3.2.2/lib/libruby.so.3.2(callable_method_entry_or_negative) ruby-3.2.2/vm_method.c:1365
ruby-3.2.2/lib/libruby.so.3.2(callable_method_entry) ruby-3.2.2/vm_method.c:1402
ruby-3.2.2/lib/libruby.so.3.2(rb_callable_method_entry) ruby-3.2.2/vm_method.c:1409
ruby-3.2.2/lib/libruby.so.3.2(gccct_method_search_slowpath+0x38) [0x70aa64d36258] ruby-3.2.2/vm_eval.c:434
ruby-3.2.2/lib/libruby.so.3.2(rb_call0+0x267) [0x70aa64d4e877] ruby-3.2.2/vm_eval.c:483
ruby-3.2.2/lib/libruby.so.3.2(rb_call+0x32) [0x70aa64d4f406] ruby-3.2.2/vm_eval.c:877
ruby-3.2.2/lib/libruby.so.3.2(rb_funcallv_kw) ruby-3.2.2/vm_eval.c:1074
ruby-3.2.2/lib/libruby.so.3.2(vm_call_cfunc_with_frame+0x127) [0x70aa64d30277] ruby-3.2.2/vm_insnhelper.c:3268
ruby-3.2.2/lib/libruby.so.3.2(vm_sendish+0x97) [0x70aa64d407a4] ruby-3.2.2/vm_insnhelper.c:5080
ruby-3.2.2/lib/libruby.so.3.2(vm_exec_core) ruby-3.2.2/insns.def:820
ruby-3.2.2/lib/libruby.so.3.2(rb_vm_exec+0xd3) [0x70aa64d460d3] ruby-3.2.2/vm.c:2374
ruby-3.2.2/lib/libruby.so.3.2(rb_vm_invoke_proc+0x5f) [0x70aa64d4bfcf] ruby-3.2.2/vm.c:1603
ruby-3.2.2/lib/libruby.so.3.2(vm_call0_body+0x5df) [0x70aa64d4c5cf] ruby-3.2.2/vm_eval.c:274
ruby-3.2.2/lib/libruby.so.3.2(vm_call0_cc+0x77) [0x70aa64d4e7e7] ruby-3.2.2/vm_eval.c:87
ruby-3.2.2/lib/libruby.so.3.2(rb_call0) ruby-3.2.2/vm_eval.c:551
ruby-3.2.2/lib/libruby.so.3.2(rb_call+0x32) [0x70aa64d4f406] ruby-3.2.2/vm_eval.c:877
ruby-3.2.2/lib/libruby.so.3.2(rb_funcallv_kw) ruby-3.2.2/vm_eval.c:1074
ruby-3.2.2/lib/libruby.so.3.2(rb_eval_cmd_kw+0x142) [0x70aa64d4f562] ruby-3.2.2/vm_eval.c:1923
ruby-3.2.2/lib/libruby.so.3.2(signal_exec+0xf6) [0x70aa64caae16] ruby-3.2.2/signal.c:1064
ruby-3.2.2/lib/libruby.so.3.2(rb_threadptr_execute_interrupts+0x36b) [0x70aa64cf7078] ruby-3.2.2/thread.c:2334
ruby-3.2.2/lib/libruby.so.3.2(rb_threadptr_execute_interrupts) ruby-3.2.2/thread.c:2291
ruby-3.2.2/lib/libruby.so.3.2(rb_vm_check_ints+0xb) [0x70aa64cf7ac5] ruby-3.2.2/vm_core.h:1994
ruby-3.2.2/lib/libruby.so.3.2(rb_vm_check_ints) ruby-3.2.2/vm_core.h:1985
ruby-3.2.2/lib/libruby.so.3.2(unblock_function_set) ruby-3.2.2/thread.c:320
ruby-3.2.2/lib/libruby.so.3.2(blocking_region_begin) ruby-3.2.2/thread.c:1485
ruby-3.2.2/lib/libruby.so.3.2(rb_nogvl+0xbf) [0x70aa64cf90cf] ruby-3.2.2/thread.c:1548
signal-bug-testcase-2/lib/signal_bug_testcase.so(fprintf+0x0) [0x70aa6518f299] ../../../../ext/signal_bug_testcase/signal_bug_testcase.c:17
signal-bug-testcase-2/lib/signal_bug_testcase.so(bug_testcase_free) ../../../../ext/signal_bug_testcase/signal_bug_testcase.c:18
ruby-3.2.2/lib/libruby.so.3.2(run_final+0xf) [0x70aa64b73172] ruby-3.2.2/gc.c:4388
ruby-3.2.2/lib/libruby.so.3.2(finalize_list) ruby-3.2.2/gc.c:4407
ruby-3.2.2/lib/libruby.so.3.2(finalize_deferred_heap_pages) ruby-3.2.2/gc.c:4436
ruby-3.2.2/lib/libruby.so.3.2(rb_objspace_call_finalizer+0x350) [0x70aa64b80d70] ruby-3.2.2/gc.c:4573
ruby-3.2.2/lib/libruby.so.3.2(rb_ec_finalize+0x2a) [0x70aa64b5d6d1] ruby-3.2.2/eval.c:168
ruby-3.2.2/lib/libruby.so.3.2(rb_ec_cleanup) ruby-3.2.2/eval.c:262
ruby-3.2.2/lib/libruby.so.3.2(ruby_run_node+0x9d) [0x70aa64b5d91d] ruby-3.2.2/eval.c:330
ruby-3.2.2/bin/ruby(rb_main+0x21) [0x5d5d1295f187] ./main.c:38
ruby-3.2.2/bin/ruby(main) ./main.c:57
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_call_main+0x80) [0x70aa64629d90] ../sysdeps/nptl/libc_start_call_main.h:58
/lib/x86_64-linux-gnu/libc.so.6(call_init+0x0) [0x70aa64629e40] ../csu/libc-start.c:392
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main_impl) ../csu/libc-start.c:379
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main) (null):0
[0x5d5d1295f1d5]
```
---Files--------------------------------
signal-bug-testcase-main.zip (7.73 KB)
--
https://bugs.ruby-lang.org/
Issue #19297 has been reported by vo.x (Vit Ondruch).
----------------------------------------
Bug #19297: Don't download content from internet to execute Ruby test suite
https://bugs.ruby-lang.org/issues/19297
* Author: vo.x (Vit Ondruch)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.0 (2022-12-25 revision a528908271) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Trying to build Ruby 3.2.0 for Fedora and execute its test suite via `make check` as we always did [1], the test suite suddenly fails (while it was working with commit:git|c5eefb7f37):
~~~
... snip ...
C-API Util function ruby_strtod
- converts a string to a double and returns the remaining string
- returns 0 and the full string if there's no numerical value
Finished in 45.737677 seconds
3827 files, 31635 examples, 177877 expectations, 0 failures, 0 errors, 0 tagged
./miniruby -I/builddir/build/BUILD/ruby-3.2.0/lib -I. -I.ext/common /builddir/build/BUILD/ruby-3.2.0/tool/runruby.rb --extout=.ext -- --disable-gems -C "/builddir/build/BUILD/ruby-3.2.0" bin/gem install --no-document \
--install-dir .bundle --conservative "bundler" "rake" "rspec:~> 3" #"ruby-prof"
ERROR: Could not find a valid gem 'bundler' (>= 0), here is why:
Unable to download data from https://rubygems.org/ - SocketError: Failed to open TCP connection to rubygems.org:443 (getaddrinfo: Temporary failure in name resolution) (https://rubygems.org/specs.4.8.gz)
ERROR: Could not find a valid gem 'rspec' (~> 3), here is why:
Unable to download data from https://rubygems.org/ - SocketError: Failed to open TCP connection to rubygems.org:443 (getaddrinfo: Temporary failure in name resolution) (https://rubygems.org/specs.4.8.gz)
make: Leaving directory '/builddir/build/BUILD/ruby-3.2.0/redhat-linux-build'
make: *** [uncommon.mk:1464: yes-test-syntax-suggest-prepare] Error 2
~~~
This is obviously due to the test suite trying to download `rspec` from the internet, while Fedora builders does not have internet access (and won't ever have for security reasons). If I am not mistaken, this is caused by commit:git|cae53842735237ccf71a13873fd0d1ae7f165582. Now
1) Can this be fixed?
2) Can the tarball be always self contained?
[1]: https://src.fedoraproject.org/rpms/ruby/blob/631163e3b8a51ed610528181aabe0d…
--
https://bugs.ruby-lang.org/
Issue #15425 has been updated by vo.x (Vit Ondruch).
This is probably no issue for Ruby 3.3. The header in question was dropped by commit:git|31f4b2d86bfbc753cec9be376719acc4b120e944. So this can be closed (I used to have powers to close the tickets myself, how have I lost them? 🤔)
----------------------------------------
Feature #15425: Store MJIT header into Ruby versioned directory.
https://bugs.ruby-lang.org/issues/15425#change-104475
* Author: vo.x (Vit Ondruch)
* Status: Feedback
* Priority: Normal
* Assignee: k0kubun (Takashi Kokubun)
----------------------------------------
This is a followup of #15391 which fixes JIT to respect the configuration options. However, I still wonder, why the file is versioned and why it is not stored in the versioned directory alongside all other internal Ruby headers. I believe, that while it now respects the header configuration flags, it still does not respect options such as "--with-ruby-version"
---Files--------------------------------
0001-Store-MJIT-header-into-Ruby-versioned-directory-2.patch (1.8 KB)
0001-Store-MJIT-header-into-Ruby-versioned-directory-1.patch (1.72 KB)
--
https://bugs.ruby-lang.org/
Issue #19075 has been updated by Dan0042 (Daniel DeLorme).
sawa (Tsuyoshi Sawada) wrote in #note-11:
> `bsearch_rindex`
I love this one, how it has a direct parallel in the way the condition must be inverted when using `index` vs `rindex`.
```ruby
a = [5,6,6,6,7]
a.index{ |e| e >= 6 } #=>1
a.rindex{ |e| e <= 6 } #=>3 (inverted condition)
a.bsearch_index{ |e| e >= 6 } #=>1
a.bsearch_rindex{ |e| e <= 6 } #=>3 (inverted condition just like rindex and bsearch(target: :last) implementation above)
```
----------------------------------------
Feature #19075: Binary searching for the last element
https://bugs.ruby-lang.org/issues/19075#change-104468
* Author: kyanagi (Kouhei Yanagita)
* Status: Open
* Priority: Normal
----------------------------------------
My latest proposal is https://bugs.ruby-lang.org/issues/19075#note-6.
I will leave the initial proposal below.
---
PR: https://github.com/ruby/ruby/pull/6611
(I'm going to talk about `Array` here, but the same argument can be made for `Range`. If `Array#bsearch_last` is acceptable, I will work also for `Range`.)
Ruby's bsearch returns the first element which satisfies the given block.
```ruby
# Search the first element greater than 18
array = [10, 15, 20, 25]
array.bsearch { |x| x > 18 } # => 20
```
If we want the last element, we need to invert the condition and step backward.
```ruby
# Search the last element less than 18
array = [10, 15, 20, 25]
index = array.bsearch_index { |x| !(x < 18) }
array[index-1] # => 15
```
Of course, we need to consider `nil` and the boundary.
```ruby
# Search the last element less than 100
index = array.bsearch_index { |x| !(x < 100) } # => nil
if index.nil?
array.last # => 25
else
array[index-1]
end
```
```ruby
# Search the last element less than 0
index = array.bsearch_index { |x| !(x < 0) } # => 0
if index.nil?
array.last
elsif index == 0
nil
else
array[index-1]
end
```
This is where mistakes can easily be made, so I propose `Array#bsearch_last` and `Array#bsearch_last_index`.
`Array#bsearch_last` returns the last element which satisfies the given block.
`Array#bsearch` requires that all false-evaluating elements precede all true-evaluating elements. As is clear from the meaning of the method, conversely to `bsearch`, `bsearch_last` requires that all true-evaluating elements precede all false-evaluating elements. (If `bsearch_last` is acceptable, the name "find-minimum mode" should be changed.)
```ruby
array = [10, 15, 20, 25]
array.bsearch_last { |x| x < 18 } # => 15
array.bsearch_last { |x| x < 100 } # => 25
array.bsearch_last { |x| x < 0 } # => nil
```
There are several possible options for find-any mode.
(1) `bsearch_last` does not support find-any mode.
A block for `bsearch_last` must return `true`, `false` or `nil`.
```
[1, 2, 3].bsearch_last { 0 } # => TypeError
```
My pull request tentatively includes this implementation.
(2) `bsearch_last` supports find-any mode and it behaves like `bsearch`.
`bsearch` with find-any mode returns an element, for which the block returns zero.
If multiple elements satisfy the condition, it is not determined which of them will be returned.
It is conceivable that `bsearch_last` behaves in the same way as `bsearch`.
```
# current behavior
# It is not specified whether `:b`, `:c`, or `:d` is returned.
[[1,:a], [2, :b], [2, :c], [2, :d], [3, :e]].bsearch { |a, b| 2 <=> a } # => [2, :c]
```
(3) `bsearch_last` supports find-any mode and returns the last element. Make `bsearch` return the first element.
Change the behavior of `bsearch` to return the first element for which the block returns zero.
`bsearch_last` returns the last element for which the block returns zero.
```
# Change it like this:
[[1,:a], [2, :b], [2, :c], [2, :d], [3, :e]].bsearch { |a, b| 2 <=> a } # => [2, :b]
[[1,:a], [2, :b], [2, :c], [2, :d], [3, :e]].bsearch_last { |a, b| 2 <=> a } # => [2, :d]
```
(If this option is adopted, the name "find-any mode" should be renamed.)
--
https://bugs.ruby-lang.org/