1

I was thinking about it and I was curious as to how one would code an efficient repeating alarm clock in C? Would you set an alarm time and then offset the time with the ms time equivalent of a day (or two days or a week depending on how often it needed to repeat)? And then poll periodically to see if the times are equal? That sounds like a very inefficient solution, to me at least. I was am interested in how an alarm clock would work programatically.

  • What sort of hardware and O/S? – Jay Elston Jan 28 '16 at 21:18
4

There's numerous ways to acheive this, all depending on what kind of environment you're working with. Another concern is of course the latency you can afford.

If you are working on a modern operating system, you most likely have an API that will put your thread to sleep until the given time has arrived. Posix has nanosleep() and C11 has the thread_sleep() function to do this.

If you're on more primitive operating systems or programming directly for embedded hardware you may have to resort to waiting for periodic interrupts, or even using busy loops.

Update: To handle multiple alarms using the thread_sleep() approach, you could for instance store all coming alarms in a sorted list. Sleep until the first alarm triggers, do what you're supposed to and then calculate the time until the next alarm and go back to sleep. This approach uses only one timer thread, which will be very efficient as long as it satisfies your latency requirements.

  • 1
    Most systems have at least one external interrupt somewhere, so on embedded, the best solution can easily be to spend $1 on a real-time-clock chip and program it to raise an interrupt at a certain time. And then, if you have nothing else to do, put your CPU entirely to sleep. :) – hobbs Jan 23 '16 at 19:38
  • I think for an application I am thinking of, purely programmatic, an external interrupt would not be applicable. If one were to be working on a Linux system, you suggest it would work best by instantiating multiple threads to handle different alarms, such as 2:30am , 12:30 PM, etc? Where I am having a block is if you tell the thread to sleep, a) how long do you tell it to sleep for, and b) if it is waiting on the queue, how would you send it to the termination state instead of wait state ? -well I suppose you could have a Boolean check to make sure the alarm should still be active. – cg14 Jan 23 '16 at 19:48
  • You could dig out the source to cron or at for an example.. – pjc50 Jan 23 '16 at 21:02
  • 1
    @cg14, see updated answer. You could of course use one thread for each alarm, but if efficiency is a goal I wouldn't do that. (I probably wouldn't anyways :) – harald Jan 23 '16 at 21:36
  • Let's say a user decides to add another alarm to the system that falls in between the last alarm that sounded and the next alarm that was in the queue. If the thread is asleep, then wouldn't the newest alarm get overlooked? It seems some form of interrupt would be needed for added alarms? – cg14 Jan 24 '16 at 1:20
1

for a repeating timer.

Use setitimer()

here is info from the man page

    NAME
           getitimer, setitimer - get or set value of an interval timer

    SYNOPSIS
           #include <sys/time.h>

           int getitimer(int which, struct itimerval *curr_value);
           int setitimer(int which, const struct itimerval *new_value,
                         struct itimerval *old_value);

    DESCRIPTION
           The  system  provides  each  process  with  three interval timers, each
           decrementing in a distinct time domain.  *When any timer expires, a sig‐
           nal is sent to the process*, and the timer (potentially) restarts.

           ITIMER_REAL    decrements in real time, and delivers SIGALRM upon expi‐
                          ration.

           ITIMER_VIRTUAL decrements only  when  the  process  is  executing,  and
                          delivers SIGVTALRM upon expiration.

           ITIMER_PROF    decrements  both  when the process executes and when the
                          system is executing on behalf of the  process.   Coupled
                          with  ITIMER_VIRTUAL, this timer is usually used to pro‐
                          file the time spent by the application in user and  ker‐
                          nel space.  SIGPROF is delivered upon expiration.

           Timer values are defined by the following structures:

               struct itimerval {
                   struct timeval it_interval; /* next value */
                   struct timeval it_value;    /* current value */
               };

               struct timeval {
                   time_t      tv_sec;         /* seconds */
                   suseconds_t tv_usec;        /* microseconds */
               };

           The  function  getitimer() fills the structure pointed to by curr_value
           with the current setting for the  timer  specified  by  which  (one  of
           ITIMER_REAL,  ITIMER_VIRTUAL, or ITIMER_PROF).  The element it_value is
           set to the amount of time remaining on the timer, or zero if the  timer
           is disabled.  Similarly, it_interval is set to the reset value.

           The  function  setitimer()  sets  the  specified  timer to the value in
           new_value.  If old_value is non-NULL, the old value  of  the  timer  is
           stored there.

           Timers decrement from it_value to zero, generate a signal, and reset to
           it_interval.  A timer which is set to zero (it_value  is  zero  or  the
           timer expires and it_interval is zero) stops.

           Both  tv_sec and tv_usec are significant in determining the duration of
           a timer.

           Timers will never expire before the requested time, but may expire some
           (short)  time  afterward,  which depends on the system timer resolution
           and on the system load; see time(7).  (But see BUGS below.)  Upon expi‐
           ration,  a  signal will be generated and the timer reset.  If the timer
           expires while the process is active (always  true  for  ITIMER_VIRTUAL)
           the signal will be delivered immediately when generated.  Otherwise the
           delivery will be offset by a small time dependent on the  system  load‐
           ing.

    RETURN VALUE
           On  success,  zero is returned.  On error, -1 is returned, and errno is
           set appropriately.

    ERRORS
           EFAULT new_value, old_value, or curr_value is not valid a pointer.

           EINVAL which is not one of ITIMER_REAL, ITIMER_VIRTUAL, or ITIMER_PROF;
                  or  (since Linux 2.6.22) one of the tv_usec fields in the struc‐
                  ture pointed to by new_value contains a value outside the  range
                  0 to 999999.

    CONFORMING TO
           POSIX.1-2001,  SVr4,  4.4BSD  (this  call  first  appeared  in 4.2BSD).
           POSIX.1-2008 marks getitimer() and setitimer()  obsolete,  recommending
           the  use  of  the POSIX timers API (timer_gettime(2), timer_settime(2),
           etc.) instead.

    NOTES
           A child created via fork(2) does  not  inherit  its  parent's  interval
           timers.  Interval timers are preserved across an execve(2).

           POSIX.1 leaves the interaction between setitimer() and the three inter‐
           faces alarm(2), sleep(3), and usleep(3) unspecified.

           The standards are silent on the meaning of the call:

               setitimer(which, NULL, &old_value);

           Many systems (Solaris, the BSDs, and  perhaps  others)  treat  this  as
           equivalent to:

               getitimer(which, &old_value);

           In  Linux,  this  is treated as being equivalent to a call in which the
           new_value fields are zero; that is, the timer is disabled.   Don't  use
           this Linux misfeature: it is nonportable and unnecessary.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.