4

The task is to execute a function (say Processfunction()) every x (say x=10) seconds.

With below code, I'm able to call Processfunction() every x seconds.

Question: How to handle the case where the function takes more than 10 seconds to finish execution?

One way would be to have a flag to indicate the end of Processfunction() execution and check for it before calling Processfunction(). Is there a better way to do this ?


#include <pthread.h>
#include <unistd.h> // for sleep() and usleep()

void *timerthread(void *timer_parms) {  

  struct itimerspec new_value;
  int max_exp, fd;
  struct timespec now;
  uint64_t exp;
  ssize_t s;

  struct timer_params *p =(struct timer_params*)timer_parms;

  printf("starttimer Start\n");
  /* Create a CLOCK_REALTIME absolute timer with initial
     expiration and interval as specified in command line */
  if (clock_gettime(CLOCK_REALTIME, &now) == -1)
    handle_error("clock_gettime");

  new_value.it_value.tv_sec = now.tv_sec;
  new_value.it_value.tv_nsec = now.tv_nsec + p->tv_nsec;
  new_value.it_interval.tv_sec = p->tv_sec;
  new_value.it_interval.tv_nsec = p->tv_nsec;
  //max_exp = 5; //No of times

  fd = timerfd_create( CLOCK_REALTIME , 0);
  if (fd == -1)
    handle_error("timerfd_create");

  if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)
    handle_error("timerfd_settime");

  printf("timer started\n");

  while(1) // keep checking
  {
    s = read(fd, &exp, sizeof(uint64_t));
    if (s != sizeof(uint64_t))
      handle_error("read");
    Processfunction(); // Say after X seconds call this function
  }
  return NULL;
}

int main() {

  struct timer_params timer_params_obj;
  int res;void *thread_result;
  timer_params_obj.tv_sec = 10; 
  //timer_params_obj.tv_nsec = 10000000  ; //10ms
  timer_params_obj.tv_nsec = 0  ; 

  pthread_t pt;
  pthread_create(&pt, NULL, timerthread, &timer_params_obj);
  // thread is running and will call Processfunction() every 10 sec
}
moooeeeep
  • 31,622
  • 22
  • 98
  • 187
m4n07
  • 2,267
  • 12
  • 50
  • 65
  • you don't want to have two `Processfunction()` running at the same time? Would you rather want to delay the invocation or to skip an invocation in case of a longer execution time than the interval duration normally would allow? – moooeeeep Mar 17 '13 at 18:45
  • Yes,I don't want to have it running at same time. I want to skip/delay incase of longer execution time. – m4n07 Mar 19 '13 at 15:05

2 Answers2

6

Why do you need a timer for this?

You could just measure the execution time and take a sleep according to the relation of elapsed time to desired interval duration.

Example:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

int main() {
    srand(1);
    for (;;) {
        double interval = 10; /* seconds */

        /* start time */
        time_t start = time(NULL);

        /* do something */
        int duration = rand() % 13;
        printf("%2d seconds of work started at %s", duration, ctime(&start));
        sleep(duration);

        /* end time */
        time_t end = time(NULL);

        /* compute remaining time to sleep and sleep */
        double elapsed = difftime(end, start);
        int seconds_to_sleep = (int)(interval - elapsed);
        if (seconds_to_sleep > 0) { /* don't sleep if we're already late */
            sleep(seconds_to_sleep);
        }
    }
    return 0;
}

Output:

$ gcc test.c && ./a.out
 0 seconds of work started at Sun Mar 17 21:20:28 2013
 9 seconds of work started at Sun Mar 17 21:20:38 2013
11 seconds of work started at Sun Mar 17 21:20:48 2013
 4 seconds of work started at Sun Mar 17 21:20:59 2013
 1 seconds of work started at Sun Mar 17 21:21:09 2013
^C
moooeeeep
  • 31,622
  • 22
  • 98
  • 187
  • +1 - yup - that's the way I would do it. Timers are often inappropriately used :( – Martin James Mar 19 '13 at 12:19
  • Can this be done without linux specific code (boost or c++11) and without sleeps? The program has other things to do in the meantime. – Andrew Hundt Jul 30 '15 at 02:16
  • @AndrewHundt You can use Boost.Asio's deadline timers to asynchronously wait for a specific time. You might need to restructure your code a bit due to the asynchronous program flow (if that is not already the case). – moooeeeep Aug 05 '15 at 11:41
  • @moooeeeep yeah that is a good way to go about it too, I've used them before. However, I wanted to put the code in a very tiny example and I find the typical developer has a tough time grasping asio immediately so I wanted to avoid too much complexity. I put the solution I went with in another answer below in case someone else comes across this that can't use the accepted answer's dependencies. – Andrew Hundt Aug 06 '15 at 21:17
1

I had a nearly identical use case except I needed it to be cross platform C++11 and needed to do other tasks in the meantime rather than sleeping. Here is my code in case it is useful to someone:

// TimeT.h - Calls the passed function if the minimum time interval has elapsed
#ifndef TIME_T_H_
#define TIME_T_H_

#include <chrono>

/// Tracks the time since execution() was called, and only
/// Calls the passed function if the minimum time interval has elapsed
/// @see https://stackoverflow.com/questions/2808398/easily-measure-elapsed-time for the code I based this on
template<typename TimeT = std::chrono::milliseconds>
struct periodic
{
    periodic(TimeT duration = TimeT(1))
        : start(std::chrono::system_clock::now())
        , period_duration(duration)
        , previous_duration(TimeT::zero())
    {};

    template<typename F, typename ...Args>
    TimeT execution(F func, Args&&... args)
    {
        auto duration = std::chrono::duration_cast<TimeT>
                            (std::chrono::system_clock::now() - start);
        if (duration > previous_duration + period_duration)
        {
            std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
            previous_duration = duration;
        }
        return duration;
    }

    std::chrono::time_point<std::chrono::system_clock> start;
    // The minimum duration to wait before the function can be called again
    TimeT period_duration;
    // The duration between startup and the last time the function was called
    TimeT previous_duration;
};

#endif // TIME_T_H_

Here is an example of using it:

#include "TimeT.h"    

#include <iostream>
#include <thread>

int main(int argc, char* argv[])
{
     periodic<> callIfMinPeriodPassed(std::chrono::milliseconds(1));
     std::size_t num_periods;

     while(true)
     {
          callIfMinPeriodPassed.execution( [&num_periods]()
          {
                  std::cout << ++num_periods << "timesteps have passed\n"
          });
          // do other stuff here, this example will work
          // but spins at 100% CPU without the sleep
          std::this_thread::sleep_for(std::chrono::milliseconds(1));
     }
}

This implementation is based on a modification of this stackoverflow question about measuring elapsed time.

SCG82
  • 85
  • 1
  • 6
Andrew Hundt
  • 2,551
  • 2
  • 32
  • 64