summaryrefslogtreecommitdiff
path: root/test/ruby/test_gc_compact.rb
diff options
context:
space:
mode:
authortenderlove <tenderlove@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2019-04-20 01:19:47 +0000
committertenderlove <tenderlove@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2019-04-20 01:19:47 +0000
commit91793b8967e0531bd1159a8ff0cc7e50739c7620 (patch)
tree87ba81af05456fe8bdeb29227a968a413e480635 /test/ruby/test_gc_compact.rb
parente3d547f6df76a48834cfd9893baf4f51567b3afb (diff)
Add `GC.compact` again.
🙏 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67620 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'test/ruby/test_gc_compact.rb')
-rw-r--r--test/ruby/test_gc_compact.rb97
1 files changed, 97 insertions, 0 deletions
diff --git a/test/ruby/test_gc_compact.rb b/test/ruby/test_gc_compact.rb
new file mode 100644
index 0000000000..16c62915fd
--- /dev/null
+++ b/test/ruby/test_gc_compact.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+require 'test/unit'
+require 'fiddle'
+
+class TestGCCompact < Test::Unit::TestCase
+ def memory_location(obj)
+ (Fiddle.dlwrap(obj) >> 1)
+ end
+
+ def assert_object_ids(list)
+ same_count = list.find_all { |obj|
+ memory_location(obj) == obj.object_id
+ }.count
+ list.count - same_count
+ end
+
+ def big_list
+ 1000.times.map {
+ # try to make some empty slots by allocating an object and discarding
+ Object.new
+ Object.new
+ } # likely next to each other
+ end
+
+ # Find an object that's allocated in a slot that had a previous
+ # tenant, and that tenant moved and is still alive
+ def find_object_in_recycled_slot(addresses)
+ new_object = nil
+
+ 100_000.times do
+ new_object = Object.new
+ if addresses.include? memory_location(new_object)
+ break
+ end
+ end
+
+ new_object
+ end
+
+ def test_find_collided_object
+ list_of_objects = big_list
+
+ ids = list_of_objects.map(&:object_id) # store id in map
+ addresses = list_of_objects.map(&self.:memory_location)
+
+ assert_equal ids, addresses
+
+ # All object ids should be equal
+ assert_equal 0, assert_object_ids(list_of_objects) # should be 0
+
+ GC.verify_compaction_references
+
+ # Some should have moved
+ id_count = assert_object_ids(list_of_objects)
+ skip "couldn't get objects to move" if id_count == 0
+ assert_operator id_count, :>, 0
+
+ new_ids = list_of_objects.map(&:object_id)
+
+ # Object ids should not change after compaction
+ assert_equal ids, new_ids
+
+ new_tenant = find_object_in_recycled_slot(addresses)
+ assert new_tenant
+
+ # This is the object that used to be in new_object's position
+ previous_tenant = list_of_objects[addresses.index(memory_location(new_tenant))]
+
+ assert_not_equal previous_tenant.object_id, new_tenant.object_id
+
+ # Should be able to look up object by object_id
+ assert_equal new_tenant, ObjectSpace._id2ref(new_tenant.object_id)
+
+ # Should be able to look up object by object_id
+ assert_equal previous_tenant, ObjectSpace._id2ref(previous_tenant.object_id)
+
+ int = (new_tenant.object_id >> 1)
+ # These two should be the same! but they are not :(
+ assert_equal int, ObjectSpace._id2ref(int.object_id)
+ end
+
+ def test_many_collisions
+ list_of_objects = big_list
+ ids = list_of_objects.map(&:object_id)
+ addresses = list_of_objects.map(&self.:memory_location)
+
+ GC.verify_compaction_references
+
+ new_tenants = 10.times.map {
+ find_object_in_recycled_slot(addresses)
+ }
+
+ collisions = GC.stat(:object_id_collisions)
+ skip "couldn't get objects to collide" if collisions == 0
+ assert_operator collisions, :>, 0
+ end
+end