2017-06-07 16:15:08 +08:00
|
|
|
#pragma once
|
|
|
|
|
2017-04-05 16:27:13 +08:00
|
|
|
#include <atomic>
|
|
|
|
#include <chrono>
|
|
|
|
#include <condition_variable>
|
|
|
|
#include <ctime>
|
2017-04-07 16:54:18 +08:00
|
|
|
#include <thread>
|
2017-04-05 16:27:13 +08:00
|
|
|
|
2017-04-07 17:33:24 +08:00
|
|
|
#include "utils/assert.hpp"
|
|
|
|
|
2017-04-05 16:27:13 +08:00
|
|
|
/**
|
2017-04-07 17:33:24 +08:00
|
|
|
* Class used to run scheduled function execution. Class is templated with
|
2017-04-07 16:54:18 +08:00
|
|
|
* mutex class TMutex which is used to synchronize threads. Default template
|
2017-04-05 16:27:13 +08:00
|
|
|
* value is std::mutex.
|
|
|
|
*/
|
|
|
|
template <typename TMutex = std::mutex>
|
|
|
|
class Scheduler {
|
|
|
|
public:
|
2017-04-07 17:33:24 +08:00
|
|
|
Scheduler() {}
|
2017-04-05 16:27:13 +08:00
|
|
|
/**
|
2017-05-30 16:26:09 +08:00
|
|
|
* @param pause - Duration between two function executions. If function is
|
|
|
|
* still running when it should be ran again, it will not be ran and next
|
|
|
|
* start time will be increased to current time plus pause.
|
|
|
|
* @param f - Function
|
|
|
|
* @Tparam TRep underlying arithmetic type in duration
|
|
|
|
* @Tparam TPeriod duration in seconds between two ticks
|
2017-04-05 16:27:13 +08:00
|
|
|
*/
|
2017-05-30 16:26:09 +08:00
|
|
|
template <typename TRep, typename TPeriod>
|
|
|
|
void Run(const std::chrono::duration<TRep, TPeriod> &pause,
|
|
|
|
const std::function<void()> &f) {
|
2017-04-07 17:33:24 +08:00
|
|
|
debug_assert(is_working_ == false, "Thread already running.");
|
2017-06-07 16:15:08 +08:00
|
|
|
debug_assert(pause > std::chrono::seconds(0), "Pause is invalid.");
|
2017-04-07 17:33:24 +08:00
|
|
|
is_working_ = true;
|
|
|
|
thread_ = std::thread([this, pause, f]() {
|
2017-04-07 16:54:18 +08:00
|
|
|
auto start_time = std::chrono::system_clock::now();
|
|
|
|
for (;;) {
|
|
|
|
if (!is_working_.load()) break;
|
2017-04-05 16:27:13 +08:00
|
|
|
|
2017-04-07 17:33:24 +08:00
|
|
|
f();
|
2017-04-05 16:27:13 +08:00
|
|
|
|
2017-04-07 16:54:18 +08:00
|
|
|
std::unique_lock<std::mutex> lk(mutex_);
|
2017-04-05 16:27:13 +08:00
|
|
|
|
2017-04-07 16:54:18 +08:00
|
|
|
auto now = std::chrono::system_clock::now();
|
2017-04-07 17:33:24 +08:00
|
|
|
while (now >= start_time) start_time += pause;
|
2017-04-05 16:27:13 +08:00
|
|
|
|
2017-04-07 16:54:18 +08:00
|
|
|
condition_variable_.wait_for(
|
|
|
|
lk, start_time - now, [&] { return is_working_.load() == false; });
|
|
|
|
lk.unlock();
|
|
|
|
}
|
2017-04-05 16:27:13 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-04-07 17:33:24 +08:00
|
|
|
/**
|
|
|
|
* @brief Stops the thread execution. This is a blocking call and may take as
|
|
|
|
* much time as one call to the function given previously to Run takes.
|
|
|
|
*/
|
|
|
|
void Stop() {
|
2017-04-07 16:54:18 +08:00
|
|
|
is_working_.store(false);
|
2017-04-05 16:27:13 +08:00
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lk(mutex_);
|
|
|
|
condition_variable_.notify_one();
|
|
|
|
}
|
|
|
|
if (thread_.joinable()) thread_.join();
|
|
|
|
}
|
|
|
|
|
2017-04-07 17:33:24 +08:00
|
|
|
~Scheduler() { Stop(); }
|
|
|
|
|
2017-04-05 16:27:13 +08:00
|
|
|
private:
|
|
|
|
/**
|
|
|
|
* Variable is true when thread is running.
|
|
|
|
*/
|
2017-04-07 17:33:24 +08:00
|
|
|
std::atomic<bool> is_working_{false};
|
|
|
|
|
2017-04-05 16:27:13 +08:00
|
|
|
/**
|
|
|
|
* Mutex used to synchronize threads using condition variable.
|
|
|
|
*/
|
|
|
|
TMutex mutex_;
|
|
|
|
|
|
|
|
/**
|
2017-04-07 16:54:18 +08:00
|
|
|
* Condition variable is used to stop waiting until the end of the
|
2017-04-07 17:33:24 +08:00
|
|
|
* time interval if destructor is called.
|
2017-04-05 16:27:13 +08:00
|
|
|
*/
|
|
|
|
std::condition_variable condition_variable_;
|
2017-04-07 17:33:24 +08:00
|
|
|
|
2017-04-05 16:27:13 +08:00
|
|
|
/**
|
|
|
|
* Thread which runs function.
|
|
|
|
*/
|
|
|
|
std::thread thread_;
|
|
|
|
};
|