HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
loop.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 "function_fifo.hpp"
8#include "function_timer.hpp"
9#include "utility/module.hpp"
10#include "net/network_event.hpp"
11#include <functional>
12#include <type_traits>
13#include <concepts>
14#include <vector>
15#include <memory>
16
17namespace hi::inline v1 {
18class gui_window;
19
20class loop {
21public:
23
24 class impl_type {
25 public:
26 bool is_main = false;
27
28 impl_type() = default;
29 virtual ~impl_type() {}
30 impl_type(impl_type const&) = delete;
31 impl_type(impl_type&&) = delete;
32 impl_type& operator=(impl_type const&) = delete;
33 impl_type& operator=(impl_type&&) = delete;
34
35 virtual void set_maximum_frame_rate(double frame_rate) noexcept = 0;
36
37 void wfree_post_function(auto&& func) noexcept
38 {
39 return _function_fifo.add_function(hi_forward(func));
40 }
41
42 void post_function(auto&& func) noexcept
43 {
44 _function_fifo.add_function(hi_forward(func));
45 notify_has_send();
46 }
47
48 [[nodiscard]] auto async_function(auto&& func) noexcept
49 {
50 auto future = _function_fifo.add_async_function(hi_forward(func));
51 notify_has_send();
52 return future;
53 }
54
55 timer_callback_token delay_function(utc_nanoseconds time_point, auto&& func) noexcept
56 {
57 auto [token, first_to_call] = _function_timer.delay_function(time_point, hi_forward(func));
58 if (first_to_call) {
59 // Notify if the added function is the next function to call.
60 notify_has_send();
61 }
62 return token;
63 }
64
65 timer_callback_token repeat_function(std::chrono::nanoseconds period, utc_nanoseconds time_point, auto&& func) noexcept
66 {
67 auto [token, first_to_call] = _function_timer.repeat_function(period, time_point, hi_forward(func));
68 if (first_to_call) {
69 // Notify if the added function is the next function to call.
70 notify_has_send();
71 }
72 return token;
73 }
74
75 timer_callback_token repeat_function(std::chrono::nanoseconds period, auto&& func) noexcept
76 {
77 auto [token, first_to_call] = _function_timer.repeat_function(period, hi_forward(func));
78 if (first_to_call) {
79 // Notify if the added function is the next function to call.
80 notify_has_send();
81 }
82 return token;
83 }
84
85 virtual void add_window(std::weak_ptr<gui_window> window) noexcept = 0;
86 virtual void add_socket(int fd, network_event event_mask, std::function<void(int, network_events const&)> f) = 0;
87 virtual void remove_socket(int fd) = 0;
88 virtual int resume(std::stop_token stop_token) noexcept = 0;
89 virtual void resume_once(bool block) noexcept = 0;
90
91 [[nodiscard]] bool on_thread() const noexcept
92 {
93 // Some functions check on_thread() while resume() has not been called yet.
94 // calling functions outside of the loop's thread if the loop is not being resumed is valid.
95 return _thread_id == 0 or current_thread_id() == _thread_id;
96 }
97
98 protected:
101 virtual void notify_has_send() noexcept = 0;
102
103 function_fifo<> _function_fifo;
104 function_timer<> _function_timer;
105
106 std::optional<int> _exit_code = {};
107 double _maximum_frame_rate = 30.0;
108 std::chrono::nanoseconds _minimum_frame_time = std::chrono::nanoseconds(33'333'333);
109 thread_id _thread_id = 0;
111 };
112
117 loop(loop const&) = delete;
118 loop(loop&&) noexcept = default;
119 loop& operator=(loop const&) = delete;
120 loop& operator=(loop&&) noexcept = default;
121
124 [[nodiscard]] hi_no_inline static loop& local() noexcept
125 {
126 if (not _local) {
127 _local = std::make_unique<loop>();
128 }
129 return *_local;
130 }
131
137 [[nodiscard]] hi_no_inline static loop& main() noexcept
138 {
139 if (auto ptr = _main.load(std::memory_order::acquire)) {
140 return *ptr;
141 }
142
143 auto ptr = std::addressof(local());
144 ptr->_pimpl->is_main = true;
145 _main.store(ptr, std::memory_order::release);
146 return *ptr;
147 }
148
153 [[nodiscard]] hi_no_inline static loop& timer() noexcept
154 {
155 return *start_subsystem_or_terminate(_timer, nullptr, timer_init, timer_deinit);
156 }
157
164 void set_maximum_frame_rate(double frame_rate) noexcept
165 {
166 hi_assert_not_null(_pimpl);
167 return _pimpl->set_maximum_frame_rate(frame_rate);
168 }
169
179 void wfree_post_function(auto&& func) noexcept
180 {
181 hi_assert_not_null(_pimpl);
182 return _pimpl->wfree_post_function(hi_forward(func));
183 }
184
190 void post_function(auto&& func) noexcept
191 {
192 hi_assert_not_null(_pimpl);
193 return _pimpl->post_function(hi_forward(func));
194 }
195
203 [[nodiscard]] auto async_function(auto&& func) noexcept
204 {
205 hi_assert_not_null(_pimpl);
206 return _pimpl->async_function(hi_forward(func));
207 }
208
214 [[nodiscard]] timer_callback_token delay_function(utc_nanoseconds time_point, auto&& func) noexcept
215 {
216 hi_assert_not_null(_pimpl);
217 return _pimpl->delay_function(time_point, hi_forward(func));
218 }
219
226 [[nodiscard]] timer_callback_token
227 repeat_function(std::chrono::nanoseconds period, utc_nanoseconds time_point, auto&& func) noexcept
228 {
229 hi_assert_not_null(_pimpl);
230 return _pimpl->repeat_function(period, time_point, hi_forward(func));
231 }
232
238 [[nodiscard]] timer_callback_token repeat_function(std::chrono::nanoseconds period, auto&& func) noexcept
239 {
240 hi_assert_not_null(_pimpl);
241 return _pimpl->repeat_function(period, hi_forward(func));
242 }
243
249 {
250 hi_assert_not_null(_pimpl);
251 return _pimpl->add_window(std::move(window));
252 }
253
266 void add_socket(int fd, network_event event_mask, std::function<void(int, network_events const&)> f)
267 {
268 hi_assert_not_null(_pimpl);
269 return _pimpl->add_socket(fd, event_mask, std::move(f));
270 }
271
276 void remove_socket(int fd)
277 {
278 hi_assert_not_null(_pimpl);
279 return _pimpl->remove_socket(fd);
280 }
281
289 int resume(std::stop_token stop_token = {}) noexcept
290 {
291 hi_assert_not_null(_pimpl);
292 return _pimpl->resume(stop_token);
293 }
294
308 void resume_once(bool block = false) noexcept
309 {
310 hi_assert_not_null(_pimpl);
311 return _pimpl->resume_once(block);
312 }
313
318 [[nodiscard]] bool on_thread() const noexcept
319 {
320 hi_assert_not_null(_pimpl);
321 return _pimpl->on_thread();
322 }
323
324private:
325 static loop *timer_init() noexcept
326 {
327 hi_assert(not _timer_thread.joinable());
328
329 _timer_thread = std::jthread{[](std::stop_token stop_token) {
330 _timer.store(std::addressof(loop::local()), std::memory_order::release);
331
332 set_thread_name("timer");
333 loop::local().resume(stop_token);
334 }};
335
336 while (true) {
337 if (auto ptr = _timer.load(std::memory_order::relaxed)) {
338 return ptr;
339 }
341 }
342 }
343
344 static void timer_deinit() noexcept
345 {
346 if (auto const * const ptr = _timer.exchange(nullptr, std::memory_order::acquire)) {
347 hi_assert(_timer_thread.joinable());
348 _timer_thread.request_stop();
349 _timer_thread.join();
350 }
351 }
352
353 inline static thread_local std::unique_ptr<loop> _local;
354
357 inline static std::atomic<loop *> _main;
358
361 inline static std::atomic<loop *> _timer;
362
363 inline static std::jthread _timer_thread;
364
366};
367
368} // namespace hi::inline v1
#define hi_assert(expression,...)
Assert if expression is true.
Definition assert.hpp:184
#define hi_assert_not_null(x,...)
Assert if an expression is not nullptr.
Definition assert.hpp:223
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition utility.hpp:29
DOXYGEN BUG.
Definition algorithm.hpp:13
@ local
Call the function asynchronously from the current thread's loop.
A fifo (First-in, Firts-out) for asynchronous calls.
Definition function_fifo.hpp:23
A time that calls functions.
Definition function_timer.hpp:22
Definition loop.hpp:20
void add_window(std::weak_ptr< gui_window > window) noexcept
Add a window to be redrawn from the event loop.
Definition loop.hpp:248
void resume_once(bool block=false) noexcept
Resume for a single iteration.
Definition loop.hpp:308
bool on_thread() const noexcept
Check if the current thread is the same as the loop's thread.
Definition loop.hpp:318
static hi_no_inline loop & timer() noexcept
Get or create the timer event-loop.
Definition loop.hpp:153
auto async_function(auto &&func) noexcept
Call a function from the loop.
Definition loop.hpp:203
timer_callback_token delay_function(utc_nanoseconds time_point, auto &&func) noexcept
Call a function at a certain time.
Definition loop.hpp:214
int resume(std::stop_token stop_token={}) noexcept
Resume the loop on the current thread.
Definition loop.hpp:289
void set_maximum_frame_rate(double frame_rate) noexcept
Set maximum frame rate.
Definition loop.hpp:164
void wfree_post_function(auto &&func) noexcept
Wait-free post a function to be called from the loop.
Definition loop.hpp:179
void add_socket(int fd, network_event event_mask, std::function< void(int, network_events const &)> f)
Add a callback that reacts on a socket.
Definition loop.hpp:266
void post_function(auto &&func) noexcept
Post a function to be called from the loop.
Definition loop.hpp:190
timer_callback_token repeat_function(std::chrono::nanoseconds period, auto &&func) noexcept
Call a function repeatedly.
Definition loop.hpp:238
static hi_no_inline loop & main() noexcept
Get or create the main-loop.
Definition loop.hpp:137
void remove_socket(int fd)
Remove the callback associated with a socket.
Definition loop.hpp:276
loop()
Construct a loop.
timer_callback_token repeat_function(std::chrono::nanoseconds period, utc_nanoseconds time_point, auto &&func) noexcept
Call a function repeatedly.
Definition loop.hpp:227
Definition loop.hpp:24
Definition network_event.hpp:71
T addressof(T... args)
T move(T... args)
T sleep_for(T... args)