Skip to content

Instantly share code, notes, and snippets.

@Dobiasd
Last active May 10, 2017 13:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Dobiasd/99f3b5b0632d57d1f66c94658760c986 to your computer and use it in GitHub Desktop.
Save Dobiasd/99f3b5b0632d57d1f66c94658760c986 to your computer and use it in GitHub Desktop.
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <iostream>
#include <thread>
// Executes a function f in a fixed interval,
// i.e. an average timespan between two consecutive calls of f,
// given in microseconds.
// f is a unary function, taking the time delta (in microseconds)
// between the last and the current call as its argument.
// In case of a delay outdated calls are be executed immediately.
// So the average executation time of f should be way shorter
// than the requested interval.
// Call ticker::start() to run.
// The ticker stops when ticker::stop() is called
// or the instance runs out of scope.
//
// Example usage:
//
// void say_hi(std::int64_t)
// {
// std::cout << "hi " << std::endl;
// }
// int main()
// {
// ticker hi_ticker(say_hi, 2 * 1000 * 1000);
// hi_ticker.start();
// std::this_thread::sleep_for(std::chrono::milliseconds(4500));
// }
class ticker
{
public:
typedef std::function<void(std::int64_t)> function;
ticker(const function& f, std::int64_t interval_us) :
f_(f),
interval_us_(interval_us),
control_mutex_(),
is_running_(false),
thread_(),
stop_mutex_()
{
}
bool is_running()
{
std::lock_guard<std::mutex> lock(control_mutex_);
return is_running_;
}
bool start()
{
std::lock_guard<std::mutex> lock(control_mutex_);
if (is_running_)
return false;
stop_mutex_.lock();
thread_ = std::thread([this]() { thread_function(); });
is_running_ = true;
return true;
}
bool stop()
{
std::lock_guard<std::mutex> lock(control_mutex_);
if (!is_running_)
return false;
stop_mutex_.unlock();
if (thread_.joinable())
{
thread_.join();
thread_ = std::thread();
}
is_running_ = false;
return true;
}
~ticker()
{
stop();
}
private:
void thread_function()
{
auto last_wake_up_time = std::chrono::steady_clock::now();
auto last_time = last_wake_up_time;
bool quit = false;
while (!quit)
{
const auto wake_up_time =
last_wake_up_time + std::chrono::microseconds{ interval_us_ };
const auto sleep_time =
wake_up_time - std::chrono::steady_clock::now();
if (stop_mutex_.try_lock_for(sleep_time))
{
stop_mutex_.unlock();
quit = true;
}
const auto current_time = std::chrono::steady_clock::now();
const auto elapsed = current_time - last_time;
last_wake_up_time = wake_up_time;
last_time = current_time;
const auto elapsed_us =
std::chrono::duration_cast<std::chrono::microseconds>(
elapsed).count();
try
{
f_(elapsed_us);
}
catch (...)
{
}
}
}
const function f_;
const std::int64_t interval_us_;
std::mutex control_mutex_;
bool is_running_;
std::thread thread_;
std::timed_mutex stop_mutex_;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment