Closures not thread-safe?

João Paulo Varandas joaovarandas at inpaas.com
Mon Feb 20 14:35:12 UTC 2017


A JS closure is just like a POJO. If you share a POJO between threads,
values are the same for all threads, if you need threads to have their own
values, you need a new POJO for each thread. Just like POJO instances,
closures are "independent", you may have N instances of a specific closure
in the same engine and they may have different values inside.

Refering to MDN docs:
Closures are functions that refer to independent (free) variables. In other
words, the function defined in the closure ‘remembers’ the environment in
which it was created.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

--

So it seems to me that Nashorn handles closures perfectly, the problem is
that most javascript libraries are not thread-safe, by the way, maybe your
issue is that wycat's handlebars is not thread safe...







João Varandas
*Arquiteto de Soluções Cloud*
inPaaS - Idéias em Aplicações

p: +55 11 5091-2777  m: +55 11 99889-2321
a: Rua Nebraska, 443 - 1o Andar
  Brooklin Paulista, São Paulo, SP
w: www.inpaas.com  e: joaovarandas at inpaas.com


2017-02-20 10:50 GMT-03:00 João Paulo Varandas <joaovarandas at inpaas.com>:

> There is no issue there.
> When your code is evaluated, currentThreadName is not a pointer to current
> thread name. It is a string, that has been evaluated only once.
>
> It won't change in different executions, remaining as "main" despite the
> executing thread(the closure has been created and it's variables are safely
> stored inside the closure).
>
> Check out the changes in the code:
>
>   @Test
>     public void testClosureThreadSafety() throws ScriptException {
>         String testJsFunction = (
>             "    (function outerFunction(currentThreadName) {\n" +
>             "          function innerFunction() {\n" +
>             "            print(currentThreadName, java.lang.Thread.currentThread().toString());
> return currentThreadName;\n" +
>             "          }\n" +
>             "          return innerFunction;\n" +
>             "    })(java.lang.Thread.currentThread().toString())\n");
>
>         ScriptObjectMirror jsFunction = (ScriptObjectMirror)
> e.eval(testJsFunction);
>
>         String currentThreadName = Thread.currentThread().toString();
>
>         IntConsumer invokeAndTest = i-> {
>             Object received = jsFunction.call(jsFunction);
>
>             Assert.assertEquals(currentThreadName, received);
>         };
>         IntStream.range(0, 10).parallel().forEach(invokeAndTest);
>     }
>
> I've added a "print(currentThreadName, java.lang.Thread.
> currentThread().toString()); " to prove that currentThreadName stays the
> same in all executions despite which Thread it is executing.
>
>
> Check out this one now:
>
>   @Test
>     public void testClosureThreadSafety2() throws ScriptException {
>         String testJsFunction = (
>             "    (function outerFunction(threadPointer) {\n" +
>             "          function innerFunction() {\n" +
>             "            return threadPointer().toString();\n" +
>             "          }\n" +
>             "          return innerFunction;\n" +
>             "    })(java.lang.Thread.currentThread)\n");
>
>         ScriptObjectMirror jsFunction = (ScriptObjectMirror)
> e.eval(testJsFunction);
>
>         IntConsumer invokeAndTest = i-> {
>             Object received = jsFunction.call(jsFunction);
>
>             Assert.assertEquals(Thread.currentThread().toString(),
> received);
>         };
>         IntStream.range(0, 10).parallel().forEach(invokeAndTest);
>     }
>
> Now there's a pointer to currentThread(not a string), and you can use it
> in every thread returning different data.
>
>
>
>
>
>
>
>
> João Varandas
> *Arquiteto de Soluções Cloud*
> inPaaS - Idéias em Aplicações
>
> p: +55 11 5091-2777 <+55%2011%205091-2777>  m: +55 11 99889-2321
> <+55%2011%2099889-2321>
> a: Rua Nebraska, 443 - 1o Andar
>   Brooklin Paulista, São Paulo, SP
> w: www.inpaas.com  e: joaovarandas at inpaas.com
>
>
> 2017-02-20 10:32 GMT-03:00 Frantzius, Jörg <Joerg.Frantzius at aperto.com>:
>
>> Hi Joao,
>>
>> the following test fails immediately for me with
>> "java.lang.RuntimeException: Expected: Thread[ForkJoinPool.commonPool-worker-4,5,main],
>> received: Thread[main,5,main]":
>>
>>    @Test
>>     public void testClosureThreadSafety() throws ScriptException {
>>         final ScriptEngine engine = new ScriptEngineManager().getEngin
>> eByName("nashorn");
>>
>>         String testJsFunction = (
>>             "    (function outerFunction(currentThreadName) {\n" +
>>             "          function innerFunction() {\n" +
>>             "            return currentThreadName;\n" +
>>             "          }\n" +
>>             "          return innerFunction;\n" +
>>             "    })(java.lang.Thread.currentThread().toString())\n");
>>
>>         ScriptObjectMirror jsFunction = (ScriptObjectMirror) engine.eval(
>> testJsFunction);
>>
>>         IntConsumer invokeAndTest = i-> {
>>             String currentThreadName = Thread.currentThread().toString();
>>             Object received = jsFunction.call(jsFunction);
>>             if (!currentThreadName.equals(received)) {
>>                 throw new RuntimeException("Expected: " +
>> currentThreadName + ", received: " + received);
>>             }
>>         };
>>         IntStream.range(0, 10).parallel().forEach(invokeAndTest);
>>     }
>>
>> The outer function returns its inner function, which contains
>> „currentThread“ as a reference to its closure (i.e. a reference to
>> outerFunction’s „currentThread“ parameter). That closure property
>> „currentThread“ will be set to the name of the current thread only once (in
>> engine.eval(testJsFunction)), and subsequent calls to innerFunction will
>> always return the name of that thread (and not of the current thread that
>> calls innerFunction).
>>
>> If your Javascript code is under your control, this may not be a problem,
>> as you can change the code. In our case, we are using an existing
>> Javascript library „Handlebars“ that we cannot change, which seems to be
>> keeping function objects with closures around just like the above code does
>> in Java.
>>
>> Regards,
>> Jörg
>>
>> Am 20.02.2017 um 13:00 schrieb João Paulo Varandas <
>> joaovarandas at inpaas.com>:
>>
>> Hi Jorg.
>>
>> Could you send us a code snippet?
>>
>> I have never seem such problem when using closures. In my project, I use
>> a single engine for whole web application. My tomcat is running with 150
>> maxThreads and it seems to be working fine. I test that in each build by
>> running the test case below:
>>
>> https://gist.github.com/joaovarandas/f80a9cb5548a9d620e4da1ace2729911
>>
>> The idea in this test is to use a single engine and run a closure from
>> one-thread or multiple-threads simultaneously and then read data from those
>> closures.
>>
>>
>> PS.: Should I send the source code directly in the mail body for future
>> readers?
>>
>>
>>
>>
>>
>> João Varandas
>> *Arquiteto de Soluções Cloud*
>> inPaaS - Idéias em Aplicações
>>
>> p: +55 11 5091-2777 <+55%2011%205091-2777>  m: +55 11 99889-2321
>> <+55%2011%2099889-2321>
>> a: Rua Nebraska, 443 - 1o Andar
>>   Brooklin Paulista, São Paulo, SP
>> w: www.inpaas.com  e: joaovarandas at inpaas.com
>>
>>
>> 2017-02-19 18:30 GMT-03:00 Frantzius, Jörg <Joerg.Frantzius at aperto.com>:
>>
>>> … to correct myself, with code that contains closures, it’s probably
>>> global-per-thread on a single engine that remains as the least
>>> resource-consuming option (we were using a single global on single engine
>>> for all threads, in order to share expensively computed Javascript state
>>> between them).
>>>
>>> From what I understand, global-per-thread could be implemented e.g. by
>>> having a ThreadLocal<ScriptContext> and always using that as the context in
>>> ScriptEngine.eval(script, context).
>>>
>>> It would be good to know then whether global-per-thread on single engine
>>> still allows for sharing Nashorn’s code optimization between threads? That
>>> would already be great (and as Nashorn *is* great, I’m positive here :)
>>>
>>> Regards,
>>> Jörg
>>>
>>>
>>> Am 19.02.2017 um 00:47 schrieb Frantzius, Jörg <
>>> Joerg.Frantzius at aperto.com<mailto:Joerg.Frantzius at aperto.com>>:
>>>
>>> Hi,
>>>
>>> it begins to dawn on me that closures aren’t thread-safe, at least that
>>> would explain crosstalk issues we’re seeing in JMeter tests (with a single
>>> engine for multiple threads).
>>>
>>> It would be good to know (and I guess for others as well) if somebody
>>> can confirm this?
>>>
>>> Perhaps thread-safety of closures was thinkable if Nashorn somehow
>>> stored closure state in ThreadLocals, but I guess that’s neither happening
>>> nor planned?
>>>
>>> From what I understand, closures are pervasive in Javascript code out
>>> there, and anybody using such code will currently be forced to use
>>> engine-per-thread.
>>>
>>> Thanks for any hints,
>>> Jörg
>>>
>>>
>>> ---
>>>
>>> Dipl. Inf. Jörg von Frantzius, Technical Director
>>>
>>> E-Mail joerg.frantzius at aperto.com<mailto: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
>>>
>>>
>>>
>>> ---
>>>
>>> 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
>>>
>>>
>>
>> "Esta mensagem, incluindo seus anexos, pode conter informacoes
>> confidenciais e privilegiadas.
>> Se voce a recebeu por engano, solicitamos que a apague e avise o
>> remetente imediatamente.
>> Opinioes ou informacoes aqui contidas nao refletem necessariamente a
>> posicao oficial da Plusoft."
>>
>> "Antes de imprimir, pense em sua responsabilidade e compromisso com o
>> MEIO AMBIENTE"
>>
>>
>> *---*
>>
>> *Dipl. Inf. Jörg von Frantzius, Technical Director*
>>
>> E-Mail joerg.frantzius at aperto.com
>>
>> Phone +49 30 283921-318 <+49%2030%20283921318>
>> Fax +49 30 283921-29 <+49%2030%2028392129>
>>
>> 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
>>
>>
>

-- 
"Esta mensagem, incluindo seus anexos, pode conter informacoes 
confidenciais e privilegiadas. 
Se voce a recebeu por engano, solicitamos que a apague e avise o remetente 
imediatamente. 
Opinioes ou informacoes aqui contidas nao refletem necessariamente a 
posicao oficial da Plusoft."

"Antes de imprimir, pense em sua responsabilidade e compromisso com o MEIO 
AMBIENTE"



More information about the nashorn-dev mailing list