samestep / boot-refresh

A Boot task used to reload Clojure code on the fly while developing.
MIT License
14 stars 2 forks source link

Error reporting from a different thread (?) #3

Open jimrthy opened 7 years ago

jimrthy commented 7 years ago

Basic Idea: Trying to run (boot (refresh)) failed without much information.

Expected:

(boot (refresh)) ...error report

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

Details:

I have my doubts that this is a boot-refresh issue. I can easily see it being a problem in clojure.tools.namespace, boot, or even CIDER or nrepl. Or maybe just that I need to buckle down and learn a refactoring library. But I figured I should start somewhere, asking someone who might have ideas about where to go with it. And...maybe.

I'm using the straight 0.1.0 release in a pretty vanilla project. I've added some dependencies on some logging libraries, clojure 1.9.0-alpha17, and org.clojure/spec.alpha 0.1.123. It's basically a homework assignment.

After a refactoring session, trying to run (boot (refresh)) from CIDER (I'm not sure whether that matters or not) failed with "java.lang.IllegalStateException: Can't set!: *e from non-binding thread"

Doing a google search for that led me to https://stackoverflow.com/questions/43077104/boot-refresh-cant-set-e-from-non-binding-thread.

Digging through the nrepl message buffer didn't reveal anything interesting, except a map of the namespaces that it was trying to refresh.

Going back through those one at a time in dependency order revealed that the actual problem was that I'd moved a function but was still trying reference it directly from the original ns.

The actual problem for me was trivial, since I'm dealing with about half a dozen namespaces. If this had been a bigger project, though...I guess I could have tracked it down by killing my JVM and debugging the errors as I tried to start it back up.

Yuck.

Any thoughts about who we should point this out to?

Thanks, James

samestep commented 7 years ago

Thanks for raising this! You've clearly done a lot more research in this topic than I have. The issue is indeed perplexing, and also not something I haven't run into before; my current usage of with-bindings in the refresh task was introduced in response to this same IllegalStateException you're seeing (or at least a very similar one), and was meant to solve this problem, but I can see now that it has not.

I think your use case is a perfectly reasonable one, and I would certainly expect the library to print an informative error rather than this odd "Can't set!" business. One thing I could do is add #'*e nil to that map I pass to with-bindings, as Karl Mikkelsen suggested. If you can give me a more concrete sequence of steps so I can try to reproduce the issue on my machine (to verify that this would indeed resolve it), I could then release that change as v0.1.1 of this library if it works.

jimrthy commented 7 years ago

I will try to come up with real repro steps, hopefully this weekend.

(with-bindings {#'*e nil} (boot (refresh))) did not work for me.

jimrthy commented 7 years ago

Progress report: I tried to update the project where I ran into this originally to get some kind of concrete repro steps.

(boot (refresh)) just fails with this error there, no matter what I try. Even when everything builds fine incrementally, or I restart the JVM and make some completely safe change.

So...curiouser and curiouser.

samestep commented 7 years ago

Could you give a minimal sample project that causes this failure? Perhaps by starting with your existing project and stripping out things that aren't relevant to this bug until getting rid of anything else causes the bug not to occur? It's a bit difficult at the moment for me to try to debug this further, since I don't have a way to reproduce it.

jimrthy commented 7 years ago

I know, this is the worst kind of bug report.

So, yes. I'm going to try a) stripping it down one step at a time and b) building it back up from the bottom.

There obviously has to be some point in the middle that shows the actual problem.

jimrthy commented 7 years ago

My root problem was a bad ns declaration.

You can see the problem in test/refreshing_failure/core_test.clj in https://github.com/jimrthy/refreshing-failure. It shows up if you change the ns to core-test instead of refreshing-failure.core-test. It was difficult to track down because both versions compile manually without a complaint.

I can also reproduce the symptom by adding an extra symbol in the middle of a let form, so it has an odd number of args. That one's much more obvious, since it also results in a compiler error.

(sorry this took so long...it's been a busy couple of weeks)

jimrthy commented 7 years ago

No idea whether it's related, but piggieback just included this release note:

The current nREPL's session's *e binding is now set properly when an uncaught exception occurs.

in

https://github.com/cemerick/piggieback/blob/master/CHANGES.md

samestep commented 7 years ago

Thanks for reminding me of this! I've been quite busy as well, unfortunately, but I'll look into piggieback and your refreshing-failure repo as soon as I can.

samestep commented 6 years ago

Sorry for taking so long to come back to this. I'm trying to reproduce your issue on my end using your refreshing-failure repository, and I'm having a bit of trouble, probably just because I haven't used Emacs or boot-refresh in a while. From the repository root, I execute boot repl -s watch refresh; then when I change the ns declaration from

(ns refreshing-failure.core-test
  #_core-test
  (:require [clojure.test :refer :all]
            [refreshing-failure.core :as core]))

to

(ns #_refreshing-failure.core-test
  core-test
  (:require [clojure.test :refer :all]
            [refreshing-failure.core :as core]))

I get this message:

:reloading (core-test)
:error-while-loading core-test
                              java.lang.Thread.run              Thread.java:  748
java.util.concurrent.ThreadPoolExecutor$Worker.run  ThreadPoolExecutor.java:  624
 java.util.concurrent.ThreadPoolExecutor.runWorker  ThreadPoolExecutor.java: 1149
               java.util.concurrent.FutureTask.run          FutureTask.java:  266
                                               ...                               
               clojure.core/binding-conveyor-fn/fn                 core.clj: 2027
                                 boot.core/boot/fn                 core.clj: 1029
                               boot.core/run-tasks                 core.clj: 1019
                    boot.task.built-in/fn/fn/fn/fn             built_in.clj:  477
                    boot.task.built-in/fn/fn/fn/fn             built_in.clj:  479
                    boot.task.built-in/fn/fn/fn/fn             built_in.clj:  427
                 boot.task.built-in/fn/fn/fn/fn/fn             built_in.clj:  430
              boot.task.built-in/fn/fn/fn/fn/fn/fn             built_in.clj:  430
            samestep.boot-refresh/eval782/fn/fn/fn         boot_refresh.clj:   13
                                               ...                               
                       clojure.core/with-bindings*                 core.clj: 1970 (repeats 2 times)
                                clojure.core/apply                 core.clj:  657
                                               ...                               
         samestep.boot-refresh/eval782/fn/fn/fn/fn         boot_refresh.clj:   14
                                               ...                               
              clojure.tools.namespace.repl/refresh                 repl.clj:  128
              clojure.tools.namespace.repl/refresh                 repl.clj:  145
           clojure.tools.namespace.repl/do-refresh                 repl.clj:   94
                                               ...                               
                       clojure.core/alter-var-root                 core.clj: 5409
                       clojure.core/alter-var-root                 core.clj: 5414
                                               ...                               
       clojure.tools.namespace.reload/track-reload               reload.clj:   52
   clojure.tools.namespace.reload/track-reload-one               reload.clj:   35
                                               ...                               
                              clojure.core/require                 core.clj: 5911 (repeats 2 times)
                                clojure.core/apply                 core.clj:  659
                                               ...                               
                            clojure.core/load-libs                 core.clj: 5873
                            clojure.core/load-libs                 core.clj: 5889
                                clojure.core/apply                 core.clj:  659
                                               ...                               
                             clojure.core/load-lib                 core.clj: 5832
                             clojure.core/load-lib                 core.clj: 5851
                          clojure.core/load-lib/fn                 core.clj: 5852
                             clojure.core/load-one                 core.clj: 5812
                                               ...                               
                                 clojure.core/load                 core.clj: 5991
                                 clojure.core/load                 core.clj: 6007
                              clojure.core/load/fn                 core.clj: 6008
                                               ...                               
java.io.FileNotFoundException: Could not locate core_test__init.class or core_test.clj on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.
Elapsed time: 0.274 sec

This seems different from the "Can't set!" error you say you're getting; are there different steps I should take to reproduce the issue?