This commit extracts the Ractor safe table used for frozen strings into
ractor_safe_table.c, which will allow it to be used elsewhere, including
for the global symbol table.
In commit 12f7ba5ed4, ractor safety was added to String#crypt, however
in certain cases it can cause a deadlock. When we lock a native mutex,
we cannot allocate ruby objects because they might trigger GC which
starts a VM barrier. If the barrier is triggered and other native threads
are waiting on this mutex, they will not be able to be woken up in order to join
the barrier. To fix this, we don't allocate ruby objects when we hold the
lock.
The following could reproduce the problem:
```ruby
strings = []
10_000.times do |i|
strings << "my string #{i}"
end
STRINGS = Ractor.make_shareable(strings)
rs = []
100.times do
rs << Ractor.new do
STRINGS.each do |s|
s.dup.crypt(s.dup)
end
end
end
while rs.any?
r, obj = Ractor.select(*rs)
rs.delete(r)
end
```
I will not be adding tests because I am almost finished a PR to enable
running test-all test cases inside many ractors at once, which is how I
found the issue.
Co-authored-by: jhawthorn <john@hawthorn.email>
The `FL_FREEZE` flag is redundant with `SHAPE_ID_FL_FROZEN`, so
ideally it should be eliminated in favor of the later.
Doing so would eliminate the risk of desync between the two, but
also solve the problem of the frozen status being global in namespace
context (See Bug #21330).
This makes `RBobject` `4B` larger on 32 bit systems
but simplifies the implementation a lot.
[Feature #21353]
Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
And get rid of the `obj_to_id_tbl`
It's no longer needed, the `object_id` is now stored inline
in the object alongside instance variables.
We still need the inverse table in case `_id2ref` is invoked, but
we lazily build it by walking the heap if that happens.
The `object_id` concern is also no longer a GC implementation
concern, but a generic implementation.
Co-Authored-By: Matt Valentine-House <matt@eightbitraptor.com>
```
../string.c:660:38: warning: comparison of integers of different signs: 'rb_atomic_t' (aka 'unsigned int') and 'int' [-Wsign-compare]
660 | RUBY_ASSERT(table->count < table->capacity / 2);
```
The fstring table size used to be reported as part of the VM
size, but since it was refactored to be lock-less it was no
longer reported.
Since it's now wrapped by a `T_DATA`, we can implement its
`dsize` function and get a valuable insight into the size
of the table.
```
{"address":"0x100ebff18", "type":"DATA", "shape_id":0, "slot_size":80,
"struct":"VM/fstring_table", "memsize":131176, ...
```