Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setting the execution rate of while loop in a C++ code for real time synchronization

I am doing a real_time simulation using a .cpp source code. I have to take a sample every 0.2 seconds (200 ms) ... There is a while loop that takes a sample every time step... I want to synchronize the execution of this while loop to get a sample every (200 ms) ... How should I modify the while loop ?

while (1){
          // get a sample every 200 ms
         }
like image 339
user2056096 Avatar asked Feb 18 '13 20:02

user2056096


4 Answers

Simple and accurate solution with std::this_thread::sleep_until:

#include "date.h"
#include <chrono>
#include <iostream>
#include <thread>

int
main()
{
    using namespace std::chrono;
    using namespace date;
    auto next = steady_clock::now();
    auto prev = next - 200ms;
    while (true)
    {
        // do stuff
        auto now = steady_clock::now();
        std::cout << round<milliseconds>(now - prev) << '\n';
        prev = now;

        // delay until time to iterate again
        next += 200ms;
        std::this_thread::sleep_until(next);
    }
}

"date.h" isn't needed for the delay part. It is there to provide the round<duration> function (which is now in C++17), and to make it easier to print out durations. This is all under "do stuff", and doesn't matter for the loop delay.

Just get a chrono::time_point, add your delay to it, and sleep until that time_point. Your loop will on average stay true to your delay, as long as your "stuff" takes less time than your delay. No other thread needed. No timer needed. Just <chrono> and sleep_until.

This example just output for me:

200ms
205ms
200ms
195ms
205ms
198ms
202ms
199ms
196ms
203ms
...
like image 105
Howard Hinnant Avatar answered Oct 17 '22 01:10

Howard Hinnant


what you are asking is tricky, unless you are using a real-time operating system.

However, Boost has a library that supports what you want. (There is, however, no guarantee that you are going to be called exactly every 200ms.

The Boost ASIO library is probably what you are looking for though, here is code from their tutorial:

//
// timer.cpp
// ~~~~~~~~~
//
// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

int main()
{
  boost::asio::io_service io;

  boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));
  t.wait();

  std::cout << "Hello, world!\n";

  return 0;
}

link is here: link to boost asio.

You could take this code, and re-arrange it like this

#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

int main()
{
  boost::asio::io_service io;

  while(1)
  {
    boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));

    // process your IO here - not sure how long your IO takes, so you may need to adjust your timer

    t.wait();
  }    

  return 0;
}

There is also a tutorial for handling the IO asynchronously on the next page(s).

like image 30
David Hope Avatar answered Oct 17 '22 03:10

David Hope


The offered answers show you that there are tools available in Boost to help you accomplish this. My late offering illustrates how to use setitimer(), which is a POSIX facility for iterative timers.

You basically need a change like this:

while (1){
          // wait until 200 ms boundary
          // get a sample
         }

With an iterative timer, the fired signal would interrupt any blocked signal call. So, you could just block on something forever. select will do fine for that:

while (1){
          int select_result = select(0, 0, 0, 0, 0);
          assert(select_result < 0 && errno == EINTR);
          // get a sample
         }

To establish an interval timer for every 200 ms, use setitimer(), passing in an appropriate interval. In the code below, we set an interval for 200 ms, where the first one fires 150 ms from now.

struct itimerval it = { { 0, 200000 }, { 0, 150000 } };
if (setitimer(ITIMER_REAL, &it, 0) != 0) {
    perror("setitimer");
    exit(EXIT_FAILURE);
}

Now, you just need to install a signal handler for SIGALRM that does nothing, and the code is complete.

You can follow the link to see the completed example.

If it is possible for multiple signals to be fired during the program execution, then instead of relying on the interrupted system call, it is better to block on something that the SIGALRM handler can wake up in a deterministic way. One possibility is to have the while loop block on read of the read end of a pipe. The signal handler can then write to the write end of that pipe.

void sigalarm_handler (int)
{
    if (write(alarm_pipe[1], "", 1) != 1) {
        char msg[] = "write: failed from sigalarm_handler\n";
        write(2, msg, sizeof(msg)-1);
        abort();
    }
}

Follow the link to see the completed example.

like image 1
jxh Avatar answered Oct 17 '22 03:10

jxh


#include <thread>
#include <chrono>
#include <iostream>

int main() {
    std::thread timer_thread;
    while (true) {
        timer_thread = std::thread([](){
            std::this_thread::sleep_for (std::chrono::seconds(1));
         });

         // do stuff 
         std::cout << "Hello World!" << std::endl;

         // waits until thread has "slept" 
         timer_thread.join();

         // will loop every second unless the stuff takes longer than that.
    }

    return 0;
}
like image 1
Charles Earp Avatar answered Oct 17 '22 02:10

Charles Earp