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 "subsystem.hpp"
10#include "cast.hpp"
11#include "net/network_event.hpp"
12#include <functional>
13#include <type_traits>
14#include <concepts>
15#include <vector>
16#include <memory>
17
18namespace hi::inline v1 {
19class gui_window;
20
21class loop {
22public:
24
25 class impl_type {
26 public:
27 bool is_main = false;
28
29 impl_type() : _thread_id(0) {}
30
31 virtual ~impl_type() {}
32
33 virtual void set_maximum_frame_rate(double frame_rate) noexcept = 0;
34
35 [[nodiscard]] void wfree_post_function(auto&& func) noexcept
36 {
37 return _function_fifo.add_function(hi_forward(func));
38 }
39
40 [[nodiscard]] void post_function(auto&& func) noexcept
41 {
42 _function_fifo.add_function(hi_forward(func));
43 notify_has_send();
44 }
45
46 [[nodiscard]] auto async_function(auto&& func) noexcept
47 {
48 auto future = _function_fifo.add_async_function(hi_forward(func));
49 notify_has_send();
50 return future;
51 }
52
53 timer_token_type delay_function(utc_nanoseconds time_point, auto&& func) noexcept
54 {
55 auto [token, first_to_call] = _function_timer.delay_function(time_point, hi_forward(func));
56 if (first_to_call) {
57 // Notify if the added function is the next function to call.
58 notify_has_send();
59 }
60 return token;
61 }
62
63 timer_token_type repeat_function(std::chrono::nanoseconds period, utc_nanoseconds time_point, auto&& func) noexcept
64 {
65 auto [token, first_to_call] = _function_timer.repeat_function(period, time_point, hi_forward(func));
66 if (first_to_call) {
67 // Notify if the added function is the next function to call.
68 notify_has_send();
69 }
70 return token;
71 }
72
73 timer_token_type repeat_function(std::chrono::nanoseconds period, auto&& func) noexcept
74 {
75 auto [token, first_to_call] = _function_timer.repeat_function(period, hi_forward(func));
76 if (first_to_call) {
77 // Notify if the added function is the next function to call.
78 notify_has_send();
79 }
80 return token;
81 }
82
83 virtual void add_window(std::weak_ptr<gui_window> window) noexcept = 0;
84 virtual void add_socket(int fd, network_event event_mask, std::function<void(int, network_events const&)> f) = 0;
85 virtual void remove_socket(int fd) = 0;
86 virtual int resume(std::stop_token stop_token) noexcept = 0;
87 virtual void resume_once(bool block) noexcept = 0;
88
89 protected:
92 virtual void notify_has_send() noexcept = 0;
93
94 [[nodiscard]] bool is_same_thread() const noexcept
95 {
96 return _thread_id == 0 or current_thread_id() == _thread_id;
97 }
98
99 function_fifo<> _function_fifo;
100 function_timer<> _function_timer;
101
102 std::optional<int> _exit_code;
103 double _maximum_frame_rate = 30.0;
104 std::chrono::nanoseconds _minimum_frame_time = std::chrono::nanoseconds(33'333'333);
105 thread_id _thread_id = 0;
107 };
108
113 loop(loop const&) = delete;
114 loop(loop&&) noexcept = default;
115 loop& operator=(loop const&) = delete;
116 loop& operator=(loop&&) noexcept = default;
117
120 [[nodiscard]] hi_no_inline static loop& local() noexcept
121 {
122 if (not _local) {
123 _local = std::make_unique<loop>();
124 }
125 return *_local;
126 }
127
133 [[nodiscard]] hi_no_inline static loop& main() noexcept
134 {
135 if (auto ptr = _main.load(std::memory_order::acquire)) {
136 return *ptr;
137 }
138
139 auto ptr = std::addressof(local());
140 ptr->_pimpl->is_main = true;
141 _main.store(ptr, std::memory_order::release);
142 return *ptr;
143 }
144
149 [[nodiscard]] hi_no_inline static loop& timer() noexcept
150 {
151 return *start_subsystem_or_terminate(_timer, nullptr, timer_init, timer_deinit);
152 }
153
160 void set_maximum_frame_rate(double frame_rate) noexcept
161 {
162 hi_axiom(_pimpl);
163 return _pimpl->set_maximum_frame_rate(frame_rate);
164 }
165
175 [[nodiscard]] void wfree_post_function(auto&& func) noexcept
176 {
177 hi_axiom(_pimpl);
178 return _pimpl->wfree_post_function(hi_forward(func));
179 }
180
186 [[nodiscard]] void post_function(auto&& func) noexcept
187 {
188 hi_axiom(_pimpl);
189 return _pimpl->post_function(hi_forward(func));
190 }
191
199 [[nodiscard]] auto async_function(auto&& func) noexcept
200 {
201 hi_axiom(_pimpl);
202 return _pimpl->async_function(hi_forward(func));
203 }
204
210 [[nodiscard]] timer_token_type delay_function(utc_nanoseconds time_point, auto&& func) noexcept
211 {
212 hi_axiom(_pimpl);
213 return _pimpl->delay_function(time_point, hi_forward(func));
214 }
215
222 [[nodiscard]] timer_token_type
223 repeat_function(std::chrono::nanoseconds period, utc_nanoseconds time_point, auto&& func) noexcept
224 {
225 hi_axiom(_pimpl);
226 return _pimpl->repeat_function(period, time_point, hi_forward(func));
227 }
228
234 [[nodiscard]] timer_token_type repeat_function(std::chrono::nanoseconds period, auto&& func) noexcept
235 {
236 hi_axiom(_pimpl);
237 return _pimpl->repeat_function(period, hi_forward(func));
238 }
239
245 {
246 hi_axiom(_pimpl);
247 return _pimpl->add_window(std::move(window));
248 }
249
262 void add_socket(int fd, network_event event_mask, std::function<void(int, network_events const&)> f)
263 {
264 hi_axiom(_pimpl);
265 return _pimpl->add_socket(fd, event_mask, std::move(f));
266 }
267
272 void remove_socket(int fd)
273 {
274 hi_axiom(_pimpl);
275 return _pimpl->remove_socket(fd);
276 }
277
285 int resume(std::stop_token stop_token = {}) noexcept
286 {
287 hi_axiom(_pimpl);
288 return _pimpl->resume(stop_token);
289 }
290
304 void resume_once(bool block = false) noexcept
305 {
306 hi_axiom(_pimpl);
307 return _pimpl->resume_once(block);
308 }
309
310private:
311 static loop *timer_init() noexcept
312 {
313 hi_axiom(not _timer_thread.joinable());
314
315 _timer_thread = std::jthread{[](std::stop_token stop_token) {
316 _timer.store(std::addressof(loop::local()), std::memory_order::release);
317
318 set_thread_name("timer");
319 loop::local().resume(stop_token);
320 }};
321
322 while (true) {
323 if (auto ptr = _timer.load(std::memory_order::relaxed)) {
324 return ptr;
325 }
327 }
328 }
329
330 static void timer_deinit() noexcept
331 {
332 if (auto ptr = _timer.exchange(nullptr, std::memory_order::acquire)) {
333 hi_axiom(_timer_thread.joinable());
334 _timer_thread.request_stop();
335 _timer_thread.join();
336 }
337 }
338
339 inline static thread_local std::unique_ptr<loop> _local;
340
343 inline static std::atomic<loop *> _main;
344
347 inline static std::atomic<loop *> _timer;
348
349 inline static std::jthread _timer_thread;
350
352};
353
354} // namespace hi::inline v1
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition required.hpp:29
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:21
void add_window(std::weak_ptr< gui_window > window) noexcept
Add a window to be redrawn from the event loop.
Definition loop.hpp:244
timer_token_type repeat_function(std::chrono::nanoseconds period, utc_nanoseconds time_point, auto &&func) noexcept
Call a function repeatedly.
Definition loop.hpp:223
void resume_once(bool block=false) noexcept
Resume for a single iteration.
Definition loop.hpp:304
static hi_no_inline loop & timer() noexcept
Get or create the timer event-loop.
Definition loop.hpp:149
auto async_function(auto &&func) noexcept
Call a function from the loop.
Definition loop.hpp:199
int resume(std::stop_token stop_token={}) noexcept
Resume the loop on the current thread.
Definition loop.hpp:285
void set_maximum_frame_rate(double frame_rate) noexcept
Set maximum frame rate.
Definition loop.hpp:160
void wfree_post_function(auto &&func) noexcept
Wait-free post a function to be called from the loop.
Definition loop.hpp:175
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:262
void post_function(auto &&func) noexcept
Post a function to be called from the loop.
Definition loop.hpp:186
timer_token_type delay_function(utc_nanoseconds time_point, auto &&func) noexcept
Call a function at a certain time.
Definition loop.hpp:210
static hi_no_inline loop & main() noexcept
Get or create the main-loop.
Definition loop.hpp:133
void remove_socket(int fd)
Remove the callback associated with a socket.
Definition loop.hpp:272
timer_token_type repeat_function(std::chrono::nanoseconds period, auto &&func) noexcept
Call a function repeatedly.
Definition loop.hpp:234
loop()
Construct a loop.
Definition loop.hpp:25
Definition network_event.hpp:72
T addressof(T... args)
T move(T... args)
T sleep_for(T... args)