1

Timers seem to disarm after process is resumed (SIGCONT) ONLY when interval is too small. I use timer_create with CLOCK_REALTIME.

My linux distribution is ubuntu 20.04.1

This is the code that shows the problem

    #include <stdio.h>
    #include <unistd.h>
    #include <signal.h>
    #include <errno.h>
    #include <time.h>
    #include <stdlib.h>
    #include <memory.h>

    volatile siginfo_t sigInfo;

    void childFunction();
    void parentFunction(pid_t pid);
    void ignoreChild();
    void signalFunction(int signo, siginfo_t* SI, void* data);

    int main(int argc, char* argv[])
    {
        ignoreChild();
        pid_t pid;
        pid = fork();

        switch(pid)
        {
            case -1:
            {
                perror("Error in fork!\n");
                exit(EXIT_FAILURE);
            }
            case 0:
            {
                childFunction();
                exit(EXIT_SUCCESS);
            }
            default:
            {
                parentFunction(pid);
                break;
            }
        }

        return 0;
    }

    void signalFunction(int signo, siginfo_t* SI, void* data)
    {
        sigInfo = *SI;
    }

    void parentFunction(pid_t pid)
    {
        struct sigaction sa;
        struct timespec sleepTime = { .tv_sec = 1, .tv_nsec = 0};
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_SIGINFO;
        sa.sa_sigaction = signalFunction;
        if (sigaction(SIGCHLD, &sa, NULL) == -1)
        {
            perror("Error in sigaction!\n");
            exit(EXIT_FAILURE);
        }
        //---- for blocking
        sigset_t set = {0};
        if (sigaddset(&set, SIGCHLD) == -1)
        {
            perror("Error in sigaddset!\n");
            exit(EXIT_FAILURE);
        }
        while (1)
        {
            if (pause() == -1)
            {
                if (errno == EINTR)
                {
                    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1)
                    {
                        perror("Error in sigprocmask!\n");
                        exit(EXIT_FAILURE);
                    }

                    if (sigInfo.si_status == SIGSTOP)
                    {
                        printf("Child received SIG_STOP signal\n");
                        nanosleep(&sleepTime, NULL);
                        kill(pid, SIGCONT);
                    }
                    if (sigInfo.si_status == SIGCONT)
                    {
                        printf("Child received SIGCONT signal!\n");
                    }
                    if (sigInfo.si_code == CLD_EXITED)
                    {
                        printf("Child is dead!\n");
                        break;
                    }
                    if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1)
                    {
                        perror("Error in sigprocmask2!\n");
                        exit(EXIT_FAILURE);
                    }
                }
            }
        }
    }

    void ignoreChild()
    {
        struct sigaction sa;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_NOCLDWAIT;
        if (sigaction(SIGCHLD, &sa, NULL) == -1)
        {
            perror("Error in sigaction!\n");
            exit(EXIT_FAILURE);
        }
    }

    void childFunction()
    {
        struct sigevent sev;
        sev.sigev_notify = SIGEV_SIGNAL;
        sev.sigev_signo = SIGSTOP;
        struct timespec timeForTimer = { .tv_sec = 1, .tv_nsec = 0}; //     set time for timer here!
        struct itimerspec ts = { .it_interval = timeForTimer, .it_value = timeForTimer };
        timer_t timer;
        if (timer_create(CLOCK_REALTIME, &sev,&timer) == -1)
        {
            perror("Error in timer_create!\n");
            exit(EXIT_FAILURE);
        }
        if (timer_settime(timer, 0, &ts, NULL) == -1)
        {
            perror("Error in timer_settime!\n");
            exit(EXIT_FAILURE);
        }
        struct timespec timeToWaitInLoop = { .tv_sec = 0, .tv_nsec = 300000000};
        for (int i=0; i<1000; ++i)
        {
            printf("I'm working!\n");
            nanosleep(&timeToWaitInLoop,NULL);
        }
    }

When I set timeForTimer to 3 seconds the program works correctly, for 2 seconds it still works but for 1 second it doesn't. To compile:

gcc -Wall main.c -lrt

This is shorter example

    #include <stdio.h>
    #include <signal.h>
    #include <time.h>
    #include <stdlib.h>
    #include <memory.h>
    #include <unistd.h>
    
    int main(int argc, char* argv[])
    {
        struct sigevent sev;
        sev.sigev_notify = SIGEV_SIGNAL;
        sev.sigev_signo = SIGSTOP;
        struct timespec timeForTimer = { .tv_sec = 1, .tv_nsec = 0}; // set time for timer here!
        struct itimerspec ts = { .it_interval = timeForTimer, .it_value = timeForTimer };
        timer_t timer;
        if (timer_create(CLOCK_REALTIME, &sev,&timer) == -1)
        {
            perror("Error in timer_create!\n");
            exit(EXIT_FAILURE);
        }
        if( timer_settime(timer, 0, &ts, NULL) == -1)
        {
            perror("Error in timer_settime!\n");
            exit(EXIT_FAILURE);
        }
        struct timespec timeToWaitInLoop = { .tv_sec = 0, .tv_nsec = 300000000};
        for (int i=0; i<1000; ++i)
        {
            printf("I'm working! (PID) %d\n", getpid());
            nanosleep(&timeToWaitInLoop,NULL);
        }
    }

In above example only one SIGSTOP signal occured. After sending SIGCONT with command kill -18 <pid> there was no more SIGSTOP signals.It looks like my timer disarmed.

Mikołaj Głodziak
  • 4,775
  • 7
  • 28
K.Wierciak
  • 11
  • 3
  • 2
    We would be prepared to address this question in light of a [mre]. As it is, you really haven't given us much to work with. – John Bollinger Jan 20 '21 at 17:57
  • 1
    Ok, the new, shorter example is the kind of thing we're looking for in a MRE. Thank you. – John Bollinger Jan 24 '21 at 16:55
  • Your short example works for me as I think is expected. When first started, it prints the "I'm working" message four times before stopping itself. Each time thereafter that I send it a `SIGCONT`, it prints the message between zero and four more times (most often twice) before stopping itself again. – John Bollinger Jan 24 '21 at 17:40
  • I don't find any clear documentation of the effect of the timer firing while the process is stopped, so perhaps there is some variation among implementations in this area. If your objective is for the process to run for short intervals before stopping itself, then it might be more reliable to catch `SIGCONT` and handle it by rearming the timer, instead of relying on the timer to rearm itself. (`timer_settime()` is async-signal safe.) Manually re-arming would also give you a consistent duration for each running period. – John Bollinger Jan 24 '21 at 17:49

0 Answers0