Take RSTRING_PTR() of the input buffer after resizing the output
buffer, as the input and output buffers may be the same String object.
Also explicitly reject in-place Cipher#update when the input is too
large to process in a single EVP_CipherUpdate() call. We should not
implicitly split the input across multiple calls for in-place
operation because this appears to be unreliable.
Reported by TheSecguy <thesecguy45@gmail.com>.
https://github.com/ruby/openssl/commit/8df76d73ba
A character that is not replaced by String#tr with a Hash can be longer
than 4 bytes, e.g., 6 bytes for a supplementary character in CESU-8.
Reported-by: Regy Robson (https://github.com/Atract0n)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When deflateParams returned Z_BUF_ERROR, the buffer was expanded before
the bytes just written were added to its length, so the next call wrote
over them and the length was counted twice.
Reported-by: Dániel Percze (https://hackerone.com/flashmyname)
https://github.com/ruby/zlib/commit/cba9abc7bd
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
slen + vlen - len in rb_str_update_1 could wrap around where rb_len_t is 32 bits wide. Check it before the capacity test, as str_buf_cat4 does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RESIZE_CAPA reallocates a heap string to the exact size, so an insertion by bytesplice or String#[]= shrank a buffer that already had room, and the next append grew it again. Each of those reallocs can move and copy the whole buffer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GC.start has to run even when GC is disabled, so we enable collection,
run the GC and then disable it again. #134 fixed the crash we saw when
another Ractor requested a GC before we could re-disable, but we still
restore whatever state we saw before the GC.
That's a problem when another Ractor changes the state in the meantime.
If it enables GC while we're collecting, we disable it again on its
behalf and GC stays off for good. Ractor.new disables and enables GC
around thread allocation, so this isn't hard to hit. GC.disable and the
internal gc_disable also share a single flag in mmtk-core, so one can
undo the other.
In this commit we track the internal flag, the user flag and any forced
GC.start requests in the binding, and mirror the combined state into
mmtk-core. A forced GC.start only enables collection until the next
pause ends, and we reapply the state in resume_mutators before any
mutator can request another GC.
GC workers also toggle the internal flag during a pause, through
DURING_GC_COULD_MALLOC_REGION. That reads the flag and then sets it, so
two workers running in parallel can leave it set. mmtk-core used to
ignore these because it can't disable collection mid-pause, so we do the
same and drop internal changes between stop_all_mutators and
resume_mutators.
This replaces the retry loop from #134, if we fail to disable because a
GC is pending, the end of that GC puts the state right.
AtomicU64::update needs Rust 1.95, so we declare that in Cargo.toml.
https://github.com/ruby/mmtk/commit/7430ebeaa6
use retain_mut in process_obj_free_candidates instead of building a new
Vec on every GC.
This saves about 38MiB of peak RSS when running the Lobsters benchmark
with 12 workers.
https://github.com/ruby/mmtk/commit/1292d9a488
add_obj_free_candidates_batch walked the whole batch once for every
bucket and kept the objects where i % num_buckets == idx. Each flush of
128 candidates ran 128 * num_buckets modulo operations on the mutator
thread. There is one bucket per GC worker, so the cost grew with the
number of cores.
In this commit we visit only the objects that belong to each bucket
with skip(idx).step_by(num_buckets). Each object still goes to bucket
i % num_buckets, and each flush visits every object once.
On a c6i.metal with 64 GC workers, the ruby-bench headline geomean
speedup with YJIT is 1.15x. With 1 GC worker there is no change.
https://github.com/ruby/mmtk/commit/477de42ae7
Fixes YJIT compilation bug. During YJIT compilation, it could see a
`Ractor::IsolationError` by getting the attached object of the blockparamproxy's
singleton class's (the proxy object). This object needs to be shareable in order
for the compilation to work on a non-main Ractor.
object_id took the VM lock for every shareable object in multi-Ractor mode, even
when the object already had an id.
Kernel#hash reaches object_id through rb_obj_hash, so a shareable T_DATA such as
an Encoding, a Ractor or a Ractor::Port used as a Hash key serialised every
lookup across Ractors.
In this commit we make generic_field_set copy the fields of a shareable object
in multi-Ractor mode, and publish the new fields_obj of a T_DATA with an atomic
write.
```ruby
t = Process.clock_gettime(Process::CLOCK_MONOTONIC)
ractors = 4.times.map do
Ractor.new do
h = { Encoding::UTF_8 => 1 }
1_000_000.times { h[Encoding::UTF_8] }
end
end
ractors.each(&:join)
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t
puts "#{elapsed.round(2)}s"
```
I ran this 3 times before this patch and after and saw
before: 0.58s 0.61s 0.64s
after: 0.05s 0.05s 0.05s
If we have dispatch where the receiver is polymorphic in a class, right
now we just break up the dispatch by the class. This unfortunately leads
to polymorphic `attr_reader` being completely unspecialized by shape.
In this commit, make sure we, for each polymorphic branch, filter the
profiled types by the receiver's specialized class. That way we still
get a chance at monomorphizing by shape and specializing `attr_reader`
away.
An old and very obscure bug resulting from using fixed-size
integers to hold state stacks.
Prism uses u32, and parse.y uses u64. Both of them have problems
if they overflow. Instead, use a growable state stack that can
extend indefinitely so this is never a problem.
https://github.com/ruby/prism/commit/372df2460f
Set is a core class implemented in C now, so define Enumerable#to_set there and let Set#to_set share it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Extensions calling __builtin_cpu_supports, such as json 3.1.0 and older, failed to link against a clang-cl build with an undefined __cpu_model, which compiler-rt defines and clang-cl does not link by default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A box always rebuilds its feature index on the first require, so this
covers the rebuild path that test_require.rb reaches only after
$LOADED_FEATURES has been modified.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A frozen array is never shared, so the check for user modifications
always fires and rb_ary_replace() raises FrozenError before
rb_provide_feature() can report the freeze. Appending to
$LOADED_FEATURES before freezing it is enough to reach this. Under
RUBY_BOX=1 every box reaches it, because a box starts with an empty
index snapshot.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This came from io.c, where the converted separator is written back through a pointer argument. Inlined here it became a self-assignment:
stringio.c:1507:6: warning: explicitly assigning value of variable of type 'VALUE' (aka 'unsigned long') to itself [-Wself-assign]
1507 | rs = rs;
| ~~ ^ ~~
https://github.com/ruby/stringio/commit/f434104c18
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every piece the Happy Eyeballs Version 2 path needed from pthread, pipe(2) and select(2) now has a Windows counterpart, so a host that answers with both families no longer waits for the IPv6 attempt to time out there. MinGW stays out, since its pthread emulation was never tested against this path, and its tests are omitted instead of passing by connecting sequentially.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Winsock has no EAI_ADDRFAMILY, and its getsockopt(2) takes a char * where the option value is an int. Neither compiles as written once the fast fallback is built for Windows.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Winsock reports a failed non-blocking connect(2) through the exceptfds of select(2) alone, so the fast fallback never learned that an attempt had been refused. Only Windows fills the set, so select(2) behaves as before elsewhere.
[Bug #18661]
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rb_w32_select_with_thread() polls the read end of a pipe, so the resolution threads can report through one there too. The waiter goes through nonblock_set() because the Windows fcntl() has no F_GETFL.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The connection attempts race each other non-blocking, and TCPSocket.new wrapped the winner as it was. Windows creates every other socket blocking, and a read parked on a non-blocking one there cannot be interrupted by IO#close. POSIX sockets start out non-blocking, so leave them alone and test that they stay so.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
cl.exe does not implement variable length arrays, and none of these ever holds more than the two address families the fast fallback resolves. The hints array only needed one live element at a time, so fill the entry in place instead.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Windows has neither SIGPIPE nor pthread_sigmask(3), so skip the masking there. The test-only resolution delay uses SleepEx() there, because Sleep() reaches rb_w32_Sleep() through the ruby DLL and waits through a Ruby thread this thread does not have.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use rb_yjit_attached_object_is() for the compile-time attachment check.
It keeps the singleton flag test in C and returns only a boolean to Rust.
The predicate cannot raise, allocate, or take locks. Guard emission is
unchanged, and the IO exclusion stays in Rust.