-
-
Save Dobiasd/99f3b5b0632d57d1f66c94658760c986 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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