From: Michal Suchanek Date: 2008-08-21T21:19:48+09:00 Subject: [ruby-core:18364] popen IO bug #2, isolated OK, here goes a reorganization of the code that looks a bit simpler: def try_analyze *words analyzer = IO.popen( 'cat', IO::RDWR ) res = [] t = Thread.new( (IO::for_fd analyzer.fileno),words.dup){|fd,ary| ary.each{|w| STDERR.putc '.'[0] fd.puts w } fd.close_write rescue nil } words.each do STDERR.putc 8 res.push analyzer.gets end analyzer.close rescue nil # hopefully prevents zombie hordes res end 1.upto(10000){|_| try_analyze *%w(a b c d e f g h i) STDERR.puts _ } The downside here is that the code relies on the text analyzer to produce the same number of lines it was sent. Now the code fails on both JRuby and MRI in first iteration. The IO::for_fd call was added specifically to make the earlier example with swapped thread roles work on JRuby. Removing the IO::for_fd call makes the code work in MRI but it still fails within few iterations on JRuby, sometimes throwing a nice null pointer exception. java.lang.NullPointerException at org.jruby.util.io.OpenFile.finalize(OpenFile.java:241) at org.jruby.util.io.OpenFile.cleanup(OpenFile.java:226) at org.jruby.RubyIO.close2(RubyIO.java:1684) at org.jruby.RubyIO.close(RubyIO.java:1662) at org.jruby.RubyIOInvoker$close_method_0_0.call(Unknown Source) at org.jruby.runtime.CallSite$InlineCachingCallSite.cacheAndCall(CallSite.java:123) at org.jruby.runtime.CallSite$InlineCachingCallSite.call(CallSite.java:333) at ruby.Users.hramrach.testt2.rescue_2$RUBY$__rescue__(testt2.rb) at ruby.Users.hramrach.testt2.method__0$RUBY$try_analyze(testt2.rb:16) at ruby.Users.hramrach.testt2Invokermethod__0$RUBY$try_analyzeOpt.call(Unknown Source) at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:134) at org.jruby.runtime.CallSite$InlineCachingCallSite.cacheAndCall(CallSite.java:111) at org.jruby.runtime.CallSite$InlineCachingCallSite.call(CallSite.java:282) at ruby.Users.hramrach.testt2.block_3$RUBY$__block__(testt2.rb:21) at ruby.Users.hramrach.testt2BlockCallback$block_3$RUBY$__block__xx1.call(Unknown Source) at org.jruby.runtime.CompiledBlock.yield(CompiledBlock.java:100) at org.jruby.runtime.Block.yield(Block.java:100) at org.jruby.RubyInteger.upto(RubyInteger.java:122) at org.jruby.RubyIntegerInvoker$upto_method_1_0.call(Unknown Source) at org.jruby.runtime.CallSite$InlineCachingCallSite.cacheAndCall(CallSite.java:159) at org.jruby.runtime.CallSite$InlineCachingCallSite.callIter(CallSite.java:415) at ruby.Users.hramrach.testt2.__file__(testt2.rb:20) at ruby.Users.hramrach.testt2.__file__(testt2.rb) at ruby.Users.hramrach.testt2.load(testt2.rb) at org.jruby.Ruby.runScript(Ruby.java:541) at org.jruby.Ruby.runNormally(Ruby.java:454) at org.jruby.Ruby.runFromMain(Ruby.java:327) at org.jruby.Main.run(Main.java:194) at org.jruby.Main.run(Main.java:91) at org.jruby.Main.main(Main.java:82) Thanks Michal