From 5999e6e0c32e6897b88f59f0743b4bb1fc9c521c Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 27 May 2014 01:12:46 +0200 Subject: Introduce the timed events --- src/test.cpp | 24 ++++++++ src/utils/timed_events.cpp | 54 ++++++++++++++++++ src/utils/timed_events.hpp | 111 +++++++++++++++++++++++++++++++++++++ src/utils/timed_events_manager.cpp | 55 ++++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 src/utils/timed_events.cpp create mode 100644 src/utils/timed_events.hpp create mode 100644 src/utils/timed_events_manager.cpp (limited to 'src') diff --git a/src/test.cpp b/src/test.cpp index f624bc2..fe89b5a 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -3,6 +3,7 @@ */ #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include +#include #include #include @@ -25,6 +27,28 @@ static const std::string reset(""); int main() { + /** + * Timed events + */ + std::cout << color << "Testing timed events…" << reset << std::endl; + TimedEventsManager te_manager; + // No event. + assert(te_manager.get_timeout() == utils::no_timeout); + assert(te_manager.execute_expired_events() == 0); + + // Add a single event + te_manager.add_event(TimedEvent(std::chrono::steady_clock::now() + 50ms, [](){ std::cout << "Timeout expired" << std::endl; })); + // The event should not yet be expired + assert(te_manager.get_timeout() > 0ms); + assert(te_manager.execute_expired_events() == 0); + std::chrono::milliseconds timoute = te_manager.get_timeout(); + std::cout << "Sleeping for " << timoute.count() << "ms" << std::endl; + std::this_thread::sleep_for(timoute); + + // Event is now expired + assert(te_manager.execute_expired_events() == 1); + assert(te_manager.get_timeout() == utils::no_timeout); + /** * Encoding */ diff --git a/src/utils/timed_events.cpp b/src/utils/timed_events.cpp new file mode 100644 index 0000000..10ef4c1 --- /dev/null +++ b/src/utils/timed_events.cpp @@ -0,0 +1,54 @@ +#include + +TimedEvent::TimedEvent(std::chrono::steady_clock::time_point&& time_point, + std::function callback): + time_point(std::move(time_point)), + callback(callback), + repeat(false), + repeat_delay(0) +{ +} + +TimedEvent::TimedEvent(std::chrono::milliseconds&& duration, + std::function callback): + time_point(std::chrono::steady_clock::now() + duration), + callback(callback), + repeat(true), + repeat_delay(std::move(duration)) +{ +} + +TimedEvent::TimedEvent(TimedEvent&& other): + time_point(std::move(other.time_point)), + callback(std::move(other.callback)), + repeat(other.repeat), + repeat_delay(std::move(other.repeat_delay)) +{ +} + +TimedEvent::~TimedEvent() +{ +} + +bool TimedEvent::is_after(const TimedEvent& other) const +{ + return this->is_after(other.time_point); +} + +bool TimedEvent::is_after(const std::chrono::steady_clock::time_point& time_point) const +{ + return this->time_point >= time_point; +} + +std::chrono::milliseconds TimedEvent::get_timeout() const +{ + auto now = std::chrono::steady_clock::now(); + if (now > this->time_point) + return std::chrono::milliseconds(0); + return std::chrono::duration_cast(this->time_point - now); +} + +void TimedEvent::execute() +{ + this->callback(); +} diff --git a/src/utils/timed_events.hpp b/src/utils/timed_events.hpp new file mode 100644 index 0000000..9be747d --- /dev/null +++ b/src/utils/timed_events.hpp @@ -0,0 +1,111 @@ +#ifndef TIMED_EVENTS_HPP +# define TIMED_EVENTS_HPP + +#include +#include +#include + +using namespace std::literals::chrono_literals; + +namespace utils { +static constexpr std::chrono::milliseconds no_timeout = std::chrono::milliseconds(-1); +} + +class TimedEventsManager; + +/** + * A callback with an associated date. + */ + +class TimedEvent +{ + friend class TimedEventsManager; +public: + /** + * An event the occurs only once, at the given time_point + */ + explicit TimedEvent(std::chrono::steady_clock::time_point&& time_point, + std::function callback); + explicit TimedEvent(std::chrono::milliseconds&& duration, + std::function callback); + + explicit TimedEvent(TimedEvent&&); + ~TimedEvent(); + /** + * Whether or not this event happens after the other one. + */ + bool is_after(const TimedEvent& other) const; + bool is_after(const std::chrono::steady_clock::time_point& time_point) const; + /** + * Return the duration difference between now and the event time point. + * If the difference would be negative (i.e. the event is expired), the + * returned value is 0 instead. The value cannot then be negative. + */ + std::chrono::milliseconds get_timeout() const; + void execute(); + +private: + /** + * The next time point at which the event is executed. + */ + std::chrono::steady_clock::time_point time_point; + /** + * The function to execute. + */ + const std::function callback; + /** + * Whether or not this events repeats itself until it is destroyed. + */ + const bool repeat; + /** + * This value is added to the time_point each time the event is executed, + * if repeat is true. Otherwise it is ignored. + */ + const std::chrono::milliseconds repeat_delay; + + TimedEvent(const TimedEvent&) = delete; + TimedEvent& operator=(const TimedEvent&) = delete; + TimedEvent& operator=(TimedEvent&&) = delete; +}; + +/** + * A class managing a list of TimedEvents. + * They are sorted, new events can be added, removed, fetch, etc. + */ + +class TimedEventsManager +{ +public: + explicit TimedEventsManager(); + ~TimedEventsManager(); + /** + * Add an event to the list of managed events. The list is sorted after + * this call. + */ + void add_event(TimedEvent&& event); + /** + * Returns the duration, in milliseconds, between now and the next + * available event. If the event is already expired (the duration is + * negative), 0 is returned instead (as in “it's not too late, execute it + * now”) + * Returns a negative value if no event is available. + */ + std::chrono::milliseconds get_timeout() const; + /** + * Execute all the expired events (if their expiration time is exactly + * now, or before now). The event is then removed from the list. If the + * event does repeat, its expiration time is updated and it is reinserted + * in the list at the correct position. + * Returns the number of executed events. + */ + std::size_t execute_expired_events(); + +private: + std::list events; + TimedEventsManager(const TimedEventsManager&) = delete; + TimedEventsManager(TimedEventsManager&&) = delete; + TimedEventsManager& operator=(const TimedEventsManager&) = delete; + TimedEventsManager& operator=(TimedEventsManager&&) = delete; +}; + +#endif // TIMED_EVENTS_HPP diff --git a/src/utils/timed_events_manager.cpp b/src/utils/timed_events_manager.cpp new file mode 100644 index 0000000..c3e260c --- /dev/null +++ b/src/utils/timed_events_manager.cpp @@ -0,0 +1,55 @@ +#include + +TimedEventsManager::TimedEventsManager() +{ +} + +TimedEventsManager::~TimedEventsManager() +{ +} + +void TimedEventsManager::add_event(TimedEvent&& event) +{ + for (auto it = this->events.begin(); it != this->events.end(); ++it) + { + if (it->is_after(event)) + { + this->events.emplace(it, std::move(event)); + return; + } + } + this->events.emplace_back(std::move(event)); +} + +std::chrono::milliseconds TimedEventsManager::get_timeout() const +{ + if (this->events.empty()) + return utils::no_timeout; + return this->events.front().get_timeout() + std::chrono::milliseconds(1); +} + +std::size_t TimedEventsManager::execute_expired_events() +{ + std::size_t count = 0; + const auto now = std::chrono::steady_clock::now(); + for (auto it = this->events.begin(); it != this->events.end();) + { + if (!it->is_after(now)) + { + TimedEvent copy(std::move(*it)); + it = this->events.erase(it); + ++count; + copy.execute(); + if (copy.repeat) + { + copy.time_point += copy.repeat_delay; + this->add_event(std::move(copy)); + } + continue; + } + else + break; + } + return count; +} + -- cgit v1.2.3