【问题标题】:(boot (refresh)) => Can't set!: *e from non-binding thread(boot (refresh)) => Can't set!: *e from non-binding thread
【发布时间】:2017-03-28 18:20:48
【问题描述】:

我已经安装了samestep/boot-refresh 0.1.0。在boot REPL 中,当我更改源文件并键入:

boot.user=> (boot (refresh))

我明白了:

java.lang.IllegalStateException: Can't set!: *e from non-binding thread

我做错了什么?


这是完整的堆栈跟踪:

boot.user=> *e
#error {
 :cause "Can't set!: *e from non-binding thread"
 :via
 [{:type java.lang.IllegalStateException
   :message "Can't set!: *e from non-binding thread"
   :at [clojure.lang.Var set "Var.java" 218]}]
 :trace
 [[clojure.lang.Var set "Var.java" 218]
  [clojure.tools.namespace.repl$print_and_return invokeStatic "repl.clj" 22]
  [clojure.tools.namespace.repl$print_and_return invoke "repl.clj" 20]
  [clojure.tools.namespace.repl$do_refresh invokeStatic "repl.clj" 96]
  [clojure.tools.namespace.repl$do_refresh invoke "repl.clj" 82]
  [clojure.tools.namespace.repl$refresh invokeStatic "repl.clj" 145]
  [clojure.tools.namespace.repl$refresh doInvoke "repl.clj" 128]
  [clojure.lang.RestFn invoke "RestFn.java" 397]
  [samestep.boot_refresh$eval541$fn__542$fn__547$fn__548$fn__549 invoke "boot_refresh.clj" 14]
  [clojure.lang.AFn applyToHelper "AFn.java" 152]
  [clojure.lang.AFn applyTo "AFn.java" 144]
  [clojure.core$apply invokeStatic "core.clj" 646]
  [clojure.core$with_bindings_STAR_ invokeStatic "core.clj" 1881]
  [clojure.core$with_bindings_STAR_ doInvoke "core.clj" 1881]
  [clojure.lang.RestFn invoke "RestFn.java" 425]
  [samestep.boot_refresh$eval541$fn__542$fn__547$fn__548 invoke "boot_refresh.clj" 13]
  [boot.core$run_tasks invoke "core.clj" 1019]
  [boot.core$boot$fn__918 invoke "core.clj" 1029]
  [clojure.core$binding_conveyor_fn$fn__4676 invoke "core.clj" 1938]
  [clojure.lang.AFn call "AFn.java" 18]
  [java.util.concurrent.FutureTask run "FutureTask.java" 266]
  [java.util.concurrent.ThreadPoolExecutor runWorker "ThreadPoolExecutor.java" 1142]
  [java.util.concurrent.ThreadPoolExecutor$Worker run "ThreadPoolExecutor.java" 617]
  [java.lang.Thread run "Thread.java" 745]]}

堆栈跟踪中没有提到我自己的代码。我之前已经在 REPL 中看到过 (boot (refresh)) 的工作,但我无法找出导致此错误的不同做法。


更新 1

在使用许多打印语句进行长时间的二进制搜索之后——正如 @amalloy 的回答中所解释的那样,真正异常的堆栈跟踪是不可访问的——我发现了这一点:

在名为move-test 的命名空间中,此语句:

(def subset-sum-spec fargish.workspace-test/subset-sum-spec)

导致(boot (refresh)) 失败。当我将其替换为:

(:require … [fargish.workspace-test :refer [subset-sum-spec]] …)

ns 语句中,(boot (refresh)) 再次起作用。这暂时解决了当前的问题,但我仍然想知道发生了什么。


更新 2

继续尝试让(boot (refresh)) 工作,很明显问题每次都不同。 @amalloy's answer 建议这个问题的真正答案是找到c.t.n.repl/print-and-return 未能存储在*e 中的异常和堆栈跟踪。我尝试了一些想法,但相关变量似乎是私有的,很难挖掘出来。

如何找出导致(boot (refresh)) 失败的错误?

【问题讨论】:

  • 更新真的是一个单独的问题,关于如何要求变量。但简而言之:如果你想使用它们,你必须要求来自另一个命名空间的 vars。
  • @amalloy 经过进一步探索,似乎每次的问题都不一样。现在发布第二次更新……
  • 我遇到了同样的错误消息,并通过引导刷新提交了一个关于它的问题,但我怀疑问题是否存在。 (我的问题是我将一个函数重构为另一个 ns 并且未能更新引用)。 (bind) 的技巧对我不起作用。

标签: clojure read-eval-print-loop boot-clj


【解决方案1】:

我不知道bootc.t.namespace,但该错误跟踪看起来像是从新线程运行refresh,而refresh 遇到了一些错误。它尝试通过设置*e 为您传播错误,但它无法设置*e,因为它不在repl 线程上。因此,您没有看到真正的错误,而是得到了这个无用的“由于未能报告错误而导致的错误”。

here 似乎检测到了错误,c.t.namespace 在没有运行 repl 时尝试避免设置 *e(通过检查是否绑定了 *e),但它错误地假设如果 repl 正在运行那么 repl 线程必须是它被调用的线程,而显然 boot 从另一个线程调用它。你试过只打电话给(refresh)吗?我不知道 (boot ...) 包装器应该做什么,但您可能不需要它,而且它似乎会造成麻烦。这也解释了为什么您会看到 (boot (refresh)) 工作:(boot ...) 包装器(可能)不会破坏事物本身,而是只会在其他事物被破坏时导致错误报告变得更糟。

当然,一旦您解决了该问题,结果将不会是您的刷新工作:您将只能看到实际错误,而不是这个元错误!希望这足以帮助您取得进步。

【讨论】:

  • 谢谢!这可能正是我需要的洞察力。 (refresh) 返回一个启动“任务”,它在启动时正确刷新——或者至少这是我目前的理解。要从 REPL 运行任务,我认为您需要执行 (boot (refresh)),但文档是 very light。现在继续闲逛……
【解决方案2】:

repl 通常在您执行代码的环境中绑定*e。如果任何需要*e 的东西在另一个线程或repl 之外运行,它将无法绑定*e

我已通过使用with-binding 绕过此问题,以确保刷新fn 将始终有*e 将错误绑定到。

    (with-bindings {#'*e nil} (refresh))

一旦 refresh 有 *e 绑定错误,它应该会为你 prn 错误。

【讨论】:

  • 当我执行(with-bindings {#'*e nil} (boot (refresh))) 时,我会收到相同的错误消息。我认为错误必须在boot 中。 (with-bindings {#'*e nil} (refresh)) 只是返回了(refresh) 返回的对象。你在运行boot吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-13
  • 2022-01-02
  • 2012-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多