HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
subsystem.hpp
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
5#pragma once
6
7#include "assert.hpp"
8#include "unfair_recursive_mutex.hpp"
9#include "type_traits.hpp"
10#include "global_state.hpp"
11#include <atomic>
12#include <vector>
13#include <functional>
14#include <bit>
15#include <type_traits>
16#include <mutex>
17
18namespace hi::inline v1 {
19namespace detail {
20
23inline std::vector<void (*)()> subsystem_deinit_list;
24
29inline unfair_recursive_mutex subsystem_mutex;
30
31template<typename T>
32hi_no_inline typename T::value_type start_subsystem(
33 T& check_variable,
34 typename T::value_type off_value,
35 typename T::value_type (*init_function)(),
36 void (*deinit_function)()) requires(is_atomic_v<T>)
37{
38 hi_assert_not_null(init_function);
39 hi_assert_not_null(deinit_function);
40 hilet lock = std::scoped_lock(subsystem_mutex);
41
42 hilet old_value = check_variable.load(std::memory_order::acquire);
43 if (old_value != off_value) {
44 // In the short time before the lock the subsystem became available.
45 return old_value;
46 }
47
48 if (not is_system_running()) {
49 // Only when the system is running can subsystems be started.
50 // otherwise they have to run in degraded mode.
51 return off_value;
52 }
53
54 auto new_value = init_function();
55
56 if (new_value != off_value) {
57 subsystem_deinit_list.emplace_back(deinit_function);
58 check_variable.store(new_value, std::memory_order::release);
59 }
60
61 return new_value;
62}
63
64hi_no_inline inline bool start_subsystem(global_state_type state_bit, bool (*init_function)(), void (*deinit_function)())
65{
66 hi_assert(std::popcount(to_underlying(state_bit)) == 1);
67 hi_assert_not_null(init_function);
68 hi_assert_not_null(deinit_function);
69
70 hilet lock = std::scoped_lock(subsystem_mutex);
71
72 hilet old_state = global_state.load(std::memory_order::acquire);
73 if (not is_system_running(old_state)) {
74 // Only when the system is running can subsystems be started.
75 // otherwise they have to run in degraded mode.
76 return false;
77
78 } else if (to_bool(old_state & state_bit)) {
79 // In the short time before the lock the subsystem became available.
80 return true;
81 }
82
83 if (init_function()) {
84 subsystem_deinit_list.emplace_back(deinit_function);
85 global_state_enable(state_bit, std::memory_order::release);
86 return true;
87 }
88
89 return false;
90}
91
92} // namespace detail
93
107template<typename T>
108typename T::value_type start_subsystem(
109 T& check_variable,
110 typename T::value_type off_value,
111 typename T::value_type (*init_function)(),
112 void (*deinit_function)()) requires(is_atomic_v<T>)
113{
114 // We can do a relaxed load, if:
115 // - off_value, then we will lock before writing check_variable and memory order will be guaranteed
116 // - not off_value, The system is started. If the subsystem is turning off we can't deal with that anyway.
117 hilet old_value = check_variable.load(std::memory_order::relaxed);
118 if (old_value == off_value) {
119 return detail::start_subsystem(check_variable, off_value, init_function, deinit_function);
120 } else {
121 [[likely]] return old_value;
122 }
123}
124
137inline bool start_subsystem(global_state_type state_bit, bool (*init_function)(), void (*deinit_function)())
138{
139 // We can do a relaxed load, if:
140 // - off_value, then we will lock before writing check_variable and memory order will be guaranteed
141 // - not off_value, The system is started. If the subsystem is turning off we can't deal with that anyway.
142 if (not to_bool(global_state.load(std::memory_order::relaxed) & state_bit)) {
143 return detail::start_subsystem(state_bit, init_function, deinit_function);
144 } else {
145 [[likely]] return true;
146 }
147}
148
165template<typename T>
166requires(is_atomic_v<T>) typename T::value_type start_subsystem_or_terminate(
167 T& check_variable,
168 typename T::value_type off_value,
169 typename T::value_type (*init_function)(),
170 void (*deinit_function)())
171{
172 auto old_value = check_variable.load(std::memory_order::acquire);
173 if (old_value == off_value) {
174 auto tmp = detail::start_subsystem(check_variable, off_value, init_function, deinit_function);
175 hi_assert(tmp != off_value);
176 return tmp;
177 } else {
178 [[likely]] return old_value;
179 }
180}
181
189inline void stop_subsystem(void (*deinit_function)())
190{
191 hi_assert_not_null(deinit_function);
192
193 hilet lock = std::scoped_lock(detail::subsystem_mutex);
194
195 std::erase(detail::subsystem_deinit_list, deinit_function);
196 return deinit_function();
197}
198
202inline void start_system() noexcept
203{
204 global_state |= global_state_type::system_is_running;
205}
206
213inline void shutdown_system() noexcept
214{
215 detail::subsystem_mutex.lock();
216 global_state |= global_state_type::system_is_shutting_down;
217
218 while (!detail::subsystem_deinit_list.empty()) {
219 auto deinit = std::move(detail::subsystem_deinit_list.back());
220 hi_assert_not_null(deinit);
221 detail::subsystem_deinit_list.pop_back();
222
223 detail::subsystem_mutex.unlock();
224 deinit();
225 detail::subsystem_mutex.lock();
226 }
227 detail::subsystem_mutex.unlock();
228}
229
230} // namespace hi::inline v1
Utilities to assert and bound check.
#define hi_assert_not_null(x)
Assert if an expression is not nullptr.
Definition assert.hpp:118
#define hi_assert(expression)
Assert if expression is true.
Definition assert.hpp:86
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:15
void start_system() noexcept
Start the system.
Definition subsystem.hpp:202
void shutdown_system() noexcept
Shutdown the system.
Definition subsystem.hpp:213
std::atomic< global_state_type > global_state
The global state of the hikogui framework.
Definition global_state.hpp:187
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:166
void stop_subsystem(void(*deinit_function)())
Stop a sub-system.
Definition subsystem.hpp:189
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:227
T lock(T... args)
T move(T... args)