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