4

The POSIX article for sigaction states:

If SA_SIGINFO is set in sa_flags, then subsequent occurrences of sig generated by sigqueue() or as a result of any signal-generating function that supports the specification of an application-defined value (when sig is already pending) shall be queued in FIFO order until delivered or accepted; the signal-catching function shall be invoked with three arguments. The application specified value is passed to the signal-catching function as the si_value member of the siginfo_t structure.

This says nothing about merging of occurrences of the same signal (signo). And even if it meant merging, then the phrase about FIFO would be incomplete. For example, if the FIFO is [SIGALRM, SIGIO, SIGUSR1, SIGIO], what would it be after merging, [SIGALRM, SIGIO, SIGUSR1] or [SIGALRM, SIGUSR1, SIGIO]?

However, I saw several reports (0, 1, 2) that Linux indeed merges occurrences of the same signal. I also wrote a small test program that confirms that occurrences of the same signal sent with sigqueue when that signal is blocked are merged on ArchLinux. A signal handler was installed with sigaction.

Linux [skipped] 5.3.8-arch1-1 #1 SMP PREEMPT [skipped] x86_64 GNU/Linux

If occurrences of the same signal are merged, then transmitting information via union sigval does not make sense because this information may be unpredictably lost.

Is my understanding of POSIX incorrect, or is Linux implementing it incorrectly?

[Update 2019-11-09 09:51:32+00:00. I found another chunk of documentation that corroborates my point. According to the article “2. General Information”,

If a subsequent occurrence of a pending signal is generated, it is implementation-defined as to whether the signal is delivered or accepted more than once in circumstances other than those in which queuing is required.

and another one on sigqueue,

If SA_SIGINFO is set for signo and if the resources were available to queue the signal, the signal shall be queued and sent to the receiving process.

The sigqueue() function shall fail if:

[EAGAIN] No resources are available to queue the signal. The process has already queued {SIGQUEUE_MAX} signals that are still pending at the receiver(s), or a system-wide resource limit has been exceeded.

I used sigaction with SA_SIGINFO set and sigqueue. The function sigqueue did not return EAGAIN.]

[Update 2019-11-09 17:45:25+00:00. My test program. The sigprint functions are for printing using write and a statically allocated buffer; they are not important.

#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include "sigprint.h"

void signal_handler(int signo, siginfo_t *info, void *context) {
    sigprint_string("|");
    sigprint_long(info->si_value.sival_int);
    sigprint_flush();
}

void sigqueue_check(pid_t pid, int signo, union sigval value) {
    if (sigqueue(pid, signo, value) == -1) {
        printf(" returned error code %d which is %s to EAGAIN.\n",
               errno, errno == EAGAIN ? "==" : "!=");
        exit(24);
    }
}

int main() {
    struct sigaction sigaction0;
    union sigval sigval0;
    sigset_t sigset0, sigset1;
    sigaction0.sa_sigaction = &signal_handler;
    sigemptyset(&sigaction0.sa_mask);
    sigaction0.sa_flags = SA_SIGINFO | SA_RESTART;
    sigaction(SIGALRM, &sigaction0, NULL);

    sigemptyset(&sigset0);
    sigaddset(&sigset0, SIGALRM);
    sigprocmask(SIG_BLOCK, &sigset0, &sigset1);
    sigval0.sival_int = 10;
    sigqueue_check(getpid(), SIGALRM, sigval0);
    sigval0.sival_int = 11;
    sigqueue_check(getpid(), SIGALRM, sigval0);
    sigval0.sival_int = 12;
    sigqueue_check(getpid(), SIGALRM, sigval0);
    sigprocmask(SIG_SETMASK, &sigset1, NULL);
    sleep(1);
    return 0;
}

]

beroal
  • 369
  • 4
  • 14
  • 1
    Apparently Linux does not implement queuing for standard signals, only for signals in the range [`SIGRTMIN`, `SIGRTMAX`]. If you're looking to establish a signal handler on a file descriptor, you can tell it to use a different signal (other than the default `SIGIO`) with `fcntl` and `F_SETSIG`. – Humphrey Winnebago Jul 01 '20 at 01:06

1 Answers1

2

The documented behavior on Linux is to not queue duplicate blocked signals.

From man 7 signal:

Standard signals do not queue. If multiple instances of a standard signal are generated while that signal is blocked, then only one instance of the signal is marked as pending (and the signal will be delivered just once when it is unblocked). In the case where a standard signal is already pending, the siginfo_t structure (see sigaction(2)) associated with that signal is not overwritten on arrival of subsequent instances of the same signal. Thus, the process will receive the information associated with the first instance of the signal.

However... the man page also says:

Multiple instances of real-time signals can be queued. By contrast, if multiple instances of a standard signal are delivered while that signal is currently blocked, then only one instance is queued.

Are you looking at the POSIX documentation for real time signals and testing with standard ones?

Shawn
  • 47,241
  • 3
  • 26
  • 60
  • Sorry, I forgot to add the link to the article. Fixed. By the way, I do not call `signal`, The normative part of the article does not say that it applies only to real time signals. I do not know what to answer. How was I supposed to know about that? I added another chunk of documentation that corroborates my point. – beroal Nov 09 '19 at 10:11
  • @beroal You should update your question with your actual code, but I'm 99% sure you're just expecting real time signal behavior from standard signals, which isn't something Linux does. – Shawn Nov 09 '19 at 13:49
  • You should read *Linux* documentation, not *POSIX* documentation, to learn how Linux does things. – Shawn Nov 09 '19 at 14:08
  • “You should update your question with your actual code” Done. – beroal Nov 09 '19 at 17:49
  • @beroal Yup, you're not using the real time signals. – Shawn Nov 09 '19 at 18:54