diff options
| author | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2026-04-02 19:52:37 +0900 |
|---|---|---|
| committer | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2026-04-02 20:17:37 +0900 |
| commit | e866d9534c03efa6befab2eb019cf2a10a08f412 (patch) | |
| tree | 6a33438b317aff1fd2af8ed1a7e57f98f873c751 | |
| parent | 4009b713a64f469e593ffbc5bb4b37eff43f3be3 (diff) | |
Prism: Close the open file once finished reading
| -rw-r--r-- | prism_compile.c | 46 | ||||
| -rw-r--r-- | test/ruby/test_compile_prism.rb | 6 |
2 files changed, 45 insertions, 7 deletions
diff --git a/prism_compile.c b/prism_compile.c index 4f22baaad5..b693f2e05a 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -11458,6 +11458,37 @@ pm_parse_file_script_lines(const pm_scope_node_t *scope_node, const pm_parser_t return lines; } +struct load_from_fd_args { + VALUE path; + VALUE io; + int open_mode; + int fd; +}; + +static VALUE +close_file(VALUE args) +{ + struct load_from_fd_args *arg = (void *)args; + if (arg->fd != -1) { + close(arg->fd); + } + else if (!NIL_P(arg->io)) { + rb_io_close(arg->io); + } + return Qnil; +} + +static VALUE +load_content(VALUE args) +{ + struct load_from_fd_args *arg = (void *)args; + VALUE io = rb_io_fdopen(arg->fd, arg->open_mode, RSTRING_PTR(arg->path)); + arg->io = io; + arg->fd = -1; + rb_io_wait(io, RB_INT2NUM(RUBY_IO_READABLE), Qnil); + return rb_funcall(io, rb_intern("read"), 0); +} + /** * Attempt to load the file into memory. Return a Ruby error if the file cannot * be read. @@ -11478,13 +11509,14 @@ pm_load_file(pm_parse_result_t *result, VALUE filepath, bool load_error) // For non-regular files (pipes, character devices), we need to read // through Ruby IO to properly release the GVL while waiting for data. if (init_result == PM_SOURCE_INIT_ERROR_NON_REGULAR) { - const int open_mode = O_RDONLY | O_NONBLOCK; - int fd = open(RSTRING_PTR(filepath), open_mode); - if (fd == -1) goto error_generic; - - VALUE io = rb_io_fdopen(fd, open_mode, RSTRING_PTR(filepath)); - rb_io_wait(io, RB_INT2NUM(RUBY_IO_READABLE), Qnil); - VALUE contents = rb_funcall(io, rb_intern("read"), 0); + struct load_from_fd_args args = { + .path = filepath, + .open_mode = O_RDONLY | O_NONBLOCK, + .fd = rb_cloexec_open(RSTRING_PTR(filepath), args.open_mode, 0), + .io = Qnil, + }; + if (args.fd == -1) goto error_generic; + VALUE contents = rb_ensure(load_content, (VALUE)&args, close_file, (VALUE)&args); if (!RB_TYPE_P(contents, T_STRING)) goto error_generic; diff --git a/test/ruby/test_compile_prism.rb b/test/ruby/test_compile_prism.rb index 096f85a8de..c017111c0a 100644 --- a/test/ruby/test_compile_prism.rb +++ b/test/ruby/test_compile_prism.rb @@ -2721,6 +2721,12 @@ end assert_raise TypeError do RubyVM::InstructionSequence.compile_file_prism(nil) end + + assert_nothing_raised(Errno::EMFILE, Errno::ENFILE) do + 10000.times do + RubyVM::InstructionSequence.compile_file_prism(File::NULL) + end + end end private |
