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

File::write uses nonblocking IO when MRI uses blocking #5663

Closed
byteit101 opened this Issue Mar 22, 2019 · 3 comments

Comments

Projects
None yet
4 participants
@byteit101
Copy link
Member

byteit101 commented Mar 22, 2019

Environment

  • jruby 9.2.6.0 (2.5.3) 2019-02-11 15ba00b OpenJDK 64-Bit Server VM 25.201-b09 on 1.8.0_201-b09 +jit [linux-x86_64]
  • RHEL 7.6

Expected Behavior

MRI waits if nobody is reading the other side of a pty pair:

2.5.3 :001 > require 'pty'
 => true 
2.5.3 :002 > m,s = PTY.open
 => [#<IO:masterpty:/dev/pts/15>, #<File:/dev/pts/15>] 
2.5.3 :003 > 20.times{|i| puts "writing #{i}"; s.write("_" * 1000) }
writing 0
writing 1
writing 2
writing 3
writing 4
writing 5
writing 6
writing 7
writing 8
<hang until ctrl-c>

Actual Behavior

However JRuby appears to use non-blocking IO and thus throws an exception:

jruby-9.2.6.0 :001 > require 'pty'
 => true 
jruby-9.2.6.0 :002 > m,s = PTY.open; nil
 => nil 
jruby-9.2.6.0 :003 > 20.times{|i| puts "writing #{i}"; s.write("_" * 1000) }
writing 0
writing 1
writing 2
writing 3
writing 4
writing 5
writing 6
writing 7
writing 8
writing 9
writing 10
writing 11
writing 12
Traceback (most recent call last):
        9: from /home/byteit101/.rvm/rubies/jruby-9.2.6.0/bin/irb:13:in `<main>'
        8: from org/jruby/RubyKernel.java:1179:in `catch'
        7: from org/jruby/RubyKernel.java:1179:in `catch'
        6: from org/jruby/RubyKernel.java:1411:in `loop'
        5: from org/jruby/RubyKernel.java:1047:in `eval'
        4: from (irb):3:in `evaluate'
        3: from (irb):3:in `block in evaluate'
        2: from org/jruby/RubyIO.java:1429:in `write'
        1: from org/jruby/RubyIO.java:1472:in `write'
SystemCallError (Unknown error (SystemCallError) - No message available)
jruby-9.2.6.0 :004 >

It took me a bit to figure out that the errno was EAGAIN (Resource temporarily unavailable) (not sure why that wasn't in the exception message, another issue?)

I found this as the surprise exception causes a compatibility issue with Pry in a Pty to exit on large output (think ENV inspection, ~12k on my machine) in JRuby, but not under MRI as MRI never throws here.

@headius

This comment has been minimized.

Copy link
Member

headius commented Mar 28, 2019

Huh, that's weird. Our pty library is FFI-based and lives in lib/ruby/stdlib. Maybe you can have a look there and see if something obvious is going on (like setting the IO streams O_NONBLOCK or something).

https://github.com/jruby/jruby/blob/master/lib/ruby/stdlib/pty.rb

@byteit101

This comment has been minimized.

Copy link
Member Author

byteit101 commented Mar 29, 2019

After too much strace for one night, I think I found the issue. From https://github.com/jruby/jruby/blob/master/lib/ruby/stdlib/pty.rb#L136 :

    def hack_close_on_exec(fd)
      # I assume this isn't actually supported for good reasons.
      # but let's see how close to passing test_pty we can get.
      fl = fd.fcntl(Fcntl::F_GETFL, 0)
fd.fcntl(Fcntl::F_SETFL, Fcntl::FD_CLOEXEC|fl)

Should be GETFD/SETFD according to man pages.
But that would seem to have worked regardless, because of the weirdness of https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/RubyIO.java#L2436 which says
FIXME: F_SETFL and F_SETFD are treated as the same thing here

However, I also found that
fd.fcntl(Fcntl::F_SETFL, Fcntl::FNONBLOCK|fl) triggers no fcntl call, while fd.fcntl(Fcntl::F_SETFL, fl) triggers fcntl(16, F_SETFL, O_RDWR|O_NONBLOCK|O_LARGEFILE) = 0 (ie. the exact opposite is configured. I'm pretty sure I was looking at the right lines in strace, but that seems odd.

fd.close_on_exec=true looks to work correctly though (at least in my trials), but I'm unsure why hack_close_on_exec is needed vs just using close_on_exec directly?

@Freaky

This comment has been minimized.

Copy link
Contributor

Freaky commented Mar 29, 2019

As the comment suggests, the hack predates close_on_exec=. Removing it is definitely suggested :)

@enebo enebo added this to the JRuby 9.2.7.0 milestone Mar 29, 2019

@enebo enebo closed this in #5677 Apr 5, 2019

enebo pushed a commit that referenced this issue Apr 5, 2019

enebo added a commit that referenced this issue Apr 5, 2019

Merge pull request #5677 from byteit101/master
Use close_on_exec method instead of using fcntl calls for pty. Fixes #5663
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.