(boot(refresh))=>无法从非绑定线程设置!:* e

时间:2017-03-28 18:20:48

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

我已安装Context。在samestep/boot-refresh 0.1.0中,当我更改源文件并输入:

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]]}

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

更新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))工作,每次都会发现问题不同。 boot REPL表明这个问题的真正答案是找到@amalloy's answer未能存储在*e中的异常和堆栈跟踪的某种方法。我已经尝试了一些想法,但相关的变量似乎是私有的,很难挖掘出来。

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

2 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:0)

repl通常在您执行代码的环境中绑定*e。如果需要*e的任何内容在另一个线程中或在repl外部运行,则无法绑定*e

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

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

刷新后*e将错误绑定到该错误,它应为您解决错误。