Skip to content
Advertisement

POSIX timer hangs up after a few runs

I have created a POSIX timer in the main function of my program. Each thread of the main program is setting the timer so that on expiry of it, the signal handler update one variable which awakes the next thread of the same process.

The timer is working fine most of the time but not always. It sometimes completes the full execution while in other runs, it hangs up. What could be the possible reasons? My suspicion is something related to signal delivery.

Here is the code:

#define _GNU_SOURCE
#define _POSIX_C_SOURCE 199309
#include <sched.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syscall.h>
#define NUM_THREADS 10

#define CLOCKID CLOCK_REALTIME
#define SIG SIGUSR1
int ret;
timer_t timerid;
struct sigevent sev;
struct itimerspec its;
long long freq_nanosecs;
sigset_t mask;
struct sigaction sa;

sem_t sem[NUM_THREADS];
sem_t mute;

pthread_t tid[NUM_THREADS];
int state = 0;

static void handler(int sig, siginfo_t *si, void *uc)
{
    ret = sem_post(&sem[(state+1)%NUM_THREADS]);
        if (ret)
        {
            printf("Error in Sem Postn");
        }
        state++;
}

void *threadA(void *data_)
{  
    int i = 0, s,n,value;

    long int loopNum;
    int turn = (intptr_t)data_;
    struct timespec tval_result,tval_result2;

    int sid = syscall(SYS_gettid);
    FILE *fp;
    fp=fopen("ipc.out","a");    
    fprintf(fp,"thread_%d %dn",turn,sid);  
    fclose(fp); 

    int counter=0;

    while(1)
    {
        ret = sem_wait(&sem[turn]);
        if (ret)
        {
            printf("Error in Sem Postn");
        }
        //printf("Thread # -> %dn",turn);

        its.it_value.tv_sec = 0;
        its.it_value.tv_nsec = 14000;
        its.it_interval.tv_sec = 0;
        its.it_interval.tv_nsec = 0;

        ret = timer_settime(timerid, 0, &its, NULL);
        if ( ret < 0 )
            perror("timer_settime");

        // Some heavy work

    counter++;

    if(counter==100)
    break;
    }
    printf("finished %dn",turn);

}

int main(int argc, char *argv[])
{
    int data = 0;
    int err,i;

    sa.sa_flags = SA_RESTART;
    sa.sa_sigaction = handler;
    sigemptyset(&sa.sa_mask);
    sigaction(SIG, &sa, NULL);

    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = SIG;
    sev.sigev_value.sival_ptr = &timerid;
    ret = timer_create(CLOCKID, &sev, &timerid);
    if ( ret < 0 )
        perror("timer_create");

    sem_init(&sem[0], 0, 1); 
    for ( i = 1; i < NUM_THREADS; ++i)
        {
            sem_init(&sem[i], 0, 0); 
        }

    while(data < NUM_THREADS)
    {
        //create our threads
        err = pthread_create(&tid[data], NULL, threadA, (void *)(intptr_t)data);
        if(err != 0)
            printf("ncan't create thread :[%s]", strerror(err));

        data++;
    }

    pthread_exit(NULL);
}

According to this, this program should print

finished 0
finished 1
finished 2
finished 3
finished 4
finished 5
finished 6
finished 7
finished 8
finished 9

Some of the time it prints like this but most of the time, the program hangs up.

Advertisement

Answer

The signal handler has a race condition. As soon as sem_post is called one of the other threads can start running and its timer can fire before the current signal handler completes. Which will result in the signal handler being called again in another thread. At that point state has not been incremented by the first thread and hence the second signal handler invocation will end up calling sem_post on the wrong semaphore.

One way to fix this is to ensure state is incremented before calling sem_post:

static void handler(int sig, siginfo_t *si, void *uc)
{
    state++;
    ret = sem_post(&sem[(state)%NUM_THREADS]);
    if (ret)
    {
        printf("Error in Sem Postn");
    }
}

Note that this solution still contains one problem. It does not ensure that the printf calls are in the right sequence.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement