Details

    • Type: Bug Bug
    • Status: Closed Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: JRuby 1.6.5
    • Fix Version/s: JRuby 1.6.6, JRuby 1.7.0.pre1
    • Component/s: None
    • Labels:
      None
    • Environment:
      linux
    • Number of attachments :
      0

      Description

      JRuby fails to open a fifo for writing because it tries to seek in it.

      Create a fifo on the filesystem like so:

      % mkfifo /tmp/fifo
      

      I tested this on Linux. It is likely other platforms are affected.

      Example code showing it working in MRI 1.9.2 and failing in JRuby 1.6.5 (both 1.8 and 1.9 mode)

      % rvm use 1.9.2; ruby -e 'File.new("/tmp/fifo", "w"); puts :OK'
      Using /home/jls/.rvm/gems/ruby-1.9.2-p290
      OK
      % rvm use 1.6.5; ruby -e 'File.new("/tmp/fifo", "w"); puts :OK'
      Using /home/jls/.rvm/gems/jruby-1.6.5
      IOError: Illegal seek
        initialize at org/jruby/RubyFile.java:442
               new at org/jruby/RubyIO.java:868
            (root) at -e:1
      % rvm use 1.6.5; ruby --1.9 -e 'File.new("/tmp/fifo", "w"); puts :OK'
      Using /home/jls/.rvm/gems/jruby-1.6.5
      IOError: Illegal seek
        initialize at org/jruby/RubyFile.java:464
               new at org/jruby/RubyIO.java:868
            (root) at -e:1
      

      Relevant data from strace:

      3086  open("/tmp/fifo", O_RDWR|O_CREAT, 0666) = 5
      3086  lseek(5, 0, SEEK_CUR)             = -1 ESPIPE (Illegal seek)
      

      Example code showing using the Java API working fine. Mainly, the point of me trying this in 'pure java api' was to see if it was a something related to a missed-feature in Java's cross-platformness

      >> writer = java.io.FileWriter.new(java.io.File.new("/tmp/fifo"))
      => #<Java::JavaIo::FileWriter:0x33cfa965>
      >> writer.write("Hello")
      => nil
      >> writer.flush()
      => nil
      

        Activity

        Hide
        Jordan Sissel added a comment -

        It's interesting to note that the specific lseek(2) call that fails is one that is trying to ask the OS what the current position the file write cursor is. It seems possible to skip any lseek(2) calls until the position in the file needs to be known (stat calls, file.pos, etc), but I"ll leave the specifics of implementation and resolution up to you fine folks

        Let me know if you need any more data!

        (I may offer a patch for this if I can find some energy to do so)

        Show
        Jordan Sissel added a comment - It's interesting to note that the specific lseek(2) call that fails is one that is trying to ask the OS what the current position the file write cursor is. It seems possible to skip any lseek(2) calls until the position in the file needs to be known (stat calls, file.pos, etc), but I"ll leave the specifics of implementation and resolution up to you fine folks Let me know if you need any more data! (I may offer a patch for this if I can find some energy to do so)
        Hide
        Charles Oliver Nutter added a comment -

        Ok, so here's the deal.

        In order to emulate libc behavior for opening a file for 'w', we truncate the file before returning the File object to the user. That truncate operation fails on the fifo because the JDK classes attempt to truncate the file and then position(0), with the position call failing due to fifos not being seekable.

        Switching to append mode "w+", we try to position the file at its end, which also fails.

        The missing behavior may be that our "fopen" needs to silently ignore failure to seek. Going to look at libc source to see what they do.

        Show
        Charles Oliver Nutter added a comment - Ok, so here's the deal. In order to emulate libc behavior for opening a file for 'w', we truncate the file before returning the File object to the user. That truncate operation fails on the fifo because the JDK classes attempt to truncate the file and then position(0), with the position call failing due to fifos not being seekable. Switching to append mode "w+", we try to position the file at its end, which also fails. The missing behavior may be that our "fopen" needs to silently ignore failure to seek. Going to look at libc source to see what they do.
        Hide
        Charles Oliver Nutter added a comment -

        I went with a fix that appears to match how glibc deals with unseekable files. Fixed on master@01fa54f and jruby-1_6@69bd6c5.

        commit 69bd6c5d38032e569d5129b7ea156e6dbf7fcaa2
        Author: Charles Oliver Nutter <headius@headius.com>
        Date:   Mon Jan 2 13:41:40 2012 -0600
        
            Fix JRUBY-6280: Fails to open fifo for writing.
            
            It turns out that in glibc, fopen operations that would seek in
            the resulting file descriptor do actually check for ESPIPE to know
            that a failed seek is due to the file being a pipe or FIFO. We do
            not have direct access to the actual ESPIPE, but we can check the
            IOException message to know if it's the same problem.
            
            I don't think there's any real risk in this change, since it will
            only quietly fail to seek on a file when it's impossible to seek
            on that file, which is what we want. Other error cases and open
            failures should propagate as before.
        
        Show
        Charles Oliver Nutter added a comment - I went with a fix that appears to match how glibc deals with unseekable files. Fixed on master@01fa54f and jruby-1_6@69bd6c5. commit 69bd6c5d38032e569d5129b7ea156e6dbf7fcaa2 Author: Charles Oliver Nutter <headius@headius.com> Date: Mon Jan 2 13:41:40 2012 -0600 Fix JRUBY-6280: Fails to open fifo for writing. It turns out that in glibc, fopen operations that would seek in the resulting file descriptor do actually check for ESPIPE to know that a failed seek is due to the file being a pipe or FIFO. We do not have direct access to the actual ESPIPE, but we can check the IOException message to know if it's the same problem. I don't think there's any real risk in this change, since it will only quietly fail to seek on a file when it's impossible to seek on that file, which is what we want. Other error cases and open failures should propagate as before.
        Hide
        Hiro Asari added a comment -

        The commit resulted in hanging CI jobs on Linux. (This does not manifest on Mac OS X.)

        Show
        Hiro Asari added a comment - The commit resulted in hanging CI jobs on Linux. (This does not manifest on Mac OS X.)
        Hide
        Charles Oliver Nutter added a comment -

        Since this time it seems the CI jobs have healed, so I'll re-resolve this one for now.

        Show
        Charles Oliver Nutter added a comment - Since this time it seems the CI jobs have healed, so I'll re-resolve this one for now.

          People

          • Assignee:
            Charles Oliver Nutter
            Reporter:
            Jordan Sissel
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: