Skip to content

Restore signal handlers? #46076

@mkitti

Description

@mkitti

When calling into foreign code via ccall, it is possible that the foreign libraries will try to install their own signal handlers, replacing Julia's signal handlers.

In this case, could we provide a mechanism to backup and restore Julia's default signal handlers?
https://cs.github.com/JuliaLang/julia/blob/51bb96857d26f67e62f0edc4fc4682a156cb3d08/src/signals-unix.c#L981

Background

sigaction is part of the POSIX standard and accepts three arguments.

https://pubs.opengroup.org/onlinepubs/007904875/functions/sigaction.html

int sigaction(int sig, const struct sigaction *restrict act,
       struct sigaction *restrict oact);
  • sig - the signal to install the handler for (in)
  • act - the handler to install (in)
  • oact - the original handler that was installed (out)

If act is a null pointer, no signal handler will be installed. If oact is a null pointer, the original handler that was installed will not be written.

By using sigaction(sig, NULL, oact) we can retrieve the currently installed signal handlers.

Signal Handlers used by Julia

  1. SIGFPE
  2. SIGINT, SIGPIPE, SIGTRAP
  3. SIGUSR2 or mach handler*
  4. SIGILL, SIGABRT, SIGSYS, SIGPROF, SIGINFO, SIGUSR1
  5. SIGSEGV and SIGBUS

* Does not use sigaction

Proposal

Provide exported C functions to obtain and restore current signal handlers as an opaque pointer. This is specifically designed so that Julia functions do not need to know about the sizeof(struct sigaction) or any of the underlying implementation details.

#include <signal.h>
#include <stddef.h>
#include <malloc.h>
#include <string.h>

JL_DLLEXPORT int jl_get_signal_handlers(struct sigaction ** p_signal_handlers) {
   struct sigaction * signal_handlers = malloc(sizeof(struct sigaction)*5);
   memset(signal_handlers, 0, sizeof(struct sigaction)*5);
   if(sigaction(SIGFPE,  NULL, &signal_handlers[0]) < 0)
       return -1;
   if(sigaction(SIGINT,  NULL, &signal_handlers[1]) < 0)
       return -1;
   if(sigaction(SIGUSR2, NULL, &signal_handlers[2]) < 0)
       return -1;
   if(sigaction(SIGILL,  NULL, &signal_handlers[3]) < 0)
       return -1;
   if(sigaction(SIGSEGV, NULL, &signal_handlers[4]) < 0)
       return -1;
   *p_signal_handlers = signal_handlers;
   return 0;
}
JL_DLLEXPORT int jl_set_signal_handlers(struct sigaction * signal_handlers) {
   if(sigaction(SIGFPE,  &signal_handlers[0], NULL) < 0)
       return -1;
   if(sigaction(SIGINT,  &signal_handlers[1], NULL) < 0)
       return -1;
   if(sigaction(SIGUSR2, &signal_handlers[2], NULL) < 0)
       return -1;
   if(sigaction(SIGILL,  &signal_handlers[3], NULL) < 0)
       return -1;
   if(sigaction(SIGSEGV, &signal_handlers[4], NULL) < 0)
       return -1;
   return 0;
}
function restore_signal_handlers(func::Function)
    ref = Ref{Ptr{Nothing}}(C_NULL)
    status = @ccall jl_get_signal_handlers(ref::Ptr{Ptr{Nothing}})::Cint
    status < 0 || error("Failed to get signal handlers")
    try
        func()
    finally
        status = @ccall jl_set_signal_handlers(ref[]::Ptr{Nothing})::Cint
        status < 0 || error("Failed to set signal handlers")
    end
end

Usage

restore_signal_handlers() do
   @ccall function_that_installs_signal_handlers(...)
end

While the above mechanisms do not obtain the foreign signal handlers, it would not be hard for specific applications to use the new C functions to do so.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions