mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
Struct: keep direct reference to IMEMO/fields when space allows
It's not rare for structs to have additional ivars, hence are one of the most common, if not the most common type in the `gen_fields_tbl`. This can cause Ractor contention, but even in single ractor mode means having to do a hash lookup to access the ivars, and increase GC work. Instead, unless the struct is perfectly right sized, we can store a reference to the associated IMEMO/fields object right after the last struct member. ``` compare-ruby: ruby 3.5.0dev (2025-08-06T12:50:36Z struct-ivar-fields-2 9a30d141a1) +PRISM [arm64-darwin24] built-ruby: ruby 3.5.0dev (2025-08-06T12:57:59Z struct-ivar-fields-2 2ff3ec237f) +PRISM [arm64-darwin24] warming up..... | |compare-ruby|built-ruby| |:---------------------|-----------:|---------:| |member_reader | 590.317k| 579.246k| | | 1.02x| -| |member_writer | 543.963k| 527.104k| | | 1.03x| -| |member_reader_method | 213.540k| 213.004k| | | 1.00x| -| |member_writer_method | 192.657k| 191.491k| | | 1.01x| -| |ivar_reader | 403.993k| 569.915k| | | -| 1.41x| ``` Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
This commit is contained in:
parent
9b3ad3449b
commit
f3206cc79b
8 changed files with 194 additions and 23 deletions
|
@ -1,5 +1,12 @@
|
||||||
prelude: |
|
prelude: |
|
||||||
C = Struct.new(:x) do
|
C = Struct.new(:x) do
|
||||||
|
def initialize(...)
|
||||||
|
super
|
||||||
|
@ivar = 42
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_accessor :ivar
|
||||||
|
|
||||||
class_eval <<-END
|
class_eval <<-END
|
||||||
def r
|
def r
|
||||||
#{'x;'*256}
|
#{'x;'*256}
|
||||||
|
@ -15,11 +22,16 @@ prelude: |
|
||||||
m = method(:x=)
|
m = method(:x=)
|
||||||
#{'m.call(nil);'*256}
|
#{'m.call(nil);'*256}
|
||||||
end
|
end
|
||||||
|
def r_ivar
|
||||||
|
#{'ivar;'*256}
|
||||||
|
end
|
||||||
END
|
END
|
||||||
end
|
end
|
||||||
|
C.new(nil) # ensure common shape is known
|
||||||
obj = C.new(nil)
|
obj = C.new(nil)
|
||||||
benchmark:
|
benchmark:
|
||||||
member_reader: "obj.r"
|
member_reader: "obj.r"
|
||||||
member_writer: "obj.w"
|
member_writer: "obj.w"
|
||||||
member_reader_method: "obj.rm"
|
member_reader_method: "obj.rm"
|
||||||
member_writer_method: "obj.wm"
|
member_writer_method: "obj.wm"
|
||||||
|
ivar_reader: "obj.r_ivar"
|
||||||
|
|
8
depend
8
depend
|
@ -6065,6 +6065,7 @@ hash.$(OBJEXT): $(top_srcdir)/internal/set_table.h
|
||||||
hash.$(OBJEXT): $(top_srcdir)/internal/st.h
|
hash.$(OBJEXT): $(top_srcdir)/internal/st.h
|
||||||
hash.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
|
hash.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
|
||||||
hash.$(OBJEXT): $(top_srcdir)/internal/string.h
|
hash.$(OBJEXT): $(top_srcdir)/internal/string.h
|
||||||
|
hash.$(OBJEXT): $(top_srcdir)/internal/struct.h
|
||||||
hash.$(OBJEXT): $(top_srcdir)/internal/symbol.h
|
hash.$(OBJEXT): $(top_srcdir)/internal/symbol.h
|
||||||
hash.$(OBJEXT): $(top_srcdir)/internal/thread.h
|
hash.$(OBJEXT): $(top_srcdir)/internal/thread.h
|
||||||
hash.$(OBJEXT): $(top_srcdir)/internal/time.h
|
hash.$(OBJEXT): $(top_srcdir)/internal/time.h
|
||||||
|
@ -6288,6 +6289,7 @@ hash.$(OBJEXT): {$(VPATH)}symbol.h
|
||||||
hash.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
|
hash.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
|
||||||
hash.$(OBJEXT): {$(VPATH)}thread_native.h
|
hash.$(OBJEXT): {$(VPATH)}thread_native.h
|
||||||
hash.$(OBJEXT): {$(VPATH)}util.h
|
hash.$(OBJEXT): {$(VPATH)}util.h
|
||||||
|
hash.$(OBJEXT): {$(VPATH)}variable.h
|
||||||
hash.$(OBJEXT): {$(VPATH)}vm_core.h
|
hash.$(OBJEXT): {$(VPATH)}vm_core.h
|
||||||
hash.$(OBJEXT): {$(VPATH)}vm_debug.h
|
hash.$(OBJEXT): {$(VPATH)}vm_debug.h
|
||||||
hash.$(OBJEXT): {$(VPATH)}vm_opts.h
|
hash.$(OBJEXT): {$(VPATH)}vm_opts.h
|
||||||
|
@ -12926,6 +12928,7 @@ range.$(OBJEXT): $(top_srcdir)/internal/enumerator.h
|
||||||
range.$(OBJEXT): $(top_srcdir)/internal/error.h
|
range.$(OBJEXT): $(top_srcdir)/internal/error.h
|
||||||
range.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
|
range.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
|
||||||
range.$(OBJEXT): $(top_srcdir)/internal/gc.h
|
range.$(OBJEXT): $(top_srcdir)/internal/gc.h
|
||||||
|
range.$(OBJEXT): $(top_srcdir)/internal/imemo.h
|
||||||
range.$(OBJEXT): $(top_srcdir)/internal/numeric.h
|
range.$(OBJEXT): $(top_srcdir)/internal/numeric.h
|
||||||
range.$(OBJEXT): $(top_srcdir)/internal/range.h
|
range.$(OBJEXT): $(top_srcdir)/internal/range.h
|
||||||
range.$(OBJEXT): $(top_srcdir)/internal/serial.h
|
range.$(OBJEXT): $(top_srcdir)/internal/serial.h
|
||||||
|
@ -12948,6 +12951,7 @@ range.$(OBJEXT): {$(VPATH)}config.h
|
||||||
range.$(OBJEXT): {$(VPATH)}defines.h
|
range.$(OBJEXT): {$(VPATH)}defines.h
|
||||||
range.$(OBJEXT): {$(VPATH)}encoding.h
|
range.$(OBJEXT): {$(VPATH)}encoding.h
|
||||||
range.$(OBJEXT): {$(VPATH)}id.h
|
range.$(OBJEXT): {$(VPATH)}id.h
|
||||||
|
range.$(OBJEXT): {$(VPATH)}id_table.h
|
||||||
range.$(OBJEXT): {$(VPATH)}intern.h
|
range.$(OBJEXT): {$(VPATH)}intern.h
|
||||||
range.$(OBJEXT): {$(VPATH)}internal.h
|
range.$(OBJEXT): {$(VPATH)}internal.h
|
||||||
range.$(OBJEXT): {$(VPATH)}internal/abi.h
|
range.$(OBJEXT): {$(VPATH)}internal/abi.h
|
||||||
|
@ -15580,6 +15584,7 @@ shape.$(OBJEXT): $(top_srcdir)/internal/serial.h
|
||||||
shape.$(OBJEXT): $(top_srcdir)/internal/set_table.h
|
shape.$(OBJEXT): $(top_srcdir)/internal/set_table.h
|
||||||
shape.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
|
shape.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
|
||||||
shape.$(OBJEXT): $(top_srcdir)/internal/string.h
|
shape.$(OBJEXT): $(top_srcdir)/internal/string.h
|
||||||
|
shape.$(OBJEXT): $(top_srcdir)/internal/struct.h
|
||||||
shape.$(OBJEXT): $(top_srcdir)/internal/symbol.h
|
shape.$(OBJEXT): $(top_srcdir)/internal/symbol.h
|
||||||
shape.$(OBJEXT): $(top_srcdir)/internal/variable.h
|
shape.$(OBJEXT): $(top_srcdir)/internal/variable.h
|
||||||
shape.$(OBJEXT): $(top_srcdir)/internal/vm.h
|
shape.$(OBJEXT): $(top_srcdir)/internal/vm.h
|
||||||
|
@ -16569,6 +16574,7 @@ string.$(OBJEXT): $(top_srcdir)/internal/serial.h
|
||||||
string.$(OBJEXT): $(top_srcdir)/internal/set_table.h
|
string.$(OBJEXT): $(top_srcdir)/internal/set_table.h
|
||||||
string.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
|
string.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
|
||||||
string.$(OBJEXT): $(top_srcdir)/internal/string.h
|
string.$(OBJEXT): $(top_srcdir)/internal/string.h
|
||||||
|
string.$(OBJEXT): $(top_srcdir)/internal/struct.h
|
||||||
string.$(OBJEXT): $(top_srcdir)/internal/transcode.h
|
string.$(OBJEXT): $(top_srcdir)/internal/transcode.h
|
||||||
string.$(OBJEXT): $(top_srcdir)/internal/variable.h
|
string.$(OBJEXT): $(top_srcdir)/internal/variable.h
|
||||||
string.$(OBJEXT): $(top_srcdir)/internal/vm.h
|
string.$(OBJEXT): $(top_srcdir)/internal/vm.h
|
||||||
|
@ -16766,6 +16772,7 @@ string.$(OBJEXT): {$(VPATH)}thread.h
|
||||||
string.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
|
string.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
|
||||||
string.$(OBJEXT): {$(VPATH)}thread_native.h
|
string.$(OBJEXT): {$(VPATH)}thread_native.h
|
||||||
string.$(OBJEXT): {$(VPATH)}util.h
|
string.$(OBJEXT): {$(VPATH)}util.h
|
||||||
|
string.$(OBJEXT): {$(VPATH)}variable.h
|
||||||
string.$(OBJEXT): {$(VPATH)}vm_core.h
|
string.$(OBJEXT): {$(VPATH)}vm_core.h
|
||||||
string.$(OBJEXT): {$(VPATH)}vm_debug.h
|
string.$(OBJEXT): {$(VPATH)}vm_debug.h
|
||||||
string.$(OBJEXT): {$(VPATH)}vm_opts.h
|
string.$(OBJEXT): {$(VPATH)}vm_opts.h
|
||||||
|
@ -18103,6 +18110,7 @@ variable.$(OBJEXT): $(top_srcdir)/internal/serial.h
|
||||||
variable.$(OBJEXT): $(top_srcdir)/internal/set_table.h
|
variable.$(OBJEXT): $(top_srcdir)/internal/set_table.h
|
||||||
variable.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
|
variable.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
|
||||||
variable.$(OBJEXT): $(top_srcdir)/internal/string.h
|
variable.$(OBJEXT): $(top_srcdir)/internal/string.h
|
||||||
|
variable.$(OBJEXT): $(top_srcdir)/internal/struct.h
|
||||||
variable.$(OBJEXT): $(top_srcdir)/internal/symbol.h
|
variable.$(OBJEXT): $(top_srcdir)/internal/symbol.h
|
||||||
variable.$(OBJEXT): $(top_srcdir)/internal/thread.h
|
variable.$(OBJEXT): $(top_srcdir)/internal/thread.h
|
||||||
variable.$(OBJEXT): $(top_srcdir)/internal/variable.h
|
variable.$(OBJEXT): $(top_srcdir)/internal/variable.h
|
||||||
|
|
13
gc.c
13
gc.c
|
@ -3260,6 +3260,10 @@ rb_gc_mark_children(void *objspace, VALUE obj)
|
||||||
gc_mark_internal(ptr[i]);
|
gc_mark_internal(ptr[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS)) {
|
||||||
|
gc_mark_internal(RSTRUCT_FIELDS_OBJ(obj));
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4188,6 +4192,15 @@ rb_gc_update_object_references(void *objspace, VALUE obj)
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
UPDATE_IF_MOVED(objspace, ptr[i]);
|
UPDATE_IF_MOVED(objspace, ptr[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (RSTRUCT_EMBED_LEN(obj)) {
|
||||||
|
if (!FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS)) {
|
||||||
|
UPDATE_IF_MOVED(objspace, ptr[len]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UPDATE_IF_MOVED(objspace, RSTRUCT(obj)->as.heap.fields_obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -11,10 +11,23 @@
|
||||||
#include "ruby/internal/stdbool.h" /* for bool */
|
#include "ruby/internal/stdbool.h" /* for bool */
|
||||||
#include "ruby/ruby.h" /* for struct RBasic */
|
#include "ruby/ruby.h" /* for struct RBasic */
|
||||||
|
|
||||||
|
/* Flags of RStruct
|
||||||
|
*
|
||||||
|
* 1-7: RSTRUCT_EMBED_LEN
|
||||||
|
* If non-zero, the struct is embedded (its contents follow the
|
||||||
|
* header, rather than being on a separately allocated buffer) and
|
||||||
|
* these bits are the length of the Struct.
|
||||||
|
* 8: RSTRUCT_GEN_FIELDS
|
||||||
|
* The struct is embedded and has no space left to store the
|
||||||
|
* IMEMO/fields reference. Any ivar this struct may have will be in
|
||||||
|
* the generic_fields_tbl. This flag doesn't imply the struct has
|
||||||
|
* ivars.
|
||||||
|
*/
|
||||||
enum {
|
enum {
|
||||||
RSTRUCT_EMBED_LEN_MASK = RUBY_FL_USER7 | RUBY_FL_USER6 | RUBY_FL_USER5 | RUBY_FL_USER4 |
|
RSTRUCT_EMBED_LEN_MASK = RUBY_FL_USER7 | RUBY_FL_USER6 | RUBY_FL_USER5 | RUBY_FL_USER4 |
|
||||||
RUBY_FL_USER3 | RUBY_FL_USER2 | RUBY_FL_USER1,
|
RUBY_FL_USER3 | RUBY_FL_USER2 | RUBY_FL_USER1,
|
||||||
RSTRUCT_EMBED_LEN_SHIFT = (RUBY_FL_USHIFT+1),
|
RSTRUCT_EMBED_LEN_SHIFT = (RUBY_FL_USHIFT+1),
|
||||||
|
RSTRUCT_GEN_FIELDS = RUBY_FL_USER8,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RStruct {
|
struct RStruct {
|
||||||
|
@ -23,6 +36,7 @@ struct RStruct {
|
||||||
struct {
|
struct {
|
||||||
long len;
|
long len;
|
||||||
const VALUE *ptr;
|
const VALUE *ptr;
|
||||||
|
VALUE fields_obj;
|
||||||
} heap;
|
} heap;
|
||||||
/* This is a length 1 array because:
|
/* This is a length 1 array because:
|
||||||
* 1. GCC has a bug that does not optimize C flexible array members
|
* 1. GCC has a bug that does not optimize C flexible array members
|
||||||
|
@ -116,4 +130,31 @@ RSTRUCT_GET(VALUE st, long k)
|
||||||
return RSTRUCT_CONST_PTR(st)[k];
|
return RSTRUCT_CONST_PTR(st)[k];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline VALUE
|
||||||
|
RSTRUCT_FIELDS_OBJ(VALUE st)
|
||||||
|
{
|
||||||
|
const long embed_len = RSTRUCT_EMBED_LEN(st);
|
||||||
|
VALUE fields_obj;
|
||||||
|
if (embed_len) {
|
||||||
|
RUBY_ASSERT(!FL_TEST_RAW(st, RSTRUCT_GEN_FIELDS));
|
||||||
|
fields_obj = RSTRUCT_GET(st, embed_len);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fields_obj = RSTRUCT(st)->as.heap.fields_obj;
|
||||||
|
}
|
||||||
|
return fields_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
RSTRUCT_SET_FIELDS_OBJ(VALUE st, VALUE fields_obj)
|
||||||
|
{
|
||||||
|
const long embed_len = RSTRUCT_EMBED_LEN(st);
|
||||||
|
if (embed_len) {
|
||||||
|
RUBY_ASSERT(!FL_TEST_RAW(st, RSTRUCT_GEN_FIELDS));
|
||||||
|
RSTRUCT_SET(st, embed_len, fields_obj);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RB_OBJ_WRITE(st, &RSTRUCT(st)->as.heap.fields_obj, fields_obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif /* INTERNAL_STRUCT_H */
|
#endif /* INTERNAL_STRUCT_H */
|
||||||
|
|
11
struct.c
11
struct.c
|
@ -811,13 +811,22 @@ struct_alloc(VALUE klass)
|
||||||
{
|
{
|
||||||
long n = num_members(klass);
|
long n = num_members(klass);
|
||||||
size_t embedded_size = offsetof(struct RStruct, as.ary) + (sizeof(VALUE) * n);
|
size_t embedded_size = offsetof(struct RStruct, as.ary) + (sizeof(VALUE) * n);
|
||||||
|
if (RCLASS_MAX_IV_COUNT(klass) > 0) {
|
||||||
|
embedded_size += sizeof(VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
VALUE flags = T_STRUCT | (RGENGC_WB_PROTECTED_STRUCT ? FL_WB_PROTECTED : 0);
|
VALUE flags = T_STRUCT | (RGENGC_WB_PROTECTED_STRUCT ? FL_WB_PROTECTED : 0);
|
||||||
|
|
||||||
if (n > 0 && rb_gc_size_allocatable_p(embedded_size)) {
|
if (n > 0 && rb_gc_size_allocatable_p(embedded_size)) {
|
||||||
flags |= n << RSTRUCT_EMBED_LEN_SHIFT;
|
flags |= n << RSTRUCT_EMBED_LEN_SHIFT;
|
||||||
|
|
||||||
NEWOBJ_OF(st, struct RStruct, klass, flags, embedded_size, 0);
|
NEWOBJ_OF(st, struct RStruct, klass, flags, embedded_size, 0);
|
||||||
|
if (RCLASS_MAX_IV_COUNT(klass) == 0 && embedded_size == rb_gc_obj_slot_size((VALUE)st)) {
|
||||||
|
FL_SET_RAW((VALUE)st, RSTRUCT_GEN_FIELDS);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RSTRUCT_SET_FIELDS_OBJ((VALUE)st, 0);
|
||||||
|
}
|
||||||
rb_mem_clear((VALUE *)st->as.ary, n);
|
rb_mem_clear((VALUE *)st->as.ary, n);
|
||||||
|
|
||||||
return (VALUE)st;
|
return (VALUE)st;
|
||||||
|
|
|
@ -252,3 +252,52 @@ class TestObjectIdRactor < Test::Unit::TestCase
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class TestObjectIdStruct < TestObjectId
|
||||||
|
EmbeddedStruct = Struct.new(:embedded_field)
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@obj = EmbeddedStruct.new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TestObjectIdStructGenIvar < TestObjectId
|
||||||
|
GenIvarStruct = Struct.new(:a, :b, :c)
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@obj = GenIvarStruct.new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TestObjectIdStructNotEmbed < TestObjectId
|
||||||
|
MANY_IVS = 80
|
||||||
|
|
||||||
|
StructNotEmbed = Struct.new(*MANY_IVS.times.map { |i| :"field_#{i}" })
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@obj = StructNotEmbed.new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TestObjectIdStructTooComplex < TestObjectId
|
||||||
|
StructTooComplex = Struct.new(:a) do
|
||||||
|
def initialize
|
||||||
|
@too_complex_obj_id_test = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def setup
|
||||||
|
if defined?(RubyVM::Shape::SHAPE_MAX_VARIATIONS)
|
||||||
|
assert_equal 8, RubyVM::Shape::SHAPE_MAX_VARIATIONS
|
||||||
|
end
|
||||||
|
8.times do |i|
|
||||||
|
StructTooComplex.new.instance_variable_set("@TestObjectIdStructTooComplex#{i}", 1)
|
||||||
|
end
|
||||||
|
@obj = StructTooComplex.new
|
||||||
|
@obj.instance_variable_set("@a#{rand(10_000)}", 1)
|
||||||
|
|
||||||
|
if defined?(RubyVM::Shape)
|
||||||
|
assert_predicate(RubyVM::Shape.of(@obj), :too_complex?)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -99,6 +99,24 @@ class TestRactor < Test::Unit::TestCase
|
||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_struct_instance_variables
|
||||||
|
assert_ractor(<<~'RUBY')
|
||||||
|
StructIvar = Struct.new(:member) do
|
||||||
|
def initialize(*)
|
||||||
|
super
|
||||||
|
@ivar = "ivar"
|
||||||
|
end
|
||||||
|
attr_reader :ivar
|
||||||
|
end
|
||||||
|
obj = StructIvar.new("member")
|
||||||
|
obj_copy = Ractor.new { Ractor.receive }.send(obj).value
|
||||||
|
assert_equal obj.ivar, obj_copy.ivar
|
||||||
|
refute_same obj.ivar, obj_copy.ivar
|
||||||
|
assert_equal obj.member, obj_copy.member
|
||||||
|
refute_same obj.member, obj_copy.member
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
def test_fork_raise_isolation_error
|
def test_fork_raise_isolation_error
|
||||||
assert_ractor(<<~'RUBY')
|
assert_ractor(<<~'RUBY')
|
||||||
ractor = Ractor.new do
|
ractor = Ractor.new do
|
||||||
|
|
65
variable.c
65
variable.c
|
@ -29,6 +29,7 @@
|
||||||
#include "internal/object.h"
|
#include "internal/object.h"
|
||||||
#include "internal/gc.h"
|
#include "internal/gc.h"
|
||||||
#include "internal/re.h"
|
#include "internal/re.h"
|
||||||
|
#include "internal/struct.h"
|
||||||
#include "internal/symbol.h"
|
#include "internal/symbol.h"
|
||||||
#include "internal/thread.h"
|
#include "internal/thread.h"
|
||||||
#include "internal/variable.h"
|
#include "internal/variable.h"
|
||||||
|
@ -1228,10 +1229,19 @@ rb_obj_fields(VALUE obj, ID field_name)
|
||||||
ivar_ractor_check(obj, field_name);
|
ivar_ractor_check(obj, field_name);
|
||||||
|
|
||||||
VALUE fields_obj = 0;
|
VALUE fields_obj = 0;
|
||||||
if (rb_obj_exivar_p(obj)) {
|
if (rb_shape_obj_has_fields(obj)) {
|
||||||
RB_VM_LOCKING() {
|
switch (BUILTIN_TYPE(obj)) {
|
||||||
if (!st_lookup(generic_fields_tbl_, (st_data_t)obj, (st_data_t *)&fields_obj)) {
|
case T_STRUCT:
|
||||||
rb_bug("Object is missing entry in generic_fields_tbl");
|
if (LIKELY(!FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS))) {
|
||||||
|
fields_obj = RSTRUCT_FIELDS_OBJ(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// fall through
|
||||||
|
default:
|
||||||
|
RB_VM_LOCKING() {
|
||||||
|
if (!st_lookup(generic_fields_tbl_, (st_data_t)obj, (st_data_t *)&fields_obj)) {
|
||||||
|
rb_bug("Object is missing entry in generic_fields_tbl");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1243,11 +1253,19 @@ rb_free_generic_ivar(VALUE obj)
|
||||||
{
|
{
|
||||||
if (rb_obj_exivar_p(obj)) {
|
if (rb_obj_exivar_p(obj)) {
|
||||||
st_data_t key = (st_data_t)obj, value;
|
st_data_t key = (st_data_t)obj, value;
|
||||||
|
switch (BUILTIN_TYPE(obj)) {
|
||||||
RB_VM_LOCKING() {
|
case T_STRUCT:
|
||||||
st_delete(generic_fields_tbl_no_ractor_check(), &key, &value);
|
if (LIKELY(!FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS))) {
|
||||||
RBASIC_SET_SHAPE_ID(obj, ROOT_SHAPE_ID);
|
RSTRUCT_SET_FIELDS_OBJ(obj, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// fall through
|
||||||
|
default:
|
||||||
|
RB_VM_LOCKING() {
|
||||||
|
st_delete(generic_fields_tbl_no_ractor_check(), &key, &value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
RBASIC_SET_SHAPE_ID(obj, ROOT_SHAPE_ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1260,12 +1278,20 @@ rb_obj_set_fields(VALUE obj, VALUE fields_obj, ID field_name, VALUE original_fie
|
||||||
RUBY_ASSERT(!original_fields_obj || IMEMO_TYPE_P(original_fields_obj, imemo_fields));
|
RUBY_ASSERT(!original_fields_obj || IMEMO_TYPE_P(original_fields_obj, imemo_fields));
|
||||||
|
|
||||||
if (fields_obj != original_fields_obj) {
|
if (fields_obj != original_fields_obj) {
|
||||||
RB_VM_LOCKING() {
|
switch (BUILTIN_TYPE(obj)) {
|
||||||
st_insert(generic_fields_tbl_, (st_data_t)obj, (st_data_t)fields_obj);
|
case T_STRUCT:
|
||||||
|
if (LIKELY(!FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS))) {
|
||||||
|
RSTRUCT_SET_FIELDS_OBJ(obj, fields_obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// fall through
|
||||||
|
default:
|
||||||
|
RB_VM_LOCKING() {
|
||||||
|
st_insert(generic_fields_tbl_, (st_data_t)obj, (st_data_t)fields_obj);
|
||||||
|
}
|
||||||
|
RB_OBJ_WRITTEN(obj, original_fields_obj, fields_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
RB_OBJ_WRITTEN(obj, original_fields_obj, fields_obj);
|
|
||||||
|
|
||||||
if (original_fields_obj) {
|
if (original_fields_obj) {
|
||||||
// Clear root shape to avoid triggering cleanup such as free_object_id.
|
// Clear root shape to avoid triggering cleanup such as free_object_id.
|
||||||
rb_imemo_fields_clear(original_fields_obj);
|
rb_imemo_fields_clear(original_fields_obj);
|
||||||
|
@ -1276,11 +1302,11 @@ rb_obj_set_fields(VALUE obj, VALUE fields_obj, ID field_name, VALUE original_fie
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rb_obj_replace_fields(VALUE obj, VALUE fields_obj, ID field_name)
|
rb_obj_replace_fields(VALUE obj, VALUE fields_obj)
|
||||||
{
|
{
|
||||||
RB_VM_LOCKING() {
|
RB_VM_LOCKING() {
|
||||||
VALUE original_fields_obj = rb_obj_fields(obj, field_name);
|
VALUE original_fields_obj = rb_obj_fields_no_ractor_check(obj);
|
||||||
rb_obj_set_fields(obj, fields_obj, field_name, original_fields_obj);
|
rb_obj_set_fields(obj, fields_obj, 0, original_fields_obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1608,7 +1634,7 @@ obj_transition_too_complex(VALUE obj, st_table *table)
|
||||||
{
|
{
|
||||||
VALUE fields_obj = rb_imemo_fields_new_complex_tbl(rb_obj_class(obj), table);
|
VALUE fields_obj = rb_imemo_fields_new_complex_tbl(rb_obj_class(obj), table);
|
||||||
RBASIC_SET_SHAPE_ID(fields_obj, shape_id);
|
RBASIC_SET_SHAPE_ID(fields_obj, shape_id);
|
||||||
rb_obj_replace_fields(obj, fields_obj, 0);
|
rb_obj_replace_fields(obj, fields_obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2299,12 +2325,7 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj)
|
||||||
rb_shape_copy_fields(new_fields_obj, dest_buf, dest_shape_id, src_buf, src_shape_id);
|
rb_shape_copy_fields(new_fields_obj, dest_buf, dest_shape_id, src_buf, src_shape_id);
|
||||||
RBASIC_SET_SHAPE_ID(new_fields_obj, dest_shape_id);
|
RBASIC_SET_SHAPE_ID(new_fields_obj, dest_shape_id);
|
||||||
|
|
||||||
RB_VM_LOCKING() {
|
rb_obj_replace_fields(dest, new_fields_obj);
|
||||||
st_insert(generic_fields_tbl_no_ractor_check(), (st_data_t)dest, (st_data_t)new_fields_obj);
|
|
||||||
RB_OBJ_WRITTEN(dest, Qundef, new_fields_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
RBASIC_SET_SHAPE_ID(dest, dest_shape_id);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue