Interrupt `clj` evaluation without exiting REPL


#1

Hello! Quick question.

I’m playing with core.async, and frequently freeze my REPL. Is it possible to abort the evaluation of the current form in a clj REPL without stopping the process? When I C-c, I exit the REPL.

Thanks!


#2

No, this is not possible right now.


#3

Ok, thanks for the reply.


#4

It’s not there “out of the box,” but you can write some helper utils to introduce controlled processes and poison pills. I ran into a similar problem when I was first messing with core.async, and I adopted a stack overflow post into this snippet. The (narrow) idea here is to define named worker processes (either gos or threads) that will push or pull values to or from a channel, with some default behavior enabled (pause, stop, etc.) Control is established via a “poison pill” which is helpful for killing runaway processes. Since the processes are always listening for either the poison pill or the input (even when pushing values to channels), it’s easy to recover from a runaway process and nuke it from the repl.

I wrote this in the early days, and it was useful; there are likely many better libraries now that gel better with core.async stuff though. Still…maybe it provides some ideas. Heck, implementing your own poison pill strategy and process control system could be a good learning opportunity (it was for me).


#5

Cursive/IntelliJ has a command that advertises it will do this. I‘ve used it with mixed success, but I can‘t say if it works properly for things like core.async loops. How does it work under the hood, if it’s technically not possible?


#6

@joinr That makes a lot of sense! I’ve been thinking that I should probably clean up the channels I set going, or at least have a kill switch of sorts. I’m lead to think of Component and Integrant, which let you handle and reload resources, in the same way that you might want to be able to close your channels in order to keep working on them.

I skimmed the source you linked to. If you have the time, I’d appreciate a usage example! For instance, do I need to create channels a special way in order to be able to tear them all down?

@madbonkey I believe @alexmiller is referring to the clj CLI app. Intellij does a little magic itself, so it doesn’t rely directly on the CLI interface. CIDER (Emacs) lets you do this as well, with M-x cider-interrupt. It’s not that It’s impossible to interrupt an evaluation, but when C-c interrupts the program, one would need another signal to interrupt (just) an expression evaluation. And I think that’s just not implemented as per today.

I started out working with CIDER, but because I did’t know all that well what I was doing, I ended up queuing up a bunch of blocking calls. Then I would have to M-x cider-interrupt a bunch of times to regain control. Using a “normal” REPL like clj was a little simpler, because it was very clear when I was blocking and when I was not.


#7

There’s a little example here

I updated some stuff in the main repo. All you really need are that file and core.async, although it’s bundled along with the rest of spork - it was a nascent project I started, used for a bit, and left untouched until you asked :slightly_smiling_face:

There are undoubtedly more elegant ways to do it (there’s even an erlang OTP façade over core.async out there), but this works to provide visibility and (ideally) some disaster recovery for otherwise invisible go-loops and core.async threads. Hopefully it demonstrates some techniques for not log-jamming the entire REPL during a core.async session (which happened to me a lot during my early explorations, with 0 feedback as to what I’d done wrong).


#8

That sounds familiar indeed!