SIGINT: no exception handler available
A standard way to terminate a process is using an OS signal.
With Julia we may capture a SIGINT signal, for example generated by a user pressing Ctrl-C
, and try to shutdown in a controlled way the process.
But the fact that an InterruptException
is delivered to one task chosen at discretion of the runtime task scheduler make it very difficult to design a robust application because each concurrent/parallel task have to catch and manage correctly the InterruptException
in cooperation with all the other tasks.
Besides this there are pathological cases where it became impossible: in fact the InterruptException
may be delivered to a terminated task and in this scenario nothing can be done to control the shutdown logic.
The following simple example shows the case when the interrupt is delivered to a terminated task. When this occurs a fatal error is raised:
fatal: error thrown and no exception handler available.
demo.jl
:
function task(_)
println("doing something")
end
Timer(task, 1)
function main()
try
while true
sleep(5)
end
catch e
println("got $e: clean shutdown ...")
end
end
main()
Running
julia -e 'include(pop!(ARGS))' demo.jl
and pressing Ctrl-C
after the timer timed out but in 5 seconds you get:
doing something
^Cfatal: error thrown and no exception handler available.
InterruptException()
_jl_mutex_unlock at /cache/build/default-amdci5-5/julialang/julia-release-1-dot-9/src/threading.c:798
jl_mutex_unlock at /cache/build/default-amdci5-5/julialang/julia-release-1-dot-9/src/julia_locks.h:81 [inlined]
ijl_task_get_next at /cache/build/default-amdci5-5/julialang/julia-release-1-dot-9/src/partr.c:394
poptask at ./task.jl:963
wait at ./task.jl:972
task_done_hook at ./task.jl:672
jfptr_task_done_hook_31470.clone_1 at /home/adona/.asdf/installs/julia/1.9.0-rc1/lib/julia/sys.so (unknown line)
_jl_invoke at /cache/build/default-amdci5-5/julialang/julia-release-1-dot-9/src/gf.c:2731 [inlined]
ijl_apply_generic at /cache/build/default-amdci5-5/julialang/julia-release-1-dot-9/src/gf.c:2913
jl_apply at /cache/build/default-amdci5-5/julialang/julia-release-1-dot-9/src/julia.h:1878 [inlined]
jl_finish_task at /cache/build/default-amdci5-5/julialang/julia-release-1-dot-9/src/task.c:320
start_task at /cache/build/default-amdci5-5/julialang/julia-release-1-dot-9/src/task.c:1103
The Visor way
Visor capture SIGINT and shutdown in a reliable way all supervised task following the shutdown logic configured by the application.
The above example instrumented with Visor becames:
using Visor
function task(_)
println("doing something")
end
Timer(task, 1)
function main(self)
try
while true
sleep(5)
end
catch e
println("got $e: clean shutdown ...")
end
end
supervise([process(main)]);
doing something
^Cgot Visor.ProcessInterrupt("main"): clean shutdown ...