Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IO.copy_stream should block when called against a non-blocking stream #2439

headius opened this issue Jan 7, 2015 · 0 comments


Copy link

headius commented Jan 7, 2015

The following snippit of code should always block in the copy_stream call:

r,w = IO.pipe
r2, w2 = IO.pipe
w2.nonblock = true
w2.syswrite('a' * 100000)
IO.copy_stream(r, w2)

The nonblock allows the syswrite call to write only as much as there is system-level buffer room and then return. At this point the buffer is full, so the subsequent copy from r to w2 should block attempting to write the rest of the bytes.

The MRI implementation of copy_stream is very complex, so I'm not sure yet if this is done intentionally. It's likely using blocking write logic to loop until all bytes have been copied.

This is not a particularly critical bug; it will be rare that someone wants to have a stream be both nonblocking and do blocking copy_stream calls against it, but I file it here for posterity.

This is, incidentally, the cause of TestIO#test_copy_stream_pipe_nonblock failing to succeed; the copy_stream logic sees a full system buffer and nonblock and returns 0 before the read thread has a chance to empty the buffer. MRI blocks here and waits for the read thread.

  if have_nonblock?
    def test_copy_stream_pipe_nonblock
      mkcdtmpdir {
        with_read_pipe("abc") {|r1|
          assert_equal("a", r1.getc)
          with_pipe {|r2, w2|
              w2.nonblock = true
            rescue Errno::EBADF
              skip "nonblocking IO for pipe is not implemented"
            s = w2.syswrite("a" * 100000)
            t = { sleep 0.1; }
            ret = IO.copy_stream(r1, w2)
            assert_equal(2, ret)
            assert_equal("a" * s + "bc", t.value)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
None yet

No branches or pull requests

1 participant