From: Anton Salikhmetov <[EMAIL PROTECTED]>

According to the POSIX standard, multiple real-time signals
pending to a process should be delivered in a strict order.
Specifically, the lowest-numbered signal should be delivered
first and multiple occurrences of signals with the same number
should be delivered in FIFO order.

Current Linux kernel delivers the highest-numbered signals
pending to a process first, not the lowest-numbered ones. This
contradicts to the requirement explained above. The problem can
be demonstrated by the following test program:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

#define SIG_RAND (SIGRTMIN + rand() % (SIGRTMAX - SIGRTMIN + 1))
#define NUMSIG 7

struct {
        int sig, num;
} seq[NUMSIG];

void sh(int sig, siginfo_t *info, void *unused)
{
        static int cnt;
        seq[cnt].num = info->si_value.sival_int;
        seq[cnt++].sig = sig;
}

int main(int argc, char *argv[])
{
        int i;
        pid_t p = getpid();
        struct sigaction sa;
        sigset_t ss, ssold;
        union sigval sv;

        sigemptyset(&ss);
        for (i = SIGRTMIN; i <= SIGRTMAX; i++) {
                sa.sa_sigaction = sh;
                sa.sa_flags = SA_SIGINFO;
                sigemptyset(&sa.sa_mask);
                sigaction(i, &sa, NULL);
                sigaddset(&ss, i);
        }

        sigprocmask(SIG_BLOCK, &ss, &ssold);
        switch (fork()) {
                case -1:
                        perror("fork()");
                        return 1;
                case 0:
                        srand(time(NULL));
                        for (i = 0; i < NUMSIG; i++) {
                                sv.sival_int = i;
                                sigqueue(p, SIG_RAND, sv);
                        }
                        return 0;
                default:
                        wait(NULL);
        }
        sigprocmask(SIG_SETMASK, &ssold, NULL);

        for (i = 0; i < NUMSIG; i++)
                printf("SIGRTMIN + %2d, sent with number %d\n",
                        seq[i].sig - SIGRTMIN, seq[i].num);
        return 0;
}

Example of the program output for the original kernel:

SIGRTMIN + 23, sent with number 0
SIGRTMIN + 21, sent with number 1
SIGRTMIN + 21, sent with number 6
SIGRTMIN + 16, sent with number 2
SIGRTMIN + 15, sent with number 3
SIGRTMIN + 14, sent with number 4
SIGRTMIN +  5, sent with number 5

Example of the program output for the patched kernel:

SIGRTMIN + 2, sent with number 1
SIGRTMIN + 3, sent with number 0
SIGRTMIN + 17, sent with number 3
SIGRTMIN + 23, sent with number 5
SIGRTMIN + 25, sent with number 2
SIGRTMIN + 29, sent with number 6
SIGRTMIN + 30, sent with number 4

Patch for the Linux 2.6.22.1 kernel:

Signed-off-by: Anton Salikhmetov <[EMAIL PROTECTED]>
---
--- linux-2.6.22.1.orig/kernel/signal.c 2007-07-10 22:56:30.000000000
+0400
+++ linux-2.6.22.1/kernel/signal.c      2007-07-11 04:42:17.000000000 +0400
@@ -134,34 +134,11 @@
 
 int next_signal(struct sigpending *pending, sigset_t *mask)
 {
-       unsigned long i, *s, *m, x;
-       int sig = 0;
-       
-       s = pending->signal.sig;
-       m = mask->sig;
-       switch (_NSIG_WORDS) {
-       default:
-               for (i = 0; i < _NSIG_WORDS; ++i, ++s, ++m)
-                       if ((x = *s &~ *m) != 0) {
-                               sig = ffz(~x) + i*_NSIG_BPW + 1;
-                               break;
-                       }
-               break;
-
-       case 2: if ((x = s[0] &~ m[0]) != 0)
-                       sig = 1;
-               else if ((x = s[1] &~ m[1]) != 0)
-                       sig = _NSIG_BPW + 1;
-               else
-                       break;
-               sig += ffz(~x);
-               break;
-
-       case 1: if ((x = *s &~ *m) != 0)
-                       sig = ffz(~x) + 1;
-               break;
-       }
-       
+       unsigned long *s = pending->signal.sig, *m = mask->sig, x;
+       int sig = 0, i;
+       for (i = 0; i < _NSIG_WORDS; i++, s++, m++)
+               if ((x = (*s & ~*m)))
+                       sig = fls(x) + i * _NSIG_BPW;
        return sig;
 }

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to