I need to create a class to replicate the functionality of a callback timer from a library I no longer have access to.
Basically, spawn an asynchronous thread that executes some function after a timeout is reached. However, if some condition is met before the timeout, the function is not executed, and the timer is cancelled.
I am not sure how to "stop" the timer based on some condition.
This is what I have so far (based on the answer to this post C++ Non-blocking async timer by wally)
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
#include <mutex>
#include <condition_variable>
class Timer
{
public:
Timer(size_t time, const std::function<void(void)>& callback) :
time(std::chrono::milliseconds(time)),
callback(callback) {}
~Timer()
{
if (started)
{
wait_thread.join();
started = false;
}
}
// TODO: Implement stop()
void stop()
{
// ???
}
// TODO: Implement start()
void start()
{
if (!started)
{
started = true;
wait_thread = std::thread{ [this]() { wait_then_call(); } };
}
else
{
std::cout << "Thread " << wait_thread.get_id() << " already been started! " << std::endl;
}
}
private:
void wait_then_call()
{
std::unique_lock<std::mutex> lck(mtx);
std::cout << "Thread " << wait_thread.get_id() << " starting in " << time/1000 << " seconds" << std::endl;
cv.wait_for(lck, time);
callback();
}
std::mutex mtx;
std::condition_variable cv{};
std::chrono::milliseconds time;
std::function <void(void)> callback;
std::thread wait_thread;
bool started = false;
};
Testing:
int main()
{
auto f = []() {std::cout << "Executing Callback Function"; };
Timer t1{ 3000, f };
t1.start();
Timer t2{ 10000, f };
t2.start();
// main thread is free to continue doing other things
}
I think the following meets all your requirements.
#include <condition_variable>
#include <chrono>
#include <iostream>
#include <thread>
template<class F>
class DelayedExecution {
public:
DelayedExecution(std::chrono::milliseconds ms, F&& func)
: ms_(ms)
, func_(std::forward<F>(func)) {
}
~DelayedExecution() {
if (thread_.joinable())
thread_.join();
}
void start() {
thread_ = std::thread{ [this]() {
std::unique_lock<std::mutex> lck(mutex_);
cv_.wait_for(lck, ms_, [this]() { return stop_waiting_.load(); });
if (not stop_waiting_)
func_();
}};
}
void stop() {
stop_waiting_.store(true);
cv_.notify_one();
}
private:
std::chrono::milliseconds ms_;
F func_;
std::thread thread_;
std::condition_variable cv_;
std::mutex mutex_;
std::atomic<bool> stop_waiting_{false};
};
using std::cout, std::endl;
int main(int argc, const char *argv[]) {
DelayedExecution task1(std::chrono::milliseconds{250}, []() { cout << "task1" << endl; });
DelayedExecution task2(std::chrono::milliseconds{250}, []() { cout << "task2" << endl; });
task1.start();
task2.start();
task1.stop();
return 0;
}
task2