@@ -840,9 +840,6 @@ ruby_modular_gc_init(void)
840
840
load_modular_gc_func (undefine_finalizer );
841
841
load_modular_gc_func (copy_finalizer );
842
842
load_modular_gc_func (shutdown_call_finalizer );
843
- // Object ID
844
- load_modular_gc_func (object_id );
845
- load_modular_gc_func (object_id_to_ref );
846
843
// Forking
847
844
load_modular_gc_func (before_fork );
848
845
load_modular_gc_func (after_fork );
@@ -923,9 +920,6 @@ ruby_modular_gc_init(void)
923
920
# define rb_gc_impl_undefine_finalizer rb_gc_functions.undefine_finalizer
924
921
# define rb_gc_impl_copy_finalizer rb_gc_functions.copy_finalizer
925
922
# define rb_gc_impl_shutdown_call_finalizer rb_gc_functions.shutdown_call_finalizer
926
- // Object ID
927
- # define rb_gc_impl_object_id rb_gc_functions.object_id
928
- # define rb_gc_impl_object_id_to_ref rb_gc_functions.object_id_to_ref
929
923
// Forking
930
924
# define rb_gc_impl_before_fork rb_gc_functions.before_fork
931
925
# define rb_gc_impl_after_fork rb_gc_functions.after_fork
@@ -1213,9 +1207,15 @@ rb_data_free(void *objspace, VALUE obj)
1213
1207
return true;
1214
1208
}
1215
1209
1210
+ static void obj_free_object_id (VALUE obj );
1211
+
1216
1212
void
1217
1213
rb_gc_obj_free_vm_weak_references (VALUE obj )
1218
1214
{
1215
+ if (FL_TEST_RAW (obj , FL_SEEN_OBJ_ID )) {
1216
+ obj_free_object_id (obj );
1217
+ }
1218
+
1219
1219
if (FL_TEST (obj , FL_EXIVAR )) {
1220
1220
rb_free_generic_ivar ((VALUE )obj );
1221
1221
FL_UNSET (obj , FL_EXIVAR );
@@ -1759,6 +1759,209 @@ rb_gc_pointer_to_heap_p(VALUE obj)
1759
1759
return rb_gc_impl_pointer_to_heap_p (rb_gc_get_objspace (), (void * )obj );
1760
1760
}
1761
1761
1762
+ #define OBJ_ID_INCREMENT (RUBY_IMMEDIATE_MASK + 1)
1763
+ #define OBJ_ID_INITIAL (OBJ_ID_INCREMENT)
1764
+
1765
+ static unsigned long long next_object_id = OBJ_ID_INITIAL ;
1766
+ static VALUE id_to_obj_value = 0 ;
1767
+ static st_table * id_to_obj_tbl = NULL ;
1768
+
1769
+ static int
1770
+ object_id_cmp (st_data_t x , st_data_t y )
1771
+ {
1772
+ if (RB_TYPE_P (x , T_BIGNUM )) {
1773
+ return !rb_big_eql (x , y );
1774
+ }
1775
+ else {
1776
+ return x != y ;
1777
+ }
1778
+ }
1779
+
1780
+ static st_index_t
1781
+ object_id_hash (st_data_t n )
1782
+ {
1783
+ return FIX2LONG (rb_hash ((VALUE )n ));
1784
+ }
1785
+
1786
+ static const struct st_hash_type object_id_hash_type = {
1787
+ object_id_cmp ,
1788
+ object_id_hash ,
1789
+ };
1790
+
1791
+ static void gc_mark_tbl_no_pin (st_table * table );
1792
+
1793
+ static void
1794
+ id_to_obj_tbl_mark (void * data )
1795
+ {
1796
+ st_table * table = (st_table * )data ;
1797
+ if (UNLIKELY (!RB_POSFIXABLE (next_object_id ))) {
1798
+ // It's very unlikely, but if enough object ids were generated, keys may be T_BIGNUM
1799
+ rb_mark_set (table );
1800
+ }
1801
+ // We purposedly don't mark values, as they are weak references.
1802
+ // rb_gc_obj_free_vm_weak_references takes care of cleaning them up.
1803
+ }
1804
+
1805
+ static size_t
1806
+ id_to_obj_tbl_memsize (const void * data )
1807
+ {
1808
+ return rb_st_memsize (data );
1809
+ }
1810
+
1811
+ static void
1812
+ id_to_obj_tbl_compact (void * data )
1813
+ {
1814
+ st_table * table = (st_table * )data ;
1815
+ if (LIKELY (RB_POSFIXABLE (next_object_id ))) {
1816
+ // We know keys are all FIXNUM, so no need to update them.
1817
+ gc_ref_update_table_values_only (table );
1818
+ }
1819
+ else {
1820
+ gc_update_table_refs (table );
1821
+ }
1822
+ }
1823
+
1824
+ static void
1825
+ id_to_obj_tbl_free (void * data )
1826
+ {
1827
+ id_to_obj_tbl = NULL ; // clear global ref
1828
+ st_table * table = (st_table * )data ;
1829
+ st_free_table (table );
1830
+ }
1831
+
1832
+ static const rb_data_type_t id_to_obj_tbl_type = {
1833
+ .wrap_struct_name = "VM/id_to_obj_table" ,
1834
+ .function = {
1835
+ .dmark = id_to_obj_tbl_mark ,
1836
+ .dfree = id_to_obj_tbl_free ,
1837
+ .dsize = id_to_obj_tbl_memsize ,
1838
+ .dcompact = id_to_obj_tbl_compact ,
1839
+ },
1840
+ .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
1841
+ };
1842
+
1843
+ static VALUE
1844
+ object_id (VALUE obj )
1845
+ {
1846
+ VALUE id ;
1847
+
1848
+ rb_shape_t * shape = rb_shape_get_shape (obj );
1849
+ unsigned int lock_lev ;
1850
+
1851
+ if (shape -> type == SHAPE_OBJ_TOO_COMPLEX ) {
1852
+ // we could not lock if the object isn't shareable, but may not be worth the effort
1853
+ lock_lev = rb_gc_vm_lock ();
1854
+ id = rb_attr_get (obj , internal_object_id );
1855
+ if (NIL_P (id )) {
1856
+ id = ULL2NUM (next_object_id );
1857
+ next_object_id += OBJ_ID_INCREMENT ;
1858
+ rb_ivar_set_internal (obj , internal_object_id , id );
1859
+ if (RB_UNLIKELY (id_to_obj_tbl )) {
1860
+ st_insert (id_to_obj_tbl , (st_data_t )id , (st_data_t )obj );
1861
+ }
1862
+ FL_SET_RAW (obj , FL_SEEN_OBJ_ID );
1863
+ }
1864
+
1865
+ rb_gc_vm_unlock (lock_lev );
1866
+ return id ;
1867
+ }
1868
+
1869
+ if (rb_shape_has_object_id (shape )) {
1870
+ // We could avoid locking if the object isn't shareable
1871
+ lock_lev = rb_gc_vm_lock ();
1872
+
1873
+ rb_shape_t * object_id_shape = rb_shape_object_id_shape (obj );
1874
+ id = rb_ivar_at (obj , object_id_shape );
1875
+
1876
+ rb_gc_vm_unlock (lock_lev );
1877
+ return id ;
1878
+ }
1879
+ else {
1880
+ // We could avoid locking if the object isn't shareable
1881
+ // but we'll lock anyway to lookup the next shape, and
1882
+ // we'd at least need to generate the object_id using atomics.
1883
+ lock_lev = rb_gc_vm_lock ();
1884
+
1885
+ id = ULL2NUM (next_object_id );
1886
+ next_object_id += OBJ_ID_INCREMENT ;
1887
+
1888
+ rb_shape_t * object_id_shape = rb_shape_object_id_shape (obj );
1889
+ if (object_id_shape -> type == SHAPE_OBJ_TOO_COMPLEX ) {
1890
+ rb_evict_ivars_to_hash (obj );
1891
+ rb_ivar_set_internal (obj , internal_object_id , id );
1892
+ } else {
1893
+ rb_ivar_set_at_internal (obj , object_id_shape , id );
1894
+ }
1895
+ if (RB_UNLIKELY (id_to_obj_tbl )) {
1896
+ st_insert (id_to_obj_tbl , (st_data_t )id , (st_data_t )obj );
1897
+ }
1898
+ FL_SET_RAW (obj , FL_SEEN_OBJ_ID );
1899
+
1900
+ rb_gc_vm_unlock (lock_lev );
1901
+ return id ;
1902
+ }
1903
+ }
1904
+
1905
+ static void
1906
+ build_id_to_obj_i (VALUE obj , void * data )
1907
+ {
1908
+ st_table * id_to_obj_tbl = (st_table * )data ;
1909
+ if (FL_TEST_RAW (obj , FL_SEEN_OBJ_ID )) {
1910
+ st_insert (id_to_obj_tbl , rb_obj_id (obj ), obj );
1911
+ }
1912
+ }
1913
+
1914
+ static VALUE
1915
+ object_id_to_ref (void * objspace_ptr , VALUE object_id )
1916
+ {
1917
+ rb_objspace_t * objspace = objspace_ptr ;
1918
+
1919
+ unsigned int lev = rb_gc_vm_lock ();
1920
+
1921
+ if (!id_to_obj_tbl ) {
1922
+ rb_gc_vm_barrier (); // stop other ractors
1923
+
1924
+ id_to_obj_tbl = st_init_table (& object_id_hash_type );
1925
+ id_to_obj_value = TypedData_Wrap_Struct (0 , & id_to_obj_tbl_type , id_to_obj_tbl );
1926
+ rb_gc_impl_each_object (objspace , build_id_to_obj_i , (void * )id_to_obj_tbl );
1927
+ }
1928
+
1929
+ VALUE obj ;
1930
+ bool found = st_lookup (id_to_obj_tbl , object_id , & obj ) && !rb_gc_impl_garbage_object_p (objspace , obj );
1931
+
1932
+ rb_gc_vm_unlock (lev );
1933
+
1934
+ if (found ) {
1935
+ return obj ;
1936
+ }
1937
+
1938
+ if (rb_funcall (object_id , rb_intern (">=" ), 1 , ULL2NUM (next_object_id ))) {
1939
+ rb_raise (rb_eRangeError , "%+" PRIsVALUE " is not an id value" , rb_funcall (object_id , rb_intern ("to_s" ), 1 , INT2FIX (10 )));
1940
+ }
1941
+ else {
1942
+ rb_raise (rb_eRangeError , "%+" PRIsVALUE " is a recycled object" , rb_funcall (object_id , rb_intern ("to_s" ), 1 , INT2FIX (10 )));
1943
+ }
1944
+ }
1945
+
1946
+ static void
1947
+ obj_free_object_id (VALUE obj )
1948
+ {
1949
+ GC_ASSERT (BUILTIN_TYPE (obj ) == T_NONE || FL_TEST (obj , FL_SEEN_OBJ_ID ));
1950
+
1951
+ if (RB_UNLIKELY (id_to_obj_tbl )) {
1952
+ st_data_t id = (st_data_t )rb_obj_id (obj );
1953
+ GC_ASSERT (id );
1954
+ FL_UNSET (obj , FL_SEEN_OBJ_ID );
1955
+
1956
+ if (!st_delete (id_to_obj_tbl , & id , NULL )) {
1957
+ rb_bug ("Object ID seen, but not in id_to_obj table: %s" , rb_obj_info (obj ));
1958
+ }
1959
+ }
1960
+ else {
1961
+ FL_UNSET (obj , FL_SEEN_OBJ_ID );
1962
+ }
1963
+ }
1964
+
1762
1965
/*
1763
1966
* call-seq:
1764
1967
* ObjectSpace._id2ref(object_id) -> an_object
@@ -1806,7 +2009,7 @@ id2ref(VALUE objid)
1806
2009
}
1807
2010
}
1808
2011
1809
- VALUE obj = rb_gc_impl_object_id_to_ref (rb_gc_get_objspace (), objid );
2012
+ VALUE obj = object_id_to_ref (rb_gc_get_objspace (), objid );
1810
2013
if (!rb_multi_ractor_p () || rb_ractor_shareable_p (obj )) {
1811
2014
return obj ;
1812
2015
}
@@ -1823,7 +2026,7 @@ os_id2ref(VALUE os, VALUE objid)
1823
2026
}
1824
2027
1825
2028
static VALUE
1826
- rb_find_object_id (void * objspace , VALUE obj , VALUE (* get_heap_object_id )(void * , VALUE ))
2029
+ rb_find_object_id (void * objspace , VALUE obj , VALUE (* get_heap_object_id )(VALUE ))
1827
2030
{
1828
2031
if (SPECIAL_CONST_P (obj )) {
1829
2032
#if SIZEOF_LONG == SIZEOF_VOIDP
@@ -1833,11 +2036,11 @@ rb_find_object_id(void *objspace, VALUE obj, VALUE (*get_heap_object_id)(void *,
1833
2036
#endif
1834
2037
}
1835
2038
1836
- return get_heap_object_id (objspace , obj );
2039
+ return get_heap_object_id (obj );
1837
2040
}
1838
2041
1839
2042
static VALUE
1840
- nonspecial_obj_id (void * _objspace , VALUE obj )
2043
+ nonspecial_obj_id (VALUE obj )
1841
2044
{
1842
2045
#if SIZEOF_LONG == SIZEOF_VOIDP
1843
2046
return (VALUE )((SIGN
741A
ED_VALUE )(obj )|FIXNUM_FLAG );
@@ -1888,7 +2091,7 @@ rb_obj_id(VALUE obj)
1888
2091
* Otherwise, the object ID is a Numeric that is a non-zero multiple of
1889
2092
* (RUBY_IMMEDIATE_MASK + 1) which guarantees that it does not collide with
1890
2093
* any immediates. */
1891
- return rb_find_object_id (rb_gc_get_objspace (), obj , rb_gc_impl_object_id );
2094
+ return rb_find_object_id (rb_gc_get_objspace (), obj , object_id );
1892
2095
}
1893
2096
1894
2097
static enum rb_id_table_iterator_result
@@ -4992,6 +5195,8 @@ void
4992
5195
Init_GC (void )
4993
5196
{
4994
5197
#undef rb_intern
5198
+ rb_gc_register_address (& id_to_obj_value );
5199
+
4995
5200
malloc_offset = gc_compute_malloc_offset ();
4996
5201
4997
5202
rb_mGC = rb_define_module ("GC" );
0 commit comments