大家好,这里有一个有点棘手的虫子报告……我们在使用 ClojureScript 中的 core.async 在 Safari 7 中发现了一些问题。我们的应用程序围绕一个大型事件循环构建,该循环阻塞在用户活动或 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 死锁的情况下,日志看起来像下面这样:
[日志] process-message 完成,循环 (main.js,行 62)
[日志] alts! 通道哈希: (16 12 19 33)(main.js,行 82)
[日志] Socket 已连接。 (socket.js,行 309)
[日志] 发送到带有哈希 19 的通道 (socket.js,行 86)
[日志] 消息是 [: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)
[日志] put! 回调提供了 true (socket.js,行 89)
[Debug] Metronome: staff data decoded. put! complete.: 12.282ms (socket.js,行 93)
[日志] 发送到带有哈希 19 的通道 (socket.js,行 86)
[日志] 消息是 [:metronome [:class-types [{:deletable false, :picture nil, :name "CycleCore", :id 2, :description "CycleCore is a 55-minute dual workou... (socket.js,行 87)
[日志] put! 回调提供了 true (socket.js,行 89)
[Debug] Metronome: class-types data decoded. put! complete.: 1.288ms (socket.js,行 93)
[日志] 发送到带有哈希 19 的通道 (socket.js,行 86)
[日志] 消息是 [: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 data decoded. put! complete.: 0.884ms (socket.js,行 93)
请注意,我们在日志中看到了“alts! channel hashes”的条目,但从未看到“alts! unblocked”。然而,请注意传递给alts!的哈希列表,提到了通道19,但是随后我们将...放入通道19,但我们仍然无法取消阻止。还有一件事让我感觉可疑,那就是当我们被阻断在alts!时,对put!的两个调用立即成功,而对通道只有一次元素的限制。也许我误解了某些东西,但我没有想到立即调用的回调会被调用多于一次。有趣的是,最后的put!并没有调用回调。
遗憾的是,重现这个错误相当困难。我可以通过退出Safari,重新打开它并导航到开发服务器来有一定程度的可靠性地重现它。大约有15%的尝试会以这种方式卡住。我觉得这可能和Safari的MessageChannel实现有关——你可以在日志条目中看到nexttick.js调用其回调,这好像是我的浏览器中分配的工作方式。
我非常乐意提供任何有用的更多信息,但现在我超出了解决问题的能力。虽然代码是专有的,但我愿意暂时将人们添加到Github项目中,以尝试修复这个问题。我们有一些开发API服务器,你可以指向它们,所以这只是一个运行{{lein cljs}}的问题。
我已经附上了我们Socket.io包装和主事件循环的代码。遗憾的是,我还没有一个最小测试用例——我根本不知道从哪里开始。