Thursday, February 25, 2010

JRuby Embed (Red Bridge) Update: termination and skipping sharing variables

Hers' recent update of RedBridge. Performance got much better, but the change on termination might affect your code. If you expect at_exit blocks to be executed automatically, you need to add termination.

By the recent change, Embed Core, JSR223, and BSF, all three implementations had changes in their behaviors of evaluation and method invocation. Termination is no longer executed automatically. This means, at_exit blocks are not executed at the end of every runScriplet, run and callMethod (eval, invokeMethod and invoekeFunction in JSR223). It is effective since commit 673df9f.

This rather big changes was made for two reasons. One is to avoid possible unexpected behavior caused by at_exit blocks to be executed too early. For example, a gem might have a class that has an at_exit block, which should run after other code have finished. The second reason is a performance improvement. Terminate method takes much time to complete. Because of this, evaluation and method invocation of embedding API were very slow. Now, embedding API got much better performance than before.

Then, how to do that? To terminate explicitly, call terminate method on Embed Core and BSF. It's simple. However, JSR223 doesn't have terminate method defined by the specification. So, use newly added attribute, AttributeName.TERMINATION or org.jruby.embed.termination to trun termination on. Next, evaluate blank code.

Usage examples:

[Embed Core]

ScriptingContainer container = null;
try {
container = new ScriptingContainer();
conatiner.runScriptlet(PathType.CLASSPATH, testname);
} catch (Throwable t) {
t.printStackTrace();
} finally {
container.terminate();
}

[JSR223]

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");
engine.eval("$x='GVar'");
engine.eval("at_exit { puts \"#{$x} in an at_exit block\" }"); // nothing is printed here
engine.getContext().setAttribute(AttributeName.TERMINATION.toString(), true, ScriptContext.ENGINE_SCOPE);
engine.eval(""); // prints "GVar in an at_exit block"


Let's move on to the second change. A new option has been added to skip sharing variables. Sharing variables is a useful or required feature for users from JSR223 and BSF background. But, it is not a necessary for others especially who have directly used JRuby's internal API. Sharing variables just slowed down the evaluations and method invocations. When sharing variables is skipped, the performance will be a bit better.

Usage example:

[Embed Core]
container.setAttribute(AttributeName.SHARING_VARIABLES, false);

[JSR223]
engine.getContext().setAttribute(AttributeName.SHARING_VARIABLES.toString(), false, ScriptContext.ENGINE_SCOPE);


Have fun with RedBridge!

3 comments:

Computer Laptop said...

I enjoyed every bit of your article. It is very informative and helpful to me as well as to all the commentators. Thanks for taking the time to discuss on this. I will love to read more from you.
Computer Laptop.

Anonymous said...

Yoko,

Thanks for your blog posts, I found them to be THE most practical info on JRuby embedding on the Web.

I'm a bit confused by fact that .getRuntime() is being deprecated though... Many scripts seem to use it, so I thought it is a good practice? For example, in widely used jirb_swing:

tar = org.jruby.demo.TextAreaReadline.new(text,
" Welcome to the JRuby IRB Console [#{JRUBY_VERSION}] \n\n")
JRuby.objectspace = true
tar.hook_into_runtime_with_streams(JRuby.runtime)

How do you go about it now, as JRuby.runtime call is no longer supported?

yokolet said...

Hi arvitallian,

Thanks for using JRuby embedding API and posing a good question!

The reason ScriptingContainer.getRuntime() method was deprecated is not to expose JRuby's internal API too much. So, now, .getRuntime() method goes to ScriptingContainer.getProvider().getRuntime() so that users will recognize it is an internal API.

But, as you pointed out, JRuby.runtime has the same effect as ScriptingContainer.getRuntime() and makes the same pitfalls. I don't think JRuby.runtime will be deprecated. But, we'd better rethink how we can avoid exposing internal API.