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 "required.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:
27
28 using result_type = function_type::result_type;
29
30 constexpr function_timer() noexcept = default;
31
32 [[nodiscard]] constexpr bool empty() const noexcept
33 {
34 return _functions.empty();
35 }
36
43 std::pair<token_type, bool> delay_function(utc_nanoseconds time_point, auto&& func) noexcept
44 {
45 hilet it = std::lower_bound(_functions.begin(), _functions.end(), time_point, [](hilet& x, hilet& time_point) {
46 return x.time_point > time_point;
47 });
48
49 hilet next_to_call = it == _functions.end();
50
51 auto token = std::make_shared<function_type>(hi_forward(func));
52 _functions.emplace(it, time_point, std::chrono::nanoseconds::max(), token);
53 return {std::move(token), next_to_call};
54 }
55
63 std::pair<token_type, bool> repeat_function(std::chrono::nanoseconds period, utc_nanoseconds time_point, auto&& func) noexcept
64 {
65 auto it = std::lower_bound(_functions.begin(), _functions.end(), time_point, [](hilet& x, hilet& time_point) {
66 return x.time_point > time_point;
67 });
68
69 auto token = std::make_shared<function_type>(hi_forward(func));
70 it = _functions.emplace(it, time_point, period, token);
71 return {std::move(token), it + 1 == _functions.end()};
72 }
73
82 {
83 return repeat_function(period, std::chrono::utc_clock::now(), hi_forward(func));
84 }
85
90 utc_nanoseconds current_deadline() const noexcept
91 {
92 if (_functions.empty()) {
93 return utc_nanoseconds::max();
94 } else {
95 return _functions.back().time_point;
96 }
97 }
98
104 void run_all(utc_nanoseconds current_time, auto&&...args) noexcept
105 {
106 while (current_deadline() <= current_time) {
107 run_one(current_time, args...);
108 }
109 }
110
111private:
112 struct timer_type {
113 utc_nanoseconds time_point;
115 weak_token_type token;
116
117 timer_type() noexcept = default;
118 timer_type(timer_type const&) noexcept = default;
119 timer_type(timer_type&&) noexcept = default;
120 timer_type& operator=(timer_type const&) noexcept = default;
121 timer_type& operator=(timer_type&&) noexcept = default;
122
123 timer_type(utc_nanoseconds time_point, std::chrono::nanoseconds period, weak_token_type token) noexcept :
124 time_point(time_point), period(period), token(std::move(token))
125 {
126 }
127
128 timer_type(utc_nanoseconds time_point, weak_token_type token) noexcept :
129 timer_type(time_point, std::chrono::nanoseconds::max(), std::move(token))
130 {
131 }
132
133 timer_type(std::chrono::nanoseconds period, weak_token_type token) noexcept :
134 timer_type(std::chrono::utc_clock::now(), period, std::move(token))
135 {
136 }
137
138 [[nodiscard]] constexpr bool repeats() const noexcept
139 {
140 return period != std::chrono::nanoseconds::max();
141 }
142 };
143
144 void remove_or_reinsert(utc_nanoseconds current_time) noexcept
145 {
146 hi_axiom(not _functions.empty());
147
148 if (_functions.back().repeats() and not _functions.back().token.expired()) {
149 // When the function is repeating, calculate the new.
150 auto item = std::move(_functions.back());
151 _functions.pop_back();
152
153 // Delay the function to be called on the next period.
154 // However if the current_time already is passed the deadline, delay it even further.
155 item.time_point += item.period;
156 if (item.time_point > current_time) {
157 item.time_point = current_time + item.period;
158 }
159
160 // Reinsert the function in the sorted list of functions.
161 hilet it = std::lower_bound(_functions.begin(), _functions.end(), item.time_point, [](hilet& x, hilet& time_point) {
162 return x.time_point > time_point;
163 });
164
165 _functions.insert(it, std::move(item));
166
167 } else {
168 _functions.pop_back();
169 }
170 }
171
179 result_type run_one(utc_nanoseconds current_time, auto&&...args)
180 {
181 hi_axiom(not _functions.empty());
182
183 if constexpr (std::is_same_v<result_type, void>) {
184 if (auto token = _functions.back().token.lock()) {
185 (*token)(hi_forward(args)...);
186 }
187 remove_or_reinsert(current_time);
188 return;
189 } else {
190 if (auto token = _functions.back().token.lock()) {
191 auto result = (*token)(hi_forward(args)...);
192 remove_or_reinsert(current_time);
193 return result;
194 } else {
195 return {};
196 }
197 }
198 }
199
202 std::vector<timer_type> _functions;
203};
204
205} // namespace hi::inline v1
This file includes required definitions.
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition required.hpp:29
STL namespace.
A time that calls functions.
Definition function_timer.hpp:22
std::pair< token_type, bool > repeat_function(std::chrono::nanoseconds period, utc_nanoseconds time_point, auto &&func) noexcept
Add a function to be called repeatedly.
Definition function_timer.hpp:63
std::pair< token_type, bool > delay_function(utc_nanoseconds time_point, auto &&func) noexcept
Add a function to be called at a certain time.
Definition function_timer.hpp:43
utc_nanoseconds current_deadline() const noexcept
Get the deadline of the next function to call.
Definition function_timer.hpp:90
std::pair< token_type, bool > repeat_function(std::chrono::nanoseconds period, auto &&func) noexcept
Add a function to be called repeatedly.
Definition function_timer.hpp:81
void run_all(utc_nanoseconds current_time, auto &&...args) noexcept
Run all the function that should have run by the current_time.
Definition function_timer.hpp:104
T lower_bound(T... args)
T move(T... args)