Heads up for those not aware: it’s not hard to get burned when you’re executing other Ruby processes from within a JRuby process.
Suppose you satisfy these conditions:
- You’re running a Ruby program via JRuby.
- Your program needs to execute another program via something like
- The command for the new program looks like another Ruby program. Examples1:
`ruby -e 'puts "hi"'`
`jruby -e 'puts "hi"'`
Under these conditions JRuby will execute your program in a new JRuby instance within the running JVM process. This has the upside of reduced startup time for the new Ruby program. It has the downside of the occasional surprise—your program may assume it has its own process and not run correctly when sharing its process with other JRuby instances.
I ran into a number of these surprises a few years ago while developing a JRuby application responsible for launching lots of other processes. I filed several JRuby bug reports; the JRuby maintainers, in turn, fixed the bugs quickly. But those fixes don’t cover all of the quirks you can run into.
There’s a JRuby parameter,
jruby.launch.inproc, that you can set to
false to disable this behavior. I recommend disabling it if you’re going to be launching Ruby processes and can take the performance hit. It is no fun to get surprised and spend hours digging into why things aren’t working.
A few years ago I wrote to the JRuby mailing list arguing that
jruby.launch.inproc should default to
false because it is the safer behavior; setting it to
true is an optimization that doesn’t always work. In my mailing list post, I speculated that there could be some nasty interactions in JNI code, but didn’t have anything to test that with.
Last month I got an email from a JRuby user confirming that, indeed, you can get surprised by the
inproc behavior and JNI. He wrote:
We have a bunch of C++ libraries that we have wrapped with JNI using swig. Then from java, we load the DLL library that implements the JNI interfaces. In an jruby.launch.inproc=true world, each instance of jruby and the JVM thinks it’s entirely separate from all other instances. But our C++ code we certainly doesn’t! Our DLL code is not designed to be loaded on top of itself and it relies on some global state which cannot be shared.
It was interesting how this user had found my post from three years ago and sent me a note about it!
In a separate mailing list discussion, Charlie Nutter says:
This isn’t even the worst case; Rake, among other libraries, launches subprocesses to run groups of tests. So the current slow test times for Rake/Rails users would get much, much worse. I don’t claim to have any answers. The in-proc stuff is absolutely crucial to keeping JRuby working nicely and reasonably quickly with the way Rubyists have wired all those bits together….
There’s no good solution that will appease all perspectives. Dang.
In summary, if you’re running subprocesses with JRuby and discover mysterious issues, start by setting
false and see if things improve. If they do, you can either leave the parameter set to
false or use another trick to get your command to run in a separate process. (One trick that works is to put some bogus text in front of your command, like
`FOO=bar ruby -e 'puts "hi"'`)
1 These examples are hardly exhaustive.