1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Fix passing actual object_id to finalizer

Previously we were passing the memory_id. This was broken previously if
compaction was run (which changes the memory_id) and now that object_id
is a monotonically increasing number it was always broken.

This commit fixes this by defering removal from the object_id table
until finalizers have run (for objects with finalizers) and also copying
the SEEN_OBJ_ID flag onto the zombie objects.
This commit is contained in:
John Hawthorn 2019-11-07 14:50:05 -08:00 committed by Aaron Patterson
parent 9deca1a3b9
commit 3b6954f8b9
Notes: git 2019-11-09 05:41:37 +09:00
2 changed files with 44 additions and 18 deletions

54
gc.c
View file

@ -2484,7 +2484,7 @@ static inline void
make_zombie(rb_objspace_t *objspace, VALUE obj, void (*dfree)(void *), void *data)
{
struct RZombie *zombie = RZOMBIE(obj);
zombie->basic.flags = T_ZOMBIE;
zombie->basic.flags = T_ZOMBIE | (zombie->basic.flags & FL_SEEN_OBJ_ID);
zombie->dfree = dfree;
zombie->data = data;
zombie->next = heap_pages_deferred_final;
@ -2498,6 +2498,23 @@ make_io_zombie(rb_objspace_t *objspace, VALUE obj)
make_zombie(objspace, obj, (void (*)(void*))rb_io_fptr_finalize, fptr);
}
static void
obj_free_object_id(rb_objspace_t *objspace, VALUE obj)
{
VALUE id;
GC_ASSERT(FL_TEST(obj, FL_SEEN_OBJ_ID));
FL_UNSET(obj, FL_SEEN_OBJ_ID);
if (st_delete(objspace->obj_to_id_tbl, (st_data_t *)&obj, &id)) {
GC_ASSERT(id);
st_delete(objspace->id_to_obj_tbl, (st_data_t *)&id, NULL);
}
else {
rb_bug("Object ID seen, but not in mapping table: %s\n", obj_info(obj));
}
}
static int
obj_free(rb_objspace_t *objspace, VALUE obj)
{
@ -2519,18 +2536,8 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
FL_UNSET(obj, FL_EXIVAR);
}
if (FL_TEST(obj, FL_SEEN_OBJ_ID)) {
VALUE id;
FL_UNSET(obj, FL_SEEN_OBJ_ID);
if (st_delete(objspace->obj_to_id_tbl, (st_data_t *)&obj, &id)) {
GC_ASSERT(id);
st_delete(objspace->id_to_obj_tbl, (st_data_t *)&id, NULL);
}
else {
rb_bug("Object ID see, but not in mapping table: %s\n", obj_info(obj));
}
if (FL_TEST(obj, FL_SEEN_OBJ_ID) && !FL_TEST(obj, FL_FINALIZE)) {
obj_free_object_id(objspace, obj);
}
#if USE_RGENGC
@ -3308,7 +3315,7 @@ run_finalizer(rb_objspace_t *objspace, VALUE obj, VALUE table)
saved.safe = rb_safe_level();
saved.errinfo = rb_errinfo();
saved.objid = nonspecial_obj_id(obj);
saved.objid = rb_obj_id(obj);
saved.cfp = ec->cfp;
saved.finished = 0;
@ -3353,6 +3360,11 @@ finalize_list(rb_objspace_t *objspace, VALUE zombie)
run_final(objspace, zombie);
GC_ASSERT(BUILTIN_TYPE(zombie) == T_ZOMBIE);
if (FL_TEST(zombie, FL_SEEN_OBJ_ID)) {
obj_free_object_id(objspace, zombie);
}
RZOMBIE(zombie)->basic.flags = 0;
if (LIKELY(heap_pages_final_slots)) heap_pages_final_slots--;
page->final_slots--;
@ -3595,7 +3607,7 @@ rb_objspace_garbage_object_p(VALUE obj)
*/
static VALUE
id2ref(VALUE obj, VALUE objid)
id2ref(VALUE objid)
{
#if SIZEOF_LONG == SIZEOF_VOIDP
#define NUM2PTR(x) NUM2ULONG(x)
@ -3636,6 +3648,12 @@ id2ref(VALUE obj, VALUE objid)
}
}
static VALUE
os_id2ref(VALUE os, VALUE objid)
{
return id2ref(objid);
}
static VALUE
rb_find_object_id(VALUE obj, VALUE (*get_heap_object_id)(VALUE))
{
@ -5946,7 +5964,7 @@ verify_internal_consistency_i(void *page_start, void *page_end, size_t stride, v
}
else {
if (BUILTIN_TYPE(obj) == T_ZOMBIE) {
GC_ASSERT(RBASIC(obj)->flags == T_ZOMBIE);
GC_ASSERT((RBASIC(obj)->flags & ~FL_SEEN_OBJ_ID) == T_ZOMBIE);
data->zombie_object_count++;
}
}
@ -10429,7 +10447,7 @@ wmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self))
TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w);
/* Get reference from object id. */
obj = obj_id_to_ref(objid);
obj = id2ref(objid);
/* obj is original referenced object and/or weak reference. */
orig = (st_data_t)obj;
@ -11840,7 +11858,7 @@ Init_GC(void)
rb_define_module_function(rb_mObjSpace, "define_finalizer", define_final, -1);
rb_define_module_function(rb_mObjSpace, "undefine_finalizer", undefine_final, 1);
rb_define_module_function(rb_mObjSpace, "_id2ref", id2ref, 1);
rb_define_module_function(rb_mObjSpace, "_id2ref", os_id2ref, 1);
rb_vm_register_special_exception(ruby_error_nomemory, rb_eNoMemError, "failed to allocate memory");

View file

@ -372,6 +372,14 @@ class TestGc < Test::Unit::TestCase
assert_empty(out)
end
def test_finalizer_passed_object_id
assert_in_out_err(%w[--disable-gems], <<-EOS, ["true"], [])
o = Object.new
obj_id = o.object_id
ObjectSpace.define_finalizer(o, ->(id){ p id == obj_id })
EOS
end
def test_verify_internal_consistency
assert_nil(GC.verify_internal_consistency)
end