Skip to content

Latest commit

 

History

History
579 lines (478 loc) · 28.8 KB

File metadata and controls

579 lines (478 loc) · 28.8 KB

NEWS for Ruby 4.1.0

This document is a list of user-visible feature changes since the 4.0.0 release, except for bug fixes.

Note that each entry is kept to a minimum, see links for details.

Language changes

  • Module#clone and Module#dup no longer rewrite the lexical scope of copied methods. Constants and class variables resolve through the original class, consistent with inheritance and mixins. [Feature #21981]

Core classes updates

Note: We're only listing outstanding class updates.

  • C API

    • RB_NOGVL_PENDING_INTR_FAIL is added as a flag for rb_nogvl. rb_nogvl does not enter the blocking region (and does not call the given function), returning 0, if the current thread has pending interrupts, including interrupts masked by Thread.handle_interrupt. As with the existing skip path, errno is 0 because the function was never called.
  • Array

    • Array#pack accepts new formats R and r for unsigned and signed LEB128 encoded integers. [Feature #21785]
    • Array#pack accepts new formats x! and @! to align the current offset to a byte boundary or to the ABI alignment of another directive. [Feature #22185]
  • ENV

    • ENV.fetch_values is added. It returns an array of values for the given names, raising KeyError for missing names unless a block is given. [Feature #21781]
  • Fiber::Scheduler

    • The fiber_interrupt hook is now required. Schedulers which do not implement it can no longer be installed with Fiber.set_scheduler.
  • Hash

    • Hash.ruby2_keywords_hash? and Hash.ruby2_keywords_hash are deprecated and will be removed in Ruby 4.5. [Feature #22205]
  • Integer

    • Integer#bit_count is added. It returns the number of 1 bits in the binary representation of a non-negative integer (its population count). [Feature #20163]
  • IO::Buffer

    • read, write, pread, and pwrite now perform one IO operation using (offset, length), where length is the maximum transfer size. Short transfers are returned directly.

    • The corresponding fiber scheduler hooks are no longer experimental and now use the same (offset, length) argument order and single-operation semantics.

  • Kernel

    • Kernel#autoload_relative and Module#autoload_relative are added. These methods work like autoload, but resolve the file path relative to the file where the method is called, similar to require_relative. This makes it easier to autoload constants from files in the same directory without hardcoding absolute paths or manipulating $LOAD_PATH. [Feature #15330]
  • MatchData

    • MatchData#integer_at is added. It converts the matched substring to integer and return the result. [Feature #21932]
  • Module

    • Module#descendants is added. It returns an array of classes and modules that have the receiver in their ancestors. [Feature #9779]
    • Module#ruby2_keywords and top-level ruby2_keywords are deprecated and will be removed in Ruby 4.4. [Feature #22205]
  • ObjectSpace

  • Proc

    • Proc#refined is added. It returns a new Proc that behaves like the receiver but with the refinements activated by the given modules in effect inside its body, without affecting the original Proc. [Feature #22097]
    • Proc#ruby2_keywords is deprecated and will be removed in Ruby 4.4. [Feature #22205]
  • Range

    • Range#clamp is added. It returns a new Range instance whose begin and end values are clamped to the given bounds. [Feature #22175]
  • Regexp

    • All instances of Regexp are now frozen, not just literals. Subclasses of Regexp are not frozen for compatibility. [Feature #8948]
  • Set

    • A deprecated behavior, Set#to_set, Range#to_set, and Enumerable#to_set accepting arguments, was removed. [Feature #21390]
  • String

    • String#unpack and String#unpack1 accept new formats R and r for unsigned and signed LEB128 encoded integers. [Feature #21785]
    • String#unpack and String#unpack1 accept a new format ^ that returns the current offset. Useful when combined with variable width formats like LEB128. [Feature #21796]
    • String#unpack and String#unpack1 accept new formats x! and @! to align the current offset to a byte boundary or to the ABI alignment of another directive. [Feature #22185]
    • Basic bit operations are added. String#bit_get, String#bit_set?, String#bit_set, String#bit_clear, String#bit_flip and String#bit_count handle individual bits, and String#bitwise_not, String#bitwise_and, String#bitwise_or, String#bitwise_xor (with their ! variants) handle whole strings. [Feature #22118]
    • String#tr now accept a Hash for multi-character replacement. [Feature #22238]
  • Symbol

  • Thread::Backtrace::Location

    • Thread::Backtrace::Location#source_range is added. It returns a Ruby::SourceRange for the Ruby expression associated with the frame.

Stdlib updates

  • Psych

    • An experimental libfyaml backend has been added. It is only enabled when psych is built with --enable-libfyaml, and the default libyaml backend remains unchanged otherwise. It is not supported on Windows. [GH-psych #805]

The following bundled gems are added.

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.4.0 if it has GitHub releases.

The following bundled gems are promoted from default gems.

  • tsort 0.2.0
  • win32-registry 0.1.2

The following bundled gems are removed.

  • net-ftp 0.3.9
  • net-pop 0.1.2

They are still available on rubygems.org and can be installed with gem install. [Feature #21835]

The following default gem is added.

The following default gems are updated.

The following bundled gems are updated.

RubyGems and Bundler

Ruby 4.0 bundled RubyGems and Bundler version 4. see the following links for details.

Supported platforms

  • Support code for the following platforms has been removed. Note that all of them reached end of life many years ago and Ruby has long been unbuildable on them.

    • Interix (Windows Services for UNIX)
    • SunOS 4 (Solaris, i.e. SunOS 5, is unaffected)
    • BSD/OS (BSDi)
  • Windows 10 1703 or later no longer needs the LongPathsEnabled registry value to use paths longer than 260 characters. This applies to any process running the interpreter, including a program which embeds libruby. Each path component is still limited to 255 characters, and a child process still starts with the MAX_PATH limited current directory. [Bug #18947]

Compatibility issues

  • A class or module can now be modified only by the Ractor which created it, its owner. Defining, removing or undefining methods, alias, changing visibility, include/prepend, Module#refine, defining or removing constants, registering an autoload, writing the class's own instance variables and class variables, Module#freeze and Module#set_temporary_name raise Ractor::IsolationError in any other Ractor. Reading is unchanged. Everything defined at boot or by the main Ractor, required libraries included, is owned by the main Ractor, so a non-main Ractor can no longer monkey-patch it; and since defining a constant in a foreign class is prohibited, it can not define a top-level class or module name either. In exchange a Ractor has full use of the classes it creates itself, including unshareable constant, instance variable and class variable values, which not even the main Ractor could do before. See doc/language/ractor.md. [Feature #22226]

  • Kernel#at_exit and END {} now raise Ractor::IsolationError when called in a non-main Ractor. Previously the registered handler ran in the main Ractor at process exit, which was confusing. [Feature #22139]

  • Ractor.make_shareable now raises Ractor::Error for an IO object, and Ractor.shareable? returns false for it. A frozen IO used to be shareable, but almost all of its methods raise FrozenError and it can still refer to unshareable objects through the members of rb_io_t.

Stdlib compatibility issues

  • Etc.getlogin on Windows now returns the login name determined when the process starts, from the USER or USERNAME environment variable or GetUserName(). It used to follow later changes to ENV['USER'].

C API updates

Embedded TypedData

  • The RUBY_TYPED_EMBEDDABLE flag is now public and documented and can be used by C extensions. It allows allocating C structs directly into Ruby object slots, which reduces pointer chasing, and in some case memory usage. See the C extension documentation for details. [Feature #21853]

  • Added new C23 inspired allocator functions, that takes the previous memory size. This allow the Ruby GC to better keep track of memory usage, improving its heuristics. It also improves the performance of system allocators that support C23 free_sized.

    However, it is important to note that passing an incorrect size to these function is undefined behavior and may result in crashes or memory leaks.

    • ruby_xfree_sized(void *ptr, size_t size)
    • ruby_xrealloc_sized(void *ptr, size_t newsiz, size_t oldsiz)
    • ruby_xrealloc2_sized(void *ptr, size_t newelems, size_t newsiz, size_t oldelems)

    [Feature #21861]

Removed APIs

The following APIs, which have been deprecated for many years, are removed. [Feature #21768]

  • old postponed job functions,
  • untyped data object type/functions,
  • old APIs to allocate a data object,
  • taintedness/trustedness enums/macros,
  • rb_gc_force_recycle function,
  • rb_iterate function,
  • and some functions and constants for internal use.

Implementation improvements

  • The default garbage collector has been switched from a freelist allocator to a bump pointer allocator. [PR #17201]

  • The error_highlight, did_you_mean, and syntax_suggest gems are now loaded lazily on the first error display instead of at interpreter boot, which reduces startup time. Process.warmup loads them eagerly so that pre-forking servers keep their code in copy-on-write shared memory. [Feature #21951]

Ractor

A lot of work has gone into making Ractors more stable, performant, and usable. These improvements bring Ractor implementation closer to leaving experimental status.

  • The default GC now runs per Ractor: each Ractor collects its own heap on its own thread without stopping the others, and a stop-the-world collection only runs when it is really needed (explicit full GC.start, shareable-object growth, reclaiming dead Ractors' heaps). Allocation-heavy Ractor programs now scale like forked processes.

    Visible behavior changes:

    • Ractor#value returns the value only once; a second call raises Ractor::Error.
    • GC.disable/GC.enable act as per-Ractor holds on a process-wide switch: one Ractor's GC.enable no longer overrides another Ractor's GC.disable.
    • ObjectSpace.each_object enumerates the calling Ractor's own objects plus other Ractors' shareable objects (ObjectSpace.dump_all still covers everything).
    • ObjectSpace.define_finalizer on another Ractor's object raises Ractor::IsolationError.

M:N thread scheduler

  • The scheduler scales with the number of waiters and of Ractors, where it used to walk a list or take one lock for all of them:

    • A timed wait sits in a hierarchical timer wheel rather than on a list sorted by deadline, which was inserted into by a linear scan.
    • An fd stays armed in the backend between waits, instead of being added before each wait and removed after each wake.
    • The io-wait bookkeeping is sharded by fd, rather than serialized on one lock across every fd.
    • A timed wait on an fd rides the scheduler instead of going to a blocking region, which cost a native thread handoff per wait.
    • A context switch, and leaving or rejoining the shared pool, no longer take the scheduler's global lock.
  • The RUBY_MN_THREADS environment variable now runs from no M:N scheduling at all to all of it. -1 is new: a Ractor's threads have been M:N since the scheduler was added, with no way to turn that off. 0 and 1 are unchanged.

    main thread the main Ractor's other threads a Ractor's threads
    -1 1:1 1:1 1:1
    0 or unset 1:1 1:1 M:N
    1 1:1 M:N M:N
    2 M:N M:N M:N
  • RUBY_MN_THREADS=2 is new. The main thread is resumed like any other M:N thread rather than woken on a native thread of its own, which costs an order of magnitude more. It pays off when the main thread drives the work, and does nothing for one that only starts other threads and waits.

    The main thread is then no longer bound to one OS thread, which is what the M:N scheduler already meant for every other thread:

    • A C extension that keeps state per OS thread has to call rb_thread_lock_native_thread().
    • What must run on the process's initial thread does not work at all, pinning included: macOS AppKit and CFRunLoop, and hosts that embed Ruby and return into their own main loop.
  • The OS thread name is no longer set from the Ruby thread for M:N threads: one native thread runs many of them over its life. Thread#name= was already skipped for the same reason.

JIT