-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
WIP: User-defined signal handling #59147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
omus
wants to merge
13
commits into
master
Choose a base branch
from
cv/signal-handlers
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+569
−107
Draft
Changes from 10 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
af391bf
Refactor `strsignal` in signals-win.c
omus 5587db9
Define `sigabbrev_np` on BSD/macOS/Windows
omus 1b58518
Create `signal_name`/`signal_abbrev` functions
omus f8a81e8
fixup! Create `signal_name`/`signal_abbrev` functions
omus 5de4471
Export `jl_sigabbrev`
omus 71253be
Define `SIG*` constants
omus e7bc94d
Create `jl_install_default_signal_handler` function
omus bf03eaa
Add `kill` methods for signals
omus 4319871
User-defined Julia signal handlers
omus 9f695f9
Add TODO entries
omus f091511
Abort register if condition unset
omus eaf5c87
Remove print
omus 5a1d862
Use function to assign condition
omus File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,6 +50,7 @@ export | |
Iterators, | ||
Broadcast, | ||
MathConstants, | ||
Signals, | ||
|
||
# Types | ||
AbstractChannel, | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
module Signals | ||
|
||
using Base: AsyncCondition | ||
using Base.Intrinsics: atomic_pointerset | ||
|
||
export signal_abbrev, signal_name | ||
public register_handler, deregister_handler | ||
|
||
""" | ||
signal_abbrev(signum::Integer) -> Union{String,Nothing} | ||
|
||
Returns the signal abbreviation (e.g. "TERM") associated with the signal number for standard | ||
signals on this architecture. If the signal number is invalid `nothing` will be returned | ||
instead. | ||
""" | ||
function signal_abbrev(signum::Integer) | ||
abbrev = ccall(:jl_sigabbrev, Cstring, (Cint,), signum) | ||
abbrev != C_NULL || return nothing | ||
return @static Sys.isbsd() ? uppercase(unsafe_string(abbrev)) : unsafe_string(abbrev) | ||
end | ||
|
||
""" | ||
signal_name(signum::Integer) -> Union{String,Nothing} | ||
|
||
Returns the signal name (e.g. "SIGTERM") associated with the signal number for standard | ||
signals on this architecture. If the signal number is invalid `nothing` will be returned | ||
instead. | ||
""" | ||
function signal_name(signum::Integer) | ||
abbrev = signal_abbrev(signum) | ||
!isnothing(abbrev) || return nothing | ||
return string("SIG", abbrev) | ||
end | ||
|
||
# Generate the `SIG*` constants for standard POSIX signals. We need to generate these | ||
# constants as the associated signal numbers are architecture specific. | ||
for signum in 1:31 | ||
signame = signal_name(signum) | ||
|
||
if !isnothing(signame) | ||
sigsym = Symbol(signame) | ||
@eval begin | ||
const $sigsym = $signum | ||
export $sigsym | ||
end | ||
end | ||
end | ||
|
||
Base.kill(pid::Integer, signum::Integer) = ccall(:kill, Cvoid, (Cint, Cint), pid, signum) | ||
Base.kill(signum::Integer) = kill(getpid(), signum) | ||
|
||
const _SIGNAL_HANDLER_LOCK = Base.ReentrantLock() | ||
const _SIGNAL_HANDLER = Dict{Cint,Base.Callable}() | ||
|
||
const _SIGNAL_ROUTER_TASK_LOCK = Base.ReentrantLock() | ||
const _SIGNAL_ROUTER_TASK = Ref{Task}() | ||
|
||
function _initialize_signal_router() | ||
condition = AsyncCondition() | ||
signal_router_task = Threads.@spawn signal_router(condition) | ||
errormonitor(signal_router_task) | ||
|
||
# Allow C code to notify the `AsyncCondition`. | ||
jl_signal_router_condition_ptr = cglobal(:jl_signal_router_condition, Ptr{Cvoid}) | ||
atomic_pointerset(jl_signal_router_condition_ptr, condition.handle, :release) | ||
|
||
return signal_router_task | ||
end | ||
|
||
""" | ||
signal_router() -> Nothing | ||
|
||
Routes signals to user-defined signal handler functions via the `SIGNAL_CONDITION`. | ||
Typically, this function is run in a separate thread and user-defined signal handlers | ||
are run within that thread. | ||
""" | ||
function signal_router(condition::AsyncCondition) | ||
while isopen(condition) | ||
wait(condition) # Wait until notified by `jl_signal_router_condition` | ||
signum = ccall(:jl_consume_user_signal, Cint, ()) | ||
|
||
# Process all queued signals while the thread is active | ||
while signum != -1 | ||
signal_handler = @lock _SIGNAL_HANDLER_LOCK begin | ||
get(_SIGNAL_HANDLER, signum, nothing) | ||
end | ||
|
||
if !isnothing(signal_handler) | ||
invokelatest(signal_handler, signum) | ||
end | ||
|
||
signum = ccall(:jl_consume_user_signal, Cint, ()) | ||
end | ||
end | ||
return nothing | ||
end | ||
|
||
""" | ||
register_handler(handler, signum::Integer) -> Nothing | ||
|
||
Registers a Julia function as a signal handler for the given signal. The provided signal | ||
handler will respond to process-directed signals. | ||
|
||
The signals `SIGKILL` and `SIGSTOP` cannot be caught and attempting to register a handler | ||
will throw an `ArgumentError`. | ||
""" | ||
function register_handler(handler, signum::Integer) | ||
if signum == SIGKILL || signum == SIGSTOP | ||
throw(ArgumentError("$(signal_name(signum)) is impossible to catch")) | ||
end | ||
|
||
# Initialize the `signal_router` task if this is the first time a user signal handler | ||
# is being registered | ||
@lock _SIGNAL_ROUTER_TASK_LOCK begin | ||
if !isassigned(_SIGNAL_ROUTER_TASK) | ||
_SIGNAL_ROUTER_TASK[] = _initialize_signal_router() | ||
end | ||
end | ||
|
||
@lock _SIGNAL_HANDLER_LOCK begin | ||
_SIGNAL_HANDLER[signum] = handler | ||
end | ||
ccall(:jl_register_user_signal, Cvoid, (Cint,), signum) | ||
return nothing | ||
end | ||
|
||
""" | ||
deregister_handler(signum::Integer) -> Nothing | ||
|
||
Disassociates the Julia function from being triggered when this process receives the given | ||
signal and restores the default Julia signal handler. | ||
""" | ||
function deregister_handler(signum::Integer) | ||
@lock _SIGNAL_HANDLER_LOCK begin | ||
if haskey(_SIGNAL_HANDLER, signum) | ||
delete!(_SIGNAL_HANDLER, signum) | ||
end | ||
end | ||
ccall(:jl_deregister_user_signal, Cvoid, (Cint,), signum) | ||
return nothing | ||
end | ||
|
||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably automatically deregister the signal handler if anything in here throws, so that we don't infinitely accumulate signal messages in the runtime.