8000 Refactor obj_id and id2ref to no longer be GC implementation specific · ruby/ruby@63e2d8d · GitHub
[go: up one dir, main page]

Skip to content

Commit 63e2d8d

Browse files
committed
Refactor obj_id and id2ref to no longer be GC implementation specific
Since we assume most programs don't ever build the id2obj table, we can just implement it as a simple TypedData object.
1 parent 9119fee commit 63e2d8d

File tree

4 files changed

+217
-337
lines changed

4 files changed

+217
-337
lines changed

gc.c

Lines changed: 216 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -840,9 +840,6 @@ ruby_modular_gc_init(void)
840840
load_modular_gc_func(undefine_finalizer);
841841
load_modular_gc_func(copy_finalizer);
842842
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);
846843
// Forking
847844
load_modular_gc_func(before_fork);
848845
load_modular_gc_func(after_fork);
@@ -923,9 +920,6 @@ ruby_modular_gc_init(void)
923920
# define rb_gc_impl_undefine_finalizer rb_gc_functions.undefine_finalizer
924921
# define rb_gc_impl_copy_finalizer rb_gc_functions.copy_finalizer
925922
# 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
929923
// Forking
930924
# define rb_gc_impl_before_fork rb_gc_functions.before_fork
931925
# define rb_gc_impl_after_fork rb_gc_functions.after_fork
@@ -1213,9 +1207,15 @@ rb_data_free(void *objspace, VALUE obj)
12131207
return true;
12141208
}
12151209

1210+
static void obj_free_object_id(VALUE obj);
1211+
12161212
void
12171213
rb_gc_obj_free_vm_weak_references(VALUE obj)
12181214
{
1215+
if (FL_TEST_RAW(obj, FL_SEEN_OBJ_ID)) {
1216+
obj_free_object_id(obj);
1217+
}
1218+
12191219
if (FL_TEST(obj, FL_EXIVAR)) {
12201220
rb_free_generic_ivar((VALUE)obj);
12211221
FL_UNSET(obj, FL_EXIVAR);
@@ -1759,6 +1759,209 @@ rb_gc_pointer_to_heap_p(VALUE obj)
17591759
return rb_gc_impl_pointer_to_heap_p(rb_gc_get_objspace(), (void *)obj);
17601760
}
17611761

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+
17621965
/*
17631966
* call-seq:
17641967
* ObjectSpace._id2ref(object_id) -> an_object
@@ -1806,7 +2009,7 @@ id2ref(VALUE objid)
18062009
}
18072010
}
18082011

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);
18102013
if (!rb_multi_ractor_p() || rb_ractor_shareable_p(obj)) {
18112014
return obj;
18122015
}
@@ -1823,7 +2026,7 @@ os_id2ref(VALUE os, VALUE objid)
18232026
}
18242027

18252028
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))
18272030
{
18282031
if (SPECIAL_CONST_P(obj)) {
18292032
#if SIZEOF_LONG == SIZEOF_VOIDP
@@ -1833,11 +2036,11 @@ rb_find_object_id(void *objspace, VALUE obj, VALUE (*get_heap_object_id)(void *,
18332036
#endif
18342037
}
18352038

1836-
return get_heap_object_id(objspace, obj);
2039+
return get_heap_object_id(obj);
18372040
}
18382041

18392042
static VALUE
1840-
nonspecial_obj_id(void *_objspace, VALUE obj)
2043+
nonspecial_obj_id(VALUE obj)
18412044
{
18422045
#if SIZEOF_LONG == SIZEOF_VOIDP
18432046
return (VALUE)((SIGN 741A ED_VALUE)(obj)|FIXNUM_FLAG);
@@ -1888,7 +2091,7 @@ rb_obj_id(VALUE obj)
18882091
* Otherwise, the object ID is a Numeric that is a non-zero multiple of
18892092
* (RUBY_IMMEDIATE_MASK + 1) which guarantees that it does not collide with
18902093
* 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);
18922095
}
18932096

18942097
static enum rb_id_table_iterator_result
@@ -4992,6 +5195,8 @@ void
49925195
Init_GC(void)
49935196
{
49945197
#undef rb_intern
5198+
rb_gc_register_address(&id_to_obj_value);
5199+
49955200
malloc_offset = gc_compute_malloc_offset();
49965201

49975202
rb_mGC = rb_define_module("GC");

0 commit comments

Comments
 (0)
0