Should I Use a Separate Scriptengine and Compiledscript Instances Per Each Thread

Should I use a separate ScriptEngine and CompiledScript instances per each thread?

You can share a ScriptEngine and CompiledScript objects across threads. They are threadsafe. Actually, you should share them, as a single engine instance is a holder for a class cache and for JavaScript objects' hidden classes, so by having only one you cut down on repeated compilation.

What you can't share is Bindings objects. The bindings object basically corresponds to the JavaScript runtime environment's Global object. The engine starts with a default bindings instance, but if you use it in multithreaded environment, you need to use engine.createBindings() to obtain a separate Bindings object for every thread -- its own global, and evaluate the compiled scripts into it. That way you'll set up isolated global scopes with the same code. (Of course, you can also pool them, or synchronize on 'em, just make sure there's never more than one thread working in one bindings instance). Once you evaluated the script into the bindings, you can subsequently efficiently invoke functions it defined with ((JSObject)bindings.get(fnName).call(this, args...)

If you must share state across threads, then at least try to make it not mutable. If your objects are immutable, you might as well evaluate the script into single Bindings instance and then just use that across threads (invoking hopefully side-effect free functions). If it is mutable, you'll have to synchronize; either the whole bindings, or you can also use the var syncFn = Java.synchronized(fn, lockObj) Nashorn-specific JS API to obtain versions of JS functions that synchronize on a specific object.

This presupposes that you share single bindings across threads. If you want to have multiple bindings share a subset of objects (e.g. by putting the same object into multiple bindings), again, you'll have to somehow deal with ensuring that access to shared objects is thread safe yourself.

As for THREADING parameter returning null: yeah, initially we planned on not making the engine threadsafe (saying that the language itself isn't threadsafe) so we chose the null value. We might need to re-evaluate that now, as in the meantime we did make it so that engine instances are threadsafe, just the global scope (bindings) isn't (and never will be, because of JavaScript language semantics.)

Is NashornScriptEngine thread-safe?

The engine itself is thread-safe but the answer isn't a simple yes/no so keep on reading.

Please see this answer from someone with excellent credentials on this topic.

I'm not going to copy/paste the entire answer, but it starts off with this:

You can share a ScriptEngine and CompiledScript objects across
threads. They are threadsafe. Actually, you should share them, as a
single engine instance is a holder for a class cache and for
JavaScript objects' hidden classes, so by having only one you cut down
on repeated compilation.

What you can't share is Bindings objects ...

Make sure to read the rest of the answer!

Nashorn: concurrent eval with singleton ScriptEngine ? thread safe?

My answer to my own question: I would not use a singleton as described above. Despite some potential threading issues, you likely do not want to destroy the bindings at each request (as it may require re-compiling of scripts). What we ended up doing is creating a pool of Engines and their associated scope Bindings. An engine/binding pair.

At each servlet request we grab an engine/binding pair from the pool, put the request/response into the binding, and then execute the script. There is no threading issues to worry about because a given engine/binding pair is only executed by a single thread at a time. When the request is done, the engine/binding pair is returned to the pool. Seems to work well.

Append CompiledScript or add two CompiledScript

You can't combine like that two CompiledScript objects. If it comes to Groovy - when Groovy script gets compiled it creates a class that extends groovy.lang.Script with a few methods inside. In this case adding two scripts to each other means merging two Java classes.

Consider evaluating both scripts in separation as an alternative. Take a look at following example:

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;

public class ScriptTest {

public static void main(String[] args) throws ScriptException {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("Groovy");

final Bindings bindings = new SimpleBindings();
bindings.put("a", 3);

final String script1 = "println 'Running first script...'; def c = 2; def d = c + a; return d";
final CompiledScript compiledScript1 = ((Compilable) engine).compile(script1);

//Execute some instruction to generate 'script2'
final String script2 = "println 'Running second script...';";
final CompiledScript compiledScript2 = ((Compilable) engine).compile(script2);

Integer returnedValue = (Integer) compiledScript1.eval(bindings);
System.out.println("Returned value from the first script: " + returnedValue);

if (returnedValue > 1) {
compiledScript2.eval(bindings);
}
}
}

Creating CompiledScript object is one thing. Second thing is evaluation of those scripts. If you always evaluate script1 and script2 right away, then you simply need:

compiledScript1.eval(bindings);
compiledScript2.eval(bindings);

However script1 may return some value and based on that value you can decide if script2 should be evaluated. Let's say script1 returns an Integer and I will evaluate script2 only if returned value is greater than 1:

Integer returnedValue = (Integer) compiledScript1.eval(bindings);
if (returnedValue > 1) {
compiledScript2.eval(bindings);
}

Alternatively you can base on bindings values after running script1. Let's say script1 modifies bindings a and b and set both to true (assuming they hold boolean type):

compiledScript1.eval(bindings);
if (bindings.get("a") && bindings.get("b")) {
compiledScript2.eval(bindings);
}

Calling a function defined in script1 from script2

Let's say script1 is defined as follows:

import groovy.json.JsonOutput

def json(Map map) {
return new JsonOutput().toJson(map)
}

println json([test: 1])

In case you have to call json(map) function inside script2 - you can do it. Take a look at this example:

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;

public class ScriptTest {

public static void main(String[] args) throws ScriptException {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("Groovy");

final Bindings bindings = new SimpleBindings();
bindings.put("a", 3);

final String script1 = "import groovy.json.JsonOutput\n" +
"\n" +
"def json(Map map) {\n" +
" return new JsonOutput().toJson(map)\n" +
"}\n" +
"\n" +
"println json([test: 1])";

final CompiledScript compiledScript1 = ((Compilable) engine).compile(script1);

//Execute some instruction to generate 'script2'
final String script2 = "println 'Running second script...'; println json([test: 2]);";
final CompiledScript compiledScript2 = ((Compilable) engine).compile(script2);

compiledScript1.eval(bindings);
compiledScript2.eval(bindings);
}
}

script2 is defined as:

println 'Running second script...';
println json([test: 2]);

When you run it you will see in the console:

{"test":1}
Running second script...
{"test":2}

The first two lines come from script1 while the last line is generated by script2. Keep in mind that both CompiledScript hold a reference to the same engine, so you can refer to functions defined in previously evaluated scripts.

Nashorn MultiThread Programming

You can initialize a ScriptEngine and use it in a multithreaded application.

Instead of engine.eval(myScript); you'll want to create a CompiledScript instance that you can later evaluate into Bindings instances:

Compilable compilable = (Compilable) engine;
CompiledScript script = compilable.compile(myScript);

And instead of using your engine as an Invocable you'll need to create a Bindings instance for each thread/test, evaluate the compiled script into it, get the script object mirror that wraps the function, and then call the function:

@Test(dataProvider = "validReferenceNumbers", timeOut = 3000)
public final void testValidReferenceNumber(String referenceNumber) throws ScriptException {
Map<String, String> parameters = new HashMap<>();
parameters.put("referenceNumber", referenceNumber);
Bindings bindings = engine.createBindings();
script.eval(bindings);
ScriptObjectMirror scriptObjectMirror = (ScriptObjectMirror) bindings.get("validate");
Object result = scriptObjectMirror.call(null, parameters);
/* insert result assertions here */
}

Sources:

  • https://stackoverflow.com/a/30159424/3255152
  • Reuse Nashorn ScriptEngine in Servlet
  • https://stackoverflow.com/a/32252478/3255152

What is the lifecycle and concurrency semantics of Rhino Script Engine

So I've run the experiment and the Rhino engine reports "Mozilla Rhino" is MULTITHREADED which the JavaDocs asserts

"MULTITHREADED" - The engine implementation is internally thread-safe
and scripts may execute concurrently although effects of script
execution on one thread may be visible to scripts on other threads."

Here's the code...it looks threadsafe to me, as long as the bindings you pass in are threadsafe too.

package org.rekdev;
import java.util.*;
import javax.script.*;
public class JavaScriptWTF {
public static void main( String[] args ) {
ScriptEngineManager mgr = new ScriptEngineManager();
List<ScriptEngineFactory> factories = mgr.getEngineFactories();
for ( ScriptEngineFactory factory : factories ) {
System.out.println( String.format(
"engineName: %s, THREADING: %s",
factory.getEngineName(),
factory.getParameter( "THREADING" ) ) );
}
}
}

...the output is...

engineName: AppleScriptEngine, THREADING: null

engineName: Mozilla Rhino, THREADING: MULTITHREADED

To answer your exact question...

  1. Is Bindings supposed to be thread safe?

    It sounds to me that it is your responsibility to make them Thread-safe. In other words, pass in only immutable objects and whether the engine is Thread-safe or not becomes a non-issue.

  2. Should multiple threads be allowed to share a single ScriptEngine instance?

    It sounds to me like they can, but the key is the state sharing that can occur through the Bindings. Immutable objects are your friend.

  3. ...or should each thread construct a short-lived instance?

    It seems to me that the best way to think of this is that each execution of eval is a short lived instance.

  4. ... or keep them in a pool?

    In this day and age attempting to pool resources on your own is rarely a good idea. Give the short-lived instance a shot, measure its performance, and work out from there.

  5. What happens if multiple threads concurrently call ScriptEngine.eval(...)?

    If I understand the Rhino engine's repsonse to MULTITHREADING correctly, ScriptEngine.eval should be fine with concurrent calls.

  6. Same question for CompiledScript instances

    The JavaDocs state that "Changes in the state of the ScriptEngine caused by execution of the CompiledScript may visible during subsequent executions of scripts by the engine." http://docs.oracle.com/javase/6/docs/api/javax/script/CompiledScript.html. So they don't sound Thread-safe at all in an environment where you appear to be trying to minimize the number of ScriptEngine instances.

  7. Same questions for interface implementations generated using Invocable.getInterface(...)?
    You are on your own here. I don't understand exactly why or when this capability would be used and it sounds to me like you may be "jumping the shark" here. If you want to go this deep into the scripting language, I recommend that you abandon JavaScript and look at Groovy for a more scriptable Java.

  8. Presumably, objects placed in Bindings follow Java's garbage collection. What about garbage collection of objects that don't end up in the bindings?

    If they don't end up in bindings I expect them to be bound to the ScriptEngine and follow its lifecycle (based upon the docs that I have read). Pooling the ScriptEngine instances does not sound like a great idea.

Is Groovy ScriptingEngine thread safe?

Unfortunately this information is misleading if it comes to GroovyScriptEngineImpl class. The Javadoc you mentioned says:

"MULTITHREADED" - The engine implementation is internally thread-safe and scripts may execute concurrently although effects of script execution on one thread may be visible to scripts on other threads.

GroovyScriptEngineImpl does not apply to this, because e.g. you can change the classloader with GroovyScriptEngineImpl.setClassLoader(GroovyClassLoader classLoader) method and it may cause unpredictable behavior when it happens in concurrent execution (this method is not even atomic and does not synchronize execution between threads).

Regarding scriptEngine.eval(script, bindings) execution, you have to be aware of its non-deterministic nature when you share same bindings across many different threads. javax.script.SimpleBindings default constructor uses HashMap and you should definitely avoid it - in case of multithreaded execution it's better to use ConcurrentHashMap<String,Object> to at least allow safe concurrent access. But even though you can't get any guarantee when you evaluate concurrently multiple scripts and those scripts will change global bindings. Consider following example:

import org.codehaus.groovy.jsr223.GroovyScriptEngineImpl

import javax.script.ScriptContext
import javax.script.SimpleBindings
import javax.script.SimpleScriptContext
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future

class GroovyScriptEngineExample {

static void main(args) {
def script1 = '''
def x = 4
y++
x++
'''

def script2 = '''
def y = 10
x += y
'''

final GroovyScriptEngineImpl engine = new GroovyScriptEngineImpl()
final ExecutorService executorService = Executors.newFixedThreadPool(5)

(0..3).each {
List<Future> tasks = []

final SimpleBindings bindings = new SimpleBindings(new ConcurrentHashMap<String, Object>())
bindings.put('x', 1)
bindings.put('y', 1)

(0..<5).each {
tasks << executorService.submit {
engine.setClassLoader(new GroovyClassLoader())
engine.eval(script1, bindings)
}
tasks << executorService.submit {
println engine.getClassLoader()
engine.eval(script2, bindings)
}
}

tasks*.get()

println bindings.entrySet()
}

executorService.shutdown()
}
}

In this example we define two Groovy scripts:

def x = 4
y++
x++

and:

def y = 10
x += y

In the first script we define a local variable def x = 4 and x++ increments only our local script variable. When we print x binding after running this script we will see that it won't change during the execution. However y++ in this case incremented y binding value.

In the second script we define local variable def y = 10 and we add value of local y (10 in this case) to current global x binding value.

As you can see both scripts modify global bindings. In the exemplary code shown in this post we run both scripts 20 times concurrently. We have no idea in what order both scripts get executed (imagine that there is a random timeout in each execution, so one script may hang for couple of seconds). Our bindings use ConcurrentHashMap internally so we are only safe if it comes to concurrent access - two threads won't update same binding at the same time. But we have no idea what is the result. After each execution. The first level loop executes 4 times and internal loop executes 5 times and during each execution it submits script evaluation using shared script engine and shared bindings. Also first task replaces GroovyClassLoader in the engine to show you that it is not safe to share its instance across multiple threads. Below you can find exemplary output (exemplary, because each time you'll run there is a high probability you will get different results):

groovy.lang.GroovyClassLoader@1d6b34d4
groovy.lang.GroovyClassLoader@1d6b34d4
groovy.lang.GroovyClassLoader@64f061f1
groovy.lang.GroovyClassLoader@1c8107ef
groovy.lang.GroovyClassLoader@1c8107ef
[x=41, y=2]
groovy.lang.GroovyClassLoader@338f357a
groovy.lang.GroovyClassLoader@2bc966b6
groovy.lang.GroovyClassLoader@2bc966b6
groovy.lang.GroovyClassLoader@48469ff3
groovy.lang.GroovyClassLoader@48469ff3
[x=51, y=4]
groovy.lang.GroovyClassLoader@238fb21e
groovy.lang.GroovyClassLoader@798865b5
groovy.lang.GroovyClassLoader@17685149
groovy.lang.GroovyClassLoader@50d12b8b
groovy.lang.GroovyClassLoader@1a833027
[x=51, y=6]
groovy.lang.GroovyClassLoader@62e5f0c5
groovy.lang.GroovyClassLoader@62e5f0c5
groovy.lang.GroovyClassLoader@7c1f39b5
groovy.lang.GroovyClassLoader@657dc5d2
groovy.lang.GroovyClassLoader@28536260
[x=51, y=6]

A few conclusions:

  • replacing GroovyClassLoader is non-deterministic (in the 1st loop 3 different classloader instances were printed while in the 3rd one we have printed 5 different classloader instances)
  • final bindings calculation is non-deterministic. We have avoided concurrent write with ConcurrentHashMap but we have no control over the execution order, so in case of relying on binding value from previous execution you never know what value to expect.

So, how to be thread-safe when using GroovyScriptEngineImpl in multithreaded environment?

  • don't use global bindings
  • when using global bindings, make sure that scripts don't override bindings (you can use new SimpleBindings(Collections.unmodifiableMap(map)) for that
  • otherwise you have to accept non-deterministic nature of bindings state modification
  • extend GroovyScriptEngineImpl and don't allow changing classloader after object was initialized
  • otherwise accept that some other thread may mess up a little bit.

Hope it helps.



Related Topics



Leave a reply



Submit