HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
subsystem.hpp
1// Copyright Take Vos 2019-2021.
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 tt {
19namespace detail {
20
23inline std::vector<void (*)()> subsystem_deinit_list;
24
29inline unfair_recursive_mutex subsystem_mutex;
30
31template<typename T>
32requires(is_atomic_v<T>) tt_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)())
37{
38 ttlet lock = std::scoped_lock(subsystem_mutex);
39
40 auto old_value = check_variable.load(std::memory_order::acquire);
41 if (old_value != off_value) {
42 // In the short time before the lock the subsystem became available.
43 return old_value;
44 }
45
46 if (not is_system_running()) {
47 // Only when the system is running can subsystems be started.
48 // otherwise they have to run in degraded mode.
49 return off_value;
50 }
51
52 auto new_value = init_function();
53
54 if (new_value != off_value) {
55 subsystem_deinit_list.emplace_back(deinit_function);
56 check_variable.store(new_value, std::memory_order::release);
57 }
58
59 return new_value;
60}
61
62tt_no_inline inline bool start_subsystem(global_state_type state_bit, bool (*init_function)(), void (*deinit_function)())
63{
64 tt_axiom(std::popcount(to_underlying(state_bit)) == 1);
65 ttlet lock = std::scoped_lock(subsystem_mutex);
66
67 ttlet old_state = global_state.load(std::memory_order::acquire);
68 if (not is_system_running(old_state)) {
69 // Only when the system is running can subsystems be started.
70 // otherwise they have to run in degraded mode.
71 return false;
72
73 } else if (to_bool(old_state & state_bit)) {
74 // In the short time before the lock the subsystem became available.
75 return true;
76 }
77
78 if (init_function()) {
79 subsystem_deinit_list.emplace_back(deinit_function);
80 global_state_enable(state_bit, std::memory_order::release);
81 return true;
82 }
83
84 return false;
85}
86
87} // namespace detail
88
102template<typename T>
103requires(is_atomic_v<T>) typename T::value_type start_subsystem(
104 T &check_variable,
105 typename T::value_type off_value,
106 typename T::value_type (*init_function)(),
107 void (*deinit_function)())
108{
109 // We can do a relaxed load, if:
110 // - off_value, then we will lock before writing check_variable and memory order will be guaranteed
111 // - not off_value, The system is started. If the subsystem is turning off we can't deal with that anyway.
112 auto old_value = check_variable.load(std::memory_order::relaxed);
113 if (old_value == off_value) {
114 return detail::start_subsystem(check_variable, off_value, init_function, deinit_function);
115 } else {
116 [[likely]] return old_value;
117 }
118}
119
132inline bool start_subsystem(global_state_type state_bit, bool (*init_function)(), void (*deinit_function)())
133{
134 // We can do a relaxed load, if:
135 // - off_value, then we will lock before writing check_variable and memory order will be guaranteed
136 // - not off_value, The system is started. If the subsystem is turning off we can't deal with that anyway.
137 if (not to_bool(global_state.load(std::memory_order::relaxed) & state_bit)) {
138 return detail::start_subsystem(state_bit, init_function, deinit_function);
139 } else {
140 [[likely]] return true;
141 }
142}
143
160template<typename T>
161requires(is_atomic_v<T>) typename T::value_type start_subsystem_or_terminate(
162 T &check_variable,
163 typename T::value_type off_value,
164 typename T::value_type (*init_function)(),
165 void (*deinit_function)())
166{
167 auto old_value = check_variable.load(std::memory_order::acquire);
168 if (old_value == off_value) {
169 auto tmp = detail::start_subsystem(check_variable, off_value, init_function, deinit_function);
170 tt_assert(tmp != off_value);
171 return tmp;
172 } else {
173 [[likely]] return old_value;
174 }
175}
176
184inline void stop_subsystem(void (*deinit_function)())
185{
186 ttlet lock = std::scoped_lock(detail::subsystem_mutex);
187
188 std::erase(detail::subsystem_deinit_list, deinit_function);
189 return deinit_function();
190}
191
195inline void start_system() noexcept
196{
197 global_state |= global_state_type::system_is_running;
198}
199
206inline void shutdown_system() noexcept
207{
208 detail::subsystem_mutex.lock();
209 global_state |= global_state_type::system_is_shutting_down;
210
211 while (!detail::subsystem_deinit_list.empty()) {
212 auto deinit = std::move(detail::subsystem_deinit_list.back());
213 detail::subsystem_deinit_list.pop_back();
214
215 detail::subsystem_mutex.unlock();
216 deinit();
217 detail::subsystem_mutex.lock();
218 }
219 detail::subsystem_mutex.unlock();
220}
221
222} // namespace tt
T lock(T... args)
T move(T... args)