Age | Commit message (Collapse) | Author |
|
(#13526)
Fixes the following:
```ruby
Thread.new { Fiber.current.kill }.join
```
Notes:
Merged-By: ioquatix <[email protected]>
|
|
`Ractor#take` was deprecated but some libraries can use it as
an alias for `Ractor#value` (i.e., to wait for a Ractor's
temrination and retrieve its result).
Therefore `Ractor#take` is simply an alias for `Ractor#value`.
This method will remain available until the end of August 2025,
unless there is further discussion.
Notes:
Merged: https://github.com/ruby/ruby/pull/13512
|
|
* Added `Ractor::Port`
* `Ractor::Port#receive` (support multi-threads)
* `Rcator::Port#close`
* `Ractor::Port#closed?`
* Added some methods
* `Ractor#join`
* `Ractor#value`
* `Ractor#monitor`
* `Ractor#unmonitor`
* Removed some methods
* `Ractor#take`
* `Ractor.yield`
* Change the spec
* `Racotr.select`
You can wait for multiple sequences of messages with `Ractor::Port`.
```ruby
ports = 3.times.map{ Ractor::Port.new }
ports.map.with_index do |port, ri|
Ractor.new port,ri do |port, ri|
3.times{|i| port << "r#{ri}-#{i}"}
end
end
p ports.each{|port| pp 3.times.map{port.receive}}
```
In this example, we use 3 ports, and 3 Ractors send messages to them respectively.
We can receive a series of messages from each port.
You can use `Ractor#value` to get the last value of a Ractor's block:
```ruby
result = Ractor.new do
heavy_task()
end.value
```
You can wait for the termination of a Ractor with `Ractor#join` like this:
```ruby
Ractor.new do
some_task()
end.join
```
`#value` and `#join` are similar to `Thread#value` and `Thread#join`.
To implement `#join`, `Ractor#monitor` (and `Ractor#unmonitor`) is introduced.
This commit changes `Ractor.select()` method.
It now only accepts ports or Ractors, and returns when a port receives a message or a Ractor terminates.
We removes `Ractor.yield` and `Ractor#take` because:
* `Ractor::Port` supports most of similar use cases in a simpler manner.
* Removing them significantly simplifies the code.
We also change the internal thread scheduler code (thread_pthread.c):
* During barrier synchronization, we keep the `ractor_sched` lock to avoid deadlocks.
This lock is released by `rb_ractor_sched_barrier_end()`
which is called at the end of operations that require the barrier.
* fix potential deadlock issues by checking interrupts just before setting UBF.
https://bugs.ruby-lang.org/issues/21262
Notes:
Merged: https://github.com/ruby/ruby/pull/13445
|
|
We should not copy the FL_PROMOTED flag when we move an object in Ractor#send(move: true)
because the newly created object may not be old.
Notes:
Merged: https://github.com/ruby/ruby/pull/13442
|
|
The FL_PROMOTED flag was not copied when moving objects, causing assertions
to fail when an old object is moved:
gc/default/default.c:834: Assertion Failed: RVALUE_AGE_SET:age <= RVALUE_OLD_AGE
Co-Authored-By: Luke Gruber <[email protected]>
Notes:
Merged: https://github.com/ruby/ruby/pull/13415
|
|
Currently, this can be reproduced by:
r = Ractor.new do
a = [1, 2, 3]
a.object_id
a.dup # this frees the generic ivar for `object_id` on the copied object
:done
end
r.take
In debug builds, this hits an assertion failure without this fix.
Notes:
Merged: https://github.com/ruby/ruby/pull/13401
|
|
In non-main ractors, don't use `sym_proc_cache`. It is not thread-safe
to add to this array without a lock and also it leaks procs from one
ractor to another. Instead, we create a new proc each time. If this
results in poor performance we can come up with a solution later.
Fixes [Bug #21354]
Notes:
Merged: https://github.com/ruby/ruby/pull/13380
|
|
in same ractor
Rework ractors so that any ractor action (Ractor.receive, Ractor#send, Ractor.yield, Ractor#take,
Ractor.select) will operate on the thread that called the action. It will put that thread to sleep if
it's a blocking function and it needs to put it to sleep, and the awakening action (Ractor.yield,
Ractor#send) will wake up the blocked thread.
Before this change every blocking ractor action was associated with the ractor struct and its fields.
If a ractor called Ractor.receive, its wait status was wait_receiving, and when another ractor calls
r.send on it, it will look for that status in the ractor struct fields and wake it up. The problem was that
what if 2 threads call blocking ractor actions in the same ractor. Imagine if 1 thread has called Ractor.receive
and another r.take. Then, when a different ractor calls r.send on it, it doesn't know which ruby thread is associated
to which ractor action, so what ruby thread should it schedule? This change moves some fields onto the ruby thread
itself so that ruby threads are the ones that have ractor blocking statuses, and threads are then specifically scheduled
when unblocked.
Fixes [#17624]
Fixes [#21037]
Notes:
Merged: https://github.com/ruby/ruby/pull/12633
|
|
[Bug #19367]
Notes:
Merged: https://github.com/ruby/ruby/pull/7174
|
|
[Bug #18119]
When we create classes, it pushes the class to the subclass list of the
superclass. This access needs to be synchronized because multiple Ractors
may be creating classes with the same superclass, which would cause race
conditions and cause the linked list to be corrupted.
For example, we can reproduce with this script crashing:
workers = (0...8).map do
Ractor.new do
loop do
100.times.map { Class.new }
Ractor.yield nil
end
end
end
100.times { Ractor.select(*workers) }
With ASAN enabled, we can see that there are use-after-free errors:
==176013==ERROR: AddressSanitizer: heap-use-after-free on address 0x5030000974f0 at pc 0x62f9e56f892d bp 0x7a503f1ffd90 sp 0x7a503f1ffd88
WRITE of size 8 at 0x5030000974f0 thread T4
#0 0x62f9e56f892c in rb_class_remove_from_super_subclasses class.c:149:24
#1 0x62f9e58c9dd2 in rb_gc_obj_free gc.c:1262:9
#2 0x62f9e58f6e19 in gc_sweep_plane gc/default/default.c:3450:21
#3 0x62f9e58f686a in gc_sweep_page gc/default/default.c:3535:13
#4 0x62f9e58f12b4 in gc_sweep_step gc/default/default.c:3810:9
#5 0x62f9e58ed2a7 in gc_sweep gc/default/default.c:4058:13
#6 0x62f9e58fac93 in gc_start gc/default/default.c:6402:13
#7 0x62f9e58e8b69 in heap_prepare gc/default/default.c:2032:13
#8 0x62f9e58e8b69 in heap_next_free_page gc/default/default.c:2255:9
#9 0x62f9e58e8b69 in newobj_cache_miss gc/default/default.c:2362:38
...
0x5030000974f0 is located 16 bytes inside of 24-byte region [0x5030000974e0,0x5030000974f8)
freed by thread T4 here:
#0 0x62f9e562f28a in free (miniruby+0x1fd28a) (BuildId: 5ad6d9e7cec8318df6726ea5ce34d3c76d0d0233)
#1 0x62f9e58ca2ab in rb_gc_impl_free gc/default/default.c:8102:9
#2 0x62f9e58ca2ab in ruby_sized_xfree gc.c:5029:13
#3 0x62f9e58ca2ab in ruby_xfree gc.c:5040:5
#4 0x62f9e56f88e6 in rb_class_remove_from_super_subclasses class.c:152:9
#5 0x62f9e58c9dd2 in rb_gc_obj_free gc.c:1262:9
#6 0x62f9e58f6e19 in gc_sweep_plane gc/default/default.c:3450:21
#7 0x62f9e58f686a in gc_sweep_page gc/default/default.c:3535:13
#8 0x62f9e58f12b4 in gc_sweep_step gc/default/default.c:3810:9
#9 0x62f9e58ed2a7 in gc_sweep gc/default/default.c:4058:13
...
previously allocated by thread T5 here:
#0 0x62f9e562f70d in calloc (miniruby+0x1fd70d) (BuildId: 5ad6d9e7cec8318df6726ea5ce34d3c76d0d0233)
#1 0x62f9e58c8e1a in calloc1 gc/default/default.c:1472:12
#2 0x62f9e58c8e1a in rb_gc_impl_calloc gc/default/default.c:8138:5
#3 0x62f9e58c8e1a in ruby_xcalloc_body gc.c:4964:12
#4 0x62f9e58c8e1a in ruby_xcalloc gc.c:4958:34
#5 0x62f9e56f906e in push_subclass_entry_to_list class.c:88:13
#6 0x62f9e56f906e in rb_class_subclass_add class.c:111:38
#7 0x62f9e56f906e in RCLASS_SET_SUPER internal/class.h:257:9
#8 0x62f9e56fca7a in make_metaclass class.c:786:5
#9 0x62f9e59db982 in rb_class_initialize object.c:2101:5
Notes:
Merged: https://github.com/ruby/ruby/pull/13284
|
|
Ractor objects that are available in a child process should raise a
`Ractor::ClosedError` exception when called with `send` or `take`
Co-authored-by: John Hawthorn <[email protected]>
Notes:
Merged: https://github.com/ruby/ruby/pull/12982
|
|
Ractors created in a parent process should be properly shut down in the
child process. They need their cache cleared and status set to
"terminated"
Co-authored-by: John Hawthorn <[email protected]>
Notes:
Merged: https://github.com/ruby/ruby/pull/12982
|
|
Ivars will longer be the only thing stored inline
via shapes, so keeping the `iv_index` and `ivptr` names
would be confusing.
Instance variables won't be the only thing stored inline
via shapes, so keeping the `ivptr` name would be confusing.
`field` encompass anything that can be stored in a VALUE array.
Similarly, `gen_ivtbl` becomes `gen_fields_tbl`.
Notes:
Merged: https://github.com/ruby/ruby/pull/13159
|
|
Avoid generating an infinite loop in the case where:
1. Block `first` is adjacent to block `second`, and the branch from `first` to
`second` is a fallthrough, and
2. Block `second` immediately exits to the interpreter, and
3. Block `second` is invalidated and YJIT is OOM
While pondering how to fix this, I think I've stumbled on another related edge case:
1. Block `incoming_one` and `incoming_two` both branch to block `second`. Block
`incoming_one` has a fallthrough
2. Block `second` immediately exits to the interpreter (so it starts with its exit)
3. When Block `second` is invalidated, the incoming fallthrough branch from
`incoming_one` might be rewritten first, which overwrites the start of block
`second` with a jump to a new branch stub.
4. YJIT runs of out memory
5. The incoming branch from `incoming_two` is then rewritten, but because we're
OOM we can't generate a new stub, so we use `second`'s exit as the branch
target. However `second`'s exit was already overwritten with a jump to the
branch stub for `incoming_one`, so `incoming_two` will end up jumping to
`incoming_one`'s branch stub.
Fixes [Bug #21257]
Notes:
Merged: https://github.com/ruby/ruby/pull/13186
Merged-By: XrXr
|
|
(https://github.com/Shopify/zjit/pull/40)
* Assert everything is compiled in test_zjit
* Update a comment on rb_zjit_assert_compiles
Co-authored-by: Maxime Chevalier-Boisvert <[email protected]>
* Add a comment about assert_compiles
* Actually use pipe_fd
---------
Co-authored-by: Maxime Chevalier-Boisvert <[email protected]>
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
* Load Param off of cfp->ep
* Test with --zjit-call-threshold=1 as well
* Fix get_opnd's debug output
* Return Mem operand from gen_param
* Test both first and second calls
* Spell out the namespace for Opnd returns
* Update a comment about gen_param
* Explain why we take a lock
* Fix a typo
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
(https://github.com/Shopify/zjit/pull/30)
* Implement FixnumAdd and stub PatchPoint/GuardType
Co-authored-by: Max Bernstein <[email protected]>
Co-authored-by: Maxime Chevalier-Boisvert <[email protected]>
* Clone Target for arm64
* Use $create instead of use create
Co-authored-by: Alan Wu <[email protected]>
* Fix misindentation from suggested changes
* Drop an unneeded variable for mut
* Load operand into a register only if necessary
---------
Co-authored-by: Max Bernstein <[email protected]>
Co-authored-by: Maxime Chevalier-Boisvert <[email protected]>
Co-authored-by: Alan Wu <[email protected]>
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
This implements a hash set which is wait-free for lookup and lock-free
for insert (unless resizing) to use for fstring de-duplication.
As highlighted in https://bugs.ruby-lang.org/issues/19288, heavy use of
fstrings (frozen interned strings) can significantly reduce the
parallelism of Ractors.
I tried a few other approaches first: using an RWLock, striping a series
of RWlocks (partitioning the hash N-ways to reduce lock contention), and
putting a cache in front of it. All of these improved the situation, but
were unsatisfying as all still required locks for writes (and granular
locks are awkward, since we run the risk of needing to reach a vm
barrier) and this table is somewhat write-heavy.
My main reference for this was Cliff Click's talk on a lock free
hash-table for java https://www.youtube.com/watch?v=HJ-719EGIts. It
turns out this lock-free hash set is made easier to implement by a few
properties:
* We only need a hash set rather than a hash table (we only need keys,
not values), and so the full entry can be written as a single VALUE
* As a set we only need lookup/insert/delete, no update
* Delete is only run inside GC so does not need to be atomic (It could
be made concurrent)
* I use rb_vm_barrier for the (rare) table rebuilds (It could be made
concurrent) We VM lock (but don't require other threads to stop) for
table rebuilds, as those are rare
* The conservative garbage collector makes deferred replication easy,
using a T_DATA object
Another benefits of having a table specific to fstrings is that we
compare by value on lookup/insert, but by identity on delete, as we only
want to remove the exact string which is being freed. This is faster and
provides a second way to avoid the race condition in
https://bugs.ruby-lang.org/issues/21172.
This is a pretty standard open-addressing hash table with quadratic
probing. Similar to our existing st_table or id_table. Deletes (which
happen on GC) replace existing keys with a tombstone, which is the only
type of update which can occur. Tombstones are only cleared out on
resize.
Unlike st_table, the VALUEs are stored in the hash table itself
(st_table's bins) rather than as a compact index. This avoids an extra
pointer dereference and is possible because we don't need to preserve
insertion order. The table targets a load factor of 2 (it is enlarged
once it is half full).
Notes:
Merged: https://github.com/ruby/ruby/pull/12921
|
|
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13078
|
|
Follow-up for https://github.com/ruby/ruby/commit/a2b03ba7cb721d698bebee74c535dea4583a9c28
Notes:
Merged-By: ono-max <[email protected]>
|
|
Using `rb_obj_clone` introduce other problems, such as `initialize_*`
callbacks invocation in the context of the parent ractor.
So we can revert back to copy the content of the object slots,
but in a way that is aware of size pools.
Notes:
Merged: https://github.com/ruby/ruby/pull/13070
|
|
When calling a method that accepts an anonymous splat and literal
keywords without any arguments, an assertion failure was previously
raised. Set rest_index to 0 when setting rest to the frozen hash,
so the args_argc calculation is accurate.
While here, add more tests for methods with anonymous splats with
and without keywords and keyword splats to confirm behavior is
correct.
Also add a basic bootstrap test that would hit the previous assertion
failure.
Co-authored-by: Jean Boussier <[email protected]>
Notes:
Merged: https://github.com/ruby/ruby/pull/13046
|
|
That seemed like the logical thing to do to me, but ko1 disagree.
Notes:
Merged: https://github.com/ruby/ruby/pull/13008
|
|
[Bug #20271]
[Bug #20267]
[Bug #20255]
`rb_obj_alloc(RBASIC_CLASS(obj))` will always allocate from the basic
40B pool, so if `obj` is larger than `40B`, we'll create a corrupted
object when we later copy the shape_id.
Instead we can use the same logic than ractor copy, which is
to use `rb_obj_clone`, and later ask the GC to free the original
object.
We then must turn it into a `T_OBJECT`, because otherwise
just changing its class to `RactorMoved` leaves a lot of
ways to keep using the object, e.g.:
```
a = [1, 2, 3]
Ractor.new{}.send(a, move: true)
[].concat(a) # Should raise, but wasn't.
```
If it turns out that `rb_obj_clone` isn't performant enough
for some uses, we can always have carefully crafted specialized
paths for the types that would benefit from it.
Notes:
Merged: https://github.com/ruby/ruby/pull/13008
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13015
|
|
The following method call:
```ruby
a(*nil)
```
A method call such as `a(*nil)` previously allocated an array, because
it calls `nil.to_a`, but I have determined this array allocation is
unnecessary. The instructions in this case are:
```
0000 putself ( 1)[Li]
0001 putnil
0002 splatarray false
0004 opt_send_without_block <calldata!mid:a, argc:1, ARGS_SPLAT|FCALL>
0006 leave
```
The method call uses `ARGS_SPLAT` without `ARGS_SPLAT_MUT`, so the
returned array doesn't need to be mutable. I believe all cases where
`splatarray false` are used allow the returned object to be frozen,
since the `false` means to not duplicate the array. The optimization
in this case is to have `splatarray false` push a shared empty frozen
array, instead of calling `nil.to_a` to return a newly allocated array.
There is a slightly backwards incompatibility with this optimization,
in that `nil.to_a` is not called. However, I believe the new behavior
of `*nil` not calling `nil.to_a` is more consistent with how `**nil`
does not call `nil.to_hash`. Also, so much Ruby code would break if
`nil.to_a` returned something different from the empty hash, that it's
difficult to imagine anyone actually doing that in real code, though
we have a few tests/specs for that.
I think it would be bad for consistency if `*nil` called `nil.to_a`
in some cases and not others, so this changes other cases to not
call `nil.to_a`:
For `[*nil]`, this uses `splatarray true`, which now allocates a
new array for a `nil` argument without calling `nil.to_a`.
For `[1, *nil]`, this uses `concattoarray`, which now returns
the first array if the second array is `nil`.
This updates the allocation tests to check that the array allocations
are avoided where possible.
Implements [Feature #21047]
Notes:
Merged: https://github.com/ruby/ruby/pull/12597
|
|
Proc objects are now traversed like other objects when making them
shareable.
Fixes [Bug #19372]
Fixes [Bug #19374]
Notes:
Merged: https://github.com/ruby/ruby/pull/12977
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/12981
|
|
This reverts commit eb91c664dc0b4d69db09ae913f2d7a5ef3490d74.
Notes:
Merged: https://github.com/ruby/ruby/pull/12968
|
|
* Increase timeout to fix flaky tests?
* Fix bundler test - wording changed.
expect "fatal: Remote branch deadbeef not found in upstream origin" to
include "Revision deadbeef does not exist in the repository"
Notes:
Merged-By: ioquatix <[email protected]>
|
|
When we forward an FCALL (a method call with an implicit self), we
shouldn't forward the FCALL flag because it ignores method visibility
checks. This patch removes the FCALL flag from callers.
[Bug #21196]
|
|
This changes reference_count on rb_method_definition_struct into an
atomic.
Ractors can create additional references as part of `bind_call` or
(presumably) similar. Because this can be done inside Ractors, we should
use a lock or atomics so that we don't race and avoid incrementing.
Co-authored-by: wanabe <[email protected]>
Notes:
Merged: https://github.com/ruby/ruby/pull/12951
|
|
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/12740
|
|
* YJIT: Fix indentation [ci skip]
Fixes: cdf33ed5f37f9649c482c3ba1d245f0d80ac01ce
* YJIT: Initialize locals in ISeqs defined with `...`
Previously, callers of forwardable ISeqs moved the stack pointer up
without writing to the stack. If there happens to be a stale value in
the area skipped over, it could crash due to "try to mark T_NONE". Also,
the uninitialized local variables were observable through `binding`.
Initialize the locals to nil.
[Bug #21021]
Notes:
Merged-By: maximecb <[email protected]>
|
|
This reverts commit 58b4e249ed8d33fc78528bc77516d541c04d65f2.
The bug that it encountered was fixed in f76d40789d2c7185df26e925636827c06eda7157.
Notes:
Merged: https://github.com/ruby/ruby/pull/12543
|
|
We already check whether `ENV['GITHUB_WORKFLOW']` is equal to `Compilations`,
so we don't need to check that it's not nil.
Notes:
Merged: https://github.com/ruby/ruby/pull/12543
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/12537
|
|
Evident with the crash reported in [Bug #20997], the C replacement
codegen functions aren't authored to handle block arguments (nor
should they because the extra code from the complexity defeats
optimization). Filter sites with VM_CALL_ARGS_BLOCKARG.
Notes:
Merged: https://github.com/ruby/ruby/pull/12536
|
|
Code like the following is crashing for us on 3.4.1:
```ruby
def a(&) = yield(x: 0)
1000.times { a { |x:| x } }
```
Crash:
```
ruby: YJIT has panicked. More info to follow...
thread '<unnamed>' panicked at ./yjit/src/codegen.rs:8018:13:
assertion `left == right` failed
left: 0
right: 1
```
Co-authored-by: Dani Acherkan <[email protected]>
Notes:
Merged: https://github.com/ruby/ruby/pull/12499
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/12479
|
|
These objects didn't retain their frozen status after the move
Bug [#19408]
Notes:
Merged: https://github.com/ruby/ruby/pull/9996
|
|
It used to always try to divide by zero like:
FAIL 1/0 tests failed
|
|
to initialize ractor local storage in thread-safety.
[Feature #20875]
Notes:
Merged: https://github.com/ruby/ruby/pull/12321
|
|
currently these are flaky, so until we can make them more robust, we'll
skip them for MMTk CI
Notes:
Merged: https://github.com/ruby/ruby/pull/12212
|
|
Notes:
Merged-By: ono-max <[email protected]>
|