How to interrupt (not kill) julia from the REPL?

From the REPL, how do I interrupt execution of a function? Basically, I want to press some command key and cause it to throw an error that I can catch. For example:

function interruptable_loop()
  try
    while not_done
      # some computation
    end
  catch
     println("function terminated by user")
     # cleanup
  end
end

From reading some old threads, I got the impression that (on Windows) pressing ctrl+c would do what I want. I find that it does nothing unless I press it four times rapidly, in which case it kills the whole Julia session, not merely throw an error. Same thing in Juno.
(This is on Julia 1.0.0).

ctrl-c is to interrupt a calculation but it doesn’t always work. If you want to use it with a try-catch do:

function interruptable_loop()
  try
    while not_done
      # some computation
    end
  catch e
    if e isa InterruptException
       # cleanup
       println("function terminated by user")
       rethrow(e)
    else
      # do something else
    end
  end
end
4 Likes

Are you saying there is no reliable way to interrupt a calculation? If so, is there any way to predict the conditions under which it will/won’t work?

I am not sure what you mean by “no reliable way to interrupt a calculation”. At least pulling the power plug is definitely a reliable way to interrupt it :wink:

Wrapping the part of the code which you want to allow to be interrupted in a try/catch block and expecting the interrupt exception is pretty common (and reliable, as long is it is not caught before it bubbles up to that point). I use this construct also in Python all the time and it works reliably (try/raise and KeyboardInterrupt in that case).

I said “reliable” because @mauro3 said that ctrl+c doesn’t always work.

Wrapping the part of the code which you want to allow to be interrupted in a try/catch block and expecting the interrupt exception is pretty common (and reliable, as long is it is not caught before it bubbles up to that point)

If I understand you correctly, the type of code I posted above (and @mauro3’s more refined version) should, when I press ctrl+c, exit the loop and enter the catch clause. But in the simple case I have tried (where the loop body does something trivial, like increment a counter), pressing ctrl+c does nothing, unless you press it so rapidly it just kills the session, which is not what I want.

OK that indeed is strange. I saw this happening with code which caught the exception one layer below… I’d consider this a bug if it is not the case for the code above.

Can you post the exact code you tried?

Having to press it multiple times is perfectly normal if your computation doesn’t allocate. Exiting the repl could be something else, most likely Windows specific.

Can you be more specific? Why does no allocation implies that the InterruptException is ignored?

It does not. It can be delayed. Allocation is simply a common place the exception can be delivered but it’s far from the only one.

3 Likes

Aha, we are getting somewhere. When I employed the try/catch structure above in my real code (where the loop body involved something substantial), pressing ctrl+c worked. It appears that the loop body in my test was so simple that it didn’t present any/enough opportunity to check for the interrupt.

Which begs the question: If not all code can be (or is likely to be) interrupted, what kinds of code can? @yuyichao indicated code that allocates is one example.

@yuyichao: What heuristics can I use other than hope that my loop body allocates or is otherwise not “too simple” to be interruptible?

That should not be a problem. Pressing Ctrl-C multiple times should not be any different from a normal one.

It’s possible that what you’re actually executing has a different try/catch structure than the MWE you posted above. Namely, if you end up having multiple try/catch blocks nested (maybe from some Base function you call which uses one internally), one of the blocks may accidentally swallow the error, continue going along its business, and then the next signal (or a few after that) may end up being the ticket to breaking all the way out to the REPL again. I don’t know if there’s really a clean way to handle this other than to ensure every try/catch block properly handles InterruptException, which is a lot of try/catch blocks :slight_smile:

Edit: One of the places I encounter these sorts of issues is with anything doing I/O, such as saving/loading to/from a file on disk, or doing network operations on a socket (possibly including Julia’s Distributed functionality).

1 Like

@yuyichao Regardless of what should be the case, the following code:

   i = 0
   try
      while i<typemax(Int)
         i += 1
      end
      println(i)
   catch
      println("Interrupted by user at iteration $i")
   end
end

is not interruptable on my system, in the sense that pressing ctrl+c dozens of times has no effect, unless I press it several times in rapid succession in which case it kills the Julia session instead of executing the catch clause.

1 Like

That’s a good guess, but there are no nested try/catch blocks in my actual code (it’s protoype code). In fact, it’s my real code that does work and the test case in my response to @yuyichao (which I tried before trying it on my real code) that doesn’t work.

What I’m saying is that you’ve simply hit a bug on windows, regardless of what’s the heuristic for interruptable code (there isn’t any guarantee). Your code works on linux.

1 Like

On my Linux machine it is not interruptible with slow ctrl-c. Fast ctrl-c will do it with:

julia> f()
^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^C^CWARNING: Force throwing a SIGINT
Interrupted by user at iteration 52400256035

But I have definitely encountered situations when doing above crashed the REPL.

When it crashes on linux, it’s usually because there isn’t a better way. The whole point of the current behavior is that the exception will only be delivered when it is safe to do so. If you don’t want to wait though, you can force a delivery, with a warning as you see in the output and there’s no guarantee of the integrity of the runtime afterwards. If you are running pure julia code like this there shouldn’t be much/any problem, but if you are calling C libraries it can easily crash and there’s little julia can do about it. (The C library is usually not written to be longjmp out of and julia can’t fix it.)

The problem on windows though is that it’ll crash even when running benign julia code. I don’t know why it happens and AFAICT I didn’t touch it so it was always crashing before this behavior was introduced and now it only crashes when you force a throw. AFAICT gdb crashes when I do Ctrl-C so it’s clearly not just julia’s problem and it’s also very hard to debug. (I mean, I noticed the gdb crash when I want to use it to figure out why julia “crashed”…)

4 Likes

Ok. I was wondering if there was something I was doing wrong, but it appears that the behavior depends in a somewhat unpredictable way on the operating system.

I am using julia 1.4.2 , clt +c is neither kill nor stop the repl

1 Like

You should create new threads instead of resurrecting old discussions, the Discourse interface should have warned you about it.

If you read the discussion above, then you know the problem can be that your code has little points in which it can be safely interrupted (like calls to IO functions like println). I think the function yield also create such safe points (not tested it however) without needing to print something and without any negative consequences (unless you are writing a parallel code that can have problems if you yield at that point).

2 Likes