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

欢迎!请查看关于页面了解更多此网站的信息。

0
core.async
大家好,这里的bug有点棘手...我们在ClojureScript中使用Safari 7上的core.async时遇到了一些问题。我们的应用程序围绕着一个大型事件循环构建,该循环在一个对应于用户活动或API调用的多个通道之一接收消息时阻塞。问题似乎出在这个事件循环中 - 我们使用alts!从任何可用的通道中拉取消息,但有时日志显示我们到达了alts!却从未解锁。然而,通过更多的日志,我可以看到在alts!通道列表中的一个通道后面有随后的写入操作,所以我真的不知道发生了什么。

这就是总体情况,现在让我们看看代码。

我们的主要事件循环如下


  (log "进入主事件循环。")
  (go
    (while true
      (log "alts! 通道散列: " (map hash (:channels @app)))
      (let [[message channel] (alts! (seq (:channels @app)))]
        (log "alts! 解锁,调用我们的 process-message")
        (swap! app process-message message channel)
        (log "process-message 完成,循环"))


{{process-message}}是我们在应用程序中的一个内部函数,但我想它的细节并不一定很重要。在Safari卡住的情况下,日志看起来像


[Log] process-message 完成,循环 (main.js, 第62行)
[Log] alts! 通道散列:  (16 12 19 33) (main.js, 第82行)
[Log] Socket 已连接。 (socket.js, 第309行)
[Log]向散列为  19 的通道 put! (socket.js, 第86行)
[Log] 消息是 [:metronome [:staff [{:description nil, :deletable true, :email nil, :isAdmin true, :isTrainer false, :telephone nil, :name "Fynder Admin", :picture nil, :userId 1} {:description nil, :deletable fa... (socket.js, 第87行)
[Log] put! 回调返回 us true (socket.js, 第89行)
[Debug] Metronome: staff 数据解码完成。 put! 完成.: 12.282ms (socket.js, 第93行)
[Log]向散列为  19 的通道 put! (socket.js, 第86行)
[Log] 消息是 [:metronome [:class-types [{:deletable false, :picture nil, :name "CycleCore", :id 2, :description "CycleCore 是一种 55 分钟的双项锻炼方案,将 30 分钟的激烈有氧运动与 25 分钟的锻炼强度结合一起... (socket.js, 第87行)
[Log] put! 回调返回 us true (socket.js, 第89行)
[Debug] Metronome: class-types 数据解码完成。 put! 完成.: 1.288ms (socket.js, 第93行)
[Log]向散列为  19 的通道 put! (socket.js, 第86行)
[Log] 消息是 [:metronome [:locations [{:studios [{:deletable false, :name "Kensington", :id 1, :locationId 1, :description "Studio (11a) sits just off Stratford Road in Stratford Studios. To find us, just pass ... (socket.js, 第87行)
[Debug] Metronome: locations 数据解码完成。 put! 完成.: 0.884ms (socket.js, 第93行)


请注意,我们在日志中看到了“alts! channel hashes”的条目,但从未见过“alts! unblocked”。然而,请注意传递给alts!的哈希列表,提到了通道19,但我们随后将其放入通道19,但我们仍然没有被解除阻塞。还有一些事情让我感到可疑,那就是在我们被阻塞在alts!时,对于只能一次包含一个元素的通道,两个put!的调用都立即成功了。也许我理解错了什么,但我不认为立即put回调会被调用多次。有趣的是,最后的put!没有调用回调。

不幸的是,重现这个错误相对困难。我可以通过退出Safari,重新打开它,并导航到开发服务器来有些可靠地重现这个问题。大约15次尝试中会有一次卡在这一状态。我想知道这与Safari的MessageChannel实现有关吗——你可以在日志条目中看到nexttick.js调用回调,这好像是我浏览器中分发的做法。

我将非常乐意提供任何有关信息,但现在我已无法调试这个问题。虽然代码是专有的,但我愿意暂时为GitHub项目添加人员,以尝试修复这个问题。我们有一个开发API服务器,你可以指向它,所以这只需要运行{{lein cljs}}。

我已经附上了我们的Socket.io包装器和主事件循环的代码。遗憾的是,我还没有最小测试用例——我真的不知道该从哪里开始。

6 答案

0
通过

评论者:ocharles

我深入到Google Closure库的底层,并将{{getSetImmediateEmulator_}}修改为

`
goog.async.nextTick.getSetImmediateEmulator_ = function() {
// 用setTimeout回退到0,在浏览器中这会产生5ms或更长的延迟
//。
return function(cb) {

goog.global.setTimeout(cb, 0);

};
};
`

并且我还没有遇到卡住的情况,所以也许MessageChannel在Safari中存在问题...

0
通过

评论者:gshayban

您好,奥利弗,这看起来像是一场竞争,我们会找出原因的。

您介意比较运行在0.1.319.0-6b1aca-alpha与0.1.346.0-17112a-alpha之间的差异吗?

alts!应该传递一个有索引的集合/向量。传递序列不会引起这个错误,但提请注意。

0

评论者:ocharles

嗨 Ghadi,

0.1.319.0-6b1aca-alpha 是初始报告中提到的版本 - 我应该提到这一点。因此,0.1.319.0-6b1aca-alpha 的确会卡住。

然而,0.1.346.0-17112a-alpha 并不会卡住,这很奇怪 - 因为我相信我已经尝试升级到这个版本!我在两个通常有问题的 Mac 上尝试过,它们一次也没有卡住。我将把这个版本推送给我们更多的测试人员,并看看会发生什么。

0

评论者:ocharles

哎呀,我知道事情不会那么简单!当将此版本投入生产后,它立即又冻结了。但是开发服务器的运行优化却非常不同,所以我会构建一个生产版本并在本地提供服务 - 将看看那里会发生什么。

0
_由 ocharles_ 发表的评论

是的,这确实是一个优化的问题。以下是我的 Shadow Build 配置


(ns fynder.shadowbuild
  (:require [clojure.java.io :as io]
            [shadow.cljs.build :as cljs]))

(defn define-modules [state]
  (-> state
      (cljs/step-configure-module :cljs '[cljs.core clojure.walk clojure.string cljs.reader cljs.core.async] #{})
      (cljs/step-configure-module :test-support '[inflections.core no.en.core enfocus.bind fynder.winchan] #{:cljs})
      (cljs/step-configure-module :devel '[fynder.devel] #{:cljs})
      (cljs/step-configure-module :admin '[fynder-admin.main] #{:cljs})
      (cljs/step-configure-module :trainer '[fynder-trainer.main] #{: cljs})
      (cljs/step-configure-module :mobile '[fynder-mobile.main] #{:cljs})
      (cljs/step-configure-module :sweatybetty '[fynder-sweatybetty.main] #{:cljs})
      (cljs/step-configure-module :loader '[fynder-loader.loader] #{:cljs})))

(defn dev
  "构建项目,等待文件更改,重复"
  [& args]
  (let [state (-> (cljs/init-state)
                  (cljs/enable-source-maps)
                  (assoc :optimizations :advanced
                         :pretty-print false
                         :work-dir (io/file "target/ cljs-work")
                         :public-dir (io/file "resources/dev")
                         :public-path "")
                  (cljs/step-find-resources-in-jars)
                  (cljs/step-find-resources "src/cljs/")
                  (cljs/step-finalize-config)
                  (cljs/step-compile-core)
                  (define-modules))
    ;; 编译,刷新,重新加载,重复
    ;(loop [state state]
      (let [new-state (try
                        (-> state
                            (clarsimp/step-compile-modules)
                            (cljs/flush-unoptimized)
                            (cljs/wait-and-reload!))
                       (catch Throwable t
                          (print [:failed-to-compile t])
                          (.printStackTrace t)
                          (cljs/wait-and-reload! state)))]
        (recur new-state)))))

(defn prod
  "构建项目,等待文件更改,重复"
  [& args]
  (-> (cljs/init-state)
      (cljs/enable-emit-constants)
      (cljs/enable-source-maps)
      (assoc :optimizations :advanced
             :pretty-print false
             :work-dir (io/file "target/cljs-work")
             :public-dir (io/file "resources/prod")
             :externs ["react/externs/react.js"
                       "externs/base32.js"
                       "externs/jquery.js"
                       "externs/react_addons.js"
                       "externs/fastclick.js"
                       "externs/socket.io.js"
                       "externs/moment.js"
                       "externs/papa.js"
                       "externs/markdown.js"
                       "externs/xss.js"
                       "externs/facebook.js"
                       "externs/checkout.js"])
      (cljs/step-find-resources-in-jars)
      (cljs/step-find-resources "src/cljs/")
      (cljs/step-finalize-config)
      (cljs/step-compile-core)
      (define-modules)
      (cljs/step-compile-modules)
      (cljs/closure-optimize)
      (cljs/flush-to-disk)
      (cljs/flush-modules-to-disk)))


如果在开发配置下运行,则不会卡住。如果切换到生产配置(并使用Python的SimpleHTTPServer提供lein publish的结果),则Safari会被卡住。
0
回答
参考: https://clojure.atlassian.net/browse/ASYNC-97 (由alex+import报告)
...