2024 Clojure状态调查!中分享您的想法。

欢迎!请参阅关于页面获取更多关于如何使用此工具的信息。

0
ClojureScript
浏览器REPL的评估有时会挂起。似乎REPL环境和浏览器有时会互相错过,他们的“约会”失败了。浏览器正在等待POST回复,而REPL正在尝试发送一个命令,但他们没有相遇。

我们发现这个问题是在我们将测试从nodejs环境切换到浏览器环境时。幸运的是,我找到了一个非常小的示例,它在执行时会挂起。似乎(模拟的)高负载增加了“挂起”的可能性。

最小设置


(ns race.condition
  (:require [cljs.repl.browser :as browser]
            [cljs.repl :as repl]
            [cljs.env :as env]
            [cljs.build.api :as api]))


(api/build '[(ns race.repl
               (:require [clojure.browser.repl]))
             (clojure.browser.repl/connect "https://127.0.0.1:9000/repl")]
           {:output-to  "target/cljs-race/main.js"
            :output-dir "target/cljs-race"
            :main       'race.repl})

(spit "target/cljs-race/index.html"
      (str "<html>" "<body>"
           "<script type=\"text/javascript\" src=\"main.js\">"
           "</script>" "</body>" "</html>"))


现在启动环境


(def env (browser/repl-env :static-dir ["target/cljs-race" "."] :port 9000 :src nil))

(env/with-compiler-env (env/default-compiler-env)
  (repl/-setup env {}))


伸出手指并开始这个无限循环


(loop [i 0]
  (println (java.util.Date.) i)
  (dotimes [j 100]
    (let [result (repl/-evaluate env "<exec>" "1"  "true")]
      (when-not (= :success (:status result))
        (println i j result))))
  (recur (inc i)))


从附件中运行 heavy-load.sh 来模拟高负载。

经过一些迭代(例如55个大循环i)后,执行停止。如果您调查堆栈(参见race-condition.jstack),您可以在一个线程中看到


    在 clojure.core$promise$reify__6779.deref(core.clj:6816)
    在 clojure.core$deref.invoke(core.clj:2206)
    在 cljs.repl.browser$send_for_eval.invoke(browser.clj:65)
    在 cljs.repl.browser$browser_eval.invoke(browser.clj:193)
    在 cljs.repl.browser.BrowserEnv._evaluate(browser.clj:262)


代码正在等待一个带有连接的承诺(该连接已经到达)。

我的猜测是 cljs.repl.server 函数中的可疑代码 connection 和 set-connection。这两个函数以非标准方式访问原子。它们分两步 deref 一个值并 swap!。

有谁对REPL内部有更深入的了解可以调查吗?谢谢。

2 个答案

0

评论者:dnolen

欢迎为此修复提出补丁。

0
参考:https://clojure.atlassian.net/browse/CLJS-1479(由 skardan 报告)
...