IO#write and IO#wait_writable block for write pipe if read pipe is closed in other thread on OpenBSD
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 == 'pass' write = ARGV == '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.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
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#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