diff --git a/test/ruby/test_ractor.rb b/test/ruby/test_ractor.rb index a25a24ce8ea..1950d4cb966 100644 --- a/test/ruby/test_ractor.rb +++ b/test/ruby/test_ractor.rb @@ -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 diff --git a/thread.c b/thread.c index e90e344a0de..f84836f315f 100644 --- a/thread.c +++ b/thread.c @@ -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