Now GVL is not process *Global* so this patch try to use
another words.
* `rb_global_vm_lock_t` -> `struct rb_thread_sched`
* `gvl->owner` -> `sched->running`
* `gvl->waitq` -> `sched->readyq`
* `rb_gvl_init` -> `rb_thread_sched_init`
* `gvl_destroy` -> `rb_thread_sched_destroy`
* `gvl_acquire` -> `thread_sched_to_running` # waiting -> ready -> running
* `gvl_release` -> `thread_sched_to_waiting` # running -> waiting
* `gvl_yield` -> `thread_sched_yield`
* `GVL_UNLOCK_BEGIN` -> `THREAD_BLOCKING_BEGIN`
* `GVL_UNLOCK_END` -> `THREAD_BLOCKING_END`
* removed
* `rb_ractor_gvl`
* `rb_vm_gvl_destroy` (not used)
There are GVL functions such as `rb_thread_call_without_gvl()` yet
but I don't have good name to replace them. Maybe GVL stands for
"Greate Valuable Lock" or something like that.
I have this scripts that deadlocks after about
5 minutes if I repeatedly run it with a shell loop:
```ruby
$VERBOSE = nil
lamb = ->(main, gc) do
gc.verify_internal_consistency
gc.verify_internal_consistency
main << 1
gc.verify_internal_consistency
gc.verify_internal_consistency
main << 2
gc.verify_internal_consistency
gc.verify_internal_consistency
main << 3
gc.verify_internal_consistency
gc.verify_internal_consistency
end
lamb[[], GC]
lamb[[], GC]
r = Ractor.new Ractor.current, GC, &lamb
a = []
a << Ractor.receive_if{|msg| msg == 2}
a << Ractor.receive_if{|msg| msg == 3}
a << Ractor.receive_if{|msg| msg == 1}
```
Shell loop:
```shell
while ./miniruby deadlock.rb; do date; done
```
Once it locks up, CTRL-C doesn't interrupt the process which
led me to infer `receive_if` is looping in `ractor_receive_if()`
without checking for interrupts. This can be confirmed by
attaching a debugger to the deadlocked miniruby.
The deadlock has one thread looping in `receive_if`
and another waiting in `rb_vm_barrier()`. The barrier relies
on interrupt checking to finish. Theoretically speaking the
`rb_vm_barrier()` could come from one thread naturally starting GC.
We found this while developing YJIT but it dead locks running
with YJIT disabled. YJIT currently relies on `rb_vm_barrier()`
to synchronize before changing memory protection.
This diff adds an interrupt check in the loop in `Ractor#receive_if`
which seems to fix the deadlock.
In addition, this commit allows interrupting the following single
ractor script with CTRL-C.
```shell
ruby -e 'Ractor.current.send(3); Ractor.receive_if { false }'
```
`USE_RUBY_DEBUG_LOG` was only defined when `RUBY_DEVEL` is defined.
This patch removes this dependency (`USE_RUBY_DEBUG_LOG` is defined
independently from `RUBY_DEVEL`).
Do not commit a patch which enables `USE_RUBY_DEBUG_LOG`.
* ujit: implement opt_getinlinecache
Aggressively bet that writes to constants don't happen and invalidate
all opt_getinlinecache blocks on any and all constant writes.
Use alignment padding on block_t to track this assumption. No change to
sizeof(block_t).
* Fix compile warnings when not RUBY_DEBUG
* Fix reversed condition
* Switch to st_table to keep track of assumptions
Co-authored-by: Aaron Patterson <aaron.patterson@gmail.com>
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
rb_objspace_reachable_objects_from requires that the GC not be active.
Since the Ractor barrier is not executed for incremental sweeping,
Ractor may call rb_objspace_reachable_objects_from after sweeping
has started to share objects. This causes a crash that looks like
the following:
```
<internal:ractor>:627: [BUG] rb_objspace_reachable_objects_from() is not supported while during_gc == true
```
Co-authored-by: Vinicius Stock <vinicius.stock@shopify.com>
When a Ractor is removed, the freelist in the Ractor cache is not
returned to the GC, leaving the freelist permanently lost. This commit
recycles the freelist when the Ractor is destroyed, preventing a memory
leak from occurring.
If the GC has been disabled we need to re-enable it so we can evacuate
the transient heap.
Fixes https://bugs.ruby-lang.org/issues/17985
[Bug #17985] [ruby-core:104260]
Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
Ractor.yield(obj, move: true) and
Ractor.select(..., yield_value: obj, move: true) tried to yield a
value with move semantices, but if the trial is faild, the obj
should not become a moved object.
To keep this rule, `wait_moving` wait status is introduced.
New yield/take process:
(1) If a ractor tried to yield (move:true), make taking racotr's
wait status `wait_moving` and make a moved object by
`ractor_move(obj)` and wakeup taking ractor.
(2) If a ractor tried to take a message from a ractor waiting fo
yielding (move:true), wakeup the ractor and wait for (1).
When `literal`, check if the literal about to be assigned to a
constant is ractor-shareable, otherwise raise `Ractor::Error` at
runtime instead of `SyntaxError`.
separate some fields from rb_ractor_t to rb_ractor_pub and put it
at the beggining of rb_ractor_t and declare it in vm_core.h so
vm_core.h can access rb_ractor_pub fields.
Now rb_ec_ractor_hooks() is a complete inline function and no
MJIT related issue.
Ractor has several restrictions to keep each ractor being isolated
and some operation such as `CONST="foo"` in non-main ractor raises
an exception. This kind of operation raises an error but there is
confusion (some code raises RuntimeError and some code raises
NameError).
To make clear we introduce Ractor::IsolationError which is raised
when the isolation between ractors is violated.
Ractor.make_shareable(obj) tries to make obj a shareable object
by changing the attribute of obj and traversable objects from obj
(mainly freeze them).
"copy: true" option is more conservative approach by make deep
copied object and make it sharable. It doesn't affect any existing
objects.
This adds rb_category_compile_warn in order to emit compiler warnings
with categories. Note that Ripper currently ignores the category
for these warnings, but by default it ignores the warnings completely,
so this shouldn't matter.
ractor_sleep() can remain wait.status by interrupt, so that this
patch handles more correctly.
This patch fixed this kind of assertion failures:
Assertion Failed: ../src/ractor.c:1332:ractor_yield_atexit:cr->sync.wait.status == wait_none
Thread's interrupt set Ractor's wakeup_status as interrupted, but
the status remains next Ractor communication API. This patch makes
to ignore the previous interrupt state.
[Bug #17366]
Also this patch solves the Thread#kill and Ractor#take issues.