summaryrefslogtreecommitdiff
path: root/src/common/polyfill_thread.h
diff options
context:
space:
mode:
authorGravatar liamwhite2022-12-03 12:09:21 -0500
committerGravatar GitHub2022-12-03 12:09:21 -0500
commit22aff09b33941cdf907e474cb86117fef838abba (patch)
tree73a747be44fd2ba994c3d40c8f6ea18633c0f880 /src/common/polyfill_thread.h
parentMerge pull request #9353 from vonchenplus/draw_indexed (diff)
parentgeneral: fix compile for Apple Clang (diff)
downloadyuzu-22aff09b33941cdf907e474cb86117fef838abba.tar.gz
yuzu-22aff09b33941cdf907e474cb86117fef838abba.tar.xz
yuzu-22aff09b33941cdf907e474cb86117fef838abba.zip
Merge pull request #9289 from liamwhite/fruit-company
general: fix compile for Apple Clang
Diffstat (limited to 'src/common/polyfill_thread.h')
-rw-r--r--src/common/polyfill_thread.h323
1 files changed, 323 insertions, 0 deletions
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h
new file mode 100644
index 000000000..5a8d1ce08
--- /dev/null
+++ b/src/common/polyfill_thread.h
@@ -0,0 +1,323 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4//
5// TODO: remove this file when jthread is supported by all compilation targets
6//
7
8#pragma once
9
10#include <version>
11
12#ifdef __cpp_lib_jthread
13
14#include <stop_token>
15#include <thread>
16
17namespace Common {
18
19template <typename Condvar, typename Lock, typename Pred>
20void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) {
21 cv.wait(lock, token, std::move(pred));
22}
23
24} // namespace Common
25
26#else
27
28#include <atomic>
29#include <functional>
30#include <list>
31#include <memory>
32#include <mutex>
33#include <optional>
34#include <thread>
35#include <type_traits>
36
37namespace std {
38namespace polyfill {
39
40using stop_state_callbacks = list<function<void()>>;
41
42class stop_state {
43public:
44 stop_state() = default;
45 ~stop_state() = default;
46
47 bool request_stop() {
48 stop_state_callbacks callbacks;
49
50 {
51 scoped_lock lk{m_lock};
52
53 if (m_stop_requested.load()) {
54 // Already set, nothing to do
55 return false;
56 }
57
58 // Set as requested
59 m_stop_requested = true;
60
61 // Copy callback list
62 callbacks = m_callbacks;
63 }
64
65 for (auto callback : callbacks) {
66 callback();
67 }
68
69 return true;
70 }
71
72 bool stop_requested() const {
73 return m_stop_requested.load();
74 }
75
76 stop_state_callbacks::const_iterator insert_callback(function<void()> f) {
77 stop_state_callbacks::const_iterator ret{};
78 bool should_run{};
79
80 {
81 scoped_lock lk{m_lock};
82 should_run = m_stop_requested.load();
83 m_callbacks.push_front(f);
84 ret = m_callbacks.begin();
85 }
86
87 if (should_run) {
88 f();
89 }
90
91 return ret;
92 }
93
94 void remove_callback(stop_state_callbacks::const_iterator it) {
95 scoped_lock lk{m_lock};
96 m_callbacks.erase(it);
97 }
98
99private:
100 mutex m_lock;
101 atomic<bool> m_stop_requested;
102 stop_state_callbacks m_callbacks;
103};
104
105} // namespace polyfill
106
107class stop_token;
108class stop_source;
109struct nostopstate_t {
110 explicit nostopstate_t() = default;
111};
112inline constexpr nostopstate_t nostopstate{};
113
114template <class Callback>
115class stop_callback;
116
117class stop_token {
118public:
119 stop_token() noexcept = default;
120
121 stop_token(const stop_token&) noexcept = default;
122 stop_token(stop_token&&) noexcept = default;
123 stop_token& operator=(const stop_token&) noexcept = default;
124 stop_token& operator=(stop_token&&) noexcept = default;
125 ~stop_token() = default;
126
127 void swap(stop_token& other) noexcept {
128 m_stop_state.swap(other.m_stop_state);
129 }
130
131 [[nodiscard]] bool stop_requested() const noexcept {
132 return m_stop_state && m_stop_state->stop_requested();
133 }
134 [[nodiscard]] bool stop_possible() const noexcept {
135 return m_stop_state != nullptr;
136 }
137
138private:
139 friend class stop_source;
140 template <typename Callback>
141 friend class stop_callback;
142 stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(move(stop_state)) {}
143
144private:
145 shared_ptr<polyfill::stop_state> m_stop_state;
146};
147
148class stop_source {
149public:
150 stop_source() : m_stop_state(make_shared<polyfill::stop_state>()) {}
151 explicit stop_source(nostopstate_t) noexcept {}
152
153 stop_source(const stop_source&) noexcept = default;
154 stop_source(stop_source&&) noexcept = default;
155 stop_source& operator=(const stop_source&) noexcept = default;
156 stop_source& operator=(stop_source&&) noexcept = default;
157 ~stop_source() = default;
158 void swap(stop_source& other) noexcept {
159 m_stop_state.swap(other.m_stop_state);
160 }
161
162 [[nodiscard]] stop_token get_token() const noexcept {
163 return stop_token(m_stop_state);
164 }
165 [[nodiscard]] bool stop_possible() const noexcept {
166 return m_stop_state != nullptr;
167 }
168 [[nodiscard]] bool stop_requested() const noexcept {
169 return m_stop_state && m_stop_state->stop_requested();
170 }
171 bool request_stop() noexcept {
172 return m_stop_state && m_stop_state->request_stop();
173 }
174
175private:
176 friend class jthread;
177 explicit stop_source(shared_ptr<polyfill::stop_state> stop_state)
178 : m_stop_state(move(stop_state)) {}
179
180private:
181 shared_ptr<polyfill::stop_state> m_stop_state;
182};
183
184template <typename Callback>
185class stop_callback {
186 static_assert(is_nothrow_destructible_v<Callback>);
187 static_assert(is_invocable_v<Callback>);
188
189public:
190 using callback_type = Callback;
191
192 template <typename C>
193 requires constructible_from<Callback, C>
194 explicit stop_callback(const stop_token& st,
195 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
196 : m_stop_state(st.m_stop_state) {
197 if (m_stop_state) {
198 m_callback = m_stop_state->insert_callback(move(cb));
199 }
200 }
201 template <typename C>
202 requires constructible_from<Callback, C>
203 explicit stop_callback(stop_token&& st,
204 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
205 : m_stop_state(move(st.m_stop_state)) {
206 if (m_stop_state) {
207 m_callback = m_stop_state->insert_callback(move(cb));
208 }
209 }
210 ~stop_callback() {
211 if (m_stop_state && m_callback) {
212 m_stop_state->remove_callback(*m_callback);
213 }
214 }
215
216 stop_callback(const stop_callback&) = delete;
217 stop_callback(stop_callback&&) = delete;
218 stop_callback& operator=(const stop_callback&) = delete;
219 stop_callback& operator=(stop_callback&&) = delete;
220
221private:
222 shared_ptr<polyfill::stop_state> m_stop_state;
223 optional<polyfill::stop_state_callbacks::const_iterator> m_callback;
224};
225
226template <typename Callback>
227stop_callback(stop_token, Callback) -> stop_callback<Callback>;
228
229class jthread {
230public:
231 using id = thread::id;
232 using native_handle_type = thread::native_handle_type;
233
234 jthread() noexcept = default;
235
236 template <typename F, typename... Args,
237 typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>>
238 explicit jthread(F&& f, Args&&... args)
239 : m_stop_state(make_shared<polyfill::stop_state>()),
240 m_thread(make_thread(move(f), move(args)...)) {}
241
242 ~jthread() {
243 if (joinable()) {
244 request_stop();
245 join();
246 }
247 }
248
249 jthread(const jthread&) = delete;
250 jthread(jthread&&) noexcept = default;
251 jthread& operator=(const jthread&) = delete;
252
253 jthread& operator=(jthread&& other) noexcept {
254 m_thread.swap(other.m_thread);
255 m_stop_state.swap(other.m_stop_state);
256 return *this;
257 }
258
259 void swap(jthread& other) noexcept {
260 m_thread.swap(other.m_thread);
261 m_stop_state.swap(other.m_stop_state);
262 }
263 [[nodiscard]] bool joinable() const noexcept {
264 return m_thread.joinable();
265 }
266 void join() {
267 m_thread.join();
268 }
269 void detach() {
270 m_thread.detach();
271 m_stop_state.reset();
272 }
273
274 [[nodiscard]] id get_id() const noexcept {
275 return m_thread.get_id();
276 }
277 [[nodiscard]] native_handle_type native_handle() {
278 return m_thread.native_handle();
279 }
280 [[nodiscard]] stop_source get_stop_source() noexcept {
281 return stop_source(m_stop_state);
282 }
283 [[nodiscard]] stop_token get_stop_token() const noexcept {
284 return stop_source(m_stop_state).get_token();
285 }
286 bool request_stop() noexcept {
287 return get_stop_source().request_stop();
288 }
289 [[nodiscard]] static unsigned int hardware_concurrency() noexcept {
290 return thread::hardware_concurrency();
291 }
292
293private:
294 template <typename F, typename... Args>
295 thread make_thread(F&& f, Args&&... args) {
296 if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) {
297 return thread(move(f), get_stop_token(), move(args)...);
298 } else {
299 return thread(move(f), move(args)...);
300 }
301 }
302
303 shared_ptr<polyfill::stop_state> m_stop_state;
304 thread m_thread;
305};
306
307} // namespace std
308
309namespace Common {
310
311template <typename Condvar, typename Lock, typename Pred>
312void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) {
313 if (token.stop_requested()) {
314 return;
315 }
316
317 std::stop_callback callback(token, [&] { cv.notify_all(); });
318 cv.wait(lock, [&] { return pred() || token.stop_requested(); });
319}
320
321} // namespace Common
322
323#endif