doptimusprime
doptimusprime

Reputation: 9395

signalfd() misses signals

In my program, I am using signalfd to handle signals and combine it with poll for async IO. Below is my code:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/signalfd.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <poll.h>
#include <assert.h>
#include <errno.h>


volatile sig_atomic_t cont = 1;
volatile sig_atomic_t usrcnt = 0;
volatile sig_atomic_t susrcnt = 0;

volatile sig_atomic_t wsig = 0;
volatile sig_atomic_t wtid = 0;

int GetCurrentThreadId()
{
    return syscall(__NR_gettid);
}

void Segv1(int p1, siginfo_t * p2, void * p3)
{
    //printf("SIGSEGV signal on illegal memory access handled by thread: %d\n", GetCurrentThreadId());
    wtid = GetCurrentThreadId();
    wsig = SIGSEGV;
    _exit(SIGSEGV);
}

void Fpe1(int p1 , siginfo_t * p2, void * p3)
{
    //printf is only for test.
    //printf("FPE signal handled by thread: %d\n", GetCurrentThreadId());
    wtid = GetCurrentThreadId();
    wsig = SIGFPE;
    _exit(SIGFPE);
}

void User1(int p1 , siginfo_t * p2, void * p3)
{
    printf("User signal 1 handled by thread: %d\n", GetCurrentThreadId());
    ++susrcnt;
    wtid = GetCurrentThreadId();
    wsig = SIGUSR1;
}


void* ThreadFunc (void* d)
{

    //Let us use signalfd.
    int sfd;
    sigset_t mask;

    /* We will handle SIGTERM and SIGINT. */
    sigemptyset (&mask);
    sigaddset (&mask, SIGUSR1);

    /* Create a file descriptor from which we will read the signals. */
    sfd = signalfd (-1, &mask, 0);
    if (sfd < 0) {
        printf ("signalfd failed with %d\n", errno);
        return NULL;
    }

    pthread_sigmask(SIG_BLOCK, &mask, NULL);

  /* This is the main loop */
        struct pollfd pfd[1];
        int ret;
        ssize_t bytes;

        pfd[0].fd = sfd;
        pfd[0].events = POLLIN | POLLERR | POLLHUP;

        for (;;) {
                ret = poll(pfd, 1, -1);

                /* Bail on errors (for simplicity) */
                assert(ret > 0);
                assert(pfd[0].revents & POLLIN);

                /* We have a valid signal, read the info from the fd */
                struct signalfd_siginfo info;
                bytes = read(sfd, &info, sizeof(info));
                assert(bytes == sizeof(info));

                unsigned sig = info.ssi_signo;
                unsigned user = info.ssi_uid;

                if (sig == SIGUSR1) {
                    ++usrcnt;
                    printf ("Got SIGUSR1 by POLL in thread: %d: Handler count: %d,  %d\n", GetCurrentThreadId(), susrcnt, usrcnt);
                }
        }

    /* Close the file descriptor if we no longer need it. */
    close (sfd);

    return NULL;
}


int main()
{

    const int numthreads = 1;
    sigset_t sset;
    struct sigaction act;
    int sleepval = 15;
    int pid;
    int i;
        int * a = 0;
    //*a = 1;
    int c=0;
    //c = 0;
    int b;


    printf("My PID: %d\n", getpid());
    printf("SIGSEGV: %d\nSIGFPE: %d\nSIGUSR1: %d\n", SIGSEGV, SIGFPE, SIGUSR1);
    //Create a thread for signal
    memset(&act, 0, sizeof act);
    act.sa_sigaction = User1;
    act.sa_flags    = SA_SIGINFO;

    //Set Handler for SIGUSR1 signal.
    if(sigaction(SIGUSR1, &act, NULL)<0) {
        fprintf(stderr, "sigaction failed\n");
        return 1;
    }


    //Set handler for SIGSEGV signal.
    act.sa_sigaction    = Segv1;
    sigaction(SIGSEGV, &act, NULL);

    //Set handler for SIGFPE (floating point exception) signal.
    act.sa_sigaction    = Fpe1;
    sigaction(SIGFPE, &act, NULL);


    sigemptyset(&sset);
    sigaddset(&sset, SIGUSR1);

    sigprocmask(SIG_UNBLOCK, &sset, NULL);

    pthread_t tid[numthreads];
    for(i=0;i<numthreads;++i)
        pthread_create(&tid[i], NULL, ThreadFunc, NULL);

    //Block the signal for main thread so that other thread handles the the signal.
    pthread_sigmask(SIG_BLOCK, &sset, NULL);

    sleep(numthreads/2);

    //Raise user signal SIGUSR1.
    //raise(SIGUSR1);
    pid = fork();
    if(pid) {
        while(sleepval) {
            sleepval = sleep(sleepval);
            if(sleepval)
                switch(wsig) {
                    case SIGSEGV:
                        printf("[Main] Segmenation fault in thread: %d\n", wtid);
                        exit(1);
                        break;
                    case SIGFPE:
                        printf("[Main] Floating point exception in thread: %d\n", wtid);
                        exit(1);
                        break;
                    case SIGUSR1:
                        printf("[Main] User 1 signal in thread: %d\n", wtid);
                        break;
                    default:
                        printf("[Main] Unhandled signal: %d in thread: %d\n", wsig, wtid);
                        break;
                }
        }

    } else {
         sleep(1); //To avoid race between signal handler and signal fd.

        for(i=0;i<10;++i) {
            //If sleep is not used, signal SIGUSR1 will be handled one time in parent
            //as other signals will be ignored while SIGUSR1 is being handled.
            sleep(1);
            //Problem is here. When the sleep(1) is commented out, it missed the signals.
            kill(getppid(), SIGUSR1);
        }
        return 0;
    }

    return 0;
}

In the program, process spawns a thread which create signalfd and starts using poll. Then process spawns a child process which sends SIGUSR1 to the parent process. When signals are send at the interval of 1s, then it processes all the signals. However, when sleep is removed, it missed notification.

I would like to know does signalfd also discards signal notification if it is processing the same signal. Also, what is priority order between signal handler and signalfd?

Upvotes: 6

Views: 2437

Answers (2)

wilson
wilson

Reputation: 1

So here if change the signal > 32, it works:

  • If the signal < 32, happened multiple times only trigger once before the signal already handled.
  • If the signal >= 32 and also valid, like here is 34, will create a list to cache every trigger.
  #include <assert.h>
  #include <errno.h>

+ #undef SIGUSR1
+ #define SIGUSR1 34

  volatile sig_atomic_t cont = 1;
  volatile sig_atomic_t usrcnt = 0;
  volatile sig_atomic_t susrcnt = 0;

The following patch just force flush printf:

          int b;

+         setbuf(stdout, NULL);                                                
          printf("My PID: %d\n", getpid());

Tested on Ubuntu 14.04, the output:

My PID: 5249
SIGSEGV: 11
SIGFPE: 8
SIGUSR1: 34
Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  1
Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  2
Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  3
Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  4
Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  5
Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  6
Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  7
Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  8
Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  9
Got SIGUSR1 by POLL in thread: 5250: Handler count: 0,  10

Upvotes: 0

alk
alk

Reputation: 70941

If multiple standard (that is: non real time) signals are pending for a process, the OS might decide to merge several signals of the same type into one.

From POSIX:

2.4.1 Signal Generation and Delivery

[...]

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.

Standard signals are not queued by default. The only way to have a standard signal queued is by issuing them using sigqueue().

Upvotes: 6

Related Questions