1

There is this answer for python from an old thread which I used in the past to great effect (see first answer here How to prevent a block of code from being interrupted by KeyboardInterrupt in Python?). I'd like to know if it is possible to do something similar in Julia.

That's pretty much it, but here's some background.

I'm developing a Markov Chain Monte Carlo algorithm in Julia and I need to insure that the state of the chain is always valid, especially following a Ctrl+C/SIGINT. This is particularly important during development in the REPL where I want to be able to stop the chain but then restart it where it was left off without having to start from scratch and wait for the burn-in period all over again.

As it stand it is almost always the case that the chain will be in an invalid state following a SIGINT.

In other words I want to have blocks of code that are uninterruptibles, namely critical blocks within which a SIGINT is deferred until the block has terminated.

Alice
  • 13
  • 3

2 Answers2

3

Ctrl+C/SIGINT causes the InterruptException, so you can place your code within a try block and catch the InterruptException.

  try
    # ... your algorithm here ...
  catch e
    if e isa InterruptException 
      save_your_chain_to_a_valid_state()
      then_exit()
    else
      rethrow()
    end
  end

If you're running the code outside of the REPL, you'll first have to call Base.exit_on_sigint(false) before the try block.

Sundar R
  • 13,776
  • 6
  • 49
  • 76
  • The problem with this approach I think is that it's the code within the ```try``` block that needs to finish completely because it is within it that there's a small window of time where the state is invalid. If it is interrupted I cannot easily repair and restore it into a valid state by calling a second function, here ```save_your_chain_to_a_valid_state()``` – Alice Jun 23 '22 at 19:17
  • You can think of it as a single line of code that does some critical heavy lifting, i.e. popping and pushing some things around within the state during a Gibbs update. If at anytime this line is interrupted the state becomes invalid and it's a pain to repair. I really need to defer the throwing of the ```InterruptException```. – Alice Jun 23 '22 at 19:36
0

disable_sigint prevents a block of code to be interrupted immediately, whereas catching InterruptException is a mean to handle the interrupt:

try
    disable_sigint() do
        for i in 1:5
            sleep(1)
            print(".")
        end
    end

    println("\nnow it is safe to interrupt immediately")
    while true
        sleep(1)
        print("*")
    end
catch InterruptException
    println("interrupt catched")
end

If running a script remember how to catch Ctrl-C:

julia -e 'include(pop!(ARGS))' script.jl

disable_sigint is able to mask the InterruptException generated by a SIGINT signal (for example a signal coming from a kill command on unix). For example if the running code is notified throwing directly an interruptException() it is interrupted as soon as possible ignoring the disable_sigint directive.

This may be the case of a jupyter environment where communication between client and kernel processes happens through a IPC channel (ZeroMQ transport channel) or the case of vscode where a "supervisor" process manage a child julia execution context.

Chances are that the kernel interrupt command delivered from jupiter frontend to jupyter kernel backend that "supervises" julia code is not delivered to child process using a SIGINT OS-signal.

attdona
  • 17,196
  • 7
  • 49
  • 60
  • This is almost exactly what I need. The only remaining issue is that there is almost no code, or very fast code, outside of the do block, so the window of time during which `SIGINT` can be caught is micro/milliseconds, much too short for human reflexes. This is because `disable_sigint()` truly disables and throws away `SIGINT` rather than queues/delays/defers it until exiting the do-block. Here it only works because of the artificial `sleep(1)` line. – Alice Jun 24 '22 at 17:50
  • Are you sure? On my side I've tried on windows and linux and the SIGINT is queued and handled correctly at the termination of disable_sigint block without code outside do block (just wrapping disable_sigint with a while true). – attdona Jun 25 '22 at 10:05
  • I'm on MacOS. I can confirm that it actually works but only when using the REPL directly. When using the REPL within VSCode it interrupts immediately even within the do-block. When using IJulia in jupyter it simply throws the SIGINT away and doesn't interrupt wrapping the do-block within a while-true. – Alice Jun 25 '22 at 16:48
  • Thanks for the details, I've added some general considerations about the jupyter/vscode cases, I hope they may help. – attdona Jun 27 '22 at 07:20
  • This makes a lot of sense! I noticed in Jupyter+IJulia using an enclosing while true loop (no slow block) that while it doesn't interrupt after a single interrupt signal, if you insist and send repeated interrupt signals it will eventually interrupt the code with a warning message `WARNING: Force throwing a SIGINT`. I don't understand how this work. But regarding VSCode you're saying that it throws an InterruptException() directly without SIGINT, thus bypassing the effect of disable_sigint() and interrupting immediately? – Alice Jun 27 '22 at 17:28
  • I said "it may be ..." because it sounds reasonable but I do not know exactly the internals of jupyter and vscode plugin. Looking at [vscode sources](https://github.com/julia-vscode/julia-vscode/blob/master/scripts/packages/VSCodeServer/src/eval.jl#L60) reveals that an InterrupException() is delivered directly. – attdona Jun 27 '22 at 20:48
  • You were right about VScode https://github.com/julia-vscode/julia-vscode/issues/2936 – Alice Jun 30 '22 at 17:48