summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Zhu <peter@peterzhu.ca>2023-01-06 09:09:18 -0500
committerPeter Zhu <peter@peterzhu.ca>2023-01-09 08:49:29 -0500
commit3be2acfafd3b3c6168e2266c7c6561d143d7ae5c (patch)
treeabb57c1ed4aca5e9050cb8803fb9adad366aedf4
parent29dc9378d971a66ad3fcb21281aac23f0521afe5 (diff)
downloadruby-3be2acfafd3b3c6168e2266c7c6561d143d7ae5c.tar.gz
Fix re-embedding of strings during compaction
The reference updating code for strings is not re-embedding strings because the code is incorrectly wrapped inside of a `if (STR_SHARED_P(obj))` clause. Shared strings can't be re-embedded so this ends up being a no-op. This means that strings can be moved to a large size pool during compaction, but won't be re-embedded, which would waste the space.
-rw-r--r--gc.c16
-rw-r--r--string.c12
-rw-r--r--test/ruby/test_gc_compact.rb8
3 files changed, 21 insertions, 15 deletions
diff --git a/gc.c b/gc.c
index ef1f0adf69..449dd03ec4 100644
--- a/gc.c
+++ b/gc.c
@@ -10609,16 +10609,18 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
#if USE_RVARGC
VALUE new_root = any->as.string.as.heap.aux.shared;
rb_str_update_shared_ary(obj, old_root, new_root);
+#endif
+ }
- // if, after move the string is not embedded, and can fit in the
- // slot it's been placed in, then re-embed it
- if (rb_gc_obj_slot_size(obj) >= rb_str_size_as_embedded(obj)) {
- if (!STR_EMBED_P(obj) && rb_str_reembeddable_p(obj)) {
- rb_str_make_embedded(obj);
- }
+#if USE_RVARGC
+ /* If, after move the string is not embedded, and can fit in the
+ * slot it's been placed in, then re-embed it. */
+ if (rb_gc_obj_slot_size(obj) >= rb_str_size_as_embedded(obj)) {
+ if (!STR_EMBED_P(obj) && rb_str_reembeddable_p(obj)) {
+ rb_str_make_embedded(obj);
}
-#endif
}
+#endif
break;
}
diff --git a/string.c b/string.c
index fc5629638c..9fa482dbc7 100644
--- a/string.c
+++ b/string.c
@@ -312,14 +312,18 @@ rb_str_make_embedded(VALUE str)
RUBY_ASSERT(rb_str_reembeddable_p(str));
RUBY_ASSERT(!STR_EMBED_P(str));
- char *buf = RSTRING_PTR(str);
- long len = RSTRING_LEN(str);
+ char *buf = RSTRING(str)->as.heap.ptr;
+ long len = RSTRING(str)->as.heap.len;
STR_SET_EMBED(str);
STR_SET_EMBED_LEN(str, len);
- memmove(RSTRING_PTR(str), buf, len);
- ruby_xfree(buf);
+ if (len > 0) {
+ memcpy(RSTRING_PTR(str), buf, len);
+ ruby_xfree(buf);
+ }
+
+ TERM_FILL(RSTRING(str)->as.embed.ary + len, TERM_LEN(str));
}
void
diff --git a/test/ruby/test_gc_compact.rb b/test/ruby/test_gc_compact.rb
index 40a388ca04..0c49b36513 100644
--- a/test/ruby/test_gc_compact.rb
+++ b/test/ruby/test_gc_compact.rb
@@ -382,7 +382,7 @@ class TestGCCompact < Test::Unit::TestCase
def test_moving_strings_up_size_pools
omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1
- assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
begin;
STR_COUNT = 500
@@ -394,14 +394,14 @@ class TestGCCompact < Test::Unit::TestCase
stats = GC.verify_compaction_references(expand_heap: true, toward: :empty)
assert_operator(stats[:moved_up][:T_STRING], :>=, STR_COUNT)
- assert(ary) # warning: assigned but unused variable - ary
+ assert_include(ObjectSpace.dump(ary[0]), '"embedded":true')
end;
end
def test_moving_strings_down_size_pools
omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1
- assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
+ assert_separately(%w[-robjspace], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
begin;
STR_COUNT = 500
@@ -412,7 +412,7 @@ class TestGCCompact < Test::Unit::TestCase
stats = GC.verify_compaction_references(expand_heap: true, toward: :empty)
assert_operator(stats[:moved_down][:T_STRING], :>=, STR_COUNT)
- assert(ary) # warning: assigned but unused variable - ary
+ assert_include(ObjectSpace.dump(ary[0]), '"embedded":true')
end;
end
end