Search code examples
ctimerposixglibcubuntu-15.04

POSIX timer fail after many repeats


Somebody can explain why timer fail with SIGSEGV after 5-7 iteration?

It happens in both cases: with synchronization and without. Operating system is Ubuntu 15.04, Ubuntu GLIBC 2.21-0ubuntu4.

void timer_thread (sigval signal_value) {
    printf ("Timer callback!\n");
}

int main(int argc, char* argv[]) {

    const int TIMER_COUNT = 300;

    for (int i = 0; i < 10000; i++) {
        int status = 0;

        timer_t timer_id[TIMER_COUNT] = {};
        memset(&timer_id[0], 0, sizeof(timer_t)*TIMER_COUNT);

        for (int j = 0; j < TIMER_COUNT; j++) {

            struct itimerspec ts = {};
            struct sigevent se = {};

            memset(&ts, 0, sizeof(itimerspec));
            memset(&se, 0, sizeof(sigevent));

            se.sigev_notify = SIGEV_THREAD;
            se.sigev_value.sival_int = j;
            se.sigev_notify_function = timer_thread;

            // Specify a repeating timer that fires each 100000 nanosec.
            memset(&ts, 0, sizeof(ts));
            ts.it_value.tv_nsec = 100000;
            ts.it_interval.tv_nsec = 100000;

            status = timer_create(CLOCK_REALTIME, &se, &timer_id[j]);
            assert(!status && "Create timer");

            status = timer_settime(timer_id[j], 0, &ts, 0);
            assert(!status && "Set timer");
        }

        for (int j = 0; j < TIMER_COUNT; j++) {
            usleep(100);
            //stop and delete

            status = timer_delete(timer_id[j]);
            assert(!status && "Fail delete timer");
        }
    }
    printf("Success!\n");
    return 0;
}

GDB back trace:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  __pthread_create_2_1 (newthread=newthread@entry=0x7f00e9817e28, attr=attr@entry=0x11c47e8, start_routine=start_routine@entry=0x7f00e93f6eb0 <timer_sigev_thread>, arg=<optimized out>)
    at pthread_create.c:711
711 pthread_create.c: No such file or directory.
(gdb) bt
#0  __pthread_create_2_1 (newthread=newthread@entry=0x7f00e9817e28, attr=attr@entry=0x11c47e8, start_routine=start_routine@entry=0x7f00e93f6eb0 <timer_sigev_thread>, arg=<optimized out>)
    at pthread_create.c:711
#1  0x00007f00e93f6e7a in timer_helper_thread (arg=<optimized out>) at ../sysdeps/unix/sysv/linux/timer_routines.c:125
#2  0x00007f00e91db6aa in start_thread (arg=0x7f00e9818740) at pthread_create.c:333
#3  0x00007f00e866feed in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109

Build command-line: /usr/bin/c++ -lrt -lpthread -g ./main.cc

Full code posix timer with synchronization

Full code posix timer with sleep


Solution

  • the following code actually runs, does not crash, cleanly compiles

    Notice the expanded time interval for each timer, this is so some 300 timers have time to call printf() and return.

    BTW: calling printf() in a signal handler is a very bad idea

    #include <stdio.h>  // printf()
    #include <stdlib.h> // exit(), EXIT_FAILURE
    #include <signal.h>
    #include <time.h>
    #include <unistd.h> // sleep()
    #include <string.h> // memset()
    
    #define TIMER_COUNT (300)
    
    #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                               } while (0)
    
    void timer_thread (union sigval sigev_value)
    {
        (void)sigev_value;
        static int count = 0;
        count++;
        fprintf ( stdout, "Timer callback count: %d\n", count);
    }
    
    int main( void )
    {
        timer_t timer_id[TIMER_COUNT];
        memset(timer_id, '\0', sizeof(timer_t)*TIMER_COUNT);
    
        struct itimerspec ts;
        struct sigevent se;
    
        for (size_t j = 0; j < TIMER_COUNT; j++)
        {
            memset(&ts, '\0', sizeof(struct itimerspec));
            memset(&se, '\0', sizeof(struct sigevent));
    
            se.sigev_notify = SIGEV_THREAD;
            se.sigev_value.sival_int = (int)j;
            se.sigev_notify_function = timer_thread;
    
            // Specify a repeating timer that fires each 2 second.
            ts.it_interval.tv_sec = 2;
            ts.it_interval.tv_nsec = 0;
            ts.it_value.tv_sec = ts.it_interval.tv_sec;
            ts.it_value.tv_nsec = ts.it_interval.tv_nsec;
    
            if( -1 == timer_create(CLOCK_REALTIME, &se, &timer_id[j]) )
                errExit( "timer_create failed" );
    
            if( -1 == timer_settime(timer_id[j], 0, &ts, NULL) )
                errExit("timer_settime failed");
        }
    
        sleep(10);
    
        for (int j = 0; j < TIMER_COUNT; j++)
        {
            //stop and delete
            fprintf( stdout, "stopping timer: %d, with ID:  %lu\n", j, (size_t)timer_id[j]);
            ts.it_value.tv_sec = 0;
            ts.it_value.tv_nsec = 0;
            ts.it_interval.tv_sec = 0;
            ts.it_interval.tv_nsec = 0;
            if( -1 == timer_settime( timer_id[j], 0, &ts, NULL) )
                errExit("timer_settime (to disable timer) failed" );
    
            fprintf( stdout, "deleting timer: %d, with ID:  %lu\n", j, (size_t)timer_id[j]);
            if( -1 == timer_delete(timer_id[j]) )
                errExit("timer_delete failed" );
        }
    
        printf("Success!\n");
        return 0;
    } // end function: main