diff --git a/NEWS.md b/NEWS.md index 45e89d4897e94..c4732bb471102 100644 --- a/NEWS.md +++ b/NEWS.md @@ -118,6 +118,8 @@ New library functions * New function `bitrotate(x, k)` for rotating the bits in a fixed-width integer ([#33937]). * One argument methods `startswith(x)` and `endswith(x)` have been added, returning partially-applied versions of the functions, similar to existing methods like `isequal(x)` ([#33193]). * New function `contains(haystack, needle)` and its one argument partially applied form have been added, it acts like `occursin(needle, haystack)`([#35132]). +* New function `Base.exit_on_sigint` is added to control if `InterruptException` is + thrown by Ctrl-C ([#29411]). New library features -------------------- diff --git a/base/c.jl b/base/c.jl index ab502ef53d0bc..42003bba32eb7 100644 --- a/base/c.jl +++ b/base/c.jl @@ -463,6 +463,25 @@ function reenable_sigint(f::Function) res end +""" + exit_on_sigint(on::Bool) + +Set `exit_on_sigint` flag of the julia runtime. If `false`, Ctrl-C +(SIGINT) is capturable as [`InterruptException`](@ref) in `try` block. +This is the default behavior in REPL, any code run via `-e` and `-E` +and in Julia script run with `-i` option. + +If `true`, `InterruptException` is not thrown by Ctrl-C. Running code +upon such event requires [`atexit`](@ref). This is the default +behavior in Julia script run without `-i` option. + +!!! compat "Julia 1.5" + Function `exit_on_sigint` requires at least Julia 1.5. +""" +function exit_on_sigint(on::Bool) + ccall(:jl_exit_on_sigint, Cvoid, (Cint,), on) +end + function ccallable(f::Function, rt::Type, argt::Type, name::Union{AbstractString,Symbol}=string(f)) ccall(:jl_extern_c, Cvoid, (Any, Any, Any, Cstring), f, rt, argt, name) end diff --git a/base/client.jl b/base/client.jl index fedcecf4f0aa4..202d709e1e2bd 100644 --- a/base/client.jl +++ b/base/client.jl @@ -290,7 +290,7 @@ function exec_options(opts) if arg_is_program # program if !is_interactive - ccall(:jl_exit_on_sigint, Cvoid, (Cint,), 1) + exit_on_sigint(true) end try include(Main, PROGRAM_FILE) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index f727fc9fd00c3..808073bc4bd84 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -1423,6 +1423,18 @@ TypeError InterruptException() The process was stopped by a terminal interrupt (CTRL+C). + +Note that, in Julia script started without `-i` (interactive) option, +`InterruptException` is not thrown by default. Calling +[`Base.exit_on_sigint(false)`](@ref Base.exit_on_sigint) in the script +can recover the behavior of the REPL. Alternatively, a Julia script +can be started with + +```sh +julia -e "include(popfirst!(ARGS))" script.jl +``` + +to let `InterruptException` be thrown by CTRL+C during the execution. """ InterruptException diff --git a/doc/src/base/c.md b/doc/src/base/c.md index 30f0e49fc8ae2..0795148cd7de2 100644 --- a/doc/src/base/c.md +++ b/doc/src/base/c.md @@ -18,6 +18,7 @@ Base.pointer_from_objref Base.unsafe_pointer_to_objref Base.disable_sigint Base.reenable_sigint +Base.exit_on_sigint Base.systemerror Base.windowserror Core.Ptr diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index e543b99ee3953..febaf5650a349 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -65,7 +65,7 @@ function fake_repl(@nospecialize(f); options::REPL.Options=REPL.Options(confirm_ end # Writing ^C to the repl will cause sigint, so let's not die on that -ccall(:jl_exit_on_sigint, Cvoid, (Cint,), 0) +Base.exit_on_sigint(false) # make sure `run_interface` can normally handle `eof` # without any special handling by the user @@ -764,7 +764,7 @@ fake_repl() do stdin_write, stdout_read, repl Base.wait(repltask) end -ccall(:jl_exit_on_sigint, Cvoid, (Cint,), 1) +Base.exit_on_sigint(true) let exename = Base.julia_cmd() # Test REPL in dumb mode diff --git a/test/stress.jl b/test/stress.jl index 23c28d485ef34..b9fb720f0596a 100644 --- a/test/stress.jl +++ b/test/stress.jl @@ -76,7 +76,7 @@ end # !Sys.iswindows # sig 2 is SIGINT per the POSIX.1-1990 standard if !Sys.iswindows() - ccall(:jl_exit_on_sigint, Cvoid, (Cint,), 0) + Base.exit_on_sigint(false) @test_throws InterruptException begin ccall(:kill, Cvoid, (Cint, Cint,), getpid(), 2) for i in 1:10 @@ -84,5 +84,5 @@ if !Sys.iswindows() ccall(:jl_gc_safepoint, Cvoid, ()) # wait for SIGINT to arrive end end - ccall(:jl_exit_on_sigint, Cvoid, (Cint,), 1) + Base.exit_on_sigint(true) end