mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
io_buffer: Reimplement dcompact for IO::Buffer
The `source` field in IO::Buffer can have a String or an IO::Buffer object, if not nil. - When the `source` is a String object. The `base` field points to the memory location of the String content, which can be embedded in RSTRING, and in that case, GC compaction can move the memory region along with the String object. Thus, IO::Buffer needs to pin the `source` object to prevent `base` pointer from becoming invalid. - When the `source` is an IO::Buffer, then `base` is a pointer to a malloced or mmapped memory region, managed by the source IO::Buffer. In this case, we don't need to pin the source IO::Buffer object, since the referred memory region won't get moved by GC. Closes: [Bug #21210]
This commit is contained in:
parent
4cc58c3a6f
commit
8aac19d598
Notes:
git
2025-06-17 05:57:14 +00:00
2 changed files with 25 additions and 3 deletions
15
io_buffer.c
15
io_buffer.c
|
@ -273,10 +273,18 @@ io_buffer_free(struct rb_io_buffer *buffer)
|
|||
}
|
||||
|
||||
static void
|
||||
rb_io_buffer_type_mark(void *_buffer)
|
||||
rb_io_buffer_type_mark_and_move(void *_buffer)
|
||||
{
|
||||
struct rb_io_buffer *buffer = _buffer;
|
||||
rb_gc_mark(buffer->source);
|
||||
if (buffer->source != Qnil) {
|
||||
if (RB_TYPE_P(buffer->source, T_STRING)) {
|
||||
// The `source` String has to be pinned, because the `base` may point to the embedded String content,
|
||||
// which can be otherwise moved by GC compaction.
|
||||
rb_gc_mark(buffer->source);
|
||||
} else {
|
||||
rb_gc_mark_and_move(&buffer->source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -303,9 +311,10 @@ rb_io_buffer_type_size(const void *_buffer)
|
|||
static const rb_data_type_t rb_io_buffer_type = {
|
||||
.wrap_struct_name = "IO::Buffer",
|
||||
.function = {
|
||||
.dmark = rb_io_buffer_type_mark,
|
||||
.dmark = rb_io_buffer_type_mark_and_move,
|
||||
.dfree = rb_io_buffer_type_free,
|
||||
.dsize = rb_io_buffer_type_size,
|
||||
.dcompact = rb_io_buffer_type_mark_and_move,
|
||||
},
|
||||
.data = NULL,
|
||||
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
|
||||
|
|
|
@ -693,4 +693,17 @@ class TestIOBuffer < Test::Unit::TestCase
|
|||
buf.set_string('a', 0, 0)
|
||||
assert_predicate buf, :empty?
|
||||
end
|
||||
|
||||
# https://bugs.ruby-lang.org/issues/21210
|
||||
def test_bug_21210
|
||||
omit "compaction is not supported on this platform" unless GC.respond_to?(:compact)
|
||||
|
||||
str = +"hello"
|
||||
buf = IO::Buffer.for(str)
|
||||
assert_predicate buf, :valid?
|
||||
|
||||
GC.verify_compaction_references(expand_heap: true, toward: :empty)
|
||||
|
||||
assert_predicate buf, :valid?
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue