From b1ef5511530a03f030e8576a83442966e9245ce6 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 30 Aug 2025 13:27:30 +0000 Subject: [PATCH 1/9] [common] use libc++ provided jthread instead of in-house one (which deadlocks on FBSD 14) Signed-off-by: lizzie --- CMakeLists.txt | 10 + src/audio_core/sink/sink_stream.cpp | 3 +- src/common/bounded_threadsafe_queue.h | 5 +- src/common/polyfill_thread.h | 351 +----------------- src/common/thread.h | 5 +- src/common/thread_worker.h | 7 +- src/common/threadsafe_queue.h | 5 +- src/video_core/cdma_pusher.cpp | 4 +- src/video_core/gpu_thread.cpp | 2 +- .../renderer_vulkan/vk_master_semaphore.cpp | 2 +- .../renderer_vulkan/vk_present_manager.cpp | 2 +- .../renderer_vulkan/vk_scheduler.cpp | 2 +- .../renderer_vulkan/vk_turbo_mode.cpp | 5 +- src/yuzu/bootmanager.cpp | 4 +- 14 files changed, 43 insertions(+), 364 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 673aab9e6e..6abdeb16a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -346,6 +346,16 @@ if (PLATFORM_LINUX OR CXX_CLANG) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv9-a -mtune=generic") endif() endif() +elseif (ANDROID OR PLATFORM_FREEBSD OR PLATFORM_OPENBSD OR PLATFORM_SUN) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # libc++ has stop_token and jthread as experimental + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fexperimental-library") + else() + # Uses glibc, mostly? + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") + endif() endif() # Other presets, e.g. steamdeck diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index 4d7f0c1d5d..b75d743494 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp @@ -293,8 +293,7 @@ void SinkStream::WaitFreeSpace(std::stop_token stop_token) { release_cv.wait_for(lk, std::chrono::milliseconds(5), [this]() { return paused || queued_buffers < max_queue_size; }); if (queued_buffers > max_queue_size + 3) { - Common::CondvarWait(release_cv, lk, stop_token, - [this] { return paused || queued_buffers < max_queue_size; }); + release_cv.wait(lk, stop_token, [this] { return paused || queued_buffers < max_queue_size; }); } } diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h index b36fc1de96..80f32e2553 100644 --- a/src/common/bounded_threadsafe_queue.h +++ b/src/common/bounded_threadsafe_queue.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -123,7 +126,7 @@ private: } else if constexpr (Mode == PopMode::WaitWithStopToken) { // Wait until the queue is not empty. std::unique_lock lock{consumer_cv_mutex}; - Common::CondvarWait(consumer_cv, lock, stop_token, [this, read_index] { + consumer_cv.wait(lock, stop_token, [this, read_index] { return read_index != m_write_index.load(std::memory_order::acquire); }); if (stop_token.stop_requested()) { diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h index 12e59a8939..0d3c963d25 100644 --- a/src/common/polyfill_thread.h +++ b/src/common/polyfill_thread.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -7,23 +10,13 @@ #pragma once -#include - -#ifdef __cpp_lib_jthread - #include #include -#include #include #include namespace Common { -template -void CondvarWait(Condvar& cv, std::unique_lock& lk, std::stop_token token, Pred&& pred) { - cv.wait(lk, token, std::forward(pred)); -} - template bool StoppableTimedWait(std::stop_token token, const std::chrono::duration& rel_time) { std::condition_variable_any cv; @@ -35,341 +28,3 @@ bool StoppableTimedWait(std::stop_token token, const std::chrono::duration -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace std { -namespace polyfill { - -using stop_state_callback = size_t; - -class stop_state { -public: - stop_state() = default; - ~stop_state() = default; - - bool request_stop() { - unique_lock lk{m_lock}; - - if (m_stop_requested) { - // Already set, nothing to do. - return false; - } - - // Mark stop requested. - m_stop_requested = true; - - while (!m_callbacks.empty()) { - // Get an iterator to the first element. - const auto it = m_callbacks.begin(); - - // Move the callback function out of the map. - function f; - swap(it->second, f); - - // Erase the now-empty map element. - m_callbacks.erase(it); - - // Run the callback. - if (f) { - f(); - } - } - - return true; - } - - bool stop_requested() const { - unique_lock lk{m_lock}; - return m_stop_requested; - } - - stop_state_callback insert_callback(function f) { - unique_lock lk{m_lock}; - - if (m_stop_requested) { - // Stop already requested. Don't insert anything, - // just run the callback synchronously. - if (f) { - f(); - } - return 0; - } - - // Insert the callback. - stop_state_callback ret = ++m_next_callback; - m_callbacks.emplace(ret, std::move(f)); - return ret; - } - - void remove_callback(stop_state_callback cb) { - unique_lock lk{m_lock}; - m_callbacks.erase(cb); - } - -private: - mutable recursive_mutex m_lock; - map> m_callbacks; - stop_state_callback m_next_callback{0}; - bool m_stop_requested{false}; -}; - -} // 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(std::move(stop_state)) {} - -private: - shared_ptr m_stop_state; -}; - -class stop_source { -public: - stop_source() : m_stop_state(make_shared()) {} - 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(std::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(std::move(cb)); - } - } - template - requires constructible_from - explicit stop_callback(stop_token&& st, - C&& cb) noexcept(is_nothrow_constructible_v) - : m_stop_state(std::move(st.m_stop_state)) { - if (m_stop_state) { - m_callback = m_stop_state->insert_callback(std::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; - polyfill::stop_state_callback 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(std::forward(f), std::forward(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(std::forward(f), get_stop_token(), std::forward(args)...); - } else { - return thread(std::forward(f), std::forward(args)...); - } - } - - shared_ptr m_stop_state; - thread m_thread; -}; - -} // namespace std - -namespace Common { - -template -void CondvarWait(Condvar& cv, std::unique_lock& lk, std::stop_token token, Pred pred) { - if (token.stop_requested()) { - return; - } - - std::stop_callback callback(token, [&] { - { std::scoped_lock lk2{*lk.mutex()}; } - cv.notify_all(); - }); - - cv.wait(lk, [&] { return pred() || token.stop_requested(); }); -} - -template -bool StoppableTimedWait(std::stop_token token, const std::chrono::duration& rel_time) { - if (token.stop_requested()) { - return false; - } - - bool stop_requested = false; - std::condition_variable cv; - std::mutex m; - - std::stop_callback cb(token, [&] { - // Wake up the waiting thread. - { - std::scoped_lock lk{m}; - stop_requested = true; - } - cv.notify_one(); - }); - - // Perform the timed wait. - std::unique_lock lk{m}; - return !cv.wait_for(lk, rel_time, [&] { return stop_requested; }); -} - -} // namespace Common - -#endif diff --git a/src/common/thread.h b/src/common/thread.h index c6976fb6cd..5ab495baaa 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -80,7 +83,7 @@ public: condvar.notify_all(); return true; } else { - CondvarWait(condvar, lk, token, + condvar.wait(lk, token, [this, current_generation] { return current_generation != generation; }); return !token.stop_requested(); } diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h index 260ad44e45..6ec9d6a2bd 100644 --- a/src/common/thread_worker.h +++ b/src/common/thread_worker.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -47,8 +50,8 @@ public: if (requests.empty()) { wait_condition.notify_all(); } - Common::CondvarWait(condition, lock, stop_token, - [this] { return !requests.empty(); }); + condition.wait(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 2ef1da0644..28bb46722b 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2010 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -99,7 +102,7 @@ public: T PopWait(std::stop_token stop_token) { if (Empty()) { std::unique_lock lock{cv_mutex}; - Common::CondvarWait(cv, lock, stop_token, [this] { return !Empty(); }); + cv.wait(lock, stop_token, [this] { return !Empty(); }); } if (stop_token.stop_requested()) { return T{}; diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp index 1b6b4c6d45..b9140d9335 100644 --- a/src/video_core/cdma_pusher.cpp +++ b/src/video_core/cdma_pusher.cpp @@ -39,8 +39,8 @@ void CDmaPusher::ProcessEntries(std::stop_token stop_token) { while (!stop_token.stop_requested()) { { std::unique_lock l{command_mutex}; - Common::CondvarWait(command_cv, l, stop_token, - [this]() { return command_lists.size() > 0; }); + command_cv.wait(l, stop_token, + [this]() { return command_lists.size() > 0; }); if (stop_token.stop_requested()) { return; } diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 9392534e7d..8739905d99 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -116,7 +116,7 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { state.queue.EmplaceWait(std::move(command_data), fence, block); if (block) { - Common::CondvarWait(state.cv, lk, thread.get_stop_token(), [this, fence] { + state.cv.wait(lk, thread.get_stop_token(), [this, fence] { return fence <= state.signaled_fence.load(std::memory_order_relaxed); }); } diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index 4e5a2ff049..5c5520072c 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp @@ -208,7 +208,7 @@ void MasterSemaphore::WaitThread(std::stop_token token) { vk::Fence fence; { std::unique_lock lock{wait_mutex}; - Common::CondvarWait(wait_cv, lock, token, [this] { return !wait_queue.empty(); }); + wait_cv.wait(lock, token, [this] { return !wait_queue.empty(); }); if (token.stop_requested()) { return; } diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp index 2c76584c72..72713f4902 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp @@ -279,7 +279,7 @@ void PresentManager::PresentThread(std::stop_token token) { std::unique_lock lock{queue_mutex}; // Wait for presentation frames - Common::CondvarWait(frame_cv, lock, token, [this] { return !present_queue.empty(); }); + frame_cv.wait(lock, token, [this] { return !present_queue.empty(); }); if (token.stop_requested()) { return; } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 530d161dfe..d109d22cab 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -166,7 +166,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) { std::unique_lock lk{queue_mutex}; // Wait for work. - Common::CondvarWait(event_cv, lk, stop_token, [&] { return TryPopQueue(work); }); + event_cv.wait(lk, stop_token, [&] { return TryPopQueue(work); }); // If we've been asked to stop, we're done. if (stop_token.stop_requested()) { diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp index 04a51f2d1e..54183be12c 100644 --- a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp +++ b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -224,7 +227,7 @@ void TurboMode::Run(std::stop_token stop_token) { #endif // Wait for the next graphics queue submission if necessary. std::unique_lock lk{m_submission_lock}; - Common::CondvarWait(m_submission_cv, lk, stop_token, [this] { + m_submission_cv.wait(lk, stop_token, [this] { return (std::chrono::steady_clock::now() - m_submission_time) <= std::chrono::milliseconds{100}; }); diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 7b5f2a314f..cdc4e4024a 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -108,13 +108,13 @@ void EmuThread::run() { m_system.Run(); m_stopped.Reset(); - Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; }); + m_should_run_cv.wait(lk, stop_token, [&] { return !m_should_run; }); } else { m_system.Pause(); m_stopped.Set(); EmulationPaused(lk); - Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; }); + m_should_run_cv.wait(lk, stop_token, [&] { return m_should_run; }); EmulationResumed(lk); } } From f4aa76bafb7b390e1ccab1ba1c72ec20ce4e7e4b Mon Sep 17 00:00:00 2001 From: lizzie Date: Fri, 19 Sep 2025 06:58:48 +0000 Subject: [PATCH 2/9] [cmake] fix android? Signed-off-by: lizzie --- CMakeLists.txt | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6abdeb16a5..cb035a32c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -316,6 +316,18 @@ if (YUZU_ROOM) add_compile_definitions(YUZU_ROOM) endif() +if (ANDROID OR PLATFORM_FREEBSD OR PLATFORM_OPENBSD OR PLATFORM_SUN) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # libc++ has stop_token and jthread as experimental + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fexperimental-library") + else() + # Uses glibc, mostly? + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") + endif() +endif() + # Build/optimization presets if (PLATFORM_LINUX OR CXX_CLANG) if (ARCHITECTURE_x86_64) @@ -346,16 +358,6 @@ if (PLATFORM_LINUX OR CXX_CLANG) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv9-a -mtune=generic") endif() endif() -elseif (ANDROID OR PLATFORM_FREEBSD OR PLATFORM_OPENBSD OR PLATFORM_SUN) - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - # libc++ has stop_token and jthread as experimental - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fexperimental-library") - else() - # Uses glibc, mostly? - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") - endif() endif() # Other presets, e.g. steamdeck From eba682390085a3ae53d21f85bf8b51e59a63773e Mon Sep 17 00:00:00 2001 From: lizzie Date: Fri, 19 Sep 2025 07:35:53 +0000 Subject: [PATCH 3/9] [cmake] fix android once for all Signed-off-by: lizzie --- src/common/threadsafe_queue.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index 28bb46722b..f9fc0ca171 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -102,7 +102,11 @@ public: T PopWait(std::stop_token stop_token) { if (Empty()) { std::unique_lock lock{cv_mutex}; - cv.wait(lock, stop_token, [this] { return !Empty(); }); + if constexpr (with_stop_token) { + cv.wait(lock, stop_token, [this] { return !Empty(); }); + } else { + cv.wait(lock, [this] { return !Empty(); }); + } } if (stop_token.stop_requested()) { return T{}; From a3c0d59dc9cbc8648d779e81df79b4f5e7ba6e16 Mon Sep 17 00:00:00 2001 From: lizzie Date: Fri, 19 Sep 2025 16:57:34 +0200 Subject: [PATCH 4/9] [android] update translations for dynarmic to say it's jit, remove "(slow)" from paranoid (#2527) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2527 Reviewed-by: MaranBr Reviewed-by: crueter Co-authored-by: lizzie Co-committed-by: lizzie --- src/android/app/src/main/res/values-ar/strings.xml | 4 ++-- src/android/app/src/main/res/values-es/strings.xml | 4 ++-- src/android/app/src/main/res/values-fa/strings.xml | 4 ++-- src/android/app/src/main/res/values-fr/strings.xml | 4 ++-- src/android/app/src/main/res/values-he/strings.xml | 4 ++-- src/android/app/src/main/res/values-hu/strings.xml | 4 ++-- src/android/app/src/main/res/values-id/strings.xml | 4 ++-- src/android/app/src/main/res/values-ja/strings.xml | 4 ++-- src/android/app/src/main/res/values-ko/strings.xml | 4 ++-- src/android/app/src/main/res/values-pt-rBR/strings.xml | 4 ++-- src/android/app/src/main/res/values-pt-rPT/strings.xml | 4 ++-- src/android/app/src/main/res/values-ru/strings.xml | 4 ++-- src/android/app/src/main/res/values-sr/strings.xml | 4 ++-- src/android/app/src/main/res/values-zh-rCN/strings.xml | 4 ++-- src/android/app/src/main/res/values-zh-rTW/strings.xml | 4 ++-- src/android/app/src/main/res/values/strings.xml | 4 ++-- 16 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index a758e1c7cd..35f7d20198 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -836,13 +836,13 @@ تمتد إلى النافذة - Dynarmic (بطيء) + Dynarmic (JIT) تنفيذ التعليمات البرمجية الأصلية (NCE) دقه غير آمن - Paranoid (بطيء) + Paranoid الأسهم diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index 2ee0e1783a..1f73df7385 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -919,13 +919,13 @@ Ajustar a la ventana - DynARMic (lento) + Dynarmic (JIT) Ejecución nativa de código (NCE) Preciso Impreciso - Paranoico (Lento) + Paranoico Cruceta diff --git a/src/android/app/src/main/res/values-fa/strings.xml b/src/android/app/src/main/res/values-fa/strings.xml index 79cf5f49e6..fd5c6ae6ca 100644 --- a/src/android/app/src/main/res/values-fa/strings.xml +++ b/src/android/app/src/main/res/values-fa/strings.xml @@ -918,13 +918,13 @@ کشش تا پر کردن پنجره - دینارمیک (کند) + Dynarmic (JIT) اجرای کد اصلی (NCE) دقیق ناامن - بدگمان (کند) + بدگمان کلیدهای جهتی diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index e9df08a4de..97496ef33d 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -967,13 +967,13 @@ Étirer à la fenêtre - Dynarmic (Lent) + Dynarmic (JIT) Exécution de code natif (NCE) Précis Risqué - Paranoïaque (Lent) + Paranoïaque Pavé directionnel diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml index 4e1624a556..359e8dff9a 100644 --- a/src/android/app/src/main/res/values-he/strings.xml +++ b/src/android/app/src/main/res/values-he/strings.xml @@ -844,13 +844,13 @@ הרחב לגודל המסך - דינמי (איטי) + דינמי ביצוע קוד מקורי (NCE) מדויק לא בטוח - פראנואידי (איטי) + פראנואידי D-pad diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml index 061ac07388..36157d1578 100644 --- a/src/android/app/src/main/res/values-hu/strings.xml +++ b/src/android/app/src/main/res/values-hu/strings.xml @@ -954,13 +954,13 @@ Ablakhoz nyújtás - Dinamikus (lassú) + Dynarmic (JIT) Natív kód végrehajtás (Native code execution (NCE)) Pontos Nem biztonságos - Paranoid (Lassú) + Paranoid D-pad diff --git a/src/android/app/src/main/res/values-id/strings.xml b/src/android/app/src/main/res/values-id/strings.xml index 6e3b64953f..18e881a97b 100644 --- a/src/android/app/src/main/res/values-id/strings.xml +++ b/src/android/app/src/main/res/values-id/strings.xml @@ -911,13 +911,13 @@ Merentangkan - Dynamic (Lambat) + Dynarmic (JIT) Native code execution (NCE) Akurat Berbahaya - Paranoid (Slow) + Paranoid D Pad diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml index 179601f182..0a873b04eb 100644 --- a/src/android/app/src/main/res/values-ja/strings.xml +++ b/src/android/app/src/main/res/values-ja/strings.xml @@ -834,13 +834,13 @@ 画面に合わせる - Dynarmic (低速) + Dynarmic (JIT) ネイティブコード実行 (NCE) 正確 不安定 - パラノイド (低速) + パラノイド 方向ボタン diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml index 6f4dd42af2..e598bb1120 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml @@ -910,13 +910,13 @@ 화면에 맞춤 - Dynarmic (느림) + Dynarmic (JIT) 네이티브 코드 실행 (NCE) 정확함 최적화 (안전하지 않음) - 최적화하지 않음 (느림) + 최적화하지 않음 십자키 diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml index eec3fdf715..5571c2aea4 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml @@ -968,13 +968,13 @@ uma tentativa de mapeamento automático Esticar à janela - Dynarmic (Lenta) + Dynarmic (JIT) Execução de código nativo (NCE) Precisa Não segura - Paranoica (Lenta) + Paranoica Botões Direcionais diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml index d45bf4bfc9..81aa7e92c0 100644 --- a/src/android/app/src/main/res/values-pt-rPT/strings.xml +++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml @@ -968,13 +968,13 @@ uma tentativa de mapeamento automático Esticar à janela - Dynarmic (Lento) + Dynarmic (JIT) Native code execution (NCE) Preciso Inseguro - Paranoid (Lento) + Paranoid Botões Direcionais diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index 2f7714257f..eee249c44a 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -969,13 +969,13 @@ Растянуть до окна - Dynarmic (Медленно) + Dynarmic (JIT) Нативное выполнение (NCE) Точно Небезопасно - Параноик (медленно) + Параноик Крестовина diff --git a/src/android/app/src/main/res/values-sr/strings.xml b/src/android/app/src/main/res/values-sr/strings.xml index e261772fc4..b123757f5a 100644 --- a/src/android/app/src/main/res/values-sr/strings.xml +++ b/src/android/app/src/main/res/values-sr/strings.xml @@ -988,13 +988,13 @@ Протезање до прозора - Динамина (споро) + Dynarmic (JIT) Извођење изворног кода (НЦЕ) Тачан Несигуран - Параноичан (споро) + Параноичан Д-пад diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index bfdc3af3d3..5cf657ef3d 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -961,13 +961,13 @@ 拉伸窗口 - 动态编译 (慢速) + Dynarmic (JIT) 本机代码执行 (NCE) 高精度 低精度 - 偏执模式 (慢速) + 偏执模式 十字方向键 diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index e64aaa9a54..f4d690dbaa 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml @@ -966,13 +966,13 @@ 延展視窗 - 動態 (慢) + Dynarmic (JIT) 機器碼執行 (NCE) 高精度 低精度 - 不合理 (慢) + 不合理 方向鍵 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 2cef5903cb..3b76c0ba79 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -1014,13 +1014,13 @@ Stretch to window - Dynarmic (Slow) + Dynarmic (JIT) Native code execution (NCE) Accurate Unsafe - Paranoid (Slow) + Paranoid D-pad From a487cea683ab4a43c25989637a8c7a9c39a7e695 Mon Sep 17 00:00:00 2001 From: Caio Oliveira Date: Fri, 19 Sep 2025 17:02:53 +0200 Subject: [PATCH 5/9] [core] Fix buiding with fmt 10 (#2524) Signed-off-by: Caio Oliveira Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2524 Reviewed-by: MaranBr Reviewed-by: crueter Co-authored-by: Caio Oliveira Co-committed-by: Caio Oliveira --- src/core/perf_stats.cpp | 12 +++++++++++- src/core/reporter.cpp | 7 ++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 4439611d2e..35e76624f4 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2017 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -41,7 +44,14 @@ PerfStats::~PerfStats() { const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::LogDir); // %F Date format expanded is "%Y-%m-%d" - const auto filename = fmt::format("{:%F-%H-%M}_{:016X}.csv", *std::localtime(&t), title_id); + const auto filename = fmt::format("{}_{:016X}.csv", + [&] { + std::ostringstream oss; + oss << std::put_time(std::localtime(&t), "%F-%H-%M"); + return oss.str(); + }(), + title_id); + const auto filepath = path / filename; if (Common::FS::CreateParentDir(filepath)) { diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 4bac8142c3..1723636811 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -33,7 +36,9 @@ std::filesystem::path GetPath(std::string_view type, u64 title_id, std::string_v std::string GetTimestamp() { const auto time = std::time(nullptr); - return fmt::format("{:%FT%H-%M-%S}", *std::localtime(&time)); + std::ostringstream oss; + oss << std::put_time(std::localtime(&time), "%FT%H-%M-%S"); + return oss.str(); } using namespace nlohmann; From 6510818fca962ebee78089e79e972221dfbe3ef4 Mon Sep 17 00:00:00 2001 From: crueter Date: Fri, 19 Sep 2025 18:20:51 +0200 Subject: [PATCH 6/9] [docs] fixup codeowners (#2529) Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2529 Reviewed-by: MaranBr Reviewed-by: Shinmegumi --- docs/CODEOWNERS | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS index 503f9ec1fe..dcb97a5e83 100644 --- a/docs/CODEOWNERS +++ b/docs/CODEOWNERS @@ -10,17 +10,18 @@ /.ci @crueter # cmake -*.cmake @crueter -*/CMakeLists.txt @crueter -*.in @crueter +*.cmake @crueter +*CMakeLists.txt @crueter +*.in @crueter # individual stuff -/src/web_service @AleksandrPopovich -/src/dynarmic @Lizzie -/src/core @Lizzie @Maufeat @PavelBARABANOV @MrPurple666 -/src/core/hle @Maufeat @PavelBARABANOV @SDK-Chan -/src/*_room @AleksandrPopovich -/src/video_core @CamilleLaVey @MaranBr @Wildcard @weakboson +src/web_service @AleksandrPopovich +src/dynarmic @Lizzie +src/core @Lizzie @Maufeat @PavelBARABANOV @MrPurple666 @JPikachu +src/core/hle @Maufeat @PavelBARABANOV @SDK-Chan +src/core/arm @Lizzie @MrPurple666 +src/*_room @AleksandrPopovich +src/video_core @CamilleLaVey @MaranBr @Wildcard @weakboson # Global owners/triage * @CamilleLaVey @Maufeat @crueter @MrPurple666 @MaranBr @Lizzie \ No newline at end of file From aac548a7b75d4dc17b31dcf4d9053194bb40f789 Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 30 Aug 2025 13:27:30 +0000 Subject: [PATCH 7/9] [common] use libc++ provided jthread instead of in-house one (which deadlocks on FBSD 14) Signed-off-by: lizzie --- CMakeLists.txt | 10 + src/audio_core/sink/sink_stream.cpp | 3 +- src/common/bounded_threadsafe_queue.h | 5 +- src/common/polyfill_thread.h | 351 +----------------- src/common/thread.h | 5 +- src/common/thread_worker.h | 7 +- src/common/threadsafe_queue.h | 5 +- src/video_core/cdma_pusher.cpp | 4 +- src/video_core/gpu_thread.cpp | 2 +- .../renderer_vulkan/vk_master_semaphore.cpp | 2 +- .../renderer_vulkan/vk_present_manager.cpp | 2 +- .../renderer_vulkan/vk_scheduler.cpp | 2 +- .../renderer_vulkan/vk_turbo_mode.cpp | 5 +- src/yuzu/bootmanager.cpp | 4 +- 14 files changed, 43 insertions(+), 364 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 673aab9e6e..6abdeb16a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -346,6 +346,16 @@ if (PLATFORM_LINUX OR CXX_CLANG) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv9-a -mtune=generic") endif() endif() +elseif (ANDROID OR PLATFORM_FREEBSD OR PLATFORM_OPENBSD OR PLATFORM_SUN) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # libc++ has stop_token and jthread as experimental + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fexperimental-library") + else() + # Uses glibc, mostly? + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") + endif() endif() # Other presets, e.g. steamdeck diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index 4d7f0c1d5d..b75d743494 100644 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp @@ -293,8 +293,7 @@ void SinkStream::WaitFreeSpace(std::stop_token stop_token) { release_cv.wait_for(lk, std::chrono::milliseconds(5), [this]() { return paused || queued_buffers < max_queue_size; }); if (queued_buffers > max_queue_size + 3) { - Common::CondvarWait(release_cv, lk, stop_token, - [this] { return paused || queued_buffers < max_queue_size; }); + release_cv.wait(lk, stop_token, [this] { return paused || queued_buffers < max_queue_size; }); } } diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h index b36fc1de96..80f32e2553 100644 --- a/src/common/bounded_threadsafe_queue.h +++ b/src/common/bounded_threadsafe_queue.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -123,7 +126,7 @@ private: } else if constexpr (Mode == PopMode::WaitWithStopToken) { // Wait until the queue is not empty. std::unique_lock lock{consumer_cv_mutex}; - Common::CondvarWait(consumer_cv, lock, stop_token, [this, read_index] { + consumer_cv.wait(lock, stop_token, [this, read_index] { return read_index != m_write_index.load(std::memory_order::acquire); }); if (stop_token.stop_requested()) { diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h index 12e59a8939..0d3c963d25 100644 --- a/src/common/polyfill_thread.h +++ b/src/common/polyfill_thread.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -7,23 +10,13 @@ #pragma once -#include - -#ifdef __cpp_lib_jthread - #include #include -#include #include #include namespace Common { -template -void CondvarWait(Condvar& cv, std::unique_lock& lk, std::stop_token token, Pred&& pred) { - cv.wait(lk, token, std::forward(pred)); -} - template bool StoppableTimedWait(std::stop_token token, const std::chrono::duration& rel_time) { std::condition_variable_any cv; @@ -35,341 +28,3 @@ bool StoppableTimedWait(std::stop_token token, const std::chrono::duration -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace std { -namespace polyfill { - -using stop_state_callback = size_t; - -class stop_state { -public: - stop_state() = default; - ~stop_state() = default; - - bool request_stop() { - unique_lock lk{m_lock}; - - if (m_stop_requested) { - // Already set, nothing to do. - return false; - } - - // Mark stop requested. - m_stop_requested = true; - - while (!m_callbacks.empty()) { - // Get an iterator to the first element. - const auto it = m_callbacks.begin(); - - // Move the callback function out of the map. - function f; - swap(it->second, f); - - // Erase the now-empty map element. - m_callbacks.erase(it); - - // Run the callback. - if (f) { - f(); - } - } - - return true; - } - - bool stop_requested() const { - unique_lock lk{m_lock}; - return m_stop_requested; - } - - stop_state_callback insert_callback(function f) { - unique_lock lk{m_lock}; - - if (m_stop_requested) { - // Stop already requested. Don't insert anything, - // just run the callback synchronously. - if (f) { - f(); - } - return 0; - } - - // Insert the callback. - stop_state_callback ret = ++m_next_callback; - m_callbacks.emplace(ret, std::move(f)); - return ret; - } - - void remove_callback(stop_state_callback cb) { - unique_lock lk{m_lock}; - m_callbacks.erase(cb); - } - -private: - mutable recursive_mutex m_lock; - map> m_callbacks; - stop_state_callback m_next_callback{0}; - bool m_stop_requested{false}; -}; - -} // 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(std::move(stop_state)) {} - -private: - shared_ptr m_stop_state; -}; - -class stop_source { -public: - stop_source() : m_stop_state(make_shared()) {} - 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(std::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(std::move(cb)); - } - } - template - requires constructible_from - explicit stop_callback(stop_token&& st, - C&& cb) noexcept(is_nothrow_constructible_v) - : m_stop_state(std::move(st.m_stop_state)) { - if (m_stop_state) { - m_callback = m_stop_state->insert_callback(std::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; - polyfill::stop_state_callback 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(std::forward(f), std::forward(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(std::forward(f), get_stop_token(), std::forward(args)...); - } else { - return thread(std::forward(f), std::forward(args)...); - } - } - - shared_ptr m_stop_state; - thread m_thread; -}; - -} // namespace std - -namespace Common { - -template -void CondvarWait(Condvar& cv, std::unique_lock& lk, std::stop_token token, Pred pred) { - if (token.stop_requested()) { - return; - } - - std::stop_callback callback(token, [&] { - { std::scoped_lock lk2{*lk.mutex()}; } - cv.notify_all(); - }); - - cv.wait(lk, [&] { return pred() || token.stop_requested(); }); -} - -template -bool StoppableTimedWait(std::stop_token token, const std::chrono::duration& rel_time) { - if (token.stop_requested()) { - return false; - } - - bool stop_requested = false; - std::condition_variable cv; - std::mutex m; - - std::stop_callback cb(token, [&] { - // Wake up the waiting thread. - { - std::scoped_lock lk{m}; - stop_requested = true; - } - cv.notify_one(); - }); - - // Perform the timed wait. - std::unique_lock lk{m}; - return !cv.wait_for(lk, rel_time, [&] { return stop_requested; }); -} - -} // namespace Common - -#endif diff --git a/src/common/thread.h b/src/common/thread.h index c6976fb6cd..5ab495baaa 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -80,7 +83,7 @@ public: condvar.notify_all(); return true; } else { - CondvarWait(condvar, lk, token, + condvar.wait(lk, token, [this, current_generation] { return current_generation != generation; }); return !token.stop_requested(); } diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h index 260ad44e45..6ec9d6a2bd 100644 --- a/src/common/thread_worker.h +++ b/src/common/thread_worker.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -47,8 +50,8 @@ public: if (requests.empty()) { wait_condition.notify_all(); } - Common::CondvarWait(condition, lock, stop_token, - [this] { return !requests.empty(); }); + condition.wait(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 2ef1da0644..28bb46722b 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2010 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -99,7 +102,7 @@ public: T PopWait(std::stop_token stop_token) { if (Empty()) { std::unique_lock lock{cv_mutex}; - Common::CondvarWait(cv, lock, stop_token, [this] { return !Empty(); }); + cv.wait(lock, stop_token, [this] { return !Empty(); }); } if (stop_token.stop_requested()) { return T{}; diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp index 1b6b4c6d45..b9140d9335 100644 --- a/src/video_core/cdma_pusher.cpp +++ b/src/video_core/cdma_pusher.cpp @@ -39,8 +39,8 @@ void CDmaPusher::ProcessEntries(std::stop_token stop_token) { while (!stop_token.stop_requested()) { { std::unique_lock l{command_mutex}; - Common::CondvarWait(command_cv, l, stop_token, - [this]() { return command_lists.size() > 0; }); + command_cv.wait(l, stop_token, + [this]() { return command_lists.size() > 0; }); if (stop_token.stop_requested()) { return; } diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 9392534e7d..8739905d99 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -116,7 +116,7 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { state.queue.EmplaceWait(std::move(command_data), fence, block); if (block) { - Common::CondvarWait(state.cv, lk, thread.get_stop_token(), [this, fence] { + state.cv.wait(lk, thread.get_stop_token(), [this, fence] { return fence <= state.signaled_fence.load(std::memory_order_relaxed); }); } diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index 4e5a2ff049..5c5520072c 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp @@ -208,7 +208,7 @@ void MasterSemaphore::WaitThread(std::stop_token token) { vk::Fence fence; { std::unique_lock lock{wait_mutex}; - Common::CondvarWait(wait_cv, lock, token, [this] { return !wait_queue.empty(); }); + wait_cv.wait(lock, token, [this] { return !wait_queue.empty(); }); if (token.stop_requested()) { return; } diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp index 2c76584c72..72713f4902 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp @@ -279,7 +279,7 @@ void PresentManager::PresentThread(std::stop_token token) { std::unique_lock lock{queue_mutex}; // Wait for presentation frames - Common::CondvarWait(frame_cv, lock, token, [this] { return !present_queue.empty(); }); + frame_cv.wait(lock, token, [this] { return !present_queue.empty(); }); if (token.stop_requested()) { return; } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 530d161dfe..d109d22cab 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -166,7 +166,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) { std::unique_lock lk{queue_mutex}; // Wait for work. - Common::CondvarWait(event_cv, lk, stop_token, [&] { return TryPopQueue(work); }); + event_cv.wait(lk, stop_token, [&] { return TryPopQueue(work); }); // If we've been asked to stop, we're done. if (stop_token.stop_requested()) { diff --git a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp index 04a51f2d1e..54183be12c 100644 --- a/src/video_core/renderer_vulkan/vk_turbo_mode.cpp +++ b/src/video_core/renderer_vulkan/vk_turbo_mode.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -224,7 +227,7 @@ void TurboMode::Run(std::stop_token stop_token) { #endif // Wait for the next graphics queue submission if necessary. std::unique_lock lk{m_submission_lock}; - Common::CondvarWait(m_submission_cv, lk, stop_token, [this] { + m_submission_cv.wait(lk, stop_token, [this] { return (std::chrono::steady_clock::now() - m_submission_time) <= std::chrono::milliseconds{100}; }); diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 7b5f2a314f..cdc4e4024a 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -108,13 +108,13 @@ void EmuThread::run() { m_system.Run(); m_stopped.Reset(); - Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return !m_should_run; }); + m_should_run_cv.wait(lk, stop_token, [&] { return !m_should_run; }); } else { m_system.Pause(); m_stopped.Set(); EmulationPaused(lk); - Common::CondvarWait(m_should_run_cv, lk, stop_token, [&] { return m_should_run; }); + m_should_run_cv.wait(lk, stop_token, [&] { return m_should_run; }); EmulationResumed(lk); } } From 74d76e8f916e4284f38f248d53d8df623b157a66 Mon Sep 17 00:00:00 2001 From: lizzie Date: Fri, 19 Sep 2025 06:58:48 +0000 Subject: [PATCH 8/9] [cmake] fix android? Signed-off-by: lizzie --- CMakeLists.txt | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6abdeb16a5..cb035a32c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -316,6 +316,18 @@ if (YUZU_ROOM) add_compile_definitions(YUZU_ROOM) endif() +if (ANDROID OR PLATFORM_FREEBSD OR PLATFORM_OPENBSD OR PLATFORM_SUN) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # libc++ has stop_token and jthread as experimental + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fexperimental-library") + else() + # Uses glibc, mostly? + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") + endif() +endif() + # Build/optimization presets if (PLATFORM_LINUX OR CXX_CLANG) if (ARCHITECTURE_x86_64) @@ -346,16 +358,6 @@ if (PLATFORM_LINUX OR CXX_CLANG) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv9-a -mtune=generic") endif() endif() -elseif (ANDROID OR PLATFORM_FREEBSD OR PLATFORM_OPENBSD OR PLATFORM_SUN) - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - # libc++ has stop_token and jthread as experimental - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fexperimental-library") - else() - # Uses glibc, mostly? - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") - endif() endif() # Other presets, e.g. steamdeck From 77c2e2bb20aaed7ec0cac3e51815e854cb5e835f Mon Sep 17 00:00:00 2001 From: lizzie Date: Fri, 19 Sep 2025 07:35:53 +0000 Subject: [PATCH 9/9] [cmake] fix android once for all Signed-off-by: lizzie --- src/common/threadsafe_queue.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index 28bb46722b..f9fc0ca171 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -102,7 +102,11 @@ public: T PopWait(std::stop_token stop_token) { if (Empty()) { std::unique_lock lock{cv_mutex}; - cv.wait(lock, stop_token, [this] { return !Empty(); }); + if constexpr (with_stop_token) { + cv.wait(lock, stop_token, [this] { return !Empty(); }); + } else { + cv.wait(lock, [this] { return !Empty(); }); + } } if (stop_token.stop_requested()) { return T{};