From 91bafbbab45d115b53399acad5ebcb16011a78d2 Mon Sep 17 00:00:00 2001 From: Dainius Jocas Date: Tue, 9 Mar 2021 09:46:48 +0200 Subject: [PATCH] fix: javascript multithreaded exception --- src/polyglot/js.clj | 11 +++++++++-- test/polyglot_test.clj | 20 +++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/polyglot/js.clj b/src/polyglot/js.clj index 2f58791..797c9cb 100644 --- a/src/polyglot/js.clj +++ b/src/polyglot/js.clj @@ -29,7 +29,12 @@ (let [^Context ctx (.build (Context/newBuilder (into-array String ["js"]))) ^String wrapped-script (format "(query) => JSON.stringify((%s)(JSON.parse(query)))" script) ^Value f (.eval ctx "js" wrapped-script)] - (fn [^String value] (.asString (.execute f (object-array [value])))))) + (fn [^String value] + ; Turns out that can't be accesses by multiple threads + ; TODO: every thread should create its own Context + ; https://medium.com/graalvm/multi-threaded-java-javascript-language-interoperability-in-graalvm-2f19c1f9c37b + (locking ctx + (.asString (.execute f (object-array [value]))))))) (defn script->transform-fn-vals "Given a JavaScript source code snippet creates a function that expects @@ -39,7 +44,9 @@ (let [^Context ctx (.build (Context/newBuilder (into-array String ["js"]))) ^String wrapped-script (format "(...args) => { args[0] = JSON.parse(args[0]); return JSON.stringify((%s).apply(null, args));}" script) ^Value f (.eval ctx "js" wrapped-script)] - (fn [& vals] (.asString (.execute f (object-array vals)))))) + (fn [& vals] + (locking ctx + (.asString (.execute f (object-array vals))))))) (comment (time diff --git a/test/polyglot_test.clj b/test/polyglot_test.clj index 08c2347..129f9d8 100644 --- a/test/polyglot_test.clj +++ b/test/polyglot_test.clj @@ -1,7 +1,8 @@ (ns polyglot-test (:require [clojure.test :refer :all] - [polyglot.js :as js] + [core.async :as a] [core.json :as json] + [polyglot.js :as js] [polyglot :as polyglot])) (def empty-map {}) @@ -18,15 +19,24 @@ (let [input-string (json/encode nested-map)] (is (= (json/encode (assoc-in nested-map [:foo :bar :quuz] "corge")) (js/string->string input-string - "(s) => { - s['foo']['bar']['quuz'] = 'corge'; - return s; - }"))))) + "(s) => { + s['foo']['bar']['quuz'] = 'corge'; + return s; + }"))))) (testing "invalid script" (let [input-string (json/encode empty-map)] (is (thrown? Exception (= input-string (js/string->string input-string "(s) => function"))))))) +(deftest multithreaded-js + (let [data (json/encode {"foo" "bar"}) + tf (polyglot.js/script->transform-fn + "(s) => { + s['foo'] = 'corge'; + return s; + }")] + (is (= 100 (count (a/map-pipeline (fn [d] (tf d)) 10 (repeat 100 data))))))) + (deftest polyglot-map-transformations (testing "simple cases" (let [m empty-map]