From 8efaf5e6b6a25e0d237f3d71b75865661ae98268 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 19 Dec 2025 15:54:02 +0900 Subject: [PATCH 1/6] Adjust Stdlib section with 4.0.0 and added reference of RubyGems release notes. --- NEWS.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index af3e78ddaaba3f..7436d574016595 100644 --- a/NEWS.md +++ b/NEWS.md @@ -284,7 +284,7 @@ The following bundled gems are promoted from default gems. We only list stdlib changes that are notable feature changes. Other changes are listed in the following sections. We also listed release -history from the previous bundled version that is Ruby 3.3.0 if it has GitHub +history from the previous bundled version that is Ruby 3.4.0 if it has GitHub releases. The following default gem is added. @@ -324,9 +324,6 @@ The following default gems are updated. * weakref 0.1.4 * zlib 3.2.2 -The following bundled gems are added. - - The following bundled gems are updated. * minitest 6.0.0 @@ -349,6 +346,15 @@ The following bundled gems are updated. * csv 3.3.5 * repl_type_completor 0.1.12 +### RubyGems and Bundler + +see the following links for details. + +* [Upgrading to RubyGems/Bundler 4 - RubyGems Blog](https://blog.rubygems.org/2025/12/03/upgrade-to-rubygems-bundler-4.html) +* [4.0.0 Released - RubyGems Blog](https://blog.rubygems.org/2025/12/03/4.0.0-released.html) +* [4.0.1 Released - RubyGems Blog](https://blog.rubygems.org/2025/12/09/4.0.1-released.html) +* [4.0.2 Released - RubyGems Blog](https://blog.rubygems.org/2025/12/17/4.0.2-released.html) + ## Supported platforms * Windows From 7b19f1a1ef4c66a8a8f8fb64cb08ecca2044884c Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 19 Dec 2025 14:24:33 +0900 Subject: [PATCH 2/6] Skip RBS Ractor test on Windows It seems hunging up. --- tool/rbs_skip_tests_windows | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tool/rbs_skip_tests_windows b/tool/rbs_skip_tests_windows index bdf3dddfd7a594..43888c98a71324 100644 --- a/tool/rbs_skip_tests_windows +++ b/tool/rbs_skip_tests_windows @@ -1,5 +1,8 @@ ARGFTest Failing on Windows +RactorSingletonTest Hangs up on Windows +RactorInstanceTest Hangs up on Windows + # NotImplementedError: fileno() function is unimplemented on this machine test_fileno(DirInstanceTest) test_fchdir(DirSingletonTest) From f0472f2d49c896bf072d327ff2a4ee009115266f Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 19 Dec 2025 17:16:15 +0900 Subject: [PATCH 3/6] [Feature #21785] [DOC] LEB128 support --- doc/language/packed_data.rdoc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/language/packed_data.rdoc b/doc/language/packed_data.rdoc index dcb4d557352d88..97079f7f08dd8f 100644 --- a/doc/language/packed_data.rdoc +++ b/doc/language/packed_data.rdoc @@ -342,6 +342,22 @@ for one element in the input or output array. s.unpack('U*') # => [4194304] +- 'r' - Signed LEB128-encoded integer + (see {Signed LEB128}[https://en.wikipedia.org/wiki/LEB128#Signed_LEB128]) + + s = [1, 127, -128, 16383, -16384].pack("r*") + # => "\x01\xFF\x00\x80\x7F\xFF\xFF\x00\x80\x80\x7F" + s.unpack('r*') + # => [1, 127, -128, 16383, -16384] + +- 'R' - Unsigned LEB128-encoded integer + (see {Unsigned LEB128}[https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128]) + + s = [1, 127, 128, 16383, 16384].pack("R*") + # => "\x01\x7F\x80\x01\xFF\x7F\x80\x80\x01" + s.unpack('R*') + # => [1, 127, 128, 16383, 16384] + - 'w' - BER-encoded integer (see {BER encoding}[https://en.wikipedia.org/wiki/X.690#BER_encoding]): From dd2f7d6ae6ab53bea7a179338378e1d32c306747 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 19 Dec 2025 13:51:27 +0900 Subject: [PATCH 4/6] [Bug #21794] Fix for platforms where O_CLOEXEC is not available --- box.c | 12 ++++++++++-- depend | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/box.c b/box.c index 3182fb4eb20758..54bd5c6258343f 100644 --- a/box.c +++ b/box.c @@ -9,6 +9,7 @@ #include "internal/file.h" #include "internal/gc.h" #include "internal/hash.h" +#include "internal/io.h" #include "internal/load.h" #include "internal/st.h" #include "internal/variable.h" @@ -627,8 +628,14 @@ copy_ext_file(const char *src_path, const char *dst_path) # else const int bin = 0; # endif - const int src_fd = open(src_path, O_RDONLY|bin); +# ifdef O_CLOEXEC + const int cloexec = O_CLOEXEC; +# else + const int cloexec = 0; +# endif + const int src_fd = open(src_path, O_RDONLY|cloexec|bin); if (src_fd < 0) return COPY_ERROR_SRC_OPEN; + if (!cloexec) rb_maygvl_fd_fix_cloexec(src_fd); struct stat src_st; if (fstat(src_fd, &src_st)) { @@ -636,11 +643,12 @@ copy_ext_file(const char *src_path, const char *dst_path) return COPY_ERROR_SRC_STAT; } - const int dst_fd = open(dst_path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|bin, S_IRWXU); + const int dst_fd = open(dst_path, O_WRONLY|O_CREAT|O_EXCL|cloexec|bin, S_IRWXU); if (dst_fd < 0) { close(src_fd); return COPY_ERROR_DST_OPEN; } + if (!cloexec) rb_maygvl_fd_fix_cloexec(dst_fd); enum copy_error_type ret = COPY_ERROR_NONE; diff --git a/depend b/depend index 8834814227b0e8..11397bf647adf1 100644 --- a/depend +++ b/depend @@ -745,6 +745,7 @@ box.$(OBJEXT): $(top_srcdir)/internal/file.h box.$(OBJEXT): $(top_srcdir)/internal/gc.h box.$(OBJEXT): $(top_srcdir)/internal/hash.h box.$(OBJEXT): $(top_srcdir)/internal/imemo.h +box.$(OBJEXT): $(top_srcdir)/internal/io.h box.$(OBJEXT): $(top_srcdir)/internal/load.h box.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h box.$(OBJEXT): $(top_srcdir)/internal/serial.h @@ -951,6 +952,7 @@ box.$(OBJEXT): {$(VPATH)}internal/value_type.h box.$(OBJEXT): {$(VPATH)}internal/variable.h box.$(OBJEXT): {$(VPATH)}internal/warning_push.h box.$(OBJEXT): {$(VPATH)}internal/xmalloc.h +box.$(OBJEXT): {$(VPATH)}io.h box.$(OBJEXT): {$(VPATH)}iseq.h box.$(OBJEXT): {$(VPATH)}method.h box.$(OBJEXT): {$(VPATH)}missing.h From 9ee2243bede65fba8b7fbce54a232b8c063a5616 Mon Sep 17 00:00:00 2001 From: Sharon Rosner Date: Fri, 19 Dec 2025 11:58:26 +0100 Subject: [PATCH 5/6] Fiber scheduler: invoke `#io_write` hook on IO flush (#15609) Previously, calling IO#flush or closing an IO with unflushed buffered writes would just invoke `#blocking_operation_wait` and flush the write buffer using a `write` syscall. This change adds flushing through the fiber scheduler by invoking the `#io_write` hook. * Prefer IO::Buffer#write in IOScheduler * Use Dir.tmpdir for test file * Correctly handle errors in io_flush_buffer_fiber_scheduler --- io.c | 24 ++++++++++ test/fiber/scheduler.rb | 23 ++++++++++ test/fiber/test_scheduler.rb | 89 ++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) diff --git a/io.c b/io.c index e739cee3795eec..99268e8f05b1a4 100644 --- a/io.c +++ b/io.c @@ -1418,10 +1418,34 @@ io_flush_buffer_sync(void *arg) return (VALUE)-1; } +static inline VALUE +io_flush_buffer_fiber_scheduler(VALUE scheduler, rb_io_t *fptr) +{ + VALUE ret = rb_fiber_scheduler_io_write_memory(scheduler, fptr->self, fptr->wbuf.ptr+fptr->wbuf.off, fptr->wbuf.len, 0); + if (!UNDEF_P(ret)) { + ssize_t result = rb_fiber_scheduler_io_result_apply(ret); + if (result > 0) { + fptr->wbuf.off += result; + fptr->wbuf.len -= result; + } + return result >= 0 ? (VALUE)0 : (VALUE)-1; + } + return ret; +} + static VALUE io_flush_buffer_async(VALUE arg) { rb_io_t *fptr = (rb_io_t *)arg; + + VALUE scheduler = rb_fiber_scheduler_current(); + if (scheduler != Qnil) { + VALUE result = io_flush_buffer_fiber_scheduler(scheduler, fptr); + if (!UNDEF_P(result)) { + return result; + } + } + return rb_io_blocking_region_wait(fptr, io_flush_buffer_sync, fptr, RUBY_IO_WRITABLE); } diff --git a/test/fiber/scheduler.rb b/test/fiber/scheduler.rb index 029c5043dc1b14..07b15c5ce4b86a 100644 --- a/test/fiber/scheduler.rb +++ b/test/fiber/scheduler.rb @@ -488,6 +488,29 @@ def blocking(&block) end end +class IOScheduler < Scheduler + def __io_ops__ + @__io_ops__ ||= [] + end + + def io_write(io, buffer, length, offset) + fd = io.fileno + str = buffer.get_string + __io_ops__ << [:io_write, fd, str] + Fiber.blocking { buffer.write(IO.for_fd(fd), 0, offset) } + end +end + +class IOErrorScheduler < Scheduler + def io_read(io, buffer, length, offset) + return -Errno::EBADF::Errno + end + + def io_write(io, buffer, length, offset) + return -Errno::EINVAL::Errno + end +end + # This scheduler has a broken implementation of `unblock`` in the sense that it # raises an exception. This is used to test the behavior of the scheduler when # unblock raises an exception. diff --git a/test/fiber/test_scheduler.rb b/test/fiber/test_scheduler.rb index 97ce94bd270b69..4a8b4ee62d8e49 100644 --- a/test/fiber/test_scheduler.rb +++ b/test/fiber/test_scheduler.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true require 'test/unit' +require 'securerandom' +require 'fileutils' require_relative 'scheduler' class TestFiberScheduler < Test::Unit::TestCase @@ -283,4 +285,91 @@ def test_post_fork_fiber_blocking ensure thread.kill rescue nil end + + def test_io_write_on_flush + fn = File.join(Dir.tmpdir, "ruby_test_io_write_on_flush_#{SecureRandom.hex}") + write_fd = nil + io_ops = nil + thread = Thread.new do + scheduler = IOScheduler.new + Fiber.set_scheduler scheduler + + Fiber.schedule do + File.open(fn, 'w+') do |f| + write_fd = f.fileno + f << 'foo' + f.flush + f << 'bar' + end + end + io_ops = scheduler.__io_ops__ + end + thread.join + assert_equal [ + [:io_write, write_fd, 'foo'], + [:io_write, write_fd, 'bar'] + ], io_ops + + assert_equal 'foobar', IO.read(fn) + ensure + thread.kill rescue nil + FileUtils.rm_f(fn) + end + + def test_io_read_error + fn = File.join(Dir.tmpdir, "ruby_test_io_read_error_#{SecureRandom.hex}") + exception = nil + thread = Thread.new do + scheduler = IOErrorScheduler.new + Fiber.set_scheduler scheduler + Fiber.schedule do + File.open(fn, 'w+') { it.read } + rescue => e + exception = e + end + end + thread.join + assert_kind_of Errno::EBADF, exception + ensure + thread.kill rescue nil + FileUtils.rm_f(fn) + end + + def test_io_write_error + fn = File.join(Dir.tmpdir, "ruby_test_io_write_error_#{SecureRandom.hex}") + exception = nil + thread = Thread.new do + scheduler = IOErrorScheduler.new + Fiber.set_scheduler scheduler + Fiber.schedule do + File.open(fn, 'w+') { it.sync = true; it << 'foo' } + rescue => e + exception = e + end + end + thread.join + assert_kind_of Errno::EINVAL, exception + ensure + thread.kill rescue nil + FileUtils.rm_f(fn) + end + + def test_io_write_flush_error + fn = File.join(Dir.tmpdir, "ruby_test_io_write_flush_error_#{SecureRandom.hex}") + exception = nil + thread = Thread.new do + scheduler = IOErrorScheduler.new + Fiber.set_scheduler scheduler + Fiber.schedule do + File.open(fn, 'w+') { it << 'foo' } + rescue => e + exception = e + end + end + thread.join + assert_kind_of Errno::EINVAL, exception + ensure + thread.kill rescue nil + FileUtils.rm_f(fn) + end end From e23a918a711c719f6ac257c83b81284fd137c1f6 Mon Sep 17 00:00:00 2001 From: Sharon Rosner Date: Fri, 19 Dec 2025 12:02:57 +0100 Subject: [PATCH 6/6] Update NEWS.md for Fiber Scheduler (#15629) --- NEWS.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/NEWS.md b/NEWS.md index 7436d574016595..b302e188520f41 100644 --- a/NEWS.md +++ b/NEWS.md @@ -105,6 +105,15 @@ Note: We're only listing outstanding class updates. waiting on a blocking IO operation when the IO operation is closed. [[Feature #21166]] + * Introduce `Fiber::Scheduler#yield` to allow the fiber scheduler to + continue processing when signal exceptions are disabled. + [[Bug #21633]] + + * Reintroduce the `Fiber::Scheduler#io_close` hook for asynchronous `IO#close`. + + * Invoke `Fiber::Scheduler#io_write` when flushing the IO write buffer. + [[Bug #21789]] + * File * `File::Stat#birthtime` is now available on Linux via the statx @@ -266,6 +275,11 @@ Note: We're only listing outstanding class updates. * Introduce support for `Thread#raise(cause:)` argument similar to `Kernel#raise`. [[Feature #21360]] +* Pathname + + * Pathname has been promoted from a default gem to a core class of Ruby. + [[Feature #17473]] + ## Stdlib updates The following bundled gems are promoted from default gems. @@ -552,8 +566,10 @@ A lot of work has gone into making Ractors more stable, performant, and usable. [Feature #21550]: https://bugs.ruby-lang.org/issues/21550 [Feature #21552]: https://bugs.ruby-lang.org/issues/21552 [Feature #21557]: https://bugs.ruby-lang.org/issues/21557 +[Bug #21633]: https://bugs.ruby-lang.org/issues/21633 [Bug #21654]: https://bugs.ruby-lang.org/issues/21654 [Feature #21678]: https://bugs.ruby-lang.org/issues/21678 [Bug #21698]: https://bugs.ruby-lang.org/issues/21698 [Feature #21701]: https://bugs.ruby-lang.org/issues/21701 [Feature #21785]: https://bugs.ruby-lang.org/issues/21785 +[Bug #21789]: https://bugs.ruby-lang.org/issues/21789