@@ -1879,6 +1879,61 @@ static const rb_data_type_t id2ref_tbl_type = {
1879
1879
.flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
1880
1880
};
1881
1881
1882
+ static VALUE obj_to_id_value = 0 ;
1883
+ static st_table * obj_to_id_tbl = NULL ;
1884
+
1885
+ static void mark_hash_values (st_table * tbl );
1886
+
1887
+ static void
1888
+ obj_to_id_tbl_mark (void * data )
1889
+ {
1890
+ st_table * table = (st_table * )data ;
1891
+ if (UNLIKELY (!RB_POSFIXABLE (LAST_OBJECT_ID ()))) {
1892
+ // It's very unlikely, but if enough object ids were generated, keys may be T_BIGNUM
1893
+ mark_hash_values (table );
1894
+ }
1895
+ // We purposedly don't mark keys, as they are weak references.
1896
+ // rb_gc_obj_free_vm_weak_references takes care of cleaning them up.
1897
+ }
1898
+
1899
+ static size_t
1900
+ obj_to_id_tbl_memsize (const void * data )
1901
+ {
1902
+ return rb_st_memsize (data );
1903
+ }
1904
+
1905
+ static void
1906
+ obj_to_id_tbl_compact (void * data )
1907
+ {
1908
+ st_table * table = (st_table * )data ;
1909
+ if (LIKELY (RB_POSFIXABLE (LAST_OBJECT_ID ()))) {
1910
+ // We know values are all FIXNUM, so no need to update them.
1911
+ gc_ref_update_table_keys_only (table );
1912
+ }
1913
+ else {
1914
+ gc_update_table_refs (table );
1915
+ }
1916
+ }
1917
+
1918
+ static void
1919
+ obj_to_id_tbl_free (void * data )
1920
+ {
1921
+ obj_to_id_tbl = NULL ; // clear global ref
1922
+ st_table * table = (st_table * )data ;
1923
+ st_free_table (table );
1924
+ }
1925
+
1926
+ static const rb_data_type_t obj_to_id_tbl_type = {
1927
+ .wrap_struct_name = "VM/obj_to_id_table" ,
1928
+ .function = {
1929
+ .dmark = obj_to_id_tbl_mark ,
1930
+ .dfree = obj_to_id_tbl_free ,
1931
+ .dsize = obj_to_id_tbl_memsize ,
1932
+ .dcompact = obj_to_id_tbl_compact ,
1933
+ },
1934
+ .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
1935
+ };
1936
+
1882
1937
#define RUBY_ATOMIC_VALUE_LOAD (x ) (VALUE)(RUBY_ATOMIC_PTR_LOAD(x))
1883
1938
1884
1939
static VALUE
@@ -1901,23 +1956,46 @@ class_object_id(VALUE klass)
1901
1956
}
1902
1957
1903
1958
static VALUE
1904
- object_id0 (VALUE obj )
1959
+ object_id0 (VALUE obj , bool shareable )
1905
1960
{
1906
1961
VALUE id = Qfalse ;
1907
1962
1908
- if (rb_shape_has_object_id (rb_obj_shape (obj ))) {
1909
- shape_id_t object_id_shape_id = rb_shape_transition_object_id (obj );
1910
- id = rb_obj_field_get (obj , object_id_shape_id );
1963
+ rb_shape_t * current_shape = rb_obj_shape (obj );
1964
+
1965
+ if (rb_shape_has_object_id (current_shape )) {
1966
+ shape_id_t object_id_shape_id = rb_shape_object_id (obj );
1967
+
1968
+ if (RB_UNLIKELY (shareable && RSHAPE (object_id_shape_id )-> type == SHAPE_EXTERNAL_OBJ_ID )) {
1969
+ RUBY_ASSERT (obj_to_id_tbl );
1970
+ st_lookup (obj_to_id_tbl , obj , & id );
1971
+ }
1972
+ else {
1973
+ id = rb_obj_field_get (obj , object_id_shape_id );
1974
+ }
1911
1975
RUBY_ASSERT (id , "object_id missing" );
1912
1976
return id ;
1913
1977
}
1914
1978
1915
- // rb_shape_object_id_shape may lock if the current shape has
1916
- // multiple children.
1917
- shape_id_t object_id_shape_id = rb_shape_transition_object_id (obj );
1918
-
1919
1979
id = generate_next_object_id ();
1920
- rb_obj_field_set (obj , object_id_shape_id , id );
1980
+ // If the object is shareable and doesn't have free capacity we can't safely
1981
+ // resize it. So we have to store the object_id externally.
1982
+ if (shareable && current_shape -> capacity && current_shape -> next_field_index == current_shape -> capacity ) {
1983
+ shape_id_t object_id_shape_id = rb_shape_transition_external_object_id (obj );
1984
+
1985
+ if (RB_UNLIKELY (!obj_to_id_tbl )) {
1986
+ obj_to_id_tbl = st_init_numtable ();
1987
+ obj_to_id_value = TypedData_Wrap_Struct (0 , & obj_to_id_tbl_type , obj_to_id_tbl );
1988
+ }
1989
+ st_insert (obj_to_id_tbl , obj , id );
1990
+ rb_shape_set_shape_id (obj , object_id_shape_id );
1991
+ }
1992
+ else {
1993
+ // rb_shape_object_id_shape may lock if the current shape has
1994
+ // multiple children.
1995
+ shape_id_t object_id_shape_id = rb_shape_transition_object_id (obj );
1996
+ rb_obj_field_set (obj , object_id_shape_id , id );
1997
+ }
1998
+
1921
1999
if (RB_UNLIKELY (id2ref_tbl )) {
1922
2000
st_insert (id2ref_tbl , (st_data_t )id , (st_data_t )obj );
1923
2001
}
@@ -1940,12 +2018,12 @@ object_id(VALUE obj)
1940
2018
1941
2019
if (UNLIKELY (rb_gc_multi_ractor_p () && rb_ractor_shareable_p (obj ))) {
1942
2020
unsigned int lock_lev = rb_gc_vm_lock ();
1943
- VALUE id = object_id0 (obj );
2021
+ VALUE id = object_id0 (obj , true );
1944
2022
rb_gc_vm_unlock (lock_lev );
1945
2023
return id ;
1946
2024
}
1947
2025
1948
- return object_id0 (obj );
2026
+ return object_id0 (obj , false );
1949
2027
}
1950
2028
1951
2029
static void
@@ -2008,23 +2086,35 @@ static inline void
2008
2086
obj_free_object_id (VALUE obj )
2009
2087
{
2010
2088
VALUE obj_id = 0 ;
2011
- if (RB_UNLIKELY (id2ref_tbl )) {
2012
- switch (BUILTIN_TYPE (obj )) {
2013
- case T_CLASS :
2014
- case T_MODULE :
2015
- if (RCLASS (obj )-> object_id ) {
2016
- obj_id = RCLASS (obj )-> object_id ;
2089
+ switch (BUILTIN_TYPE (obj )) {
2090
+ case T_CLASS :
2091
+ case T_MODULE :
2092
+ if (RB_LIKELY (!id2ref_tbl )) return ;
2093
+
2094
+ if (RCLASS (obj )-> object_id ) {
2095
+ obj_id = RCLASS (obj )-> object_id ;
2096
+ }
2097
+ break ;
2098
+ default :
2099
+ if (rb_shape_obj_has_id (obj )) {
2100
+ shape_id_t shape_id = rb_shape_object_id (obj );
2101
+ if (RSHAPE (shape_id )-> type == SHAPE_EXTERNAL_OBJ_ID ) {
2102
+ RUBY_ASSERT (obj_to_id_tbl );
2103
+
2104
+ VALUE key = obj ;
2105
+ st_delete (obj_to_id_tbl , & key , & obj_id );
2017
2106
}
2018
- break ;
2019
- default :
2020
- if ( rb_shape_obj_has_id ( obj )) {
2021
- obj_id = object_id (obj );
2107
+ else {
2108
+ if ( RB_LIKELY (! id2ref_tbl )) return ;
2109
+
2110
+ obj_id = rb_obj_field_get (obj , shape_id );
2022
2111
}
2023
- break ;
2112
+ RUBY_ASSERT ( obj_id ) ;
2024
2113
}
2114
+ break ;
2025
2115
}
2026
2116
2027
- if (RB_UNLIKELY (obj_id )) {
2117
+ if (RB_UNLIKELY (id2ref_tbl && obj_id )) {
2028
2118
RUBY_ASSERT (FIXNUM_P (obj_id ) || RB_TYPE_P (obj , T_BIGNUM ));
2029
2119
2030
2120
if (!st_delete (id2ref_tbl , (st_data_t * )& obj_id , NULL )) {
@@ -2717,6 +2807,14 @@ mark_key(st_data_t key, st_data_t value, st_data_t data)
2717
2807
return ST_CONTINUE ;
2718
2808
}
2719
2809
2810
+ static int
2811
+ mark_value (st_data_t key , st_data_t value , st_data_t data )
2812
+ {
2813
+ gc_mark_internal ((VALUE )value );
2814
+
2815
+ return ST_CONTINUE ;
2816
+ }
2817
+
2720
2818
void
2721
2819
rb_mark_set (st_table * tbl )
2722
2820
{
@@ -2725,6 +2823,14 @@ rb_mark_set(st_table *tbl)
2725
2823
st_foreach (tbl , mark_key , (st_data_t )rb_gc_get_objspace ());
2726
2824
}
2727
2825
2826
+ static void
2827
+ mark_hash_values (st_table * tbl )
2828
+ {
2829
+ if (!tbl ) return ;
2830
+
2831
+ st_foreach (tbl , mark_value , 0 );
2832
+ }
2833
+
2728
2834
static int
2729
2835
mark_keyvalue (st_data_t key , st_data_t value , st_data_t data )
2730
2836
{
@@ -4132,6 +4238,17 @@ rb_gc_vm_weak_table_foreach(vm_table_foreach_callback_func callback,
4132
4238
}
4133
4239
break ;
4134
4240
}
4241
+ case RB_GC_VM_OBJ_TO_ID_TABLE : {
4242
+ if (obj_to_id_tbl ) {
4243
+ st_foreach_with_replace (
4244
+ obj_to_id_tbl ,
4245
+ vm_weak_table_foreach_weak_key ,
4246
+ vm_weak_table_foreach_update_weak_key ,
4247
+ (st_data_t )& foreach_data
4248
+ );
4249
+ }
4250
+ break ;
4251
+ }
4135
4252
case RB_GC_VM_GENERIC_FIELDS_TABLE : {
4136
4253
st_table * generic_fields_tbl = rb_generic_fields_tbl_get ();
4137
4254
if (generic_fields_tbl ) {
@@ -5512,6 +5629,7 @@ Init_GC(void)
5512
5629
{
5513
5630
#undef rb_intern
5514
5631
rb_gc_register_address (& id2ref_value );
5632
+ rb_gc_register_address (& obj_to_id_value );
5515
5633
5516
5634
malloc_offset = gc_compute_malloc_offset ();
5517
5635
0 commit comments