std::signal

From cppreference.com
< cpp‎ | utility‎ | program
Utilities library
Defined in header <csignal>
/* signal-handler */ * signal( int sig, /* signal-handler */ * handler ) ;
(1)
extern "C" using /* signal-handler */ = void ( int ) ;
(2) (exposition only*)

Changes handling of the signal sig. Depending on handler

When signal handler is set to a function and a signal occurs, it is implementation defined whether std::signal(sig, SIG_DFL)

For some of the signals, the implementation may call std::signal(sig, SIG_IGN) at the startup of the program. For the rest, the implementation must call std::signal(sig, SIG_DFL)

(Note: POSIX introduced sigaction to standardize these implementation-defined behaviors)

Parameters

sig - the signal to set the signal handler to. It can be an implementation-defined value or one of the following values:
defines signal types
(macro constant)
handler - the signal handler. This must be one of the following:
  • SIG_DFL macro. The signal handler is set to default signal handler.
  • SIG_IGN macro. The signal is ignored.
  • A pointer to a function. The signature of the function must be equivalent to the following:
extern "C" void fun(int sig);


Return value

Previous signal handler on success or SIG_ERR on failure (setting a signal handler can be disabled on some implementations).

Signal handler

The following limitations are imposed on the user-defined function that is installed as a signal handler.

If the signal handler is called NOT as a result of std::abort or std::raise

  • the signal handler calls any function within the standard library, except
  • std::abort
  • std::_Exit
  • std::quick_exit
  • std::signal with the first argument being the number of the signal currently handled (async handler can re-register itself, but not other signals).
(until C++17)

A plain lock-free atomic operation is an invocation of a function f from <atomic> or <stdatomic.h> (since C++23)

The behavior is undefined if any signal handler performs any of the following:

  • call to any library function, except for plain lock-free atomic operations and the following signal-safe functions (note, in particular, dynamic allocation is not signal-safe):
  • access to an object with thread storage duration
  • a dynamic_cast expression
  • a throw expression
  • entry to a try block
  • initialization of a static variable that performs dynamic non-local initialization (including delayed until first ODR-use)
  • waits for completion of initialization of any variable with static storage duration due to another thread concurrently initializing it
(since C++17)

If the user defined function returns when handling SIGFPE, SIGILL, SIGSEGV

If the signal handler is called as a result of std::abort or std::raise (synchronous signal), the behavior is undefined if the signal handler calls std::raise

On entry to the signal handler, the state of the floating-point environment and the values of all objects is unspecified, except for

(since C++11)

On return from a signal handler, the value of any object modified by the signal handler that is not volatile std::sig_atomic_t or lock-free std::atomic

(until C++14)

A call to the function signal() synchronizes-with any resulting invocation of the signal handler.

If a signal handler is executed as a result of a call to std::raise (synchronously), then the execution of the handler is sequenced-after the invocation of std::raise and sequenced-before the return from it and runs on the same thread as std::raise. Execution of the handlers for other signals is unsequenced

Two accesses to the same object of type volatile std::sig_atomic_t do not result in a data race if both occur in the same thread, even if one or more occurs in a signal handler. For each signal handler invocation, evaluations performed by the thread invoking a signal handler can be divided into two groups A and B, such that no evaluations in B happen-before evaluations in A, and the evaluations of such volatile std::sig_atomic_t objects take values as though all evaluations in A happened-before the execution of the signal handler and the execution of the signal handler happened-before

(since C++14)

Notes

POSIX requires that signal is thread-safe, and specifies a list of async-signal-safe library functions

Signal handlers are expected to have C linkage

Example

#include <csignal>
#include <iostream>
 
namespace
{
    volatile std::sig_atomic_t gSignalStatus;
}
 
void signal_handler(int signal)
{
    gSignalStatus = signal;
}
 
int main()
{
    // Install a signal handler
    std::signal(SIGINT, signal_handler);
 
    std::cout << "SignalValue: " << gSignalStatus << '\n';
    std::cout << "Sending signal: " << SIGINT << '\n';
    std::raise(SIGINT);
    std::cout << "SignalValue: " << gSignalStatus << '\n';
}

Possible output:

SignalValue: 0
Sending signal: 2
SignalValue: 2

References

  • C++23 standard (ISO/IEC 14882:2024):
  • 17.13.5 Signal handlers [support.signal]
  • C++20 standard (ISO/IEC 14882:2020):
  • 17.13.5 Signal handlers [support.signal]
  • C++17 standard (ISO/IEC 14882:2017):
  • 21.10.4 Signal handlers [support.signal]

Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
LWG 3756 C++17 it was unclear whether std::atomic_flag is signal-safe it is

See also

runs the signal handler for particular signal
(function)
fence between a thread and a signal handler executed in the same thread
(function)
C documentation for signal