The size_t atomics had add, sub, cas, exchange and fetch_add but no
load, so the GC read its atomic size_t counters through the VALUE load
with a cast.
This commit adds RUBY_ATOMIC_SIZE_LOAD and rbimpl_atomic_size_load,
following rbimpl_atomic_load, and uses it for the deferred free count.
rb_len_t changes its width on mswin, and with it the layout of the lengths that extensions read through the public headers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
long is 32 bits on mswin while pointers are 64 bits, so String, Array and MatchData could not reach 2GiB there. config.h now sets SIZEOF_RB_LEN_T to 8 on mswin64, and value.h then defines rb_len_t as int64_t. mingw still uses long.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
They take pointers to the signed LONG64. clang-cl warns about the difference in every file that includes ruby/atomic.h, as MSVC does at -W4 (C4057).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
clang-cl supports labels as values and `__extension__`, but does not define __GNUC__, so the VM fell back to the switch dispatch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
clang-cl does not know `#pragma deprecated` and warns about it in every file that includes ruby.h. The warning is an error in mkmf checks with -WX.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
clang-cl defines _MSC_VER but is not MSVC for RBIMPL_COMPILER_IS, so rbimpl_atomic_or and rbimpl_atomic_cas fell through to "Unsupported platform".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Like `rb_len2int`, `rb_len2long` narrows an `rb_len_t` with a range check, for extensions that keep a length in `long` and for intentional truncations where `rb_len_t` is wider than `long`. `rb_out_of_int` cannot be reused since its message names 'int'.
https://bugs.ruby-lang.org/issues/22400
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mkmf's check_sizeof('rb_len_t') defines SIZEOF_RB_LEN_T as a number, which redefined the macro with different tokens and warned, failing builds that treat warnings as errors. A static assert checks a given value against the type.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A typedef is invisible to the preprocessor, so an extension that also builds on older Ruby has no way to see whether `rb_len_t` exists or how wide it is. This gives it a macro to test before falling back to `typedef long rb_len_t`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Once rb_len_t widens this changes the `long *` output parameters of rb_range_beg_len(), rb_str_subpos(), rb_arithmetic_sequence_beg_len_step(), ruby_enc_find_basename() and ruby_enc_find_extname(), so extensions passing `long *` to them will need to be updated. The extension guides list these signatures verbatim and follow along.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
These are the fields the length macros read, so they have to carry the type before anything built on those macros can follow. The layout stays as it is even once rb_len_t widens, because these fields are already padded to pointer alignment.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`long` is 32-bit on LLP64 platforms such as Windows, while VALUE and pointers are 64-bit. Giving the lengths and indices of objects such as String and Array a type of their own is what lets a later change widen them there. The typedef is `long` on every platform for now and a static assert pins it that way, because the conversion macros, PRI_LEN_PREFIX and the width guards around them are all still spelled in terms of `long`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An aligned load is atomic on x64, and seq_cst because stores here use xchg. The fallbacks issued lock xadd or lock cmpxchg on every load, which RUBY_VM_CHECK_INTS and inline cache reads pay for. ARM64EC also defines _M_X64, so it is excluded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit renames rb_str_tmp_frozen_no_embed_acquire to rb_str_no_gvl_safe_acquire
and makes it part of the public C API.
There is currently no way for C extensions that use no GVL operations to
safely read from a string because it has no way of guaranteeing that the
string is not embedded. Embedded strings are not safe for no GVL operations
because GC compaction can run, which will mprotect the memory region and
will cause any read to SEGV.
See https://github.com/ruby/ruby/pull/9817 for when rb_str_tmp_frozen_no_embed_acquire
was introduced.
[Feature #22232]
Guaranteeing that any hash can be converted into an st_table is
limiting the evolution of RHash.
None of the public usages of `RHASH_TBL` were justified, they all
had equivalent `rb_hash_*` public APIs to perform the same thing
more efficiently without disabling write barriers nor coercing the
hash into an st_table.
Every `RBIMPL_COMPILER_SINCE(MSVC, ...)` condition here holds for `_MSC_VER` 1915, the oldest version the headers accept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Building ruby needs `_MSC_VER` 1915 or later, but the generated config.h accepts 1910 and later for such a build, so an extension could still be compiled by 1910 through 1914.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The registered_globals list made every Ractor's GC scan every slot under
a shared lock. This commit splits it into per-Ractor lists.
We also maintain a global registry that references every Ractor that has
ever registered so that unregistering from a Ractor other than the one
that registered can look up the owning list.
When a Ractor is joined, the dead Ractor's slots are moved to the
joining Ractor's heap before the objspace merge happens.
This change tightens up the requirements for values stored in a
registered address.
Previously, Every Ractor's local GC scanned the shared list. So if a
slot held an unshareable object living in a different Ractor's heap,
that object was always kept alive regardless.
After this change, only the registering Ractor's GC scans its list. So if a
slot holds another Ractor's unshareable object, the owning Ractor's
local GC cannot see that reference, so it can collect the object while
the registered slot still points at it.
Storing such an object was already a Ractor containment violation. It
was just hidden by the shared scan.
[Feature #22277]
Concatenated large repeats reach a program past INT_MAX from a few hundred
kilobytes of source, and the offsets emitted into it wrap. Cap the size
while the program is emitted, which does not depend on the length pass that
overflows along with it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
It used InterlockedExchangeAdd64 on a 4-byte size_t, so the access also covered the following 4 bytes, which is the `tdata_unsafe_free_published` pointer for the caller in gc/default/default.c. Go through rb_atomic_t like the other size_t functions, which also fixes the missing return in the branch shared with 32-bit Solaris.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HAVE_STDCKDINT_H records what configure's compiler saw when ruby was
built. __has_include asks the compiler that is compiling the header
right now. When the two compilers are the same they always agree, so
checking __has_include first changes nothing. They can only disagree
when the compiler is different, e.g. an extension built with an older
compiler that has no <stdckdint.h>. Trusting the configure result then
means including a header that doesn't exist, which is a hard error.
Trusting __has_include falls back to __builtin_*_overflow, which works.
So only use HAVE_STDCKDINT_H when the compiler has no __has_include.
`rb_freeze_singleton_class` is an implementation detail of
`RB_OBJ_FREEZE`, so deprecate direct calls from extension libraries
before removing its public declaration in Ruby 4.2.
When running multiple Ractors, we can't call dfree of a non-thread-safe
T_DATA while other Ractors are running or doing GC work. If there are multiple
Ractors, we save the T_DATA object's `dfree` and `data` pointer in an entry. Once
the entry buffer is full per-Ractor, we publish it to the global list atomically.
Once the threshold for these deferred T_DATAs is reached (right now ~65K), the next
Ractor to run interrupts calls the `dfree` functions for these objects under the VM
barrier. It does not do any other GC work during this postponed job.
If a global GC runs, it also calls the `dfree` functions for all these objects (it drains
the deferred-free queue). When only 1 Ractor is running (`rb_gc_single_objspace_p()`),
these objects are freed normally and aren't added to any queue.
NOTE
----
Embedded T_DATA objects need to be special-cased because their `data` points to inside the
object's slot itself, which we reuse right away now. For non-thread-safe T_DATA that is
embeddable, we need to create it as unembedded - even without Ractors.
Using the `container->obj = rb_gc_location(container->obj)` pattern dirties pages unconditionally.
If we're in a forked process, this is bad for CoW. If the object hasn't moved, don't reassign.
RB_OBJ_SET_SHAREABLE() was unusable from extensions: the declaration
was outside RBIMPL_SYMBOL_EXPORT_BEGIN/END, so the symbol was compiled
with hidden visibility and did not appear in libruby.so.
Also document it, including the cost that a shareable object is not
collected by a local GC.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Share each lock count across a buffer and its slices so independent native operations retain the backing allocation until all users release it. Always acquire a lock for blocking I/O and protect each/each_byte callbacks from invalidating their backing pointers.
A char is more than large enough for counting rebuilds, and it's
fine if it rolls over.
The table being rebuilt exactly 255 times between two check seems
impossible.
On the other hand, if `Hash#shift` is abused, `entries_start` is
more likely to reach MAX_UCHAR.
So swapping the two members is preferable.
Module#descendants returns an array of classes and modules that have
the receiver in their ancestors; it is the inverse of Module#ancestors.
The receiver itself, singleton classes, and refinements are not included.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>