thread: leave RB_WAITFD_PRI waits to the blocking path

waitfd_to_waiting_flag maps RB_WAITFD_PRI to no thread_sched_waiting_*
event, so the M:N scheduler registered nothing for it yet parked the
thread: a timeout-less IO#wait(IO::PRIORITY) on an M:N thread slept
forever (a VM_ASSERT in thread_io_wait_events on debug builds).
Exclude it from thread_io_mn_schedulable; poll(2) on the blocking path
handles POLLPRI.

The test lives in test/ruby/test_ractor.rb rather than bootstraptest
because it needs the socket extension, which btest runs without.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Koichi SasadaandClaude Fable 5 committed 2026-08-15 01:35:08 +09:00
1 parent 6f176c8925
commit 4e4e3ded8b
2 files changed
+28 -1
Notes: git 2026-08-14 16:36:03 +00:00

No files matched your search

+23
View File
@@ -771,4 +771,27 @@ class TestRactor < Test::Unit::TestCase
end
RUBY
end
def test_io_priority_wait_on_mn_thread
omit 'POLLPRI/MSG_OOB semantics differ on windows' if RUBY_PLATFORM =~ /mswin|mingw/
# A timeout-less IO#wait(IO::PRIORITY) on an M:N thread must take the
# blocking path: the M:N scheduler has no event for POLLPRI and used to
# register nothing yet park the thread forever.
assert_separately([], __FILE__, __LINE__, <<-'RUBY')
Warning[:experimental] = false
require 'socket'
r = Ractor.new do
serv = TCPServer.new("127.0.0.1", 0)
c = TCPSocket.new("127.0.0.1", serv.addr[1])
s = serv.accept
t = Thread.new { c.wait(IO::PRIORITY, nil) }
sleep 0.5
s.send("!", Socket::MSG_OOB)
woken = t.join(5)
[serv, c, s].each(&:close)
woken ? :ok : :timeout
end
assert_equal :ok, r.value
RUBY
end
end
+5 -1
View File
@@ -2055,7 +2055,11 @@ static bool
thread_io_mn_schedulable(rb_thread_t *th, int events, const struct timeval *timeout)
{
#if defined(USE_MN_THREADS) && USE_MN_THREADS
return !th_has_dedicated_nt(th) && (events || timeout) && th->blocking;
// RB_WAITFD_PRI has no thread_sched_waiting_* event: the scheduler would
// register nothing and park the thread forever. POLLPRI works on the
// blocking path.
return !th_has_dedicated_nt(th) && (events || timeout) && th->blocking &&
!(events & ~(RB_WAITFD_IN | RB_WAITFD_OUT));
#else
return false;
#endif