summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/test.cpp24
-rw-r--r--src/utils/timed_events.cpp54
-rw-r--r--src/utils/timed_events.hpp111
-rw-r--r--src/utils/timed_events_manager.cpp55
4 files changed, 244 insertions, 0 deletions
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 <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>
@@ -26,6 +28,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
*/
std::cout << color << "Testing encoding…" << reset << std::endl;
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 <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();
+}
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 <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
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 <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;
+}
+