Interrupt `clj` evaluation without exiting REPL

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!

1 Like

No, this is not possible right now.

2 Likes

Ok, thanks for the reply.

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).

1 Like

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?

@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.

1 Like

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).

1 Like

That sounds familiar indeed!

Is the conclusion “No this is not possible right now” exclusive to async?

In other words, (repeating the Subject here): Is it possible to Interrupt clj evaluation without exiting REPL? But I’m asking without adding the “async” ingredient the OP included.

e.g:

$ clj
...
=>(defn f [] (recur))
=>(f)
[I hit Ctrl-C]
$ #I'm in the System Shell now

Is it possible to don’t exit the REPL?

Hello, @jgomo3,

Slow reply, but I figure you might still be interested. This is the current source of the clj script on my machine:

#!/usr/bin/env bash

if type -p rlwrap >/dev/null 2>&1; then
  exec rlwrap -r -q '\"' -b "(){}[],^%#@\";:'" clojure "$@"
else
  echo "Please install rlwrap for command editing or use \"clojure\" instead."
  exit 1
fi

rlwrap is a program that makes it simple to write a REPL. It makes line editing work, and it’s what makes sure pressing the up key selects the last expression, instead of producing a weird up button character sequence.

Why do I bother with this? Because I’m guessing rlwrap does the interrupt handling; that it’s what terminates Clojure when I press control+c.

If you want a REPL in which you can abort your evaluations, you might want to check out Rebel Readline, which doesn’t require rlwrap. Now that I’m thinking about it, I might want to check out Rebel Readline myself for the original question!

Teodor

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.