Skip to content

Instantly share code, notes, and snippets.

@smlu
Last active March 30, 2017 06:39
Show Gist options
  • Save smlu/6dd64dda84b7deadf19e5070afa98955 to your computer and use it in GitHub Desktop.
Save smlu/6dd64dda84b7deadf19e5070afa98955 to your computer and use it in GitHub Desktop.
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
#include <utility>
#include <vector>
template<typename ... Args>
class Signal
{
public:
void connect(std::function<void(Args...)> slot)
{
if(slot) {
m_slots.emplace_back(std::move(slot));
}
}
void operator()(Args&&... args)
{
for(const auto& slot: m_slots) {
slot(args...);
}
}
private:
std::vector<std::function<void(Args...)>> m_slots;
};
class Timer
{
using Clock = std::chrono::system_clock;
using TimePoint = std::chrono::time_point<Clock>;
public:
using Interval = std::chrono::milliseconds;
Signal<> timeout;
Signal<> stopped;
Timer() = default;
Timer(const Timer&) = delete;
Timer& operator=(const Timer&) = delete;
~Timer()
{
if(m_isActive) {
// maybe log
this->stop();
}
if(m_thread.joinable()){
m_thread.join();
}
}
Interval interval() const
{
return m_interval;
}
void setInterval(Interval interval)
{
m_interval = interval;
}
bool isSingleShot() const
{
return m_isSingleShot;
}
void setSingleShot(bool singleShot)
{
m_isSingleShot = singleShot;
}
void start(Interval msec)
{
this->setInterval(msec);
this->start();
}
void start()
{
if(m_isActive) {
this->stop();
}
m_isActive = true;
m_thread = std::thread([this]()
{
while(m_isActive)
{
m_intervalStart = Clock::now();
std::unique_lock<std::mutex> lk(m_mutex);
m_condition.wait_for(lk, m_interval, [this]{ return !m_isActive;});
if(m_isActive) // If timer was not stopped prematurely, emit timeout signal
{
timeout();
m_isActive = !m_isSingleShot;
}
}
});
}
void stop()
{
if(m_isActive)
{
m_isActive = false;
m_condition.notify_all();
if(m_thread.joinable()){
m_thread.join();
}
stopped();
}
}
bool isActive() const
{
return m_isActive;
}
Interval remainingTime() const
{
return std::chrono::duration_cast<Interval>((m_intervalStart + m_interval) - Clock::now());
}
private:
std::atomic_bool m_isActive = {false};
std::atomic_bool m_isSingleShot = {false};
std::condition_variable m_condition;
std::mutex m_mutex;
std::thread m_thread;
Interval m_interval;
TimePoint m_intervalStart;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment