3

I was trying to create a timer that depends on the system clock. That means when the system time changes it should affect the expiry of this timer too. So I thought, creating a timer based on CLOCK_REALTIME should do the trick. But when this timer is armed to expire after 60 sec and when I advanced the system clock by 32 sec (using date command), the timer expired exactly after 60 sec. It didn't expire 32 sec earlier.

So I calculated the time elapsed for CLOCK_REALTIME and CLOCK_MONOTONIC clocks between the 2 timer expiry. It showed 92 secs for CLOCK_REALTIME and 60 sec for CLOCK_MONOTONIC, which made me surprised that a timer based on CLOCK_REALTIME didn't look for system clock changes. Can anyone please explain this behavior?

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

timer_t timerID;
struct timespec rt1, rt2, mt1, mt2;

void TimerCalback()
{
  clock_gettime(CLOCK_REALTIME, &rt1);
  clock_gettime(CLOCK_MONOTONIC, &mt1);
  printf("%lu sec elapsed for CLOCK_REALTIME\n", rt1.tv_sec - rt2.tv_sec);
  printf("%lu sec elapsed for CLOCK_MONOTONIC\n", mt1.tv_sec - mt2.tv_sec);

  rt2 = rt1;
  mt2 = mt1;

  time_t rawtime;
  struct tm * timeinfo;
  time ( &rawtime );
  timeinfo = localtime ( &rawtime );
  printf("REALTIME Timer Expired at %s\n", asctime (timeinfo));
}

void CreateTimer()
{
  struct sigaction sa;
  sa.sa_flags     = SA_SIGINFO;
  sa.sa_sigaction = TimerCalback;

  sigemptyset(&sa.sa_mask);
  sigaction(SIGRTMIN, &sa, NULL);
  struct sigevent te;
  memset(&te,0,sizeof(struct sigevent));

  te.sigev_notify          = SIGEV_SIGNAL;
  te.sigev_signo           = SIGRTMIN;
  te.sigev_value.sival_ptr = &timerID;
  timer_create(CLOCK_REALTIME, &te, &timerID);

  struct itimerspec its;
  its.it_value.tv_sec     = 1;
  its.it_value.tv_nsec    = 0;

  its.it_interval.tv_sec  = 60;
  its.it_interval.tv_nsec = 0;

  timer_settime(timerID, 0, &its, NULL);
}

void main()
{
  CreateTimer();
  while(1)
  {
    usleep(1);
  }
}

And I got this output. After the first expiry, I advanced system clock.

$ ./realtimeTimer
1407240463 sec elapsed for CLOCK_REALTIME
17747 sec elapsed for CLOCK_MONOTONIC
REALTIME Timer Expired at Tue Aug  5 17:37:43 2014

92 sec elapsed for CLOCK_REALTIME
60 sec elapsed for CLOCK_MONOTONIC
REALTIME Timer Expired at Tue Aug  5 17:39:15 2014  

Later searching on the web, I stumbled upon this man page which tells

All implementations support the system-wide real-time clock, which is identified by CLOCK_REALTIME. Its time represents seconds and nanoseconds since the Epoch. When its time is changed, timers for a relative interval are unaffected, but timers for an absolute point in time are affected.

Can this be the reason for this behavior?
What am I doing wrong/missing here?
Is there any way to make a timer expire at a particular system time?

Community
  • 1
  • 1
MrPavanayi
  • 887
  • 8
  • 17

2 Answers2

2

That same rule is found in the manpage for timer_settime, with some additional explanation:

By default, the initial expiration time specified in new_value->it_value is interpreted relative to the current time on the timer's clock at the time of the call. This can be modified by specifying TIMER_ABSTIME in flags, in which case new_value->it_value is interpreted as an absolute value as measured on the timer's clock; that is, the timer will expire when the clock value reaches the value specified by new_value->it_value. If the specified absolute time has already passed, then the timer expires immediately, and the overrun count (see timer_getoverrun(2)) will be set correctly.

If the value of the CLOCK_REALTIME clock is adjusted while an absolute timer based on that clock is armed, then the expiration of the timer will be appropriately adjusted. Adjustments to the CLOCK_REALTIME clock have no effect on relative timers based on that clock.

Yes, you've been setting a relative timer, and that's why it ignores the adjustment to the system time.

You might try the following:

struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);

struct itimerspec its;
its.it_value.tv_sec     = ts.tv_sec + 1;
its.it_value.tv_nsec    = ts.tv_nsec;
its.it_interval.tv_sec  = 60;
its.it_interval.tv_nsec = 0;
timer_settime(timerID, TIMER_ABSTIME, &its, NULL);
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Can you please explain why we should use `gettimeofday(&tv, NULL);`. Just using the `TIMER_ABSTIME` flag alone did the trick for me. And I think using `TIMER_ABSTIME` flag with `CLOCK_MONOTONIC` timer wont be having any effect at all. Am I right? – MrPavanayi Aug 06 '14 at 08:37
  • @MrPavanayi: With `TIMER_ABSTIME`, the first due time needs to be calculated from the current time. It's possible to set a time in the past without using the current time, but that will not reproduce the behavior of the code in the question. Actually it's better to use `clock_gettime` for this, fixed. – Ben Voigt Aug 06 '14 at 13:29
0

Try this:

struct timeval tv;
gettimeofday(&tv, NULL);

struct itimerspec its;
its.it_value.tv_sec     = 1;
its.it_value.tv_nsec    = 0;
its.it_interval.tv_sec  = tv_sec + 60;
its.it_interval.tv_nsec = tv_nsec;
timer_settime(timerID, TIMER_ABSTIME, &its, NULL);
ooga
  • 15,423
  • 2
  • 20
  • 21
  • Whilst this code snippet is welcome, and may provide some help, it would be [greatly improved if it included an explanation](//meta.stackexchange.com/q/114762) of *how* and *why* this solves the problem. Remember that you are answering the question for readers in the future, not just the person asking now! Please [edit] your answer to add explanation, and give an indication of what limitations and assumptions apply. – Toby Speight Mar 15 '17 at 14:32