HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
subsystem.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2021-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
9#pragma once
10
11#include "../utility/utility.hpp"
13#include "global_state.hpp"
14#include "../macros.hpp"
15#include <atomic>
16#include <vector>
17#include <functional>
18#include <bit>
19#include <type_traits>
20#include <mutex>
21
22hi_export_module(hikogui.concurrency.subsystem);
23
24
25hi_export namespace hi { inline namespace v1 {
26namespace detail {
27
30inline std::vector<void (*)()> subsystem_deinit_list;
31
37
38template<typename T>
39hi_no_inline typename T::value_type start_subsystem(
40 T& check_variable,
41 typename T::value_type off_value,
42 typename T::value_type (*init_function)(),
43 void (*deinit_function)())
44 requires(is_atomic_v<T>)
45{
46 hi_assert_not_null(init_function);
47 hi_assert_not_null(deinit_function);
48 auto const lock = std::scoped_lock(subsystem_mutex);
49
50 auto const old_value = check_variable.load(std::memory_order::acquire);
51 if (old_value != off_value) {
52 // In the short time before the lock the subsystem became available.
53 return old_value;
54 }
55
56 if (not is_system_running()) {
57 // Only when the system is running can subsystems be started.
58 // otherwise they have to run in degraded mode.
59 return off_value;
60 }
61
62 auto new_value = init_function();
63
64 if (new_value != off_value) {
65 subsystem_deinit_list.emplace_back(deinit_function);
66 check_variable.store(new_value, std::memory_order::release);
67 }
68
69 return new_value;
70}
71
72hi_no_inline inline bool start_subsystem(global_state_type state_bit, bool (*init_function)(), void (*deinit_function)())
73{
74 hi_assert(std::popcount(std::to_underlying(state_bit)) == 1);
75 hi_assert_not_null(init_function);
76 hi_assert_not_null(deinit_function);
77
78 auto const lock = std::scoped_lock(subsystem_mutex);
79
80 auto const old_state = global_state.load(std::memory_order::acquire);
81 if (not is_system_running(old_state)) {
82 // Only when the system is running can subsystems be started.
83 // otherwise they have to run in degraded mode.
84 return false;
85
86 } else if (to_bool(old_state & state_bit)) {
87 // In the short time before the lock the subsystem became available.
88 return true;
89 }
90
91 if (init_function()) {
92 subsystem_deinit_list.emplace_back(deinit_function);
93 global_state_enable(state_bit, std::memory_order::release);
94 return true;
95 }
96
97 return false;
98}
99
100} // namespace detail
101
116template<typename T>
117typename T::value_type start_subsystem(
118 T& check_variable,
119 typename T::value_type off_value,
120 typename T::value_type (*init_function)(),
121 void (*deinit_function)())
122 requires(is_atomic_v<T>)
123{
124 // We can do a relaxed load, if:
125 // - off_value, then we will lock before writing check_variable and memory order will be guaranteed
126 // - not off_value, The system is started. If the subsystem is turning off we can't deal with that anyway.
127 auto const old_value = check_variable.load(std::memory_order::relaxed);
128 if (old_value == off_value) {
129 return detail::start_subsystem(check_variable, off_value, init_function, deinit_function);
130 } else {
131 [[likely]] return old_value;
132 }
133}
134
148inline bool start_subsystem(global_state_type state_bit, bool (*init_function)(), void (*deinit_function)())
149{
150 // We can do a relaxed load, if:
151 // - off_value, then we will lock before writing check_variable and memory order will be guaranteed
152 // - not off_value, The system is started. If the subsystem is turning off we can't deal with that anyway.
153 if (not to_bool(global_state.load(std::memory_order::relaxed) & state_bit)) {
154 return detail::start_subsystem(state_bit, init_function, deinit_function);
155 } else {
156 [[likely]] return true;
157 }
158}
159
177template<typename T>
178 requires(is_atomic_v<T>)
179typename T::value_type start_subsystem_or_terminate(
180 T& check_variable,
181 typename T::value_type off_value,
182 typename T::value_type (*init_function)(),
183 void (*deinit_function)())
184{
185 auto old_value = check_variable.load(std::memory_order::acquire);
186 if (old_value == off_value) {
187 auto tmp = detail::start_subsystem(check_variable, off_value, init_function, deinit_function);
188 hi_assert(tmp != off_value);
189 return tmp;
190 } else {
191 [[likely]] return old_value;
192 }
193}
194
203inline void stop_subsystem(void (*deinit_function)())
204{
205 hi_assert_not_null(deinit_function);
206
207 auto const lock = std::scoped_lock(detail::subsystem_mutex);
208
209 std::erase(detail::subsystem_deinit_list, deinit_function);
210 return deinit_function();
211}
212
218inline void start_system() noexcept
219{
220 global_state |= global_state_type::system_is_running;
221}
222
231inline void shutdown_system() noexcept
232{
233 detail::subsystem_mutex.lock();
234 global_state |= global_state_type::system_is_shutting_down;
235
236 while (!detail::subsystem_deinit_list.empty()) {
237 auto deinit = std::move(detail::subsystem_deinit_list.back());
238 hi_assert_not_null(deinit);
239 detail::subsystem_deinit_list.pop_back();
240
241 detail::subsystem_mutex.unlock();
242 deinit();
243 detail::subsystem_mutex.lock();
244 }
245 detail::subsystem_mutex.unlock();
246}
247
248}} // namespace hi::v1
An atomic access global variable for quick access to state of the system.
unfair_recursive_mutex subsystem_mutex
Mutex to be held when writing to system_status or accessing system_status_deinit_list.
Definition subsystem.hpp:36
std::vector< void(*)()> subsystem_deinit_list
A list of deinit function to be called on shutdown.
Definition subsystem.hpp:30
Definition of the unfair_recursive_mutex.
bool global_state_enable(global_state_type subsystem, std::memory_order order=std::memory_order::seq_cst) noexcept
Enable a subsystem.
Definition global_state.hpp:262
bool is_system_running() noexcept
Check if the HikoGUI system is running.
Definition global_state.hpp:211
T::value_type start_subsystem_or_terminate(T &check_variable, typename T::value_type off_value, typename T::value_type(*init_function)(), void(*deinit_function)())
Start a sub-system.
Definition subsystem.hpp:179
void shutdown_system() noexcept
Shutdown the system.
Definition subsystem.hpp:231
T::value_type start_subsystem(T &check_variable, typename T::value_type off_value, typename T::value_type(*init_function)(), void(*deinit_function)())
Start a sub-system.
Definition subsystem.hpp:117
std::atomic< global_state_type > global_state
The global state of the hikogui framework.
Definition global_state.hpp:203
global_state_type
The flag-type used for global state.
Definition global_state.hpp:32
void stop_subsystem(void(*deinit_function)())
Stop a sub-system.
Definition subsystem.hpp:203
void start_system() noexcept
Start the system.
Definition subsystem.hpp:218
The HikoGUI namespace.
Definition array_generic.hpp:20
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
An unfair recursive-mutex This is a fast implementation of a recursive-mutex which does not fairly ar...
Definition unfair_recursive_mutex.hpp:42
T move(T... args)