diff options
author | Samuel Williams <[email protected]> | 2025-05-24 20:33:08 +0900 |
---|---|---|
committer | Samuel Williams <[email protected]> | 2025-06-06 13:13:16 +0900 |
commit | 81a23c5793fecaff5f75cefe6a6e03dab99df16b (patch) | |
tree | a34dfc6ea30ed7cc57ab3b69ea2983abb1951e43 /test | |
parent | e093c3145ac7a2efcbc7965e43f29a992316ab64 (diff) |
`rb_io_blocking_operation_exit` should not execute with pending interrupts.
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/13437
Diffstat (limited to 'test')
-rw-r--r-- | test/fiber/test_io.rb | 59 | ||||
-rw-r--r-- | test/fiber/test_io_close.rb | 110 |
2 files changed, 139 insertions, 30 deletions
diff --git a/test/fiber/test_io.rb b/test/fiber/test_io.rb index 39e32c5987..eea06f97c8 100644 --- a/test/fiber/test_io.rb +++ b/test/fiber/test_io.rb @@ -9,7 +9,7 @@ class TestFiberIO < Test::Unit::TestCase omit unless defined?(UNIXSocket) i, o = UNIXSocket.pair - if RUBY_PLATFORM=~/mswin|mingw/ + if RUBY_PLATFORM =~ /mswin|mingw/ i.nonblock = true o.nonblock = true end @@ -44,7 +44,7 @@ class TestFiberIO < Test::Unit::TestCase 16.times.map do Thread.new do i, o = UNIXSocket.pair - if RUBY_PLATFORM=~/mswin|mingw/ + if RUBY_PLATFORM =~ /mswin|mingw/ i.nonblock = true o.nonblock = true end @@ -67,7 +67,7 @@ class TestFiberIO < Test::Unit::TestCase def test_epipe_on_read omit unless defined?(UNIXSocket) - omit "nonblock=true isn't properly supported on Windows" if RUBY_PLATFORM=~/mswin|mingw/ + omit "nonblock=true isn't properly supported on Windows" if RUBY_PLATFORM =~ /mswin|mingw/ i, o = UNIXSocket.pair @@ -242,38 +242,37 @@ class TestFiberIO < Test::Unit::TestCase # Windows has UNIXSocket, but only with VS 2019+ omit "UNIXSocket is not defined!" unless defined?(UNIXSocket) - i, o = Socket.pair(:UNIX, :STREAM) - if RUBY_PLATFORM=~/mswin|mingw/ - i.nonblock = true - o.nonblock = true - end + Socket.pair(:UNIX, :STREAM) do |i, o| + if RUBY_PLATFORM =~ /mswin|mingw/ + i.nonblock = true + o.nonblock = true + end - reading_thread = Thread.new do - Thread.current.report_on_exception = false - i.wait_readable - end + reading_thread = Thread.new do + Thread.current.report_on_exception = false + i.wait_readable + end - fs_thread = Thread.new do - # Wait until the reading thread is blocked on read: - Thread.pass until reading_thread.status == "sleep" + scheduler_thread = Thread.new do + # Wait until the reading thread is blocked on read: + Thread.pass until reading_thread.status == "sleep" - scheduler = Scheduler.new - Fiber.set_scheduler scheduler - Fiber.schedule do - i.close + scheduler = Scheduler.new + Fiber.set_scheduler scheduler + Fiber.schedule do + i.close + end end - end - assert_raise(IOError) { reading_thread.join } - refute_nil fs_thread.join(5), "expected thread to terminate within 5 seconds" + assert_raise(IOError) { reading_thread.join } + refute_nil scheduler_thread.join(5), "expected thread to terminate within 5 seconds" - assert_predicate(i, :closed?) - ensure - fs_thread&.kill - fs_thread&.join rescue nil - reading_thread&.kill - reading_thread&.join rescue nil - i&.close - o&.close + assert_predicate(i, :closed?) + ensure + scheduler_thread&.kill + scheduler_thread&.join rescue nil + reading_thread&.kill + reading_thread&.join rescue nil + end end end diff --git a/test/fiber/test_io_close.rb b/test/fiber/test_io_close.rb new file mode 100644 index 0000000000..411d709359 --- /dev/null +++ b/test/fiber/test_io_close.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true +require 'test/unit' +require_relative 'scheduler' + +class TestFiberIOClose < Test::Unit::TestCase + def with_socket_pair(&block) + omit "UNIXSocket is not defined!" unless defined?(UNIXSocket) + + UNIXSocket.pair do |i, o| + if RUBY_PLATFORM =~ /mswin|mingw/ + i.nonblock = true + o.nonblock = true + end + + yield i, o + end + end + + # Problematic on Windows. + def test_io_close_across_fibers + omit "Interrupting a io_wait read is not supported!" if RUBY_PLATFORM =~ /mswin|mingw/ + + with_socket_pair do |i, o| + error = nil + + thread = Thread.new do + scheduler = Scheduler.new + Fiber.set_scheduler scheduler + + Fiber.schedule do + i.read + rescue => error + # Ignore. + end + + Fiber.schedule do + i.close + end + end + + thread.join + + assert_instance_of IOError, error + assert_match(/closed/, error.message) + end + end + + # Okay on all platforms. + def test_io_close_blocking_thread + omit "Interrupting a io_wait read is not supported!" if RUBY_PLATFORM =~ /mswin|mingw/ + + with_socket_pair do |i, o| + error = nil + + reading_thread = Thread.new do + i.read + rescue => error + # Ignore. + end + + Thread.pass until reading_thread.status == 'sleep' + + thread = Thread.new do + scheduler = Scheduler.new + Fiber.set_scheduler scheduler + + Fiber.schedule do + i.close + end + end + + thread.join + reading_thread.join + + assert_instance_of IOError, error + assert_match(/closed/, error.message) + end + end + + # Problematic on Windows. + def test_io_close_blocking_fiber + omit "Interrupting a io_wait read is not supported!" if RUBY_PLATFORM =~ /mswin|mingw/ + + with_socket_pair do |i, o| + error = nil + + thread = Thread.new do + scheduler = Scheduler.new + Fiber.set_scheduler scheduler + + Fiber.schedule do + begin + i.read + rescue => error + # Ignore. + end + end + end + + Thread.pass until thread.status == 'sleep' + + i.close + + thread.join + + assert_instance_of IOError, error + assert_match(/closed/, error.message) + end + end +end |