@@ -1839,20 +1839,80 @@ id2ref_tbl_free(void *data)
1839
1839
st_free_table (table );
1840
1840
}
1841
1841
1842
+ static void
1843
+ id2ref_tbl_compact (void * data )
1844
+ {
1845
+ rb_gc_update_tbl_refs (id2ref_tbl );
1846
+ }
1847
+
1842
1848
static const rb_data_type_t id2ref_tbl_type = {
1843
1849
.wrap_struct_name = "VM/_id2ref_table" ,
1844
1850
.function = {
1845
1851
.dmark = id2ref_tbl_mark ,
1846
1852
.dfree = id2ref_tbl_free ,
1847
1853
.dsize = id2ref_tbl_memsize ,
1848
- // dcompact function not required because the table is reference updated
1849
- // in rb_gc_vm_weak_table_foreach
1854
+ .dcompact = id2ref_tbl_compact ,
1850
1855
},
1851
1856
.flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
1852
1857
};
1853
1858
1854
1859
#define RUBY_ATOMIC_VALUE_LOAD (x ) (VALUE)(RUBY_ATOMIC_PTR_LOAD(x))
1855
1860
1861
+ static VALUE obj_to_id_value = 0 ;
1862
+ static st_table * obj_to_id_tbl = NULL ;
1863
+
1864
+ static void mark_hash_values (st_table * tbl );
1865
+
1866
+ static void
1867
+ obj_to_id_tbl_mark (void * data )
1868
+ {
1869
+ st_table * table = (st_table * )data ;
1870
+ if (UNLIKELY (!RB_POSFIXABLE (LAST_OBJECT_ID ()))) {
1871
+ // It's very unlikely, but if enough object ids were generated, keys may be T_BIGNUM
1872
+ mark_hash_values (table );
1873
+ }
1874
+ // We purposedly don't mark keys, as they are weak references.
1875
+ // rb_gc_obj_free_vm_weak_references takes care of cleaning them up.
1876
+ }
1877
+
1878
+ static size_t
1879
+ obj_to_id_tbl_memsize (const void * data )
1880
+ {
1881
+ return rb_st_memsize (data );
1882
+ }
1883
+
1884
+ static void
1885
+ obj_to_id_tbl_compact (void * data )
1886
+ {
1887
+ st_table * table = (st_table * )data ;
1888
+ if (LIKELY (RB_POSFIXABLE (LAST_OBJECT_ID ()))) {
1889
+ // We know values are all FIXNUM, so no need to update them.
1890
+ gc_ref_update_table_keys_only (table );
1891
+ }
1892
+ else {
1893
+ gc_update_table_refs (table );
1894
+ }
1895
+ }
1896
+
1897
+ static void
1898
+ obj_to_id_tbl_free (void * data )
1899
+ {
1900
+ obj_to_id_tbl = NULL ; // clear global ref
1901
+ st_table * table = (st_table * )data ;
1902
+ st_free_table (table );
1903
+ }
1904
+
1905
+ static const rb_data_type_t obj_to_id_tbl_type = {
1906
+ .wrap_struct_name = "VM/obj_to_id_table" ,
1907
+ .function = {
1908
+ .dmark = obj_to_id_tbl_mark ,
1909
+ .dfree = obj_to_id_tbl_free ,
1910
+ .dsize = obj_to_id_tbl_memsize ,
1911
+ .dcompact = obj_to_id_tbl_compact ,
1912
+ },
1913
+ .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
1914
+ };
1915
+
1856
1916
static VALUE
1857
1917
class_object_id (VALUE klass )
1858
1918
{
@@ -1876,11 +1936,23 @@ static inline VALUE
1876
1936
object_id_get (VALUE obj , shape_id_t shape_id )
1877
1937
{
1878
1938
VALUE id ;
1879
- if (rb_shape_too_complex_p (shape_id )) {
1880
- id = rb_obj_field_get (obj , ROOT_TOO_COMPLEX_WITH_OBJ_ID );
1881
- }
1882
- else {
1883
- id = rb_obj_field_get (obj , rb_shape_object_id (shape_id ));
1939
+
1940
+ switch (rb_shape_has_object_id (shape_id )) {
1941
+ case SHAPE_NO_OBJ_ID :
1942
+ rb_bug ("Unreacheable" );
1943
+ break ;
1944
+ case SHAPE_INLINE_OBJ_ID :
1945
+ if (rb_shape_too_complex_p (shape_id )) {
1946
+ id = rb_obj_field_get (obj , ROOT_TOO_COMPLEX_WITH_OBJ_ID );
1947
+ }
1948
+ else {
1949
+ id = rb_obj_field_get (obj , rb_shape_object_id_inline (shape_id ));
1950
+ }
1951
+ break ;
1952
+ case SHAPE_EXTERNAL_OBJ_ID :
1953
+ RUBY_ASSERT (obj_to_id_tbl );
1954
+ st_lookup (obj_to_id_tbl , obj , & id );
1955
+ break ;
1884
1956
}
1885
1957
1886
1958
#if RUBY_DEBUG
@@ -1894,7 +1966,7 @@ object_id_get(VALUE obj, shape_id_t shape_id)
1894
1966
}
1895
1967
1896
1968
static VALUE
1897
- object_id0 (VALUE obj )
1969
+ object_id0 (VALUE obj , bool shareable )
1898
1970
{
1899
1971
VALUE id = Qfalse ;
1900
1972
shape_id_t shape_id = RBASIC_SHAPE_ID (obj );
@@ -1903,14 +1975,26 @@ object_id0(VALUE obj)
1903
1975
return object_id_get (obj , shape_id );
1904
1976
}
1905
1977
1906
- // rb_shape_object_id_shape may lock if the current shape has
1907
- // multiple children.
1908
- shape_id_t object_id_shape_id = rb_shape_transition_object_id (obj );
1909
-
1910
1978
id = generate_next_object_id ();
1911
- rb_obj_field_set (obj , object_id_shape_id , id );
1912
1979
1913
- RUBY_ASSERT (RBASIC_SHAPE_ID (obj ) == object_id_shape_id );
1980
+ attr_index_t capacity = RSHAPE_CAPACITY (shape_id );
1981
+ attr_index_t free_capacity = capacity - RSHAPE_LEN (shape_id );
1982
+ if (shareable && capacity && !free_capacity ) {
1983
+ // The object is shared and has no free capacity, we can't
1984
+ // safely store the object_id inline.
1985
+ shape_id_t next_shape_id = rb_shape_transition_object_id_external (obj );
1986
+ if (RB_UNLIKELY (!obj_to_id_tbl )) {
1987
+ obj_to_id_tbl = st_init_numtable ();
1988
+ obj_to_id_value = TypedData_Wrap_Struct (0 , & obj_to_id_tbl_type , obj_to_id_tbl );
1989
+ }
1990
+ st_insert (obj_to_id_tbl , obj , id );
1991
+ RBASIC_SET_SHAPE_ID (obj , next_shape_id );
1992
+ }
1993
+ else {
1994
+ shape_id_t next_shape_id = rb_shape_transition_object_id_inline (obj );
1995
+ rb_obj_field_set (obj , next_shape_id , id );
1996
+ }
1997
+
1914
1998
RUBY_ASSERT (rb_shape_obj_has_id (obj ));
1915
1999
1916
2000
if (RB_UNLIKELY (id2ref_tbl )) {
@@ -1936,14 +2020,14 @@ object_id(VALUE obj)
1936
2020
break ;
1937
2021
}
1938
2022
1939
- if (UNLIKELY (rb_gc_multi_ractor_p () && rb_ractor_shareable_p (obj ))) {
2023
+ if (UNLIKELY (rb_gc_multi_ractor_p () && RB_OBJ_SHAREABLE_P (obj ))) {
1940
2024
unsigned int lock_lev = RB_GC_VM_LOCK ();
1941
- VALUE id = object_id0 (obj );
2025
+ VALUE id = object_id0 (obj , true );
1942
2026
RB_GC_VM_UNLOCK (lock_lev );
1943
2027
return id ;
1944
2028
}
1945
2029
1946
- return object_id0 (obj );
2030
+ return object_id0 (obj , false );
1947
2031
}
1948
2032
1949
2033
static void
@@ -2045,8 +2129,18 @@ obj_free_object_id(VALUE obj)
2045
2129
break ;
2046
2130
default : {
2047
2131
shape_id_t shape_id = RBASIC_SHAPE_ID (obj );
2048
- if (rb_shape_has_object_id (shape_id )) {
2132
+ switch (rb_shape_has_object_id (shape_id )) {
2133
+ case SHAPE_NO_OBJ_ID :
2134
+ break ;
2135
+ case SHAPE_INLINE_OBJ_ID :
2049
2136
obj_id = object_id_get (obj , shape_id );
2137
+ break ;
2138
+ case SHAPE_EXTERNAL_OBJ_ID :
2139
+ // During shutdown the `obj_to_id_tbl` may be freed first.
2140
+ if (obj_to_id_tbl ) {
2141
+ st_delete (obj_to_id_tbl , & obj , & obj_id );
2142
+ }
2143
+ break ;
2050
2144
}
2051
2145
break ;
2052
2146
}
@@ -2739,6 +2833,22 @@ rb_mark_set(st_table *tbl)
2739
2833
st_foreach (tbl , mark_key , (st_data_t )rb_gc_get_objspace ());
2740
2834
}
2741
2835
2836
+ static int
2837
+ mark_value (st_data_t key , st_data_t value , st_data_t data )
2838
+ {
2839
+ gc_mark_internal ((VALUE )value );
2840
+
2841
+ return ST_CONTINUE ;
2842
+ }
2843
+
2844
+ static void
2845
+ mark_hash_values (st_table * tbl )
2846
+ {
2847
+ if (!tbl ) return ;
2848
+
2849
+ st_foreach (tbl , mark_value , 0 );
2850
+ }
2851
+
2742
2852
static int
2743
2853
mark_keyvalue (st_data_t key , st_data_t value , st_data_t data )
2744
2854
{
@@ -3971,33 +4081,6 @@ vm_weak_table_gen_fields_foreach_too_complex_replace_i(st_data_t *_key, st_data_
3971
4081
3972
4082
struct st_table * rb_generic_fields_tbl_get (void );
3973
4083
3974
- static int
3975
- vm_weak_table_id2ref_foreach (st_data_t key , st_data_t value , st_data_t data , int error )
3976
- {
3977
- struct global_vm_table_foreach_data * iter_data = (struct global_vm_table_foreach_data * )data ;
3978
-
3979
- if (!iter_data -> weak_only && !FIXNUM_P ((VALUE )key )) {
3980
- int ret = iter_data -> callback ((VALUE )key , iter_data -> data );
3981
- if (ret != ST_CONTINUE ) return ret ;
3982
- }
3983
-
3984
- return iter_data -> callback ((VALUE )value , iter_data -> data );
3985
- }
3986
-
3987
- static int
3988
- vm_weak_table_id2ref_foreach_update (st_data_t * key , st_data_t * value , st_data_t data , int existing )
3989
- {
3990
- struct global_vm_table_foreach_data * iter_data = (struct global_vm_table_foreach_data * )data ;
3991
-
3992
- iter_data -> update_callback ((VALUE * )value , iter_data -> data );
3993
-
3994
- if (!iter_data -> weak_only && !FIXNUM_P ((VALUE )* key )) {
3995
- iter_data -> update_callback ((VALUE * )key , iter_data -> data );
3996
- }
3997
-
3998
- return ST_CONTINUE ;
3999
- }
4000
-
4001
4084
static int
4002
4085
vm_weak_table_gen_fields_foreach (st_data_t key , st_data_t value , st_data_t data )
4003
4086
{
@@ -4126,17 +4209,6 @@ rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback,
4126
4209
}
4127
4210
break ;
4128
4211
}
4129
- case RB_GC_VM_ID2REF_TABLE : {
4130
- if (id2ref_tbl ) {
4131
- st_foreach_with_replace (
4132
- id2ref_tbl ,
4133
- vm_weak_table_id2ref_foreach ,
4134
- vm_weak_table_id2ref_foreach_update ,
4135
- (st_data_t )& foreach_data
4136
- );
4137
- }
4138
- break ;
4139
- }
4140
4212
case RB_GC_VM_GENERIC_FIELDS_TABLE : {
4141
4213
st_table * generic_fields_tbl = rb_generic_fields_tbl_get ();
4142
4214
if (generic_fields_tbl ) {
@@ -5543,6 +5615,7 @@ Init_GC(void)
5543
5615
{
5544
5616
#undef rb_intern
5545
5617
rb_gc_register_address (& id2ref_value );
5618
+ rb_gc_register_address (& obj_to_id_value );
5546
5619
5547
5620
malloc_offset = gc_compute_malloc_offset ();
5548
5621
0 commit comments