Skip to content

Commit 9400119

Browse files
committed
Fix object_id for classes and modules in namespace context
Given classes and modules have a different set of fields in every namespace, we can't store the object_id in fields for them. Given that some space was freed in `RClass` we can store it there instead.
1 parent 130d6aa commit 9400119

File tree

2 files changed

+69
-10
lines changed

2 files changed

+69
-10
lines changed

gc.c

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1861,9 +1861,42 @@ static const rb_data_type_t id_to_obj_tbl_type = {
18611861
.flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
18621862
};
18631863

1864+
#define RUBY_ATOMIC_VALUE_LOAD(x) (VALUE)(RUBY_ATOMIC_PTR_LOAD(x))
1865+
1866+
static VALUE
1867+
class_object_id(VALUE klass)
1868+
{
1869+
VALUE id = RUBY_ATOMIC_VALUE_LOAD(RCLASS(klass)->object_id);
1870+
if (!id) {
1871+
unsigned int lock_lev = rb_gc_vm_lock();
1872+
id = ULL2NUM(next_object_id);
1873+
next_object_id += OBJ_ID_INCREMENT;
1874+
VALUE existing_id = RUBY_ATOMIC_VALUE_CAS(RCLASS(klass)->object_id, 0, id);
1875+
if (existing_id) {
1876+
id = existing_id;
1877+
}
1878+
else if (RB_UNLIKELY(id_to_obj_tbl)) {
1879+
st_insert(id_to_obj_tbl, id, klass);
1880+
}
1881+
rb_gc_vm_unlock(lock_lev);
1882+
}
1883+
return id;
1884+
}
1885+
18641886
static VALUE
18651887
object_id(VALUE obj)
18661888
{
1889+
switch (BUILTIN_TYPE(obj)) {
1890+
case T_CLASS:
1891+
case T_MODULE:
1892+
// With namespaces, classes and modules have different fields
1893+
// in different namespaces, so we cannot store the object id
1894+
// in fields.
1895+
return class_object_id(obj);
1896+
default:
1897+
break;
1898+
}
1899+
18671900
VALUE id = Qfalse;
18681901
unsigned int lock_lev;
18691902

@@ -1896,8 +1929,19 @@ static void
18961929
build_id_to_obj_i(VALUE obj, void *data)
18971930
{
18981931
st_table *id_to_obj_tbl = (st_table *)data;
1899-
if (rb_shape_obj_has_id(obj)) {
1900-
st_insert(id_to_obj_tbl, rb_obj_id(obj), obj);
1932+
1933+
switch (BUILTIN_TYPE(obj)) {
1934+
case T_CLASS:
1935+
case T_MODULE:
1936+
if (RCLASS(obj)->object_id) {
1937+
st_insert(id_to_obj_tbl, RCLASS(obj)->object_id, obj);
1938+
}
1939+
break;
1940+
default:
1941+
if (rb_shape_obj_has_id(obj)) {
1942+
st_insert(id_to_obj_tbl, rb_obj_id(obj), obj);
1943+
}
1944+
break;
19011945
}
19021946
}
19031947

@@ -1940,16 +1984,30 @@ object_id_to_ref(void *objspace_ptr, VALUE object_id)
19401984
static inline void
19411985
obj_free_object_id(VALUE obj)
19421986
{
1987+
VALUE obj_id = 0;
19431988
if (RB_UNLIKELY(id_to_obj_tbl)) {
1944-
if (rb_shape_obj_has_id(obj)) {
1945-
VALUE obj_id = object_id(obj);
1946-
RUBY_ASSERT(FIXNUM_P(obj_id) || RB_TYPE_P(obj, T_BIGNUM));
1989+
switch (BUILTIN_TYPE(obj)) {
1990+
case T_CLASS:
1991+
case T_MODULE:
1992+
if (RCLASS(obj)->object_id) {
1993+
obj_id = RCLASS(obj)->object_id;
1994+
}
1995+
break;
1996+
default:
1997+
if (rb_shape_obj_has_id(obj)) {
1998+
obj_id = object_id(obj);
1999+
}
2000+
break;
2001+
}
2002+
}
19472003

1948-
if (!st_delete(id_to_obj_tbl, (st_data_t *)&obj_id, NULL)) {
1949-
// If we're currently building the table then it's not a bug
1950-
if (id_to_obj_tbl_built) {
1951-
rb_bug("Object ID seen, but not in id_to_obj table: object_id=%llu object=%s", NUM2ULL(obj_id), rb_obj_info(obj));
1952-
}
2004+
if (RB_UNLIKELY(obj_id)) {
2005+
RUBY_ASSERT(FIXNUM_P(obj_id) || RB_TYPE_P(obj, T_BIGNUM));
2006+
2007+
if (!st_delete(id_to_obj_tbl, (st_data_t *)&obj_id, NULL)) {
2008+
// If we're currently building the table then it's not a bug
2009+
if (id_to_obj_tbl_built) {
2010+
rb_bug("Object ID seen, but not in id_to_obj table: object_id=%llu object=%s", NUM2ULL(obj_id), rb_obj_info(obj));
19532011
}
19542012
}
19552013
}

internal/class.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ STATIC_ASSERT(shape_max_variations, SHAPE_MAX_VARIATIONS < (1 << (sizeof(((rb_cl
138138
struct RClass {
139139
struct RBasic basic;
140140
st_table *ns_classext_tbl; // ns_object -> (rb_classext_t *)
141+
VALUE object_id;
141142
/*
142143
* If ns_classext_tbl is NULL, then the prime classext is readable (because no other classext exists).
143144
* For the check whether writable or not, check flag RCLASS_PRIME_CLASSEXT_WRITABLE

0 commit comments

Comments
 (0)