summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/fs/file.cpp2
-rw-r--r--src/common/fs/fs_util.cpp1
-rw-r--r--src/common/fs/path_util.cpp1
-rw-r--r--src/common/input.h1
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/polyfill_ranges.h530
-rw-r--r--src/common/polyfill_thread.h323
-rw-r--r--src/common/thread_worker.h5
-rw-r--r--src/common/threadsafe_queue.h4
9 files changed, 865 insertions, 4 deletions
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp
index fa8422c41..656b03cc5 100644
--- a/src/common/fs/file.cpp
+++ b/src/common/fs/file.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <vector>
5
4#include "common/fs/file.h" 6#include "common/fs/file.h"
5#include "common/fs/fs.h" 7#include "common/fs/fs.h"
6#include "common/logging/log.h" 8#include "common/logging/log.h"
diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp
index eb4ac1deb..813a713c3 100644
--- a/src/common/fs/fs_util.cpp
+++ b/src/common/fs/fs_util.cpp
@@ -4,6 +4,7 @@
4#include <algorithm> 4#include <algorithm>
5 5
6#include "common/fs/fs_util.h" 6#include "common/fs/fs_util.h"
7#include "common/polyfill_ranges.h"
7 8
8namespace Common::FS { 9namespace Common::FS {
9 10
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index 1074f2421..defa3e918 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include <algorithm>
5#include <sstream>
5#include <unordered_map> 6#include <unordered_map>
6 7
7#include "common/fs/fs.h" 8#include "common/fs/fs.h"
diff --git a/src/common/input.h b/src/common/input.h
index cb30b7254..9f7b89799 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -8,6 +8,7 @@
8#include <string> 8#include <string>
9#include <unordered_map> 9#include <unordered_map>
10#include <utility> 10#include <utility>
11#include <vector>
11#include "common/logging/log.h" 12#include "common/logging/log.h"
12#include "common/param_package.h" 13#include "common/param_package.h"
13#include "common/uuid.h" 14#include "common/uuid.h"
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 15d92505e..2a3bded40 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -4,7 +4,6 @@
4#include <atomic> 4#include <atomic>
5#include <chrono> 5#include <chrono>
6#include <climits> 6#include <climits>
7#include <stop_token>
8#include <thread> 7#include <thread>
9 8
10#include <fmt/format.h> 9#include <fmt/format.h>
@@ -18,6 +17,7 @@
18#include "common/fs/fs_paths.h" 17#include "common/fs/fs_paths.h"
19#include "common/fs/path_util.h" 18#include "common/fs/path_util.h"
20#include "common/literals.h" 19#include "common/literals.h"
20#include "common/polyfill_thread.h"
21#include "common/thread.h" 21#include "common/thread.h"
22 22
23#include "common/logging/backend.h" 23#include "common/logging/backend.h"
diff --git a/src/common/polyfill_ranges.h b/src/common/polyfill_ranges.h
new file mode 100644
index 000000000..ca44bfaef
--- /dev/null
+++ b/src/common/polyfill_ranges.h
@@ -0,0 +1,530 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4//
5// TODO: remove this file when ranges are supported by all compilation targets
6//
7
8#pragma once
9
10#include <algorithm>
11#include <utility>
12#include <version>
13
14#ifndef __cpp_lib_ranges
15
16namespace std {
17namespace ranges {
18
19template <typename T>
20concept range = requires(T& t) {
21 begin(t);
22 end(t);
23};
24
25template <typename T>
26concept input_range = range<T>;
27
28template <typename T>
29concept output_range = range<T>;
30
31template <range R>
32using range_difference_t = ptrdiff_t;
33
34//
35// find, find_if, find_if_not
36//
37
38struct find_fn {
39 template <typename Iterator, typename T, typename Proj = std::identity>
40 constexpr Iterator operator()(Iterator first, Iterator last, const T& value,
41 Proj proj = {}) const {
42 for (; first != last; ++first) {
43 if (std::invoke(proj, *first) == value) {
44 return first;
45 }
46 }
47 return first;
48 }
49
50 template <ranges::input_range R, typename T, typename Proj = std::identity>
51 constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Proj proj = {}) const {
52 return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj));
53 }
54};
55
56struct find_if_fn {
57 template <typename Iterator, typename Proj = std::identity, typename Pred>
58 constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
59 for (; first != last; ++first) {
60 if (std::invoke(pred, std::invoke(proj, *first))) {
61 return first;
62 }
63 }
64 return first;
65 }
66
67 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
68 constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const {
69 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
70 }
71};
72
73struct find_if_not_fn {
74 template <typename Iterator, typename Proj = std::identity, typename Pred>
75 constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
76 for (; first != last; ++first) {
77 if (!std::invoke(pred, std::invoke(proj, *first))) {
78 return first;
79 }
80 }
81 return first;
82 }
83
84 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
85 constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const {
86 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
87 }
88};
89
90inline constexpr find_fn find;
91inline constexpr find_if_fn find_if;
92inline constexpr find_if_not_fn find_if_not;
93
94//
95// any_of, all_of, none_of
96//
97
98struct all_of_fn {
99 template <typename Iterator, typename Proj = std::identity, typename Pred>
100 constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
101 return ranges::find_if_not(first, last, std::ref(pred), std::ref(proj)) == last;
102 }
103
104 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
105 constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
106 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
107 }
108};
109
110struct any_of_fn {
111 template <typename Iterator, typename Proj = std::identity, typename Pred>
112 constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
113 return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) != last;
114 }
115
116 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
117 constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
118 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
119 }
120};
121
122struct none_of_fn {
123 template <typename Iterator, typename Proj = std::identity, typename Pred>
124 constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
125 return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) == last;
126 }
127
128 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
129 constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
130 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
131 }
132};
133
134inline constexpr any_of_fn any_of;
135inline constexpr all_of_fn all_of;
136inline constexpr none_of_fn none_of;
137
138//
139// count, count_if
140//
141
142struct count_fn {
143 template <typename Iterator, typename T, typename Proj = std::identity>
144 constexpr ptrdiff_t operator()(Iterator first, Iterator last, const T& value,
145 Proj proj = {}) const {
146 ptrdiff_t counter = 0;
147 for (; first != last; ++first)
148 if (std::invoke(proj, *first) == value)
149 ++counter;
150 return counter;
151 }
152
153 template <ranges::input_range R, typename T, typename Proj = std::identity>
154 constexpr ptrdiff_t operator()(R&& r, const T& value, Proj proj = {}) const {
155 return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj));
156 }
157};
158
159struct count_if_fn {
160 template <typename Iterator, typename Proj = std::identity, typename Pred>
161 constexpr ptrdiff_t operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
162 ptrdiff_t counter = 0;
163 for (; first != last; ++first)
164 if (std::invoke(pred, std::invoke(proj, *first)))
165 ++counter;
166 return counter;
167 }
168
169 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
170 constexpr ptrdiff_t operator()(R&& r, Pred pred, Proj proj = {}) const {
171 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
172 }
173};
174
175inline constexpr count_fn count;
176inline constexpr count_if_fn count_if;
177
178//
179// transform
180//
181
182struct transform_fn {
183 template <typename InputIterator, typename OutputIterator, typename F,
184 typename Proj = std::identity>
185 constexpr void operator()(InputIterator first1, InputIterator last1, OutputIterator result,
186 F op, Proj proj = {}) const {
187 for (; first1 != last1; ++first1, (void)++result) {
188 *result = std::invoke(op, std::invoke(proj, *first1));
189 }
190 }
191
192 template <ranges::input_range R, typename OutputIterator, typename F,
193 typename Proj = std::identity>
194 constexpr void operator()(R&& r, OutputIterator result, F op, Proj proj = {}) const {
195 return operator()(ranges::begin(r), ranges::end(r), result, std::ref(op), std::ref(proj));
196 }
197};
198
199inline constexpr transform_fn transform;
200
201//
202// sort
203//
204
205struct sort_fn {
206 template <typename Iterator, typename Comp = ranges::less, typename Proj = std::identity>
207 constexpr void operator()(Iterator first, Iterator last, Comp comp = {}, Proj proj = {}) const {
208 if (first == last)
209 return;
210
211 Iterator last_iter = ranges::next(first, last);
212 std::sort(first, last_iter,
213 [&](auto& lhs, auto& rhs) { return comp(proj(lhs), proj(rhs)); });
214 }
215
216 template <ranges::input_range R, typename Comp = ranges::less, typename Proj = std::identity>
217 constexpr void operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
218 return operator()(ranges::begin(r), ranges::end(r), std::move(comp), std::move(proj));
219 }
220};
221
222inline constexpr sort_fn sort;
223
224//
225// fill
226//
227
228struct fill_fn {
229 template <typename T, typename OutputIterator>
230 constexpr OutputIterator operator()(OutputIterator first, OutputIterator last,
231 const T& value) const {
232 while (first != last) {
233 *first++ = value;
234 }
235
236 return first;
237 }
238
239 template <typename T, ranges::output_range R>
240 constexpr ranges::iterator_t<R> operator()(R&& r, const T& value) const {
241 return operator()(ranges::begin(r), ranges::end(r), value);
242 }
243};
244
245inline constexpr fill_fn fill;
246
247//
248// for_each
249//
250
251struct for_each_fn {
252 template <typename Iterator, typename Proj = std::identity, typename Fun>
253 constexpr void operator()(Iterator first, Iterator last, Fun f, Proj proj = {}) const {
254 for (; first != last; ++first) {
255 std::invoke(f, std::invoke(proj, *first));
256 }
257 }
258
259 template <ranges::input_range R, typename Proj = std::identity, typename Fun>
260 constexpr void operator()(R&& r, Fun f, Proj proj = {}) const {
261 return operator()(ranges::begin(r), ranges::end(r), std::move(f), std::ref(proj));
262 }
263};
264
265inline constexpr for_each_fn for_each;
266
267//
268// min_element, max_element
269//
270
271struct min_element_fn {
272 template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less>
273 constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {},
274 Proj proj = {}) const {
275 if (first == last) {
276 return last;
277 }
278
279 auto smallest = first;
280 ++first;
281 for (; first != last; ++first) {
282 if (!std::invoke(comp, std::invoke(proj, *smallest), std::invoke(proj, *first))) {
283 smallest = first;
284 }
285 }
286 return smallest;
287 }
288
289 template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less>
290 constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
291 return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj));
292 }
293};
294
295struct max_element_fn {
296 template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less>
297 constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {},
298 Proj proj = {}) const {
299 if (first == last) {
300 return last;
301 }
302
303 auto largest = first;
304 ++first;
305 for (; first != last; ++first) {
306 if (std::invoke(comp, std::invoke(proj, *largest), std::invoke(proj, *first))) {
307 largest = first;
308 }
309 }
310 return largest;
311 }
312
313 template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less>
314 constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
315 return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj));
316 }
317};
318
319inline constexpr min_element_fn min_element;
320inline constexpr max_element_fn max_element;
321
322//
323// replace, replace_if
324//
325
326struct replace_fn {
327 template <typename Iterator, typename T1, typename T2, typename Proj = std::identity>
328 constexpr Iterator operator()(Iterator first, Iterator last, const T1& old_value,
329 const T2& new_value, Proj proj = {}) const {
330 for (; first != last; ++first) {
331 if (old_value == std::invoke(proj, *first)) {
332 *first = new_value;
333 }
334 }
335 return first;
336 }
337
338 template <ranges::input_range R, typename T1, typename T2, typename Proj = std::identity>
339 constexpr ranges::iterator_t<R> operator()(R&& r, const T1& old_value, const T2& new_value,
340 Proj proj = {}) const {
341 return operator()(ranges::begin(r), ranges::end(r), old_value, new_value, std::move(proj));
342 }
343};
344
345struct replace_if_fn {
346 template <typename Iterator, typename T, typename Proj = std::identity, typename Pred>
347 constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, const T& new_value,
348 Proj proj = {}) const {
349 for (; first != last; ++first) {
350 if (!!std::invoke(pred, std::invoke(proj, *first))) {
351 *first = new_value;
352 }
353 }
354 return std::move(first);
355 }
356
357 template <ranges::input_range R, typename T, typename Proj = std::identity, typename Pred>
358 constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, const T& new_value,
359 Proj proj = {}) const {
360 return operator()(ranges::begin(r), ranges::end(r), std::move(pred), new_value,
361 std::move(proj));
362 }
363};
364
365inline constexpr replace_fn replace;
366inline constexpr replace_if_fn replace_if;
367
368//
369// copy, copy_if
370//
371
372struct copy_fn {
373 template <typename InputIterator, typename OutputIterator>
374 constexpr void operator()(InputIterator first, InputIterator last,
375 OutputIterator result) const {
376 for (; first != last; ++first, (void)++result) {
377 *result = *first;
378 }
379 }
380
381 template <ranges::input_range R, typename OutputIterator>
382 constexpr void operator()(R&& r, OutputIterator result) const {
383 return operator()(ranges::begin(r), ranges::end(r), std::move(result));
384 }
385};
386
387struct copy_if_fn {
388 template <typename InputIterator, typename OutputIterator, typename Proj = std::identity,
389 typename Pred>
390 constexpr void operator()(InputIterator first, InputIterator last, OutputIterator result,
391 Pred pred, Proj proj = {}) const {
392 for (; first != last; ++first) {
393 if (std::invoke(pred, std::invoke(proj, *first))) {
394 *result = *first;
395 ++result;
396 }
397 }
398 }
399
400 template <ranges::input_range R, typename OutputIterator, typename Proj = std::identity,
401 typename Pred>
402 constexpr void operator()(R&& r, OutputIterator result, Pred pred, Proj proj = {}) const {
403 return operator()(ranges::begin(r), ranges::end(r), std::move(result), std::ref(pred),
404 std::ref(proj));
405 }
406};
407
408inline constexpr copy_fn copy;
409inline constexpr copy_if_fn copy_if;
410
411//
412// generate
413//
414
415struct generate_fn {
416 template <typename Iterator, typename F>
417 constexpr Iterator operator()(Iterator first, Iterator last, F gen) const {
418 for (; first != last; *first = std::invoke(gen), ++first)
419 ;
420 return first;
421 }
422
423 template <typename R, std::copy_constructible F>
424 requires std::invocable<F&> && ranges::output_range<R>
425 constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const {
426 return operator()(ranges::begin(r), ranges::end(r), std::move(gen));
427 }
428};
429
430inline constexpr generate_fn generate;
431
432//
433// lower_bound, upper_bound
434//
435
436struct lower_bound_fn {
437 template <typename Iterator, typename T, typename Proj = std::identity,
438 typename Comp = ranges::less>
439 constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {},
440 Proj proj = {}) const {
441 Iterator it;
442 std::ptrdiff_t _count, _step;
443 _count = std::distance(first, last);
444
445 while (_count > 0) {
446 it = first;
447 _step = _count / 2;
448 ranges::advance(it, _step, last);
449 if (comp(std::invoke(proj, *it), value)) {
450 first = ++it;
451 _count -= _step + 1;
452 } else {
453 _count = _step;
454 }
455 }
456 return first;
457 }
458
459 template <ranges::input_range R, typename T, typename Proj = std::identity,
460 typename Comp = ranges::less>
461 constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {},
462 Proj proj = {}) const {
463 return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj));
464 }
465};
466
467struct upper_bound_fn {
468 template <typename Iterator, typename T, typename Proj = std::identity,
469 typename Comp = ranges::less>
470 constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {},
471 Proj proj = {}) const {
472 Iterator it;
473 std::ptrdiff_t _count, _step;
474 _count = std::distance(first, last);
475
476 while (_count > 0) {
477 it = first;
478 _step = _count / 2;
479 ranges::advance(it, _step, last);
480 if (!comp(value, std::invoke(proj, *it))) {
481 first = ++it;
482 _count -= _step + 1;
483 } else {
484 _count = _step;
485 }
486 }
487 return first;
488 }
489
490 template <ranges::input_range R, typename T, typename Proj = std::identity,
491 typename Comp = ranges::less>
492 constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {},
493 Proj proj = {}) const {
494 return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj));
495 }
496};
497
498inline constexpr lower_bound_fn lower_bound;
499inline constexpr upper_bound_fn upper_bound;
500
501//
502// adjacent_find
503//
504
505struct adjacent_find_fn {
506 template <typename Iterator, typename Proj = std::identity, typename Pred = ranges::equal_to>
507 constexpr Iterator operator()(Iterator first, Iterator last, Pred pred = {},
508 Proj proj = {}) const {
509 if (first == last)
510 return first;
511 auto _next = ranges::next(first);
512 for (; _next != last; ++_next, ++first)
513 if (std::invoke(pred, std::invoke(proj, *first), std::invoke(proj, *_next)))
514 return first;
515 return _next;
516 }
517
518 template <ranges::input_range R, typename Proj = std::identity,
519 typename Pred = ranges::equal_to>
520 constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred = {}, Proj proj = {}) const {
521 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
522 }
523};
524
525inline constexpr adjacent_find_fn adjacent_find;
526
527} // namespace ranges
528} // namespace std
529
530#endif
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
diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h
index 62c60f724..260ad44e4 100644
--- a/src/common/thread_worker.h
+++ b/src/common/thread_worker.h
@@ -7,13 +7,13 @@
7#include <condition_variable> 7#include <condition_variable>
8#include <functional> 8#include <functional>
9#include <mutex> 9#include <mutex>
10#include <stop_token>
11#include <string> 10#include <string>
12#include <thread> 11#include <thread>
13#include <type_traits> 12#include <type_traits>
14#include <vector> 13#include <vector>
15#include <queue> 14#include <queue>
16 15
16#include "common/polyfill_thread.h"
17#include "common/thread.h" 17#include "common/thread.h"
18#include "common/unique_function.h" 18#include "common/unique_function.h"
19 19
@@ -47,7 +47,8 @@ public:
47 if (requests.empty()) { 47 if (requests.empty()) {
48 wait_condition.notify_all(); 48 wait_condition.notify_all();
49 } 49 }
50 condition.wait(lock, stop_token, [this] { return !requests.empty(); }); 50 Common::CondvarWait(condition, lock, stop_token,
51 [this] { return !requests.empty(); });
51 if (stop_token.stop_requested()) { 52 if (stop_token.stop_requested()) {
52 break; 53 break;
53 } 54 }
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 053798e79..2ef1da064 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -12,6 +12,8 @@
12#include <mutex> 12#include <mutex>
13#include <utility> 13#include <utility>
14 14
15#include "common/polyfill_thread.h"
16
15namespace Common { 17namespace Common {
16template <typename T, bool with_stop_token = false> 18template <typename T, bool with_stop_token = false>
17class SPSCQueue { 19class SPSCQueue {
@@ -97,7 +99,7 @@ public:
97 T PopWait(std::stop_token stop_token) { 99 T PopWait(std::stop_token stop_token) {
98 if (Empty()) { 100 if (Empty()) {
99 std::unique_lock lock{cv_mutex}; 101 std::unique_lock lock{cv_mutex};
100 cv.wait(lock, stop_token, [this] { return !Empty(); }); 102 Common::CondvarWait(cv, lock, stop_token, [this] { return !Empty(); });
101 } 103 }
102 if (stop_token.stop_requested()) { 104 if (stop_token.stop_requested()) {
103 return T{}; 105 return T{};