Last active
March 30, 2017 06:39
-
-
Save smlu/6dd64dda84b7deadf19e5070afa98955 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 <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