2013-02-04 25 views
3

我試圖將錯誤日誌記錄添加到從一個子進程的stdout中讀取數據的JRuby進程,並將此數據寫入另一個子進程的stdin,同時收集有關數據的一些統計信息。子流程使用IO.popen4生成。爲什麼JRuby的read_nonblock塊?

要讀取錯誤流,我不能使用阻塞讀取,因爲在正常情況下,這些流上沒有輸出。但是,當我在這些流上使用​​時,我仍然在JRuby中遇到阻塞讀取。

爲什麼​​調用被阻止,我怎麼能重寫這段代碼,以便它永遠不會阻塞,並始終顯示任何子進程輸出的stderr?

下面是我使用的代碼的簡化版本,它重現了問題。它會阻止jruby,並且不會在ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin11.4.0]上顯示預期的輸出。

if RUBY_PLATFORM != "java" && RUBY_VERSION =~ /^1\.9/ 
    class IO 
    def self.popen4(*args, &block) 
     require "open4" 
     Open4::popen4(*args, &block) 
    end 
    end 
end 

IO.popen4('echo', 'hi') do |_, _, stdout1, stderr1| 
    IO.popen4('sh', '-c', 'cat 1>&2') do |_, stdin2, _, stderr2| 
    stdout1.each_line do |line| 
     stdin2 << line 
     (IO.select([stderr1, stderr2], [], [], 0.1) or [[]]).first.each do |stream| 
     begin 
      # in jruby 1.6.8 (ruby-1.9.2-p312) (2012-09-18 1772b40) (Java HotSpot(TM) Client VM 1.6.0_37) [darwin-i386-java], read_nonblock blocks 
      # idem in jruby 1.7.2 (1.9.3p327) 2013-01-04 302c706 on Java HotSpot(TM) Client VM 1.6.0_37-b06-434-11M3909 [darwin-i386] 
      puts stream.read_nonblock(1000) 
     rescue Exception => e 
      puts e.message 
     end 
     end 
    end 
    end 
end 

我知道按CTRL當它被擋在read_nonblock因爲堆棧跟蹤+ \:

"main" prio=5 tid=0000000003110800 nid=0xb0201000 runnable [00000000b01ff000] 
java.lang.Thread.State: RUNNABLE 
at sun.nio.ch.FileDispatcher.read0(Native Method) 
at sun.nio.ch.FileDispatcher.read(FileDispatcher.java:26) 
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:198) 
at sun.nio.ch.IOUtil.read(IOUtil.java:171) 
at sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:144) 
- locked <0000000006158308> (a java.lang.Object) 
at org.jruby.util.io.ChannelStream.refillBuffer(ChannelStream.java:196) 
at org.jruby.util.io.ChannelStream.bufferedRead(ChannelStream.java:926) 
at org.jruby.util.io.ChannelStream.bufferedRead(ChannelStream.java:888) 
at org.jruby.util.io.ChannelStream.fread(ChannelStream.java:1288) 
- locked <000000000615a8f8> (a org.jruby.util.io.ChannelStream) 
at org.jruby.util.io.ChannelStream.readnonblock(ChannelStream.java:1314) 
- locked <000000000615a8f8> (a org.jruby.util.io.ChannelStream) 
at org.jruby.RubyIO.getPartial(RubyIO.java:2762) 
at org.jruby.RubyIO.read_nonblock(RubyIO.java:2697) 
at org.jruby.RubyIO$INVOKER$i$0$1$read_nonblock.call(RubyIO$INVOKER$i$0$1$read_nonblock.gen) 
at org.jruby.internal.runtime.methods.JavaMethod$JavaMethodN.call(JavaMethod.java:646) 
at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:204) 
at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:168) 
at read_nonblock_test.chained_2_rescue_1$RUBY$SYNTHETIC__file__(read_nonblock_test.rb:19) 
at read_nonblock_test.block_3$RUBY$__file__(read_nonblock_test.rb:16) 
at read_nonblock_test$block_3$RUBY$__file__.call(read_nonblock_test$block_3$RUBY$__file__) 
at org.jruby.runtime.CompiledBlock19.yield(CompiledBlock19.java:139) 
at org.jruby.runtime.Block.yield(Block.java:130) 
... 
+0

礦塊(1.6.8),但*總是*返回EAGAIN。我還沒有找到*任何*的方式來檢測數據,但不是用jruby中的管道阻塞: – rlpowell

+0

MRI不會阻塞嗎? – rogerdpack

回答

0

(這是不是一個真正的答案,更多的是問題的延續)

礦井永不堵塞(1.6.8),但總是返回EAGAIN。我還沒有找到任何方式檢測數據,但並非塊與管道JRuby中:當數據出現

jruby-1.6.8 :001 > io=IO.popen('sh -c "sleep 10"') ; select([io],nil,nil,0) 
=> [[#<IO:0x6e72d873>], [], []] 
jruby-1.6.8 :003 > sleep 10 ; begin ; io.read_nonblock(1) ; rescue Exception => e ; puts "nothing: #{e.class.name}" ; puts "#{io.eof?}" ; end 
nothing: Errno::EAGAIN 
true 
=> nil 
jruby-1.6.8 :001 > io=IO.popen('sh -c "sleep 10"') ; select([io],nil,nil,0) 
=> [[#<IO:0x6e72d873>], [], []] 
jruby-1.6.8 :006 > require 'fcntl'; io=IO.popen('sh -c "sleep 10"') ; puts "f: #{io.fcntl(Fcntl::F_GETFL)}" ; io.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK) ; puts "f: #{io.fcntl(Fcntl::F_GETFL)}" ; puts "e: #{io.gets}" 
f: 0 
f: 0 
e: 
=> nil 
jruby-1.6.8 :007 > io=IO.popen('sh -c "echo foo ; sleep 10"') ; select([io],nil,nil,0) ; begin ; io.read_nonblock(1) ; rescue Exception => e ; puts "nothing: #{e.class.name}" ;  puts "#{io.eof?}" ; end 
nothing: Errno::EAGAIN 
false 
=> nil 

這並不是說read_nonblock返回EAGAIN!在EOF! 0.o

-Robin

+0

等待*不應該提高EAGAIN?我的意思是,如果你第二次或第三次調用它,它是否也會提高它呢? – rogerdpack

+0

我每次都提高EAGAIN * *。即使有數據等待,這與EAGAIN的含義完全相反。即使FD在EOF,這是違反文檔。 – rlpowell

+0

聽起來像一個bug ... MRI是否正確地做到了這一點?如果是這樣,我會提交一個jruby jira ... – rogerdpack

1

所以,這是一個漂亮的進攻解決方案,但它是爲我工作。由於選擇總是返回「是」,並且非塊只是不工作,我沒有看到另一種選擇:

 require 'timeout' 
     begin 
     while true 
      line = nil 
      Timeout::timeout(1) { line=io.gets } 
      if line.nil? 
      break 
      else 
      puts "line: #{line}" 
      end 
     end 
     rescue Timeout::Error 
     end 

-Robin