RubyIO's close2 method has code to destroy any spawned processes associated with an IO object that gets closed. So, the workaround is to not close any of the IO streams until the process has finished or the program has finished consuming the process output. See:

Activity

commit 00d30e159ba2cbbe01c04d4fd2b8bd46d9b2ee6a
Author: Charles Oliver Nutter <headius@headius.com>
Date: Tue Dec 20 18:00:38 2011 -0600
Fix JRUBY-6291: Closing One Stream From IO.popen4 Results in Stream Closed Error When Reading Other Streams
Ben figured this one out. The logic I put in place to ensure the
child process gets nuked for a popen stream was causing it to be
nuked prematurely for popen3/4 streams. I added a "popenSpecial"
boolean to RubyIO, so we know not to do that.

Charles Oliver Nutter
added a comment - 20/Dec/11 6:02 PM Fixed by master@00d30e1.
commit 00d30e159ba2cbbe01c04d4fd2b8bd46d9b2ee6a
Author: Charles Oliver Nutter <headius@headius.com>
Date: Tue Dec 20 18:00:38 2011 -0600
Fix JRUBY-6291: Closing One Stream From IO.popen4 Results in Stream Closed Error When Reading Other Streams
Ben figured this one out. The logic I put in place to ensure the
child process gets nuked for a popen stream was causing it to be
nuked prematurely for popen3/4 streams. I added a "popenSpecial"
boolean to RubyIO, so we know not to do that.