csignal-handling

Operations on a signal handler C


I have read that only a limited set of operations is allowed in signal handlers, for example you cannot update a global variable unless it is volatile sig_atomic_t.

This means that only atomic operations should be done in signal handlers.

For example posting a semaphore (with sem_post) is atomic,so it can be done within a signal handler, right?


Solution

  • I have read that only a limited set of operations is allowed in signal handlers, for example you cannot update a global variable unless it is volatile sig_atomic_t.

    According to the C language spec,

    If the signal occurs as the result of calling the abort or raise function, the signal handler shall not call the raise function.

    If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object and that is not declared with the constexpr storage-class specifier other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than

    • the abort function,
    • the _Exit function,
    • the quick_exit function,
    • the functions in <stdatomic.h> (except where explicitly stated otherwise) when the atomic arguments are lock-free,
    • the atomic_is_lock_free function with any atomic argument, or
    • the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. Furthermore, if such a call to the signal function results in a SIG_ERR return, the object designated by errno has an indeterminate representation.

    (C23 7.14.2.1/4-5)

    If you happen to be using a POSIX-conforming C implementation then you can rely on somewhat more lenience. POSIX defines behavior for more kinds of object accesses by signal handlers, and it specifies a longer list of functions that a signal handler may definedly call, which POSIX classifies as "async-signal-safe". Even though that list is relatively long, there are still many notable omissions, such as all the stdio functions.

    This means that only atomic operations should be done in signal handlers.

    No, that's not an accurate reading:

    For example posting a semaphore (with sem_post) is atomic,so it can be done within a signal handler, right?

    Posting a semaphore via POSIX sem_post is not an atomic operation in the relevant sense. However, sem_post() is among POSIX's async-signal-safe functions, so in a POSIX environment, signal handlers do not elicit undefined behavior by calling that function.

    In contrast, few pthreads functions are async-signal-safe, and none of the pthreads mutex or condition variable functions are among those.

    If you want to stick to defined behavior in your signal handlers -- and you should, because undefined signal handler behavior can bite you hard and is difficult to debug -- then you need to learn specifically what the specs say about it. It is not sufficient to rely on hearsay and vague ideas about what is safe and what isn't. Asking questions such as this one is a good way to start.