Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Signal handling among pthreads

I am trying to learn signal handling among processes and threads. The answer to a few questions would help me understand it better.

I know that a process can send a signal to the process group and allow multiple processes to receive the same signal, but I am not sure about threads.

  • Can signals sent to all pthreads be handled by more than one pthread at the same time?

I setup my program to block all signals using pthread_sigmask(), I have two threads use sigwait(SIGUSR1) to wait for signals, and I have the main thread send SIGUSR1 signals. It seems that everything works well when only one thread is handling the signals (I comment out the code in the other), but when both are running the sigwait() code it hangs or terminates too quickly.

The code is pasted below.

sig_atomic_t signals = 0;
sig_atomic_t sigusr1_signals = 0;
sig_atomic_t sigusr2_signals = 0;
sig_atomic_t count = 0;
sig_atomic_t totalcount = 0;

sigset_t globalset;

int WAIT = 1;           /* false = 0, true = 1 */

static int SIGNALS_SENT = 0;
static int SIGNALS_RECEIVED = 0;

void *sig1handler1(void *argv);

void *reporterhandler(void *argv);

int random_number(int min, int max);

int main(void) {

    pthread_t threads[2];   /* create an array to store a number of threads */

    //int *p_status = &status;
    sigfillset(&globalset);
    pthread_sigmask(SIG_BLOCK, &globalset, NULL);

    /* Generate signal handling threads */
    if (pthread_create(&threads[0], NULL, &sig1handler1, NULL) > 0)
    {
        printf("Thread creation failure!\n");
        return -1;
    }


    /* create reporting thread */
    if (pthread_create(&threads[1], NULL, &reporterhandler, NULL) > 0)
    {
        printf("Thread creation failure!\n");
        return -1;
    }

    /* Signal all threads to begin work concurrently */
    WAIT = 0;

    int c = 0;
    while(c < 100)
    {
        int value = random_number(1, 2);
        if (value == 1)
            kill(0, SIGUSR1);
        else
            kill(0, SIGUSR2);

        SIGNALS_SENT++;
        c++;
        usleep(10000);
    }

    kill(0, SIGINT);


    /* Wait for each thread to finish and join */
    int i = 0;
    for(i = 0; i < 2; i++)
    {
        if (pthread_join(threads[i], NULL) > 0)
        {
            printf("Thread [%u] join failure!\n", (unsigned int)threads[i]);
            return -1;
        }

        printf("THREAD [%u] returned.\n", (unsigned int)threads[i]);
    }

    printf("Parent Process [%d] exiting successfully.\n", getpid());
    return EXIT_SUCCESS;
}


void *sig1handler1(void *argv)
{
    pthread_t tid = pthread_self();
    printf("THREAD[%u] sig1handler1: waiting for signal to do some work...\n", (unsigned int)tid);

//  sigset_t myset;
//  sigfillset(&myset);
//  sigdelset(&myset, SIGINT);
//  sigdelset(&myset, SIGUSR1);
//  pthread_sigmask(SIG_SETMASK, &myset, NULL);

    /* Wait for a signal to start work */
    while (WAIT);

    int sig;
    int count = 0;

    while(1)
    {
        sigwait(&globalset, &sig);
        if (sig == SIGUSR1)
        {
            sigusr1_signals++;
            signals++;
            count++;
            //printf("thread1: caught SIGUSR1 signal!\n");
        }
        else if (sig == SIGINT)
        {
            printf("thread1: caught SIGINT signal, detected SIGUSR1 %d times, and terminating!\n", count);          pthread_exit(NULL);
        }
    }

    //printf("THREAD[%u] sig1handler1: doing some work!\n", (unsigned int)tid);
    //return (void *)EXIT_SUCCESS;
    //return (void *)NULL;
    pthread_exit(NULL);
}

void *reporterhandler(void *argv)
{
    pthread_t tid = pthread_self();
    printf("THREAD[%u] reporter: waiting for signal to do some work...\n", (unsigned int)tid);

    int sig;
    int count = 0;

//  sigset_t myset;
//  sigfillset(&myset);
//  sigdelset(&myset, SIGINT);
//  sigdelset(&myset, SIGUSR1);
//  sigdelset(&myset, SIGUSR2);
//  pthread_sigmask(SIG_SETMASK, &myset, NULL);

    /* Wait for a signal to start work */
    while (WAIT);

    while(1)
    {
        sigwait(&globalset, &sig);
        if (sig == SIGUSR1)
        {
            sigusr1_signals++;
            signals++;
            count++;
            totalcount++;
            SIGNALS_RECEIVED++;
        }
        else if (sig == SIGUSR2)
        {
            sigusr2_signals++;
            signals++;
            count++;
            totalcount++;
            SIGNALS_RECEIVED++;
        }
        else if (sig == SIGINT)
        {
            printf("Reporter: SIGUSR1 detected %d times\n", sigusr1_signals);
            printf("Reporter: SIGUSR2 detected %d times\n", sigusr2_signals);
            printf("Reporter: detected %d signals\n", totalcount);
            printf("Reporter: SIGNALS_SENT %d \n", SIGNALS_SENT);
            printf("Reporter: SIGNALS_REC %d \n", SIGNALS_RECEIVED);
            pthread_exit(NULL);
        }

        /* Display Report after detecting 10 signals */
        if (count == 10)
                    sigusr1_signals, sigusr2_signals);

            count = 0;
        }
    }

    //printf("THREAD[%u] reporter: doing some work!\n", (unsigned int)tid);
    pthread_exit(NULL);
}

int random_number(int min, int max)
{
    if (min < max)
    {
        max = max + 1;      /* include the max value */
        return (rand() % (max - min)) + min;
    }

    return -1;
}   
like image 903
OwlsCIS Avatar asked Oct 20 '22 00:10

OwlsCIS


1 Answers

  • Can signals [be] sent to all pthreads [and] be handled by more than one pthread at the same time?

Not at the same time. These are the possibilities:

  1. Send a signal to a process (kill()): In this case, any thread listening for the signal can receive it, but just one will do it.
  2. Send a signal to a process group: The signal will be delivered to all processes in the group.
  3. Send a signal to a specific thread (pthread_kill()): now you are sending it to a specific thread ID.

Prototype:

int pthread_kill(pthread_t thread, int sig);

In your case, I think the only way to deliver the signal to all threads is iterating along all your thread ids to send the signal with pthread_kill().

like image 61
whoan Avatar answered Nov 03 '22 23:11

whoan