Pass errno of readdir to the GVL callback

nogvl_readdir reported a readdir failure by reading errno after
re-acquiring the GVL. Under M:N threads, the thread may be resumed on
another native thread there, and errno is per native thread, so the
exception had a wrong class.

    # strace -f -e trace=getdents64 -e inject=getdents64:error=EIO ruby test.rb
    p Ractor.new {
      4.times { Thread.new { loop { File.exist?("/nonexistent") } } }
      sleep 0.1
      Dir.children(".") rescue $!.class
    }.value
    #=> Errno::ENOENT (should be Errno::EIO)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yusuke EndohandClaude Opus 5.5 committed 2026-10-19 22:16:53 +09:00
1 parent 37561263fc
commit 8d205a7e2f
1 file changed
+6 -5
Notes: git 2026-10-19 13:17:25 +00:00
+6 -5
View File
@@ -857,11 +857,11 @@ fundamental_encoding_p(rb_encoding *enc)
# define READDIR(dir, enc) rb_w32_readdir((dir), (enc))
# define READDIR_NOGVL READDIR
#else
NORETURN(static void *sys_failure(void *function));
NORETURN(static void *readdir_failure(void *err));
static void *
sys_failure(void *function)
readdir_failure(void *err)
{
rb_sys_fail(function);
rb_syserr_fail((int)(VALUE)err, "readdir");
}
static void *
@@ -869,8 +869,9 @@ nogvl_readdir(void *dir)
{
rb_errno_set(0);
if ((dir = readdir(dir)) == NULL) {
if (rb_errno())
rb_thread_call_with_gvl(sys_failure, (void *)"readdir");
int e = rb_errno();
if (e)
rb_thread_call_with_gvl(readdir_failure, (void *)(VALUE)e);
}
return dir;
}