summaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to '')
-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 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include <vector>
+
#include "common/fs/file.h"
#include "common/fs/fs.h"
#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 @@
#include <algorithm>
#include "common/fs/fs_util.h"
+#include "common/polyfill_ranges.h"
namespace Common::FS {
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 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
+#include <sstream>
#include <unordered_map>
#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 @@
#include <string>
#include <unordered_map>
#include <utility>
+#include <vector>
#include "common/logging/log.h"
#include "common/param_package.h"
#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 @@
#include <atomic>
#include <chrono>
#include <climits>
-#include <stop_token>
#include <thread>
#include <fmt/format.h>
@@ -18,6 +17,7 @@
#include "common/fs/fs_paths.h"
#include "common/fs/path_util.h"
#include "common/literals.h"
+#include "common/polyfill_thread.h"
#include "common/thread.h"
#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 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//
+// TODO: remove this file when ranges are supported by all compilation targets
+//
+
+#pragma once
+
+#include <algorithm>
+#include <utility>
+#include <version>
+
+#ifndef __cpp_lib_ranges
+
+namespace std {
+namespace ranges {
+
+template <typename T>
+concept range = requires(T& t) {
+ begin(t);
+ end(t);
+};
+
+template <typename T>
+concept input_range = range<T>;
+
+template <typename T>
+concept output_range = range<T>;
+
+template <range R>
+using range_difference_t = ptrdiff_t;
+
+//
+// find, find_if, find_if_not
+//
+
+struct find_fn {
+ template <typename Iterator, typename T, typename Proj = std::identity>
+ constexpr Iterator operator()(Iterator first, Iterator last, const T& value,
+ Proj proj = {}) const {
+ for (; first != last; ++first) {
+ if (std::invoke(proj, *first) == value) {
+ return first;
+ }
+ }
+ return first;
+ }
+
+ template <ranges::input_range R, typename T, typename Proj = std::identity>
+ constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj));
+ }
+};
+
+struct find_if_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred>
+ constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
+ for (; first != last; ++first) {
+ if (std::invoke(pred, std::invoke(proj, *first))) {
+ return first;
+ }
+ }
+ return first;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Pred>
+ constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+struct find_if_not_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred>
+ constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
+ for (; first != last; ++first) {
+ if (!std::invoke(pred, std::invoke(proj, *first))) {
+ return first;
+ }
+ }
+ return first;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Pred>
+ constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+inline constexpr find_fn find;
+inline constexpr find_if_fn find_if;
+inline constexpr find_if_not_fn find_if_not;
+
+//
+// any_of, all_of, none_of
+//
+
+struct all_of_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred>
+ constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
+ return ranges::find_if_not(first, last, std::ref(pred), std::ref(proj)) == last;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Pred>
+ constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+struct any_of_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred>
+ constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
+ return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) != last;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Pred>
+ constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+struct none_of_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred>
+ constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
+ return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) == last;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Pred>
+ constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+inline constexpr any_of_fn any_of;
+inline constexpr all_of_fn all_of;
+inline constexpr none_of_fn none_of;
+
+//
+// count, count_if
+//
+
+struct count_fn {
+ template <typename Iterator, typename T, typename Proj = std::identity>
+ constexpr ptrdiff_t operator()(Iterator first, Iterator last, const T& value,
+ Proj proj = {}) const {
+ ptrdiff_t counter = 0;
+ for (; first != last; ++first)
+ if (std::invoke(proj, *first) == value)
+ ++counter;
+ return counter;
+ }
+
+ template <ranges::input_range R, typename T, typename Proj = std::identity>
+ constexpr ptrdiff_t operator()(R&& r, const T& value, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj));
+ }
+};
+
+struct count_if_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred>
+ constexpr ptrdiff_t operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
+ ptrdiff_t counter = 0;
+ for (; first != last; ++first)
+ if (std::invoke(pred, std::invoke(proj, *first)))
+ ++counter;
+ return counter;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Pred>
+ constexpr ptrdiff_t operator()(R&& r, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+inline constexpr count_fn count;
+inline constexpr count_if_fn count_if;
+
+//
+// transform
+//
+
+struct transform_fn {
+ template <typename InputIterator, typename OutputIterator, typename F,
+ typename Proj = std::identity>
+ constexpr void operator()(InputIterator first1, InputIterator last1, OutputIterator result,
+ F op, Proj proj = {}) const {
+ for (; first1 != last1; ++first1, (void)++result) {
+ *result = std::invoke(op, std::invoke(proj, *first1));
+ }
+ }
+
+ template <ranges::input_range R, typename OutputIterator, typename F,
+ typename Proj = std::identity>
+ constexpr void operator()(R&& r, OutputIterator result, F op, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), result, std::ref(op), std::ref(proj));
+ }
+};
+
+inline constexpr transform_fn transform;
+
+//
+// sort
+//
+
+struct sort_fn {
+ template <typename Iterator, typename Comp = ranges::less, typename Proj = std::identity>
+ constexpr void operator()(Iterator first, Iterator last, Comp comp = {}, Proj proj = {}) const {
+ if (first == last)
+ return;
+
+ Iterator last_iter = ranges::next(first, last);
+ std::sort(first, last_iter,
+ [&](auto& lhs, auto& rhs) { return comp(proj(lhs), proj(rhs)); });
+ }
+
+ template <ranges::input_range R, typename Comp = ranges::less, typename Proj = std::identity>
+ constexpr void operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::move(comp), std::move(proj));
+ }
+};
+
+inline constexpr sort_fn sort;
+
+//
+// fill
+//
+
+struct fill_fn {
+ template <typename T, typename OutputIterator>
+ constexpr OutputIterator operator()(OutputIterator first, OutputIterator last,
+ const T& value) const {
+ while (first != last) {
+ *first++ = value;
+ }
+
+ return first;
+ }
+
+ template <typename T, ranges::output_range R>
+ constexpr ranges::iterator_t<R> operator()(R&& r, const T& value) const {
+ return operator()(ranges::begin(r), ranges::end(r), value);
+ }
+};
+
+inline constexpr fill_fn fill;
+
+//
+// for_each
+//
+
+struct for_each_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Fun>
+ constexpr void operator()(Iterator first, Iterator last, Fun f, Proj proj = {}) const {
+ for (; first != last; ++first) {
+ std::invoke(f, std::invoke(proj, *first));
+ }
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Fun>
+ constexpr void operator()(R&& r, Fun f, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::move(f), std::ref(proj));
+ }
+};
+
+inline constexpr for_each_fn for_each;
+
+//
+// min_element, max_element
+//
+
+struct min_element_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less>
+ constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {},
+ Proj proj = {}) const {
+ if (first == last) {
+ return last;
+ }
+
+ auto smallest = first;
+ ++first;
+ for (; first != last; ++first) {
+ if (!std::invoke(comp, std::invoke(proj, *smallest), std::invoke(proj, *first))) {
+ smallest = first;
+ }
+ }
+ return smallest;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less>
+ constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj));
+ }
+};
+
+struct max_element_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less>
+ constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {},
+ Proj proj = {}) const {
+ if (first == last) {
+ return last;
+ }
+
+ auto largest = first;
+ ++first;
+ for (; first != last; ++first) {
+ if (std::invoke(comp, std::invoke(proj, *largest), std::invoke(proj, *first))) {
+ largest = first;
+ }
+ }
+ return largest;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less>
+ constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj));
+ }
+};
+
+inline constexpr min_element_fn min_element;
+inline constexpr max_element_fn max_element;
+
+//
+// replace, replace_if
+//
+
+struct replace_fn {
+ template <typename Iterator, typename T1, typename T2, typename Proj = std::identity>
+ constexpr Iterator operator()(Iterator first, Iterator last, const T1& old_value,
+ const T2& new_value, Proj proj = {}) const {
+ for (; first != last; ++first) {
+ if (old_value == std::invoke(proj, *first)) {
+ *first = new_value;
+ }
+ }
+ return first;
+ }
+
+ template <ranges::input_range R, typename T1, typename T2, typename Proj = std::identity>
+ constexpr ranges::iterator_t<R> operator()(R&& r, const T1& old_value, const T2& new_value,
+ Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), old_value, new_value, std::move(proj));
+ }
+};
+
+struct replace_if_fn {
+ template <typename Iterator, typename T, typename Proj = std::identity, typename Pred>
+ constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, const T& new_value,
+ Proj proj = {}) const {
+ for (; first != last; ++first) {
+ if (!!std::invoke(pred, std::invoke(proj, *first))) {
+ *first = new_value;
+ }
+ }
+ return std::move(first);
+ }
+
+ template <ranges::input_range R, typename T, typename Proj = std::identity, typename Pred>
+ constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, const T& new_value,
+ Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::move(pred), new_value,
+ std::move(proj));
+ }
+};
+
+inline constexpr replace_fn replace;
+inline constexpr replace_if_fn replace_if;
+
+//
+// copy, copy_if
+//
+
+struct copy_fn {
+ template <typename InputIterator, typename OutputIterator>
+ constexpr void operator()(InputIterator first, InputIterator last,
+ OutputIterator result) const {
+ for (; first != last; ++first, (void)++result) {
+ *result = *first;
+ }
+ }
+
+ template <ranges::input_range R, typename OutputIterator>
+ constexpr void operator()(R&& r, OutputIterator result) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::move(result));
+ }
+};
+
+struct copy_if_fn {
+ template <typename InputIterator, typename OutputIterator, typename Proj = std::identity,
+ typename Pred>
+ constexpr void operator()(InputIterator first, InputIterator last, OutputIterator result,
+ Pred pred, Proj proj = {}) const {
+ for (; first != last; ++first) {
+ if (std::invoke(pred, std::invoke(proj, *first))) {
+ *result = *first;
+ ++result;
+ }
+ }
+ }
+
+ template <ranges::input_range R, typename OutputIterator, typename Proj = std::identity,
+ typename Pred>
+ constexpr void operator()(R&& r, OutputIterator result, Pred pred, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::move(result), std::ref(pred),
+ std::ref(proj));
+ }
+};
+
+inline constexpr copy_fn copy;
+inline constexpr copy_if_fn copy_if;
+
+//
+// generate
+//
+
+struct generate_fn {
+ template <typename Iterator, typename F>
+ constexpr Iterator operator()(Iterator first, Iterator last, F gen) const {
+ for (; first != last; *first = std::invoke(gen), ++first)
+ ;
+ return first;
+ }
+
+ template <typename R, std::copy_constructible F>
+ requires std::invocable<F&> && ranges::output_range<R>
+ constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::move(gen));
+ }
+};
+
+inline constexpr generate_fn generate;
+
+//
+// lower_bound, upper_bound
+//
+
+struct lower_bound_fn {
+ template <typename Iterator, typename T, typename Proj = std::identity,
+ typename Comp = ranges::less>
+ constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {},
+ Proj proj = {}) const {
+ Iterator it;
+ std::ptrdiff_t _count, _step;
+ _count = std::distance(first, last);
+
+ while (_count > 0) {
+ it = first;
+ _step = _count / 2;
+ ranges::advance(it, _step, last);
+ if (comp(std::invoke(proj, *it), value)) {
+ first = ++it;
+ _count -= _step + 1;
+ } else {
+ _count = _step;
+ }
+ }
+ return first;
+ }
+
+ template <ranges::input_range R, typename T, typename Proj = std::identity,
+ typename Comp = ranges::less>
+ constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {},
+ Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj));
+ }
+};
+
+struct upper_bound_fn {
+ template <typename Iterator, typename T, typename Proj = std::identity,
+ typename Comp = ranges::less>
+ constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {},
+ Proj proj = {}) const {
+ Iterator it;
+ std::ptrdiff_t _count, _step;
+ _count = std::distance(first, last);
+
+ while (_count > 0) {
+ it = first;
+ _step = _count / 2;
+ ranges::advance(it, _step, last);
+ if (!comp(value, std::invoke(proj, *it))) {
+ first = ++it;
+ _count -= _step + 1;
+ } else {
+ _count = _step;
+ }
+ }
+ return first;
+ }
+
+ template <ranges::input_range R, typename T, typename Proj = std::identity,
+ typename Comp = ranges::less>
+ constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {},
+ Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj));
+ }
+};
+
+inline constexpr lower_bound_fn lower_bound;
+inline constexpr upper_bound_fn upper_bound;
+
+//
+// adjacent_find
+//
+
+struct adjacent_find_fn {
+ template <typename Iterator, typename Proj = std::identity, typename Pred = ranges::equal_to>
+ constexpr Iterator operator()(Iterator first, Iterator last, Pred pred = {},
+ Proj proj = {}) const {
+ if (first == last)
+ return first;
+ auto _next = ranges::next(first);
+ for (; _next != last; ++_next, ++first)
+ if (std::invoke(pred, std::invoke(proj, *first), std::invoke(proj, *_next)))
+ return first;
+ return _next;
+ }
+
+ template <ranges::input_range R, typename Proj = std::identity,
+ typename Pred = ranges::equal_to>
+ constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred = {}, Proj proj = {}) const {
+ return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
+ }
+};
+
+inline constexpr adjacent_find_fn adjacent_find;
+
+} // namespace ranges
+} // namespace std
+
+#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 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//
+// TODO: remove this file when jthread is supported by all compilation targets
+//
+
+#pragma once
+
+#include <version>
+
+#ifdef __cpp_lib_jthread
+
+#include <stop_token>
+#include <thread>
+
+namespace Common {
+
+template <typename Condvar, typename Lock, typename Pred>
+void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) {
+ cv.wait(lock, token, std::move(pred));
+}
+
+} // namespace Common
+
+#else
+
+#include <atomic>
+#include <functional>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <type_traits>
+
+namespace std {
+namespace polyfill {
+
+using stop_state_callbacks = list<function<void()>>;
+
+class stop_state {
+public:
+ stop_state() = default;
+ ~stop_state() = default;
+
+ bool request_stop() {
+ stop_state_callbacks callbacks;
+
+ {
+ scoped_lock lk{m_lock};
+
+ if (m_stop_requested.load()) {
+ // Already set, nothing to do
+ return false;
+ }
+
+ // Set as requested
+ m_stop_requested = true;
+
+ // Copy callback list
+ callbacks = m_callbacks;
+ }
+
+ for (auto callback : callbacks) {
+ callback();
+ }
+
+ return true;
+ }
+
+ bool stop_requested() const {
+ return m_stop_requested.load();
+ }
+
+ stop_state_callbacks::const_iterator insert_callback(function<void()> f) {
+ stop_state_callbacks::const_iterator ret{};
+ bool should_run{};
+
+ {
+ scoped_lock lk{m_lock};
+ should_run = m_stop_requested.load();
+ m_callbacks.push_front(f);
+ ret = m_callbacks.begin();
+ }
+
+ if (should_run) {
+ f();
+ }
+
+ return ret;
+ }
+
+ void remove_callback(stop_state_callbacks::const_iterator it) {
+ scoped_lock lk{m_lock};
+ m_callbacks.erase(it);
+ }
+
+private:
+ mutex m_lock;
+ atomic<bool> m_stop_requested;
+ stop_state_callbacks m_callbacks;
+};
+
+} // namespace polyfill
+
+class stop_token;
+class stop_source;
+struct nostopstate_t {
+ explicit nostopstate_t() = default;
+};
+inline constexpr nostopstate_t nostopstate{};
+
+template <class Callback>
+class stop_callback;
+
+class stop_token {
+public:
+ stop_token() noexcept = default;
+
+ stop_token(const stop_token&) noexcept = default;
+ stop_token(stop_token&&) noexcept = default;
+ stop_token& operator=(const stop_token&) noexcept = default;
+ stop_token& operator=(stop_token&&) noexcept = default;
+ ~stop_token() = default;
+
+ void swap(stop_token& other) noexcept {
+ m_stop_state.swap(other.m_stop_state);
+ }
+
+ [[nodiscard]] bool stop_requested() const noexcept {
+ return m_stop_state && m_stop_state->stop_requested();
+ }
+ [[nodiscard]] bool stop_possible() const noexcept {
+ return m_stop_state != nullptr;
+ }
+
+private:
+ friend class stop_source;
+ template <typename Callback>
+ friend class stop_callback;
+ stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(move(stop_state)) {}
+
+private:
+ shared_ptr<polyfill::stop_state> m_stop_state;
+};
+
+class stop_source {
+public:
+ stop_source() : m_stop_state(make_shared<polyfill::stop_state>()) {}
+ explicit stop_source(nostopstate_t) noexcept {}
+
+ stop_source(const stop_source&) noexcept = default;
+ stop_source(stop_source&&) noexcept = default;
+ stop_source& operator=(const stop_source&) noexcept = default;
+ stop_source& operator=(stop_source&&) noexcept = default;
+ ~stop_source() = default;
+ void swap(stop_source& other) noexcept {
+ m_stop_state.swap(other.m_stop_state);
+ }
+
+ [[nodiscard]] stop_token get_token() const noexcept {
+ return stop_token(m_stop_state);
+ }
+ [[nodiscard]] bool stop_possible() const noexcept {
+ return m_stop_state != nullptr;
+ }
+ [[nodiscard]] bool stop_requested() const noexcept {
+ return m_stop_state && m_stop_state->stop_requested();
+ }
+ bool request_stop() noexcept {
+ return m_stop_state && m_stop_state->request_stop();
+ }
+
+private:
+ friend class jthread;
+ explicit stop_source(shared_ptr<polyfill::stop_state> stop_state)
+ : m_stop_state(move(stop_state)) {}
+
+private:
+ shared_ptr<polyfill::stop_state> m_stop_state;
+};
+
+template <typename Callback>
+class stop_callback {
+ static_assert(is_nothrow_destructible_v<Callback>);
+ static_assert(is_invocable_v<Callback>);
+
+public:
+ using callback_type = Callback;
+
+ template <typename C>
+ requires constructible_from<Callback, C>
+ explicit stop_callback(const stop_token& st,
+ C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
+ : m_stop_state(st.m_stop_state) {
+ if (m_stop_state) {
+ m_callback = m_stop_state->insert_callback(move(cb));
+ }
+ }
+ template <typename C>
+ requires constructible_from<Callback, C>
+ explicit stop_callback(stop_token&& st,
+ C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
+ : m_stop_state(move(st.m_stop_state)) {
+ if (m_stop_state) {
+ m_callback = m_stop_state->insert_callback(move(cb));
+ }
+ }
+ ~stop_callback() {
+ if (m_stop_state && m_callback) {
+ m_stop_state->remove_callback(*m_callback);
+ }
+ }
+
+ stop_callback(const stop_callback&) = delete;
+ stop_callback(stop_callback&&) = delete;
+ stop_callback& operator=(const stop_callback&) = delete;
+ stop_callback& operator=(stop_callback&&) = delete;
+
+private:
+ shared_ptr<polyfill::stop_state> m_stop_state;
+ optional<polyfill::stop_state_callbacks::const_iterator> m_callback;
+};
+
+template <typename Callback>
+stop_callback(stop_token, Callback) -> stop_callback<Callback>;
+
+class jthread {
+public:
+ using id = thread::id;
+ using native_handle_type = thread::native_handle_type;
+
+ jthread() noexcept = default;
+
+ template <typename F, typename... Args,
+ typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>>
+ explicit jthread(F&& f, Args&&... args)
+ : m_stop_state(make_shared<polyfill::stop_state>()),
+ m_thread(make_thread(move(f), move(args)...)) {}
+
+ ~jthread() {
+ if (joinable()) {
+ request_stop();
+ join();
+ }
+ }
+
+ jthread(const jthread&) = delete;
+ jthread(jthread&&) noexcept = default;
+ jthread& operator=(const jthread&) = delete;
+
+ jthread& operator=(jthread&& other) noexcept {
+ m_thread.swap(other.m_thread);
+ m_stop_state.swap(other.m_stop_state);
+ return *this;
+ }
+
+ void swap(jthread& other) noexcept {
+ m_thread.swap(other.m_thread);
+ m_stop_state.swap(other.m_stop_state);
+ }
+ [[nodiscard]] bool joinable() const noexcept {
+ return m_thread.joinable();
+ }
+ void join() {
+ m_thread.join();
+ }
+ void detach() {
+ m_thread.detach();
+ m_stop_state.reset();
+ }
+
+ [[nodiscard]] id get_id() const noexcept {
+ return m_thread.get_id();
+ }
+ [[nodiscard]] native_handle_type native_handle() {
+ return m_thread.native_handle();
+ }
+ [[nodiscard]] stop_source get_stop_source() noexcept {
+ return stop_source(m_stop_state);
+ }
+ [[nodiscard]] stop_token get_stop_token() const noexcept {
+ return stop_source(m_stop_state).get_token();
+ }
+ bool request_stop() noexcept {
+ return get_stop_source().request_stop();
+ }
+ [[nodiscard]] static unsigned int hardware_concurrency() noexcept {
+ return thread::hardware_concurrency();
+ }
+
+private:
+ template <typename F, typename... Args>
+ thread make_thread(F&& f, Args&&... args) {
+ if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) {
+ return thread(move(f), get_stop_token(), move(args)...);
+ } else {
+ return thread(move(f), move(args)...);
+ }
+ }
+
+ shared_ptr<polyfill::stop_state> m_stop_state;
+ thread m_thread;
+};
+
+} // namespace std
+
+namespace Common {
+
+template <typename Condvar, typename Lock, typename Pred>
+void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) {
+ if (token.stop_requested()) {
+ return;
+ }
+
+ std::stop_callback callback(token, [&] { cv.notify_all(); });
+ cv.wait(lock, [&] { return pred() || token.stop_requested(); });
+}
+
+} // namespace Common
+
+#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 @@
#include <condition_variable>
#include <functional>
#include <mutex>
-#include <stop_token>
#include <string>
#include <thread>
#include <type_traits>
#include <vector>
#include <queue>
+#include "common/polyfill_thread.h"
#include "common/thread.h"
#include "common/unique_function.h"
@@ -47,7 +47,8 @@ public:
if (requests.empty()) {
wait_condition.notify_all();
}
- condition.wait(lock, stop_token, [this] { return !requests.empty(); });
+ Common::CondvarWait(condition, lock, stop_token,
+ [this] { return !requests.empty(); });
if (stop_token.stop_requested()) {
break;
}
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 @@
#include <mutex>
#include <utility>
+#include "common/polyfill_thread.h"
+
namespace Common {
template <typename T, bool with_stop_token = false>
class SPSCQueue {
@@ -97,7 +99,7 @@ public:
T PopWait(std::stop_token stop_token) {
if (Empty()) {
std::unique_lock lock{cv_mutex};
- cv.wait(lock, stop_token, [this] { return !Empty(); });
+ Common::CondvarWait(cv, lock, stop_token, [this] { return !Empty(); });
}
if (stop_token.stop_requested()) {
return T{};