Commit 5999e6e0 authored by louiz’'s avatar louiz’

Introduce the timed events

parent e033b6a3
......@@ -3,6 +3,7 @@
*/
#include <xmpp/xmpp_component.hpp>
#include <utils/timed_events.hpp>
#include <xmpp/xmpp_parser.hpp>
#include <utils/encoding.hpp>
#include <logger/logger.hpp>
......@@ -16,6 +17,7 @@
#include <string.h>
#include <iostream>
#include <thread>
#include <vector>
#include <assert.h>
......@@ -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
*/
......
#include <utils/timed_events.hpp>
TimedEvent::TimedEvent(std::chrono::steady_clock::time_point&& time_point,
std::function<void()> callback):
time_point(std::move(time_point)),
callback(callback),
repeat(false),
repeat_delay(0)
{
}
TimedEvent::TimedEvent(std::chrono::milliseconds&& duration,
std::function<void()> 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<std::chrono::milliseconds>(this->time_point - now);
}
void TimedEvent::execute()
{
this->callback();
}
#ifndef TIMED_EVENTS_HPP
# define TIMED_EVENTS_HPP
#include <functional>
#include <chrono>
#include <list>
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<void()> callback);
explicit TimedEvent(std::chrono::milliseconds&& duration,
std::function<void()> 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<void()> 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<TimedEvent> events;
TimedEventsManager(const TimedEventsManager&) = delete;
TimedEventsManager(TimedEventsManager&&) = delete;
TimedEventsManager& operator=(const TimedEventsManager&) = delete;
TimedEventsManager& operator=(TimedEventsManager&&) = delete;
};
#endif // TIMED_EVENTS_HPP
#include <utils/timed_events.hpp>
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;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment