0

This is probably a very basic question, but I'm using the code below to run a simple alarm. It works as I want it to, but I'm wondering if it's at all possible to run multiple alarms simultaneously that each trigger a different function when complete. Is there a way to do that?

#include <signal.h>
#include <sys/time.h>
#include <stdio.h>
#include <time.h>

void alarm_handler(int signum){
    printf("five seconds passed!!\n");
}
int main(){
    signal(SIGALRM, alarm_handler);
    alarm(5);

    pause();
    return 0;
}
Richard
  • 56,349
  • 34
  • 180
  • 251
Connor Olsen
  • 145
  • 1
  • 14
  • Have you experimented with it? – Richard May 22 '17 at 21:07
  • You can often use OS specific features. On Linux you will find the `timerfd_create` and on BSD you can use any open file descriptor (i.e. one opened using `mkstemp`) to set a timer and than poll on the file descriptors... Do be aware that timers are an OS wide limited resource. You might not always have them available. – Myst May 22 '17 at 21:14
  • 1
    You could use timer_create (POSIX) instead of alarm to achieve your goal easily. – Petr Skocik May 22 '17 at 21:14
  • 1
    @PSkocik , my experience shows that `timer_create` isn't as portable as we'd wish. For example, I doesn't seem to exist on macOS. Though I may be wrong (I often am). – Myst May 22 '17 at 21:27
  • Would it work on a Linux Kernel? – Connor Olsen May 22 '17 at 21:30
  • 1
    @ConnorOlsen On Linux 2.6 and newer, it should. – Petr Skocik May 22 '17 at 21:33

2 Answers2

3

No. According to this source:

Alarm requests are not stacked; only one SIGALRM generation can be scheduled in this manner. If the SIGALRM signal has not yet been generated, the call shall result in rescheduling the time at which the SIGALRM signal is generated.

One alternative is to create a priority queue in which you put your tasks and then always schedule your alarm for the time difference between the current time and the task at the top of the queue.

But be sure to look at this SO question: you're limited in the kinds of things you can do inside your signal handler.

Richard
  • 56,349
  • 34
  • 180
  • 251
1

Not with alarm(2), however, you can use POSIX timers to achieve your goal.

Either you can set each timer to run a different signal when it expires or you can use a single signal and pass a pointer to the timer with it via siginfo_t, based on which you can then decide what to do in the handler.

Example:

#include <signal.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

static timer_t tmid0, tmid1;

static void hndlr(int Sig, siginfo_t *Info, void *Ptr)
{
    if(Info->si_value.sival_ptr == &tmid0)
        write(2, "tmid0\n", 6);
    else{
        write(2, "tmid1\n", 6);
        _exit(0);
    }
}


int main()
{
    int r = EXIT_SUCCESS;

    sigaction(SIGALRM, &(struct sigaction){ .sa_sigaction = hndlr, .sa_flags=SA_SIGINFO }, 0);
    printf("%p %p\n", (void*)&tmid0, (void*)&tmid1);

    struct sigevent sev = { .sigev_notify = SIGEV_SIGNAL, .sigev_signo = SIGALRM };

    sev.sigev_value.sival_ptr = &tmid0; 
    if(0>timer_create(CLOCK_REALTIME,&sev,&tmid0))
        { r=EXIT_FAILURE; goto out; }

    sev.sigev_value.sival_ptr = &tmid1; 
    if(0>timer_create(CLOCK_REALTIME,&sev,&tmid1))
        { r=EXIT_FAILURE; goto out; }

    if(0>timer_settime(tmid0, 0, &(struct  itimerspec const){ .it_value={1,0} } , NULL) )
        { r=EXIT_FAILURE; goto out; }
     //tmid0 expires after 1 second

    if(0>timer_settime(tmid1, 0, &(struct  itimerspec const){ .it_value={3,0} } , NULL) )
        { r=EXIT_FAILURE; goto out; }
     //tmid1 expires after 3 seconds


    for(;;)
        pause();

out:
    if(r)
        perror(0); 
    return r;
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142