On a macOS or Ubuntu Linux system, if I start a REPL via the lein repl
command with a $HOME/.lein/profiles.clj
file that contains nothing except {}
, using Leiningen http://v2.9.it starts a REPL such that I can enter an expression that goes into an infinite loop like (def x1 (doall (range)))
, and I can then type Ctrl-C to interrupt the evaluation of that form, and get a REPL prompt back. This all seems like expected behavior to me. My question is: does anyone here happen to know the mechanisms by which this behavior is implemented? The output before the first REPL prompt mentions it is using REPL-y 0.4.4 and nREPL 0.6.0, which I would guess are involved.
For example, is there some code in nrepl and/or REPL-y that is running in the JVM process that is running the Clojure application, that perhaps receives a message from the processing running the REPL (assuming that is a separate process -- I believe it is) when the user type Ctrl-C, and as a result there is a JVM call like java.lang.Thread/stop run in the Clojure application JVM process?
lein repl starts two distinct jvms by default, one running the client, and one running the server
the client catches hard exit and resumes where it left off (maybe reconnecting?), and the server process is totally disconnected from the terminal and any signal that a keypress in the terminal would send
all the code for this is in nrepl
as I understand it there must also be server side code that terminates whatever the client was executing or it would keep going in a background thread
This: https://blog.sonatype.com/why-namespacing-matters-in-public-open-source-repositories was posted over in #clojars as well, but I'm curious if there's any way to pin a dependency to a specific repo in leiningen
Yes, I see CPU spike up running an infinite loop CPU-consuming form, and after Ctrl-C (both client and server on same system in my setup), CPU goes back down. So Clojure application JVM must have done something to stop the infinite loop thread. My best guess is that it is doing java.lang.Thread/stop call, because the form I entered contained no code checking each iteration for whether it should stop or not.
aha - I'm sure the answer is somewhere in the nrepl server code
@andy.fingerhut looks like the magic happens here https://github.com/clojure/tools.nrepl/blob/master/src/main/clojure/clojure/tools/nrepl/middleware/interruptible_eval.clj#L226
Thanks. There is at least one call to the stop
method in nrepl source code tagged version 0.6.0, which I am guessing from the lein repl
startup messages is the version of nrepl code I was using.
the snippet I linked calls stop (first checking if the thread it is stopping still exists)
I don't think I will go to the trouble of figuring out how to use a locally modified version of nrepl with extra debug print statements in it to see if those lines of code are executing. Rather I think I will create a tiny bit of test code that causes some Clojure function call to acquire a lock, sleep a second or 3 while it holds it, and leaves some inconsistent state behind if it is stopped without finishing the lock's critical section normally, and check whether I can cause that to happen.
since I would like to do the latter, for the purpose of writing an article on this topic (for fun/learning for me, first, and maybe other folks will find it interesting, too)
I'd definitely want to read it when you are ready to share
I won't keep it a secret 🙂
Will likely send out a link to #news-and-articles when I think it is ready