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