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/module.hpp"
13#include "global_state.hpp"
14#include <atomic>
15#include <vector>
16#include <functional>
17#include <bit>
18#include <type_traits>
19#include <mutex>
20
21namespace hi { inline namespace v1 {
22namespace detail {
23
26inline std::vector<void (*)()> subsystem_deinit_list;
27
33
34template<typename T>
35hi_no_inline typename T::value_type start_subsystem(
36 T& check_variable,
37 typename T::value_type off_value,
38 typename T::value_type (*init_function)(),
39 void (*deinit_function)())
40 requires(is_atomic_v<T>)
41{
42 hi_assert_not_null(init_function);
43 hi_assert_not_null(deinit_function);
44 hilet lock = std::scoped_lock(subsystem_mutex);
45
46 hilet old_value = check_variable.load(std::memory_order::acquire);
47 if (old_value != off_value) {
48 // In the short time before the lock the subsystem became available.
49 return old_value;
50 }
51
52 if (not is_system_running()) {
53 // Only when the system is running can subsystems be started.
54 // otherwise they have to run in degraded mode.
55 return off_value;
56 }
57
58 auto new_value = init_function();
59
60 if (new_value != off_value) {
61 subsystem_deinit_list.emplace_back(deinit_function);
62 check_variable.store(new_value, std::memory_order::release);
63 }
64
65 return new_value;
66}
67
68hi_no_inline inline bool start_subsystem(global_state_type state_bit, bool (*init_function)(), void (*deinit_function)())
69{
70 hi_assert(std::popcount(to_underlying(state_bit)) == 1);
71 hi_assert_not_null(init_function);
72 hi_assert_not_null(deinit_function);
73
74 hilet lock = std::scoped_lock(subsystem_mutex);
75
76 hilet old_state = global_state.load(std::memory_order::acquire);
77 if (not is_system_running(old_state)) {
78 // Only when the system is running can subsystems be started.
79 // otherwise they have to run in degraded mode.
80 return false;
81
82 } else if (to_bool(old_state & state_bit)) {
83 // In the short time before the lock the subsystem became available.
84 return true;
85 }
86
87 if (init_function()) {
88 subsystem_deinit_list.emplace_back(deinit_function);
89 global_state_enable(state_bit, std::memory_order::release);
90 return true;
91 }
92
93 return false;
94}
95
96} // namespace detail
97
112template<typename T>
113typename T::value_type start_subsystem(
114 T& check_variable,
115 typename T::value_type off_value,
116 typename T::value_type (*init_function)(),
117 void (*deinit_function)())
118 requires(is_atomic_v<T>)
119{
120 // We can do a relaxed load, if:
121 // - off_value, then we will lock before writing check_variable and memory order will be guaranteed
122 // - not off_value, The system is started. If the subsystem is turning off we can't deal with that anyway.
123 hilet old_value = check_variable.load(std::memory_order::relaxed);
124 if (old_value == off_value) {
125 return detail::start_subsystem(check_variable, off_value, init_function, deinit_function);
126 } else {
127 [[likely]] return old_value;
128 }
129}
130
144inline bool start_subsystem(global_state_type state_bit, bool (*init_function)(), void (*deinit_function)())
145{
146 // We can do a relaxed load, if:
147 // - off_value, then we will lock before writing check_variable and memory order will be guaranteed
148 // - not off_value, The system is started. If the subsystem is turning off we can't deal with that anyway.
149 if (not to_bool(global_state.load(std::memory_order::relaxed) & state_bit)) {
150 return detail::start_subsystem(state_bit, init_function, deinit_function);
151 } else {
152 [[likely]] return true;
153 }
154}
155
173template<typename T>
174 requires(is_atomic_v<T>)
175typename T::value_type start_subsystem_or_terminate(
176 T& check_variable,
177 typename T::value_type off_value,
178 typename T::value_type (*init_function)(),
179 void (*deinit_function)())
180{
181 auto old_value = check_variable.load(std::memory_order::acquire);
182 if (old_value == off_value) {
183 auto tmp = detail::start_subsystem(check_variable, off_value, init_function, deinit_function);
184 hi_assert(tmp != off_value);
185 return tmp;
186 } else {
187 [[likely]] return old_value;
188 }
189}
190
199inline void stop_subsystem(void (*deinit_function)())
200{
201 hi_assert_not_null(deinit_function);
202
203 hilet lock = std::scoped_lock(detail::subsystem_mutex);
204
205 std::erase(detail::subsystem_deinit_list, deinit_function);
206 return deinit_function();
207}
208
214inline void start_system() noexcept
215{
216 global_state |= global_state_type::system_is_running;
217}
218
227inline void shutdown_system() noexcept
228{
229 detail::subsystem_mutex.lock();
230 global_state |= global_state_type::system_is_shutting_down;
231
232 while (!detail::subsystem_deinit_list.empty()) {
233 auto deinit = std::move(detail::subsystem_deinit_list.back());
234 hi_assert_not_null(deinit);
235 detail::subsystem_deinit_list.pop_back();
236
237 detail::subsystem_mutex.unlock();
238 deinit();
239 detail::subsystem_mutex.lock();
240 }
241 detail::subsystem_mutex.unlock();
242}
243
244}} // 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:32
std::vector< void(*)()> subsystem_deinit_list
A list of deinit function to be called on shutdown.
Definition subsystem.hpp:26
Definition of the unfair_recursive_mutex.
#define hi_assert(expression,...)
Assert if expression is true.
Definition assert.hpp:199
#define hi_assert_not_null(x,...)
Assert if an expression is not nullptr.
Definition assert.hpp:238
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
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:257
bool is_system_running() noexcept
Check if the HikoGUI system is running.
Definition global_state.hpp:206
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:175
void shutdown_system() noexcept
Shutdown the system.
Definition subsystem.hpp:227
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:113
std::atomic< global_state_type > global_state
The global state of the hikogui framework.
Definition global_state.hpp:198
global_state_type
The flag-type used for global state.
Definition global_state.hpp:27
void stop_subsystem(void(*deinit_function)())
Stop a sub-system.
Definition subsystem.hpp:199
void start_system() noexcept
Start the system.
Definition subsystem.hpp:214
DOXYGEN BUG.
Definition algorithm.hpp:13
geometry/margins.hpp
Definition cache.hpp:11
An unfair recursive-mutex This is a fast implementation of a recursive-mutex which does not fairly ar...
Definition unfair_recursive_mutex.hpp:37
T move(T... args)