HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
loop_win32_intf.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
8
9#include "function_timer.hpp"
10#include "socket_event.hpp"
11#include "notifier.hpp"
12#include "../container/container.hpp"
13#include "../telemetry/telemetry.hpp"
14#include "../concurrency/concurrency.hpp"
15#include "../concurrency/unfair_mutex.hpp" // XXX #616
16#include "../concurrency/thread.hpp" // XXX #616
17#include "../time/time.hpp"
18#include "../utility/utility.hpp"
19#include "../macros.hpp"
20#include <functional>
21#include <type_traits>
22#include <concepts>
23#include <vector>
24#include <memory>
25#include <chrono>
26#include <thread>
27
28hi_export_module(hikogui.dispatch : loop_intf);
29
30hi_export namespace hi::inline v1 {
31
32class loop {
33public:
34 loop(loop const&) = delete;
35 loop(loop&&) noexcept = delete;
36 loop& operator=(loop const&) = delete;
37 loop& operator=(loop&&) noexcept = delete;
38
39 ~loop()
40 {
41 // Close all socket event handles.
42 while (_handles.size() > _socket_handle_idx) {
43 if (not WSACloseEvent(_handles.back())) {
44 hi_log_error("Could not clock socket event handle for socket {}. {}", _sockets.back(), get_last_error_message());
45 }
46
47 _handles.pop_back();
48 _sockets.pop_back();
49 }
50
51 if (_vsync_thread.joinable()) {
52 _vsync_thread.request_stop();
53 _vsync_thread.join();
54 }
55
56 if (not CloseHandle(_handles[_function_handle_idx])) {
57 hi_log_error("Could not close async-event handle. {}", get_last_error_message());
58 }
59 if (not CloseHandle(_handles[_vsync_handle_idx])) {
60 hi_log_error("Could not close vsync-event handle. {}", get_last_error_message());
61 }
62 if (not CloseHandle(_use_vsync_handle)) {
63 hi_log_error("Could not close use-vsync handle. {}", get_last_error_message());
64 }
65 }
66
67 loop() noexcept : _thread_id(current_thread_id())
68 {
69 // Create an level trigger event, to use as an on/off switch.
70 if (auto handle = CreateEventW(NULL, TRUE, TRUE, NULL)) {
71 _use_vsync_handle = handle;
72 } else {
73 hi_log_fatal("Could not create an use-vsync handle. {}", get_last_error_message());
74 }
75
76 // Create a pulse trigger event.
77 if (auto handle = CreateEventW(NULL, FALSE, FALSE, NULL)) {
78 _handles.push_back(handle);
79 _sockets.push_back(-1);
80 _socket_functions.emplace_back();
81 } else {
82 hi_log_fatal("Could not create an vsync-event handle. {}", get_last_error_message());
83 }
84
85 // Create a pulse trigger event.
86 if (auto handle = CreateEventW(NULL, FALSE, FALSE, NULL)) {
87 _handles.push_back(handle);
88 _sockets.push_back(-1);
89 _socket_functions.emplace_back();
90 } else {
91 hi_log_fatal("Could not create an async-event handle. {}", get_last_error_message());
92 }
93 }
94
97 [[nodiscard]] static loop& local() noexcept;
98
104 [[nodiscard]] hi_no_inline static loop& main() noexcept
105 {
106 if (auto ptr = _main.load(std::memory_order::acquire)) {
107 return *ptr;
108 }
109
110 hi_axiom(_timer.load(std::memory_order::relaxed) == nullptr, "loop::main() must be called before loop::timer()");
111
112 // This is the first time loop::main() is called so we must be on the main-thread
113 // So name the thread "main" so we can find it during debugging.
114 set_thread_name("main");
115
116 auto ptr = std::addressof(local());
117 _main.store(ptr, std::memory_order::release);
118 return *ptr;
119 }
120
125 [[nodiscard]] hi_no_inline static loop& timer() noexcept
126 {
127 // The first time timer() is called, make sure that the main-loop exists,
128 // or even create the main-loop on the current thread.
129 [[maybe_unused]] auto const &tmp = loop::main();
130
131 return *start_subsystem_or_terminate(_timer, nullptr, timer_init, timer_deinit);
132 }
133
140 void set_maximum_frame_rate(double frame_rate) noexcept
141 {
142 hi_axiom(on_thread());
143 }
144
147 void set_vsync_monitor_id(uintptr_t id) noexcept
148 {
149 _selected_monitor_id.store(id, std::memory_order::relaxed);
150 }
151
161 template<forward_of<void()> Func>
162 void wfree_post_function(Func&& func) noexcept
163 {
164 _function_fifo.add_function(std::forward<Func>(func));
165 }
166
172 template<forward_of<void()> Func>
173 void post_function(Func&& func) noexcept
174 {
175 _function_fifo.add_function(std::forward<Func>(func));
176 notify_has_send();
177 }
178
186 template<typename Func>
187 [[nodiscard]] auto async_function(Func&& func) noexcept
188 {
189 auto future = _function_fifo.add_async_function(std::forward<Func>(func));
190 notify_has_send();
191 return future;
192 }
193
199 template<forward_of<void()> Func>
200 [[nodiscard]] callback<void()> delay_function(utc_nanoseconds time_point, Func&& func) noexcept
201 {
202 auto [callback, first_to_call] = _function_timer.delay_function(time_point, std::forward<Func>(func));
203 if (first_to_call) {
204 // Notify if the added function is the next function to call.
205 notify_has_send();
206 }
207 return std::move(callback);
208 }
209
216 template<forward_of<void()> Func>
217 [[nodiscard]] callback<void()>
218 repeat_function(std::chrono::nanoseconds period, utc_nanoseconds time_point, Func&& func) noexcept
219 {
220 auto [callback, first_to_call] = _function_timer.repeat_function(period, time_point, std::forward<Func>(func));
221 if (first_to_call) {
222 // Notify if the added function is the next function to call.
223 notify_has_send();
224 }
225 return callback;
226 }
227
233 template<forward_of<void()> Func>
234 [[nodiscard]] callback<void()> repeat_function(std::chrono::nanoseconds period, Func&& func) noexcept
235 {
236 auto [callback, first_to_call] = _function_timer.repeat_function(period, std::forward<Func>(func));
237 if (first_to_call) {
238 // Notify if the added function is the next function to call.
239 notify_has_send();
240 }
241 return std::move(callback);
242 }
243
244 void subscribe_render(weak_callback<void(utc_nanoseconds)> callback) noexcept
245 {
246 }
247
252 template<forward_of<void(utc_nanoseconds)> Func>
253 callback<void(utc_nanoseconds)> subscribe_render(Func &&func) noexcept
254 {
255 hi_axiom(on_thread());
256
257 auto cb = callback<void(utc_nanoseconds)>{std::forward<Func>(func)};
258
259 _render_functions.push_back(cb);
260
261 // Startup the vsync thread once there is a window.
262 if (not _vsync_thread.joinable()) {
263 _vsync_thread = std::jthread{[this](std::stop_token token) {
264 return vsync_thread_proc(std::move(token));
265 }};
266 }
267
268 return cb;
269 }
270
283 void add_socket(int fd, socket_event event_mask, std::function<void(int, socket_events const&)> f)
284 {
285 hi_axiom(on_thread());
286 hi_not_implemented();
287 }
288
293 void remove_socket(int fd)
294 {
295 hi_axiom(on_thread());
296 hi_not_implemented();
297 }
298
306 int resume(std::stop_token stop_token = {}) noexcept
307 {
308 auto const is_main = this == _main.load(std::memory_order::relaxed);
309
310 // Microsoft recommends an event-loop that also renders to the screen to run at above normal priority.
311 auto const thread_handle = GetCurrentThread();
312
313 int original_thread_priority = GetThreadPriority(thread_handle);
314 if (original_thread_priority == THREAD_PRIORITY_ERROR_RETURN) {
315 original_thread_priority = THREAD_PRIORITY_NORMAL;
316 hi_log_error("GetThreadPriority() for loop failed {}", get_last_error_message());
317 }
318
319 if (is_main and original_thread_priority < THREAD_PRIORITY_ABOVE_NORMAL) {
320 if (not SetThreadPriority(thread_handle, THREAD_PRIORITY_ABOVE_NORMAL)) {
321 hi_log_error("SetThreadPriority() for loop failed {}", get_last_error_message());
322 }
323 }
324
325 _exit_code = {};
326 while (not _exit_code) {
327 resume_once(true);
328
329 if (stop_token.stop_possible()) {
330 if (stop_token.stop_requested()) {
331 // Stop immediately when stop is requested.
332 _exit_code = 0;
333 }
334 } else {
335 if (_render_functions.empty() and _function_fifo.empty() and _function_timer.empty() and
336 _handles.size() <= _socket_handle_idx) {
337 // If there is not stop token, then exit when there are no more resources to wait on.
338 _exit_code = 0;
339 }
340 }
341 }
342
343 // Set the thread priority back to what is was before resume().
344 if (is_main and original_thread_priority < THREAD_PRIORITY_ABOVE_NORMAL) {
345 if (not SetThreadPriority(thread_handle, original_thread_priority)) {
346 hi_log_error("SetThreadPriority() for loop failed {}", get_last_error_message());
347 }
348 }
349
350 return *_exit_code;
351 }
352
366 void resume_once(bool block = false) noexcept
367 {
368 using namespace std::chrono_literals;
369
370 hi_axiom(on_thread());
371
372 auto const is_main = this == _main.load(std::memory_order::relaxed);
373
374 auto current_time = std::chrono::utc_clock::now();
375 auto timeout = std::chrono::duration_cast<std::chrono::milliseconds>(_function_timer.current_deadline() - current_time);
376
377 timeout = std::clamp(timeout, 0ms, 100ms);
378 auto const timeout_ms = narrow_cast<DWORD>(timeout / 1ms);
379
380 // Only handle win32 messages when blocking.
381 // Since non-blocking is called from the win32 message-pump, we do not want to re-enter the loop.
382 auto const message_mask = is_main and block ? QS_ALLINPUT : 0;
383
384 auto const wait_r =
385 MsgWaitForMultipleObjects(narrow_cast<DWORD>(_handles.size()), _handles.data(), FALSE, timeout_ms, message_mask);
386
387 if (wait_r == WAIT_FAILED) {
388 hi_log_fatal("Failed on MsgWaitForMultipleObjects(), {}", get_last_error_message());
389
390 } else if (wait_r == WAIT_TIMEOUT) {
391 // handle_functions() and handle_timers() is called after every wake-up of MsgWaitForMultipleObjects
392 ;
393
394 } else if (wait_r == WAIT_OBJECT_0 + _vsync_handle_idx) {
395 // XXX Make sure this is not starving the win32 events.
396 // should we just empty the win32 events after every unblock?
397 handle_vsync();
398
399 } else if (wait_r == WAIT_OBJECT_0 + _function_handle_idx) {
400 // handle_functions() and handle_timers() is called after every wake-up of MsgWaitForMultipleObjects
401 ;
402
403 } else if (wait_r >= WAIT_OBJECT_0 + _socket_handle_idx and wait_r < WAIT_OBJECT_0 + _handles.size()) {
404 auto const index = wait_r - WAIT_OBJECT_0;
405
406 WSANETWORKEVENTS events;
407 if (WSAEnumNetworkEvents(_sockets[index], _handles[index], &events) != 0) {
408 switch (WSAGetLastError()) {
409 case WSANOTINITIALISED:
410 hi_log_fatal("WSAStartup was not called.");
411 case WSAENETDOWN:
412 hi_log_fatal("The network subsystem has failed.");
413 case WSAEINVAL:
414 hi_log_fatal("One of the specified parameters was invalid.");
415 case WSAEINPROGRESS:
416 hi_log_warning(
417 "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a "
418 "callback "
419 "function.");
420 break;
421 case WSAEFAULT:
422 hi_log_fatal("The lpNetworkEvents parameter is not a valid part of the user address space.");
423 case WSAENOTSOCK:
424 // If somehow the socket was destroyed, lets just remove it.
425 hi_log_error("Error during WSAEnumNetworkEvents on socket {}: {}", _sockets[index], get_last_error_message());
426 _handles.erase(_handles.begin() + index);
427 _sockets.erase(_sockets.begin() + index);
428 _socket_functions.erase(_socket_functions.begin() + index);
429 break;
430 default:
431 hi_no_default();
432 }
433
434 } else {
435 // Because of how WSAEnumNetworkEvents() work we must only handle this specific socket.
436 _socket_functions[index](_sockets[index], socket_events_from_win32(events));
437 }
438
439 } else if (wait_r == WAIT_OBJECT_0 + _handles.size()) {
440 handle_gui_events();
441
442 } else if (wait_r >= WAIT_ABANDONED_0 and wait_r < WAIT_ABANDONED_0 + _handles.size()) {
443 auto const index = wait_r - WAIT_ABANDONED_0;
444 if (index == _vsync_handle_idx) {
445 hi_log_fatal("The vsync-handle has been abandoned.");
446
447 } else if (index == _function_handle_idx) {
448 hi_log_fatal("The async-handle has been abandoned.");
449
450 } else {
451 // Socket handle has been abandoned. Remove it from the handles.
452 hi_log_error("The socket-handle for socket {} has been abandoned.", _sockets[index]);
453 _handles.erase(_handles.begin() + index);
454 _sockets.erase(_sockets.begin() + index);
455 _socket_functions.erase(_socket_functions.begin() + index);
456 }
457
458 } else {
459 hi_no_default();
460 }
461
462 // Make sure timers are handled first, possibly they are time critical.
463 handle_timers();
464
465 // When functions are added wait-free, the function-event is never triggered.
466 // So handle messages after any kind of wake up.
467 handle_functions();
468 }
469
474 [[nodiscard]] bool on_thread() const noexcept
475 {
476 return current_thread_id() == _thread_id;
477 }
478
479private:
482 inline static std::atomic<loop *> _main;
483
486 inline static std::atomic<loop *> _timer;
487
488 inline static std::jthread _timer_thread;
489
490 function_fifo<> _function_fifo;
491 function_timer _function_timer;
492
493 std::optional<int> _exit_code = {};
494 double _maximum_frame_rate = 30.0;
495 std::chrono::nanoseconds _minimum_frame_time = std::chrono::nanoseconds(33'333'333);
496 thread_id _thread_id;
497 std::vector<weak_callback<void(utc_nanoseconds)>> _render_functions;
498
499struct socket_type {
500 int fd;
501 socket_event mode;
502 std::function<void(int, socket_events const&)> callback;
503 };
504
505 constexpr static size_t _vsync_handle_idx = 0;
506 constexpr static size_t _function_handle_idx = 1;
507 constexpr static size_t _socket_handle_idx = 2;
508
516 HANDLE _use_vsync_handle;
517
521
524 bool _vsync_time_from_sleep = true;
525
530 std::atomic<uint16_t> _pull_down = 0x100;
531
536 uint64_t _sub_frame_count = 0;
537
542 uint64_t _frame_count = 0;
543
552 std::vector<HANDLE> _handles;
553
560 std::vector<int> _sockets;
561
564 std::vector<std::function<void(int, socket_events const&)>> _socket_functions;
565
568 std::jthread _vsync_thread;
569
572 HANDLE _vsync_thread_handle;
573
576 int _vsync_thread_priority = THREAD_PRIORITY_NORMAL;
577
580 std::atomic<std::uintptr_t> _selected_monitor_id = 0;
581
585 std::uintptr_t _vsync_monitor_id = 0;
586
589 IDXGIOutput *_vsync_monitor_output = nullptr;
590
591 static loop *timer_init() noexcept
592 {
593 hi_assert(not _timer_thread.joinable());
594
595 _timer_thread = std::jthread{[](std::stop_token stop_token) {
596 _timer.store(std::addressof(loop::local()), std::memory_order::release);
597
598 set_thread_name("timer");
599 loop::local().resume(stop_token);
600 }};
601
602 while (true) {
603 if (auto ptr = _timer.load(std::memory_order::relaxed)) {
604 return ptr;
605 }
607 }
608 }
609
610 static void timer_deinit() noexcept
611 {
612 if (auto const *const ptr = _timer.exchange(nullptr, std::memory_order::acquire)) {
613 hi_assert(_timer_thread.joinable());
614 _timer_thread.request_stop();
615 _timer_thread.join();
616 }
617 }
618
621 void notify_has_send() noexcept
622 {
623 if (not SetEvent(_handles[_function_handle_idx])) {
624 hi_log_error("Could not trigger async-event. {}", get_last_error_message());
625 }
626 }
627
632 void handle_vsync() noexcept
633 {
634 // XXX Reduce the number of redraws for each window based on the refresh rate of the monitor they are located on.
635 // XXX handle maximum frame rate and update vsync thread
636 // XXX Update active windows more often than inactive windows.
637
638 if (not _vsync_thread.joinable()) {
639 // Fallback for the vsync_time advancing when the vsync thread is not running.
640 _vsync_time.store(std::chrono::utc_clock::now());
641 }
642
643 auto const display_time = _vsync_time.load(std::memory_order::relaxed) + std::chrono::milliseconds(30);
644
645 for (auto& render_function : _render_functions) {
646 if (auto rf = render_function.lock()) {
647 rf(display_time);
648 }
649 }
650
651 std::erase_if(_render_functions, [](auto& render_function) {
652 return render_function.expired();
653 });
654
655 if (_render_functions.empty()) {
656 // Stop the vsync thread when there are no more windows.
657 if (_vsync_thread.joinable()) {
658 _vsync_thread.request_stop();
659 }
660 }
661 }
662
667 void handle_functions() noexcept
668 {
669 _function_fifo.run_all();
670 }
671
672 void handle_timers() noexcept
673 {
674 _function_timer.run_all(std::chrono::utc_clock::now());
675 }
676
681 void handle_gui_events() noexcept
682 {
683 MSG msg = {};
684 auto const t1 = trace<"loop:gui-events">();
685 while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE | PM_NOYIELD)) {
686 auto const t2 = trace<"loop:gui-event">();
687
688 if (msg.message == WM_QUIT) {
689 _exit_code = narrow_cast<int>(msg.wParam);
690 continue;
691 }
692
693 TranslateMessage(&msg);
694 DispatchMessageW(&msg);
695 }
696 }
697
702 void vsync_thread_update_dxgi_output() noexcept
703 {
704 if (not compare_store(_vsync_monitor_id, _selected_monitor_id.load(std::memory_order::relaxed))) {
705 return;
706 }
707
708 if (_vsync_monitor_output) {
709 _vsync_monitor_output->Release();
710 _vsync_monitor_output = nullptr;
711 }
712
713 IDXGIFactory *factory = nullptr;
714 if (FAILED(CreateDXGIFactory(__uuidof(IDXGIFactory), (void **)&factory))) {
715 hi_log_error_once("vsync:error:CreateDXGIFactory", "Could not IDXGIFactory. {}", get_last_error_message());
716 return;
717 }
718 hi_assert_not_null(factory);
719 auto d1 = defer([&] {
720 factory->Release();
721 });
722
723 IDXGIAdapter *adapter = nullptr;
724 if (FAILED(factory->EnumAdapters(0, &adapter))) {
725 hi_log_error_once("vsync:error:EnumAdapters", "Could not get IDXGIAdapter. {}", get_last_error_message());
726 return;
727 }
728 hi_assert_not_null(adapter);
729 auto d2 = defer([&] {
730 adapter->Release();
731 });
732
733 if (FAILED(adapter->EnumOutputs(0, &_vsync_monitor_output))) {
734 hi_log_error_once("vsync:error:EnumOutputs", "Could not get IDXGIOutput. {}", get_last_error_message());
735 return;
736 }
737
738 DXGI_OUTPUT_DESC description;
739 if (FAILED(_vsync_monitor_output->GetDesc(&description))) {
740 hi_log_error_once("vsync:error:GetDesc", "Could not get IDXGIOutput description. {}", get_last_error_message());
741 _vsync_monitor_output->Release();
742 _vsync_monitor_output = nullptr;
743 return;
744 }
745
746 if (description.Monitor != std::bit_cast<HMONITOR>(_vsync_monitor_id)) {
747 hi_log_error_once("vsync:error:not-primary-monitor", "DXGI primary monitor does not match desktop primary monitor");
748 _vsync_monitor_output->Release();
749 _vsync_monitor_output = nullptr;
750 return;
751 }
752
753 d2.cancel();
754 d1.cancel();
755 }
756
766 std::chrono::nanoseconds vsync_thread_update_time(bool on_sleep)
767 {
768 auto const ts = time_stamp_count(time_stamp_count::inplace_with_cpu_id{});
769 auto const new_time = time_stamp_utc::make(ts);
770
771 auto const was_sleeping = std::exchange(_vsync_time_from_sleep, on_sleep);
772 auto const old_time = _vsync_time.exchange(new_time, std::memory_order::acquire);
773
774 // If old_time was caused by sleeping it can not be used to calculate how long vsync was blocking.
775 return was_sleeping ? std::chrono::nanoseconds::max() : new_time - old_time;
776 }
777
778 void vsync_thread_wait_for_vblank() noexcept
779 {
780 using namespace std::chrono_literals;
781
782 vsync_thread_update_dxgi_output();
783
784 if (_vsync_monitor_output and FAILED(_vsync_monitor_output->WaitForVBlank())) {
785 hi_log_error_once("vsync:error:WaitForVBlank", "WaitForVBlank() failed. {}", get_last_error_message());
786 }
787
788 if (vsync_thread_update_time(false) < 1ms) {
789 hi_log_info_once("vsync:monitor-off", "WaitForVBlank() did not block; is the monitor turned off?");
790 Sleep(16);
791
792 // Fixup the time after the fallback sleep.
793 vsync_thread_update_time(true);
794 } else {
795 ++global_counter<"vsync:vertical-blank">;
796 }
797 }
798
806 [[nodiscard]] bool vsync_thread_pull_down() noexcept
807 {
808 _sub_frame_count += _pull_down.load(std::memory_order::relaxed);
809 return compare_store(_frame_count, _sub_frame_count >> 8);
810 }
811
817 void vsync_thread_update_priority(int new_priority) noexcept
818 {
819 if (std::exchange(_vsync_thread_priority, new_priority) != new_priority) {
820 if (not SetThreadPriority(_vsync_thread_handle, new_priority)) {
821 hi_log_error_once("vsync:error:SetThreadPriority", "Could not set the vsync thread priority to {}", new_priority);
822 }
823 }
824 }
825
826 void vsync_thread_proc(std::stop_token stop_token) noexcept
827 {
828 _vsync_thread_handle = GetCurrentThread();
829 set_thread_name("vsync");
830
831 while (not stop_token.stop_requested()) {
832 switch (WaitForSingleObject(_use_vsync_handle, 30)) {
833 case WAIT_TIMEOUT:
834 // When use_vsync is off wake the main loop every 30ms.
835 vsync_thread_update_time(true);
836
837 vsync_thread_update_priority(THREAD_PRIORITY_NORMAL);
838
839 ++global_counter<"vsync:low-priority">;
840 ++global_counter<"vsync:frame">;
841 SetEvent(_handles[_vsync_handle_idx]);
842 break;
843
844 case WAIT_OBJECT_0:
845 // When use_vsync is on wake the main loop based on the vertical-sync and pull_down.
846 vsync_thread_update_priority(THREAD_PRIORITY_TIME_CRITICAL);
847
848 vsync_thread_wait_for_vblank();
849
850 if (vsync_thread_pull_down()) {
851 ++global_counter<"vsync:frame">;
852 SetEvent(_handles[_vsync_handle_idx]);
853 }
854
855 break;
856
857 case WAIT_ABANDONED:
858 hi_log_error_once("vsync:error:WAIT_ABANDONED", "use_vsync_handle has been abandoned.");
859 ResetEvent(_use_vsync_handle);
860 break;
861
862 case WAIT_FAILED:
863 hi_log_error_once("vsync:error:WAIT_FAILED", "WaitForSingleObject failed. {}", get_last_error_message());
864 ResetEvent(_use_vsync_handle);
865 break;
866 }
867 }
868 }
869};
870
871namespace detail {
872inline thread_local std::unique_ptr<loop> thread_local_loop;
873}
874
877[[nodiscard]] hi_no_inline inline loop& loop::local() noexcept
878{
879 if (not detail::thread_local_loop) {
880 detail::thread_local_loop = std::make_unique<loop>();
881 }
882 return *detail::thread_local_loop;
883}
884
885template<typename R, typename... Args>
886template<forward_of<void()> Func>
887void notifier<R(Args...)>::loop_local_post_function(Func&& func) const noexcept
888{
889 return loop::local().post_function(std::forward<Func>(func));
890}
891
892template<typename R, typename... Args>
893template<forward_of<void()> Func>
894void notifier<R(Args...)>::loop_main_post_function(Func&& func) const noexcept
895{
896 return loop::main().post_function(std::forward<Func>(func));
897}
898
899template<typename R, typename... Args>
900template<forward_of<void()> Func>
901void notifier<R(Args...)>::loop_timer_post_function(Func&& func) const noexcept
902{
903 return loop::timer().post_function(std::forward<Func>(func));
904}
905
906} // namespace hi::inline v1
Functions and types for accessing operating system threads.
Definition of the unfair_mutex.
Rules for working with win32 headers.
void set_thread_name(std::string_view name) noexcept
Set the name of the current thread.
bool compare_store(T &lhs, U &&rhs) noexcept
Compare then store if there was a change.
Definition misc.hpp:53
hi_export std::string get_last_error_message(uint32_t error_code)
Get the error message from an error code.
Definition exception_win32_impl.hpp:21
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
@ local
Call the function asynchronously from the current thread's loop.
A fifo (First-in, Firts-out) for asynchronous calls.
Definition function_fifo.hpp:26
void run_all() noexcept
Run all the functions posted or send on the fifo.
Definition function_fifo.hpp:58
A timer that calls functions.
Definition function_timer.hpp:22
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:103
Definition loop_win32_intf.hpp:32
void resume_once(bool block=false) noexcept
Resume for a single iteration.
Definition loop_win32_intf.hpp:366
callback< void(utc_nanoseconds)> subscribe_render(Func &&func) noexcept
Subscribe a render function to be called on vsync.
Definition loop_win32_intf.hpp:253
bool on_thread() const noexcept
Check if the current thread is the same as the loop's thread.
Definition loop_win32_intf.hpp:474
auto async_function(Func &&func) noexcept
Call a function from the loop.
Definition loop_win32_intf.hpp:187
static hi_no_inline loop & timer() noexcept
Get or create the timer event-loop.
Definition loop_win32_intf.hpp:125
callback< void()> repeat_function(std::chrono::nanoseconds period, Func &&func) noexcept
Call a function repeatedly.
Definition loop_win32_intf.hpp:234
void wfree_post_function(Func &&func) noexcept
Wait-free post a function to be called from the loop.
Definition loop_win32_intf.hpp:162
callback< void()> delay_function(utc_nanoseconds time_point, Func &&func) noexcept
Call a function at a certain time.
Definition loop_win32_intf.hpp:200
void set_vsync_monitor_id(uintptr_t id) noexcept
Set the monitor id for vertical sync.
Definition loop_win32_intf.hpp:147
void post_function(Func &&func) noexcept
Post a function to be called from the loop.
Definition loop_win32_intf.hpp:173
int resume(std::stop_token stop_token={}) noexcept
Resume the loop on the current thread.
Definition loop_win32_intf.hpp:306
void set_maximum_frame_rate(double frame_rate) noexcept
Set maximum frame rate.
Definition loop_win32_intf.hpp:140
static hi_no_inline loop & main() noexcept
Get or create the main-loop.
Definition loop_win32_intf.hpp:104
void remove_socket(int fd)
Remove the callback associated with a socket.
Definition loop_win32_intf.hpp:293
callback< void()> repeat_function(std::chrono::nanoseconds period, utc_nanoseconds time_point, Func &&func) noexcept
Call a function repeatedly.
Definition loop_win32_intf.hpp:218
void add_socket(int fd, socket_event event_mask, std::function< void(int, socket_events const &)> f)
Add a callback that reacts on a socket.
Definition loop_win32_intf.hpp:283
A notifier which can be used to call a set of registered callbacks.
Definition notifier.hpp:26
Definition socket_event_intf.hpp:74
T addressof(T... args)
T exchange(T... args)
T load(T... args)
T move(T... args)
T sleep_for(T... args)
T store(T... args)