Misc #18725
openIO#write and IO#wait_writable block for write pipe if read pipe is closed in other thread on OpenBSD
Description
I'm not sure whether this is a Ruby issue, an OpenBSD issue, or something else, but @ioquatix (Samuel Williams) asked me to post this here. The following program hangs on OpenBSD:
require 'io/wait'
rd, wr = IO.pipe
thread_pass = ARGV[0] == 'pass'
write = ARGV[0] == 'write'
thread = Thread.new do
longer = "0" * 1024 * 1024
(1024 * 4).times do
if write
wr.write(longer)
else
while wr.write_nonblock(longer, :exception=>false) == :wait_writable
thread_pass ? Thread.pass : wr.wait_writable
end
end
end
:finished
rescue => e
e
end
sleep 1
rd.close
puts :rd_close
puts thread.value
This program will also hang if write
is given as the argument, using wr.write
instead of wr.write_nonblock
/wr.wait_writable
. However, if pass
is given as the argument, using Thread.pass
instead of wr.wait_writable
, the program will not hang.
From testing, the hang when called without an argument is in wait_writable
, and the hang with a write
argument is in write
.
Is Ruby supposed to guarantee that a closing the read end of a pipe in one thread causes a raise of EPIPE to the write end of the pipe in a different thread if already inside IO#write
/IO#wait_writable
? Or is this platform-specific behavior?
This example was extracted from one of Rack's tests, which was causing non-deterministic hangs on OpenBSD.
No data to display