HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
function_timer.hpp
1// Copyright Take Vos 2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "utility/module.hpp"
8#include "chrono.hpp"
9#include <vector>
10#include <algorithm>
11#include <chrono>
12#include <functional>
13
14namespace hi::inline v1 {
15
21template<typename Proto = void()>
23public:
24 using callback_proto = Proto;
28
29 using result_type = hi_typename function_type::result_type;
30
31 constexpr function_timer() noexcept = default;
32
33 [[nodiscard]] constexpr bool empty() const noexcept
34 {
35 return _functions.empty();
36 }
37
45 delay_function(utc_nanoseconds time_point, forward_of<callback_proto> auto&& callback) noexcept
46 {
47 hilet it = std::lower_bound(_functions.begin(), _functions.end(), time_point, [](hilet& x, hilet& time_point) {
48 return x.time_point > time_point;
49 });
50
51 hilet next_to_call = it == _functions.end();
52
53 auto token = std::make_shared<function_type>(hi_forward(callback));
54 _functions.emplace(it, time_point, std::chrono::nanoseconds::max(), token);
55 return {std::move(token), next_to_call};
56 }
57
67 utc_nanoseconds time_point,
68 forward_of<callback_proto> auto&& callback) noexcept
69 {
70 auto it = std::lower_bound(_functions.begin(), _functions.end(), time_point, [](hilet& x, hilet& time_point) {
71 return x.time_point > time_point;
72 });
73
74 auto token = std::make_shared<function_type>(hi_forward(callback));
75 it = _functions.emplace(it, time_point, period, token);
76 return {std::move(token), it + 1 == _functions.end()};
77 }
78
87 {
88 return repeat_function(period, std::chrono::utc_clock::now(), hi_forward(callback));
89 }
90
95 utc_nanoseconds current_deadline() const noexcept
96 {
97 if (_functions.empty()) {
98 return utc_nanoseconds::max();
99 } else {
100 return _functions.back().time_point;
101 }
102 }
103
109 void run_all(utc_nanoseconds current_time, auto const&...args) noexcept
110 {
111 while (current_deadline() <= current_time) {
112 run_one(current_time, args...);
113 }
114 }
115
116private:
117 struct timer_type {
118 utc_nanoseconds time_point;
120 weak_callback_token token;
121
122 timer_type() noexcept = default;
123 timer_type(timer_type const&) noexcept = default;
124 timer_type(timer_type&&) noexcept = default;
125 timer_type& operator=(timer_type const&) noexcept = default;
126 timer_type& operator=(timer_type&&) noexcept = default;
127
128 timer_type(utc_nanoseconds time_point, std::chrono::nanoseconds period, weak_callback_token token) noexcept :
129 time_point(time_point), period(period), token(std::move(token))
130 {
131 }
132
133 timer_type(utc_nanoseconds time_point, weak_callback_token token) noexcept :
134 timer_type(time_point, std::chrono::nanoseconds::max(), std::move(token))
135 {
136 }
137
138 timer_type(std::chrono::nanoseconds period, weak_callback_token token) noexcept :
139 timer_type(std::chrono::utc_clock::now(), period, std::move(token))
140 {
141 }
142
143 [[nodiscard]] constexpr bool repeats() const noexcept
144 {
145 return period != std::chrono::nanoseconds::max();
146 }
147 };
148
149 void remove_or_reinsert(utc_nanoseconds current_time) noexcept
150 {
151 hi_assert(not _functions.empty());
152
153 if (_functions.back().repeats() and not _functions.back().token.expired()) {
154 // When the function is repeating, calculate the new.
155 auto item = std::move(_functions.back());
156 _functions.pop_back();
157
158 // Delay the function to be called on the next period.
159 // However if the current_time already is passed the deadline, delay it even further.
160 item.time_point += item.period;
161 if (item.time_point > current_time) {
162 item.time_point = current_time + item.period;
163 }
164
165 // Reinsert the function in the sorted list of functions.
166 hilet it = std::lower_bound(_functions.begin(), _functions.end(), item.time_point, [](hilet& x, hilet& time_point) {
167 return x.time_point > time_point;
168 });
169
170 _functions.insert(it, std::move(item));
171
172 } else {
173 _functions.pop_back();
174 }
175 }
176
184 result_type run_one(utc_nanoseconds current_time, auto&&...args)
185 {
186 hi_assert(not _functions.empty());
187
188 if constexpr (std::is_same_v<result_type, void>) {
189 if (auto token = _functions.back().token.lock()) {
190 (*token)(hi_forward(args)...);
191 }
192 remove_or_reinsert(current_time);
193 return;
194 } else {
195 if (auto token = _functions.back().token.lock()) {
196 auto result = (*token)(hi_forward(args)...);
197 remove_or_reinsert(current_time);
198 return result;
199 } else {
200 return {};
201 }
202 }
203 }
204
207 std::vector<timer_type> _functions;
208};
209
210} // namespace hi::inline v1
#define hi_assert(expression,...)
Assert if expression is true.
Definition assert.hpp:199
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition utility.hpp:29
STL namespace.
DOXYGEN BUG.
Definition algorithm.hpp:13
A time that calls functions.
Definition function_timer.hpp:22
std::pair< callback_token, bool > repeat_function(std::chrono::nanoseconds period, utc_nanoseconds time_point, forward_of< callback_proto > auto &&callback) noexcept
Add a function to be called repeatedly.
Definition function_timer.hpp:65
utc_nanoseconds current_deadline() const noexcept
Get the deadline of the next function to call.
Definition function_timer.hpp:95
std::pair< callback_token, bool > repeat_function(std::chrono::nanoseconds period, forward_of< callback_proto > auto &&callback) noexcept
Add a function to be called repeatedly.
Definition function_timer.hpp:86
std::pair< callback_token, bool > delay_function(utc_nanoseconds time_point, forward_of< callback_proto > auto &&callback) noexcept
Add a function to be called at a certain time.
Definition function_timer.hpp:45
void run_all(utc_nanoseconds current_time, auto const &...args) noexcept
Run all the function that should have run by the current_time.
Definition function_timer.hpp:109
True if T is a forwarded type of Forward.
Definition concepts.hpp:130
T lower_bound(T... args)
T move(T... args)