Closures not thread-safe?
Frantzius, Jörg
Joerg.Frantzius at aperto.com
Mon Feb 20 17:03:43 UTC 2017
Hi Joao,
as you are overriding ThreadLocal.get(), the effect is that testJsFunction is simply eval()ed anew by every thread, so each thread creates its own closure. (I guess it’s never a good idea to override ThreadLocal.get() without calling super.get() at all …)
But anyway, the real problem I have is probably mutation of a closure. That’s possible if the closure is over a mutable object, like a HashMap.
Closure mutation works both in the same thread: https://gist.github.com/jfrantzius/0a40c963413bdeabb51ecb13a769a436#file-nashornclosuretest-java-L138
As well as in another thread: https://gist.github.com/jfrantzius/0a40c963413bdeabb51ecb13a769a436#file-nashornclosuretest-java-L166
I guess my initial proposal of Nashorn performing some magical closure resolution with ThreadLocals is just bogus.
I’ll go for dedicated global per thread, and as an optimization, I’ll pool them.
Thanks for being helpful + cheers from Berlin,
Jörg
Am 20.02.2017 um 15:54 schrieb João Paulo Varandas <joaovarandas at inpaas.com<mailto:joaovarandas at inpaas.com>>:
@Test
public void testClosureThreadSafety() throws ScriptException {
String testJsFunction = (
" (function outerFunction(currentThreadName) {\n" +
" print(indx.incrementAndGet() + ' ' + java.lang.Thread.currentThread().toString() + ': closure data is ' + currentThreadName) \n" +
" function innerFunction() {\n" +
" return currentThreadName;\n" +
" }\n" +
" return innerFunction;\n" +
" })(java.lang.Thread.currentThread().toString())\n");
e.getBindings(ScriptContext.GLOBAL_SCOPE).put("indx", new AtomicInteger(0));
ScriptObjectMirror jsFunction = (ScriptObjectMirror) e.eval(testJsFunction);
String mainThreadName = Thread.currentThread().toString();
ThreadLocal<ScriptObjectMirror> innerjs = new ThreadLocal<ScriptObjectMirror>() {
@Override
public ScriptObjectMirror get() {
try {
return (ScriptObjectMirror) e.eval(testJsFunction);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
};
IntConsumer invokeAndTest = i -> {
try {
// my main closure is unchanged
Assert.assertEquals(mainThreadName, jsFunction.call(jsFunction));
// since I have a new closure for each thread ...
final ScriptObjectMirror innerJsFunction = innerjs.get();
final String currentThreadName = Thread.currentThread().toString();
Assert.assertEquals(currentThreadName, innerJsFunction.call(innerJsFunction));
} catch(Exception e) {
throw new RuntimeException(e);
}
};
IntStream.range(0, 10).parallel().forEach(invokeAndTest);
}
---
Dipl. Inf. Jörg von Frantzius, Technical Director
E-Mail joerg.frantzius at aperto.com
Phone +49 30 283921-318
Fax +49 30 283921-29
Aperto GmbH – An IBM Company
Chausseestraße 5, D-10115 Berlin
http://www.aperto.com<http://www.aperto.de/>
http://www.facebook.com/aperto
https://www.xing.com/companies/apertoag
HRB 77049 B, AG Berlin Charlottenburg
Geschäftsführer: Dirk Buddensiek, Kai Großmann, Stephan Haagen, Daniel Simon
More information about the nashorn-dev
mailing list