1297 Commits
Author SHA1 Message Date
Benoit DalozeandClaude Opus 5.5 9ba7257f44 Add a signal fence in rb_ec_clear_vm_stack()
Since the fix for Bug #21441, ec->cfp is set to NULL before clearing
the VM stack so that thread_profile_frames() called from a signal
handler returns early instead of walking a stack that is no longer set
on the ec. But these are plain stores to distinct fields of the same
struct, so the compiler is free to reorder them.

With Apple clang 21 and GCC 16 on macOS arm64, and with clang 18 and
GCC 13 on Linux aarch64, both stores of rb_ec_set_vm_stack() were
combined into a single stp emitted before the store to ec->cfp
(vm_stack is at offset 0, vm_stack_size at 0x8 and cfp at 0x10):

    stp xzr, xzr, [x0]        ; ec->vm_stack = NULL, vm_stack_size = 0
    str xzr, [x0, #0x10]      ; ec->cfp = NULL
    ret

So a signal in between would see a non-NULL ec->cfp with a NULL
ec->vm_stack. Stopping there in lldb when a thread terminates and
calling rb_profile_frames() segfaults in thread_profile_frames(), as
end_cfp is computed from the NULL ec->vm_stack and the loop walks past
the control frames. With this change, ec->cfp is already NULL at that
point and rb_profile_frames() returns 0.

Like in vm_push_frame(), use atomic_signal_fence() to keep the store to
ec->cfp first. With it, all these compilers emit the store to ec->cfp no
later than the stores clearing the stack. The rest of vm.o is unchanged
with Apple clang 21, GCC 16 and GCC 13 on aarch64. Clang 21 and 18:

    stp xzr, xzr, [x0, #0x8]  ; ec->vm_stack_size = 0, cfp = NULL
    str xzr, [x0]             ; ec->vm_stack = NULL
    ret

GCC 16 and 13:

    str xzr, [x0, #0x10]      ; ec->cfp = NULL
    stp xzr, xzr, [x0]        ; ec->vm_stack = NULL, vm_stack_size = 0
    ret

With GCC 13 on x86_64, the store to ec->cfp is already emitted first
without the fence, and vm.o is identical with and without it:

    endbr64
    movq $0x0, 0x10(%rdi)     ; ec->cfp = NULL
    movq $0x0, (%rdi)         ; ec->vm_stack = NULL
    movq $0x0, 0x8(%rdi)      ; ec->vm_stack_size = 0
    ret

[Bug #22477]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 12:35:42 -07:00
Hiroshi SHIBATAandClaude Fable 5 f4f2dfb3c7 Defer unloading box-local extension DLLs on Windows
box_entry_free could run before other T_DATA objects are finalized in
rb_objspace_call_finalizer, and FreeLibrary there unmapped the
rb_data_type_t and dfree functions that later finalization still
dereferences, crashing any boxed process that leaves extension objects
alive at exit. Keep the copied DLLs loaded until ruby_vm_destruct, then
unload and delete them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-13 16:13:54 -07:00
Takashi Kokubun e4beaa2c9c merge revision(s) 9efe557da5, 53f7189ff9: [Backport #22224]
[PATCH] ZJIT: Drop has_blockiseq guard on local variable access (#16113)

	So `has_blockiseq` was masking two other bugs.

	First, that we had only a static EP escape check for an iseq. We need to check
	both that an iseq is not known to statically escape them *and that it has not
	escaped them before*. Only then can we use SSA locals. This popped up in a
	weird PP unit test that was reducible to the test cases added in the PR. So I
	fixed that.

	Second, that we didn't VM lock when code patching for EP escape. That showed up
	in the Ractor btests in my previous PRs #14600 and #15254 #15316, and I had no
	idea what was going on at the time, so I dropped it. But if we add this lock,
	like all the other invariant patching cases have, the problem goes away. Seems
	reasonable.

	Finally, we can use normal full SSA locals even in the presence of a blockiseq.
	We still reload all of them after each send-with-block, but we can in a later
	PR reduce that to just the locals that are (recursively) visibly written to.

	[PATCH] [Bug #22224] Ractors, YJIT: Skip re-reporting recorded EP escapes to the JITs (#18176)

	* [22224] Ractors, YJIT: Skip re-reporting recorded EP escapes to the JITs

	vm_make_env_each reports every EP escape, and both
	rb_yjit_invalidate_ep_is_bp and rb_zjit_invalidate_no_ep_escape take
	the VM lock with a stop-all-Ractors barrier on every call. For a given
	iseq only the first report does anything: afterwards YJIT's
	no_ep_escape_iseqs entry is permanently an empty set and ZJIT's
	ep_escape_iseqs insert is a no-op, so the barrier is paid for nothing.
	Under multiple Ractors this serializes the whole process on every Proc
	created from an already-escaped iseq: a workload creating one lambda
	per iteration runs 150x slower on 8 Ractors than on 1 with YJIT
	enabled.

	Record the report in a jit_ep_escape_recorded flag on the iseq body,
	set by the active JIT while it holds the VM lock, and skip the calls
	in vm_make_env_each once it is set. The flag sits in existing padding
	and the body is ZALLOC'd, so it starts false and dies with the iseq.
	Setting it in the JIT rather than in the caller keeps mid-process
	RubyVM::YJIT.enable correct: escapes reported while no JIT is
	initialized are recorded by nobody and must not set the flag. The
	unlocked read is benign: a stale false causes one redundant,
	idempotent report.

	On the micro-benchmark from the bug, --yjit at 8 Ractors goes from
	0.04M to 11.07M iterations/s (277x).

	* [22224] Deduplicate the EP escape record onto the iseq body

	The jit_ep_escape_recorded flag introduced in the previous commit is
	now the JITs' only record of an EP escape: YJIT's no_ep_escape_iseqs
	keeps only iseqs with live assumptions (the empty-set escape markers
	are gone), and ZJIT's ep_escape_iseqs set is removed together with its
	update_ep_escape_iseqs compaction pass, since the flag lives in the
	iseq body, which the GC does not move. Both JITs read the flag back at
	compile time through a new rb_jit_iseq_ep_escape_recorded_p helper.

	Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>

Only the two correctness changes of 9efe557da5 are backported: the runtime
EP-escape check in iseq_to_hir() and the VM lock in
rb_zjit_invalidate_no_ep_escape().  Without the former, suppressing repeat
escape reports would leave NoEPEscape patch points registered after an
escape never invalidated, because ZJIT here registers them unconditionally.
Its has_blockiseq removal is an optimization and is left out; keeping
has_blockiseq is strictly more conservative.

ZJIT here keeps the name invariants::iseq_escapes_ep() rather than renaming
it to iseq_seen_ep_escape(), since e0184fff1a is not backported.
2026-09-14 15:48:53 -07:00
Satoshi Tagomori 99aac00fa1 [Bug #21881] Separate the master and root box
This change separates the master box from the root box, as the single
master copy of boxes.
Before this change, the root box is the source of copies, and also
it runs builtin classes' code. Builtin code makes changes by
requiring files, thus the source of copies is mutable, be different
from the past one. This causes different internal states of boxes,
depending on when it is created.
After this change, all boxes are created from the master box, and
the master box never runs code, so it realizes the immutable source
of copies.

This also makes is possible to separate RubyGems (and other default
gems) from each boxes. All boxes will have their own copies of RubyGems.
RubyGems has its internal state, but it'll be separated between boxes
after this change.
2026-06-22 09:57:10 -07:00
John HawthornandLuke Gruber 63b082cf0e Store ractor_id directly on EC
This is easier to access as ec->ractor_id instead of pointer-chasing through
ec->thread->ractor->ractor_id

Co-authored-by: Luke Gruber <luke.gru@gmail.com>
2025-12-18 13:43:45 -08:00
John Hawthorn 28c2a5b2a4 Fix env debug assertion failure w/ Ractors+JITs
Previously when using a JIT and Ractors at the same time with debug
assertions turned on this could rarely fail with:

    vm_core.h:1448: Assertion Failed: VM_ENV_FLAGS:FIXNUM_P(flags)

When using Ractors, any time the VM lock is acquired, that may join a
barrier as another Ractor initiates GC. This could be made to happen
reliably by replacing the invalidation with a call to rb_gc().

This assertion failure happens because

    VM_STACK_ENV_WRITE(ep, 0, (VALUE)env);

Is setting VM_ENV_DATA_INDEX_FLAGS to the environment, which is not a
valid set of flags (it should be a fixnum). Although we update cfp->ep,
rb_execution_context_mark will also mark the PREV_EP, and until the
recursive calls to vm_make_env_each all finish the "next" ep may still
be pointing to the stack env we've just escaped.

I'm not completely sure why we need to store this on the stack - why is
setting cfp->ep not enough? I'm also not sure why
rb_execution_context_mark needs to mark the prev_ep.
2025-12-18 11:03:23 -08:00
Luke Gruber 4fb537b1ee Make tracepoints with set_trace_func or TracePoint.new ractor local (#15468)
Before this change, GC'ing any Ractor object caused you to lose all
enabled tracepoints across all ractors (even main). Now tracepoints are
ractor-local and this doesn't happen. Internal events are still global.

Fixes [Bug #19112]
2025-12-16 14:06:55 -05:00
Étienne Barrié 5903ed7ba9 Prevent ifunc procs from being made shareable
[Bug #21775]
2025-12-12 20:27:37 +01:00
John Hawthorn de94f88c68 Register internal tracepoints globally
Internal tracepoints only make sense to run globally, and they already
took completely different paths.
2025-12-08 16:38:45 -08:00
Max Bernstein a25196395e Add BOP_GTGT
This will help JITs (and maybe later the interpreter) optimize
Integer#>>.
2025-12-01 15:19:26 -08:00
Samuel Williams bc9ea585be Add rb_ec_close function to manage execution context cleanup. (#15253) 2025-12-01 04:49:31 +00:00
Nobuyoshi Nakada 2f53985da9 Revert miscommit at "Reset the cache variable before retrying"
This reverts commit 26a9e0b4e3 partially.
2025-11-26 11:35:15 +09:00
Nobuyoshi Nakada 26a9e0b4e3 Reset the cache variable before retrying 2025-11-26 10:47:17 +09:00
Satoshi Tagomori e84b91a292 Box: mark/move Box object referred via ENV/rb_env_t 2025-11-26 10:10:47 +09:00
Nobuyoshi Nakada a24922a680 Remove stale declaration
`rb_zjit_option_enabled_p` seems no longer used/defined since
ruby/ruby#f84bbb423836d9d0d018b8ab71ecceb5868fd5be.
2025-11-20 17:56:51 +09:00
Randy Stauner 7b1e0a6096 ZJIT: Define jit_compile_exception for ZJIT even without YJIT
Seems like an oversight
2025-11-10 13:14:13 -08:00
Satoshi Tagomori d2a587c791 renaming internal data structures and functions from namespace to box 2025-11-07 13:14:54 +09:00
Satoshi Tagomori aaa1234702 update referenced filenames from namespace to box 2025-11-07 13:14:54 +09:00
John Hawthorn cf4a034d59 Use rb_set_memsize for constant cache tables
These were converted to a set in
c0417bd094
2025-11-06 15:46:06 -08:00
Takashi Kokubun bd3b44cb0a ZJIT: Use a shared trampoline across all ISEQs (#15042) 2025-11-04 16:09:13 -08:00
Satoshi Tagomori fa4c04a7f7 Use CFUNC namespace only for IFUNC frames, its behavior should be unchanged 2025-11-03 02:06:11 +09:00
Koichi Sasada bc00c4468e use SET_SHAREABLE
to adopt strict shareable rule.

* (basically) shareable objects only refer shareable objects
* (exception) shareable objects can refere unshareable objects
  but should not leak reference to unshareable objects to Ruby world
2025-10-23 13:08:26 +09:00
Luke Gruberand 27ff586152 We can't grab the VM Lock in free functions
This is due to the way MMTK frees objects, which is on another native thread.
Due to this, there's no `ec` so we can't grab the VM Lock.

This was causing issues in release builds of MMTK on CI like:

```
  /home/runner/work/ruby/ruby/build/ruby(sigsegv+0x46) [0x557905117ef6] ../src/signal.c:948
  /lib/x86_64-linux-gnu/libc.so.6(0x7f555f845330) [0x7f555f845330]
  /home/runner/work/ruby/ruby/build/ruby(rb_ec_thread_ptr+0x0) [0x5579051d59d5] ../src/vm_core.h:2087
  /home/runner/work/ruby/ruby/build/ruby(rb_ec_ractor_ptr) ../src/vm_core.h:2036
  /home/runner/work/ruby/ruby/build/ruby(rb_current_execution_context) ../src/vm_core.h:2105
  /home/runner/work/ruby/ruby/build/ruby(rb_current_ractor_raw) ../src/vm_core.h:2104
  /home/runner/work/ruby/ruby/build/ruby(rb_current_ractor) ../src/vm_core.h:2112
  /home/runner/work/ruby/ruby/build/ruby(rb_current_ractor) ../src/vm_core.h:2110
  /home/runner/work/ruby/ruby/build/ruby(vm_locked) ../src/vm_sync.c:15
  /home/runner/work/ruby/ruby/build/ruby(rb_vm_lock_enter_body) ../src/vm_sync.c:141
  /home/runner/work/ruby/ruby/build/ruby(rb_vm_lock_enter+0xa) [0x557905390a5a] ../src/vm_sync.h:76
  /home/runner/work/ruby/ruby/build/ruby(fiber_pool_stack_release) ../src/cont.c:777
  /home/runner/work/ruby/ruby/build/ruby(fiber_stack_release+0xe) [0x557905392075] ../src/cont.c:919
  /home/runner/work/ruby/ruby/build/ruby(cont_free) ../src/cont.c:1087
  /home/runner/work/ruby/ruby/build/ruby(fiber_free) ../src/cont.c:1180
```

This would have ran into an assertion error in a debug build but we don't run debug builds of MMTK on Github's CI.

Co-authored-by: john.hawthorn@shopify.com
2025-10-15 10:49:37 -07:00
Satoshi Tagomori c78895b1d6 Add "Namespace detection information" section in bug reports
* To show environments stack when the current namespace is unexpected or
  namespace detection is broken
* It is displayed only when RUBY_BUGREPORT_NAMESPACE_ENV=1 is specified
2025-10-13 17:41:36 +09:00
Satoshi Tagomori 25c893af6d Add a control frame column "n:xxxx" as namespace id in crash reports 2025-10-07 22:18:29 +09:00
Satoshi Tagomori 9a0e857c35 Stop displaying current namespace when it crashed
To avoid crashes during displaying crash reports.
2025-10-07 22:18:29 +09:00
Peter Zhu 1858233ffa Free the native thread of the main thread on FREE_AT_EXIT 2025-10-04 18:21:13 -04:00
Peter Zhu c36c80bc25 Always free the main thread in RUBY_FREE_AT_EXIT 2025-10-02 17:09:38 -04:00
Peter Zhu d8c8623f50 Set context_stack on main thread
We allocate the stack of the main thread using malloc, but we never set
malloc_stack to true and context_stack. If we fork, the main thread may
no longer be the main thread anymore so it reports memory being leaked
in RUBY_FREE_AT_EXIT.

This commit allows the main thread to free its own VM stack at shutdown.
2025-09-30 09:47:29 -04:00
Satoshi Tagomori ccbf0662f8 No need to set namespace to the frame start evaluating main
* rb_vm_current_namespace() returns main_namespace if it's ready,
  root_namespace otherwise on the top of main_stack.
2025-09-29 01:15:38 +09:00
Satoshi Tagomori f58f7f25a3 Fix bug of uninitialized variable, missed EoCFP, return values 2025-09-29 01:15:38 +09:00
Satoshi Tagomori bff625d2a6 add VM_ENV_NAMESPACED_P to unify/simplify/correct when SPECVAL has a namespace 2025-09-29 01:15:38 +09:00
Satoshi Tagomori 20c73b1723 Skip CFUNC frames in the current namespace detection
* The current namespace should be based on the Ruby-level location (file, line no in .rb)
  and we can get it by LEP(ep) basically (VM_ENV_FLAG_LOCAL flag is set)
* But the control frame with VM_FRAME_MAGIC_CFUNC is also a LOCAL frame because
  it's a visible Ruby-level frame without block handlers
* So, for the namespace detection, LEP(ep) is not enough and we need to skip CFUNC
  frames to fetch the caller of such frames
2025-09-29 01:15:38 +09:00
Satoshi Tagomori bb21b619f0 Detect the correct loading namespace from control frames
* checking all control frames (instead of filtering by VM_FRAME_RUBYFRAME_P)
  because VM_FRAME_FLAG_NS_REQUIRE is set on non-rubyframe
* skip frames of CFUNC in the root namespace for Kernel#require (etc) to avoid
  detecting the root namespace of those frames wrongly
2025-09-29 01:15:38 +09:00
Satoshi Tagomori c755f35f0e Stop using ns->top_self here because it's set to th->top_self beforehand if needed 2025-09-29 01:15:38 +09:00
Satoshi Tagomori 76c4663a77 Fix Namespace.current to show its caller's namespace
Calling rb_current_namespace() in rb_namespace_current() means to show
the definition namespace of Namespace.current itself (it's the root always)
but the users' expectation is to show the namespace of the place where
the Namespace.current is called.
2025-09-29 01:15:38 +09:00
Satoshi Tagomori 4f47327287 Update current namespace management by using control frames and lexical contexts
to fix inconsistent and wrong current namespace detections.

This includes:
* Moving load_path and related things from rb_vm_t to rb_namespace_t to simplify
  accessing those values via namespace (instead of accessing either vm or ns)
* Initializing root_namespace earlier and consolidate builtin_namespace into root_namespace
* Adding VM_FRAME_FLAG_NS_REQUIRE for checkpoints to detect a namespace to load/require files
* Removing implicit refinements in the root namespace which was used to determine
  the namespace to be loaded (replaced by VM_FRAME_FLAG_NS_REQUIRE)
* Removing namespaces from rb_proc_t because its namespace can be identified by lexical context
* Starting to use ep[VM_ENV_DATA_INDEX_SPECVAL] to store the current namespace when
  the frame type is MAGIC_TOP or MAGIC_CLASS (block handlers don't exist in this case)
2025-09-29 01:15:38 +09:00
Koichi Sasada 55b1ba3bf2 Ractor.shareable_proc
call-seq:
  Ractor.sharable_proc(self: nil){} -> sharable proc

It returns shareable Proc object. The Proc object is
shareable and the self in a block will be replaced with
the value passed via `self:` keyword.

In a shareable Proc, the outer variables should
* (1) refer shareable objects
* (2) be not be overwritten

```ruby
  a = 42
  Ractor.shareable_proc{ p a }
  #=> OK

  b = 43
  Ractor.shareable_proc{ p b; b = 44 }
  #=> Ractor::IsolationError because 'b' is reassigned in the block.

  c = 44
  Ractor.shareable_proc{ p c }
  #=> Ractor::IsolationError because 'c' will be reassigned outside of the block.
  c = 45

  d = 45
  d = 46 if cond
  Ractor.shareable_proc{ p d }
  #=> Ractor::IsolationError because 'd' was reassigned outside of the block.
```

The last `d`'s case can be relaxed in a future version.

The above check will be done in a static analysis at compile time,
so the reflection feature such as `Binding#local_varaible_set`
can not be detected.

```ruby
  e = 42
  shpr = Ractor.shareable_proc{ p e } #=> OK
  binding.local_variable_set(:e, 43)
  shpr.call #=> 42 (returns captured timing value)
```

Ractor.sharaeble_lambda is also introduced.
[Feature #21550]
[Feature #21557]
2025-09-24 03:59:03 +09:00
Jun Aruga 971174054a Add a macro to manage the condition of no-inline version rb_current_ec
Add the macro `RB_THREAD_CURRENT_EC_NOINLINE` to manage the condition to use
no-inline version rb_current_ec for a better maintainability.

Note that the `vm_core.h` includes the `THREAD_IMPL_H` by the
`#include THREAD_IMPL_H`. The `THREAD_IMPL_H` can be `thread_none.h`,
`thread_pthread.h` or `thread_win32.h` according to the
`tool/m4/ruby_thread.m4` creating the `THREAD_IMPL_H`.

The change in this commit only defining the `RB_THREAD_CURRENT_EC_NOINLINE` in
the `thread_pthread.h` is okay in this situation. Because in the
`thread_none.h` case, the thread feature is not used at all, including
Thread-Local Storage (TLS), and in the `thread_win32.h` case, the
`RB_THREAD_LOCAL_SPECIFIER` is not defined. In the `thread_pthread.h` case, the
`RB_THREAD_LOCAL_SPECIFIER` is defined in the `configure.ac`. In the
`thread_none.h` case, the `RB_THREAD_LOCAL_SPECIFIER` is defined in the
`thread_none.h`.
2025-09-16 11:37:26 +01:00
Takashi Kokubun 4f030951f2 ZJIT: Invalidate local variables on EP escape (#14448) 2025-09-05 11:26:01 -07:00
Jun Aruga 73854a4b65 Use no-inline version rb_current_ec on ppc64le
This commit fixes the failures in bootstraptest/test_ractor.rb reported on
the issue ticket <https://bugs.ruby-lang.org/issues/21534>.

TLS (Thread-Local Storage) may not be accessed across .so on ppc64le too.
I am not sure about that.  The comment "// TLS can not be accessed across
.so on ..." in this commit comes from the following commit.

https://github.com/ruby/ruby/commit/319afed20fba8f9b44611d16e4930260f7b56b86#diff-408391c43b2372cfe1fefb3e1c2531df0184ed711f46d229b08964ec9e8fa8c7R118
> // on Darwin, TLS can not be accessed across .so`

This failures only happened when configuring with cppflags=-DRUBY_DEBUG and -O3
on ppc64le.

The reproducing steps were below.

```
$ ./autogen.sh

$ ./configure -C --disable-install-doc cppflags=-DRUBY_DEBUG

$ make -j4

$ make btest BTESTS=bootstraptest/test_ractor.rb
...
FAIL 2/147 tests failed
make: *** [uncommon.mk:913: yes-btest] Error 1
```

The steps with a reproducing script based on the `bootstraptest/test_ractor.rb`
were below.

```
$ cat test_ractor_1.rb
counts = []
counts << Ractor.count
p counts.inspect

ractors = (1..2).map { Ractor.new { Ractor.receive } }
counts << Ractor.count
p counts.inspect

ractors[0].send('End 0').join
sleep 0.1 until ractors[0].inspect =~ /terminated/
counts << Ractor.count
p counts.inspect

ractors[1].send('End 1').join
sleep 0.1 until ractors[1].inspect =~ /terminated/
counts << Ractor.count
p counts.inspect

$ make run TESTRUN_SCRIPT=test_ractor_1.rb
...
vm_core.h:2017: Assertion Failed: rb_current_execution_context:ec == rb_current_ec_noinline()
...
```

The assertion failure happened at the following line.

https://github.com/ruby/ruby/blob/f3206cc79bec2fd852e81ec56de59f0a67ab32b7/vm_core.h#L2017

This fix is similar with the following commit for the arm64.

https://github.com/ruby/ruby/commit/f7059af50a31a4d27a04ace0beadb60616f3f971

Fixes [Bug #21534]
2025-09-04 15:55:27 +01:00
Takashi Kokubun 809a9a9f29 ZJIT: Count exits coming from jit_exception (#14428) 2025-09-03 13:14:28 -07:00
Yusuke Endoh 2ccb2de677 Make RubyVM::AST.of return a parent node of NODE_SCOPE
This change makes `RubyVM::AST.of` and `.node_id_for_backtrace_location`
return a parent node of NODE_SCOPE (such as NODE_DEFN) instead of the
NODE_SCOPE node itself.
(In future, we may remove NODE_SCOPE, which is a bit hacky AST node.)

This is preparation for [Feature #21543].
2025-08-28 12:44:04 +09:00
Jean Boussier 2083fa89fc Implement gen_fields_tbl cache
There is a high likelyhood that `rb_obj_fields` is called
consecutively for the same object.

If we keep a cache of the last IMEMO/fields we interacted with,
we can save having to lookup the `gen_fields_tbl`, synchronize
the VM lock, etc.

On yjit-bench's, I instrumented the hit rate of this cache at:

  - `shipit`: 38%, with 111k hits.
  - `lobsters`: 59%, with 367k hits.
  - `rubocop`: 100% with only 300 hits.

I also ran a micro-benchmark which shows that ivar access is:

  - 1.25x faster when the cache is hit in single ractor mode.
  - 2x faster when the cache is hit in multi ractor mode.
  - 1.06x slower when the cache miss in single ractor mode.
  - 1.01x slower when the cache miss in multi ractor mode.

```yml
prelude: |
  class GenIvar < Array
    def initialize(...)
      super
      @iv = 1
    end

    attr_reader :iv
  end

  a = GenIvar.new
  b = GenIvar.new
benchmark:
  hit: a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv; a.iv;
  miss: a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv; a.iv; b.iv;
```

Single ractor:
```
compare-ruby: ruby 3.5.0dev (2025-08-12T02:14:57Z master 428937a536) +YJIT +PRISM [arm64-darwin24]
built-ruby: ruby 3.5.0dev (2025-08-12T09:25:35Z gen-fields-cache 9456c35893) +YJIT +PRISM [arm64-darwin24]
warming up..

|      |compare-ruby|built-ruby|
|:-----|-----------:|---------:|
|hit   |      4.090M|    5.121M|
|      |           -|     1.25x|
|miss  |      3.756M|    3.534M|
|      |       1.06x|         -|
```

Multi-ractor:
```
compare-ruby: ruby 3.5.0dev (2025-08-12T02:14:57Z master 428937a536) +YJIT +PRISM [arm64-darwin24]
built-ruby: ruby 3.5.0dev (2025-08-12T09:25:35Z gen-fields-cache 9456c35893) +YJIT +PRISM [arm64-darwin24]
warming up..

|      |compare-ruby|built-ruby|
|:-----|-----------:|---------:|
|hit   |      2.205M|    4.460M|
|      |           -|     2.02x|
|miss  |      2.117M|    2.094M|
|      |       1.01x|         -|
```
2025-08-13 19:54:56 +02:00
Peter Zhu 95320f1ddf Fix RUBY_FREE_AT_EXIT for static symbols
Since static symbols allocate memory, we should deallocate them at shutdown
to prevent memory leaks from being reported with RUBY_FREE_AT_EXIT.
2025-08-05 12:04:27 -04:00
Jean Boussier fc5e1541e4 Use rb_gc_mark_weak for cc->klass.
One of the biggest remaining contention point is `RClass.cc_table`.
The logical solution would be to turn it into a managed object, so
we can use an RCU strategy, given it's read heavy.

However, that's not currently possible because the table can't
be freed before the owning class, given the class free function
MUST go over all the CC entries to invalidate them.

However if the `CC->klass` reference is weak marked, then the
GC will take care of setting the reference to `Qundef`.
2025-08-01 10:42:04 +02:00
Takashi Kokubun 2cd10de330 ZJIT: Prepare for sharing JIT hooks with ZJIT (#14044) 2025-07-30 10:11:10 -07:00
Takashi Kokubun b22eb0e468 ZJIT: Add --zjit-stats (#14034) 2025-07-29 10:00:15 -07:00
Peter Zhu 2bcb155b49 Convert global symbol table to concurrent set 2025-07-21 10:58:30 -04:00
John Hawthorn cfc006d410 Always use atomics to get the shape count
When sharing between threads we need both atomic reads and writes. We
probably didn't need to use this in some cases (where we weren't running
in multi-ractor mode) but I think it's best to be consistent.
2025-07-09 10:38:04 -07:00