From 9c2ca0e1b7f776432c48a9bd91c5226c5635d848 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 28 May 2026 12:07:44 +0900 Subject: [PATCH 1/2] Free already closed IO immediately Suggested by @jhawthorn. --- gc.c | 5 +++-- internal/io.h | 1 + io.c | 9 +++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/gc.c b/gc.c index 9eb72329404a3b..cde1b44d05b115 100644 --- a/gc.c +++ b/gc.c @@ -1637,9 +1637,10 @@ rb_gc_obj_free(void *objspace, VALUE obj) break; case T_FILE: if (RFILE(obj)->fptr) { - make_io_zombie(objspace, obj); + bool closed = rb_io_fptr_finalize_closed(RFILE(obj)->fptr); + if (!closed) make_io_zombie(objspace, obj); RB_DEBUG_COUNTER_INC(obj_file_ptr); - return FALSE; + return closed; } break; case T_RATIONAL: diff --git a/internal/io.h b/internal/io.h index b81774e0a715df..2110f0b0876271 100644 --- a/internal/io.h +++ b/internal/io.h @@ -149,6 +149,7 @@ VALUE rb_io_prep_stdout(void); VALUE rb_io_prep_stderr(void); int rb_io_notify_close(struct rb_io *fptr); +bool rb_io_fptr_finalize_closed(struct rb_io *fptr); RUBY_SYMBOL_EXPORT_BEGIN /* io.c (export) */ diff --git a/io.c b/io.c index 15a05c930b8485..effcb349c3c47b 100644 --- a/io.c +++ b/io.c @@ -5707,6 +5707,15 @@ rb_io_fptr_finalize(struct rb_io *io) return 1; } +bool +rb_io_fptr_finalize_closed(struct rb_io *io) +{ + if (!io) return true; + if (io->fd >= 0) return false; + rb_io_fptr_finalize(io); + return true; +} + size_t rb_io_memsize(const rb_io_t *io) { From 8a12816b005b3cabdde0c168a52550dc4ad8bf78 Mon Sep 17 00:00:00 2001 From: Jack Bowlin Date: Mon, 25 May 2026 12:38:11 -0500 Subject: [PATCH 2/2] [DOC] Fix typos in String#dump and String#bytesplice docs --- doc/string/bytesplice.rdoc | 5 ++--- doc/string/dump.rdoc | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/string/bytesplice.rdoc b/doc/string/bytesplice.rdoc index 5689ef4a2ba79b..790f9eb9a0b491 100644 --- a/doc/string/bytesplice.rdoc +++ b/doc/string/bytesplice.rdoc @@ -20,7 +20,7 @@ And either count may be zero (i.e., specifying an empty string): '0123456789'.bytesplice(0, 0, 'abc') # => "abc0123456789" # Empty target. In the second form, just as in the first, -arugments +offset+ and +length+ determine the target bytes; +arguments +offset+ and +length+ determine the target bytes; argument +str+ _contains_ the source bytes, and the additional arguments +str_offset+ and +str_length+ determine the actual source bytes: @@ -42,7 +42,7 @@ and the source bytes are all of the given +str+: '0123456789'.bytesplice(0...0, 'abc') # => "abc0123456789" # Empty target. In the fourth form, just as in the third, -arugment +range+ determines the target bytes; +argument +range+ determines the target bytes; argument +str+ _contains_ the source bytes, and the additional argument +str_range+ determines the actual source bytes: @@ -63,4 +63,3 @@ and so has character boundaries at offsets 0, 3, 6, 9, 12, and 15. 'こんにちは'.bytesplice(0, 3, 'abc') # => "abcんにちは" 'こんにちは'.bytesplice(1, 3, 'abc') # Raises IndexError. 'こんにちは'.bytesplice(0, 2, 'abc') # Raises IndexError. - diff --git a/doc/string/dump.rdoc b/doc/string/dump.rdoc index add3c356623b15..7b688c28a64d6f 100644 --- a/doc/string/dump.rdoc +++ b/doc/string/dump.rdoc @@ -46,7 +46,7 @@ In a dump, certain special characters are escaped: In a dump, unprintable characters are replaced by printable ones; the unprintable characters are the whitespace characters (other than space itself); -here we see the ordinals for those characers, together with explanatory text: +here we see the ordinals for those characters, together with explanatory text: h = { 7 => 'Alert (BEL)',