From 8a9b4606d72f7f34e2ec6f69143916a2ece8f59c Mon Sep 17 00:00:00 2001 From: Julian P Samaroo Date: Fri, 5 May 2023 13:38:15 -0500 Subject: [PATCH] interrupt handlers: Add test and docstrings --- base/task.jl | 34 ++++++++++++++++++++++++++++------ test/stress.jl | 29 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/base/task.jl b/base/task.jl index c76da092f8bca6..687d47530af888 100644 --- a/base/task.jl +++ b/base/task.jl @@ -993,9 +993,31 @@ else pause() = ccall(:pause, Cvoid, ()) end -interrupt_handlers() = - ccall(:jl_get_interrupt_handlers, Any, ())::Vector{Task} -register_interrupt_handler(t::Task) = - ccall(:jl_register_interrupt_handler, Cvoid, (Any,), t) -unregister_interrupt_handler(t::Task) = - ccall(:jl_unregister_interrupt_handler, Cvoid, (Any,), t) +""" + register_interrupt_handler(handler::Task) + +Registers the task `handler` to handle interrupts (such as from Ctrl-C). When +an interrupt is received, all registered handler tasks will be scheduled at the +next `yield` call, with an `InterruptException` thrown to them. Once any +handler is registered, the runtime will only throw `InterruptException`s to +handlers, and not to any other task, allowing the handlers to soak up and +safely handle interrupts. + +To unregister a previously-registered handler, use +[`unregister_interrupt_handler`](@ref). + +!!! warn + Note that non-yielding tasks may block interrupt handlers from running; + this means that once an interrupt handler is registered, code like `while + true end` may become un-interruptible. +""" +register_interrupt_handler(handler::Task) = + ccall(:jl_register_interrupt_handler, Cvoid, (Any,), handler) +""" + unregister_interrupt_handler(handler::Task) + +Unregisters the interrupt handler task `handler`; see +[`register_interrupt_handler`](@ref) for further details. +""" +unregister_interrupt_handler(handler::Task) = + ccall(:jl_unregister_interrupt_handler, Cvoid, (Any,), handler) diff --git a/test/stress.jl b/test/stress.jl index b9fb720f0596ae..38b661064c3379 100644 --- a/test/stress.jl +++ b/test/stress.jl @@ -84,5 +84,34 @@ if !Sys.iswindows() ccall(:jl_gc_safepoint, Cvoid, ()) # wait for SIGINT to arrive end end + + # interrupt handlers + let exc_ref = Ref{Any}() + handler = Threads.@spawn begin + try + wait() + catch exc + exc_ref[] = exc + end + end + yield() # let the handler start + Base.register_interrupt_handler(handler) + ccall(:kill, Cvoid, (Cint, Cint,), getpid(), 2) + for i in 1:10 + Libc.systemsleep(0.1) + yield() # wait for the handler to be run + end + Base.unregister_interrupt_handler(handler) + @test isassigned(exc_ref) && exc_ref[] isa InterruptException + end + + # ensure we revert to original interrupt behavior + @test_throws InterruptException begin + ccall(:kill, Cvoid, (Cint, Cint,), getpid(), 2) + for i in 1:10 + Libc.systemsleep(0.1) + ccall(:jl_gc_safepoint, Cvoid, ()) # wait for SIGINT to arrive + end + end Base.exit_on_sigint(true) end