// 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 #ifdef __cpp_lib_jthread #include #include namespace Common { template void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) { cv.wait(lock, token, std::move(pred)); } } // namespace Common #else #include #include #include #include #include #include #include #include namespace std { namespace polyfill { using stop_state_callbacks = list>; 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 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 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 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 friend class stop_callback; stop_token(shared_ptr stop_state) : m_stop_state(move(stop_state)) {} private: shared_ptr m_stop_state; }; class stop_source { public: stop_source() = default; 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 stop_state) : m_stop_state(move(stop_state)) {} private: shared_ptr m_stop_state; }; template class stop_callback { static_assert(is_nothrow_destructible_v); static_assert(is_invocable_v); public: using callback_type = Callback; template requires constructible_from explicit stop_callback(const stop_token& st, C&& cb) noexcept(is_nothrow_constructible_v) : m_stop_state(st.m_stop_state) { if (m_stop_state) { m_callback = m_stop_state->insert_callback(move(cb)); } } template requires constructible_from explicit stop_callback(stop_token&& st, C&& cb) noexcept(is_nothrow_constructible_v) : 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 m_stop_state; optional m_callback; }; template stop_callback(stop_token, Callback) -> stop_callback; class jthread { public: using id = thread::id; using native_handle_type = thread::native_handle_type; jthread() noexcept = default; template , jthread>>> explicit jthread(F&& f, Args&&... args) : m_stop_state(make_shared()), 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 thread make_thread(F&& f, Args&&... args) { if constexpr (is_invocable_v, stop_token, decay_t...>) { return thread(move(f), get_stop_token(), move(args)...); } else { return thread(move(f), move(args)...); } } shared_ptr m_stop_state; thread m_thread; }; } // namespace std namespace Common { template 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