diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 05379c5b888c..e349f563d596 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -2396,7 +2396,7 @@ thread_state thread_ctrl::state() return static_cast(_this->m_sync & 3); } -void thread_ctrl::wait_for(u64 usec, [[maybe_unused]] bool alert /* true */) +bool thread_ctrl::wait_for(u64 usec, bool alert) { auto _this = g_tls_this_thread; @@ -2440,23 +2440,33 @@ void thread_ctrl::wait_for(u64 usec, [[maybe_unused]] bool alert /* true */) timerfd_settime(fd_timer, 0, &timeout, NULL); if (read(fd_timer, &missed, sizeof(missed)) != sizeof(missed)) sig_log.error("timerfd: read() failed"); - return; + return true; } #endif - if (_this->m_sync.bit_test_reset(2) || _this->m_taskq) + if ((alert && (_this->m_sync.bit_test_reset(2) || _this->m_taskq)) || _this->m_sync & 1) { - return; + // Notified or aborted + return false; + } + + const atomic_wait_timeout timeout{usec <= 0xffff'ffff'ffff'ffff / 1000 ? usec * 1000 : 0xffff'ffff'ffff'ffff}; + + if (!alert) + { + _this->m_sync.wait(0, 1, timeout); + return true; } // Wait for signal and thread state abort atomic_wait::list<2> list{}; list.set<0>(_this->m_sync, 0, 4 + 1); list.set<1>(_this->m_taskq, nullptr); - list.wait(atomic_wait_timeout{usec <= 0xffff'ffff'ffff'ffff / 1000 ? usec * 1000 : 0xffff'ffff'ffff'ffff}); + list.wait(timeout); + return true; // Unknown if notified for now } -void thread_ctrl::wait_for_accurate(u64 usec) +void thread_ctrl::wait_for_accurate(u64 usec, bool alert) { if (!usec) { @@ -2481,11 +2491,15 @@ void thread_ctrl::wait_for_accurate(u64 usec) { #ifdef __linux__ // Do not wait for the last quantum to avoid loss of accuracy - wait_for(usec - ((usec % host_min_quantum) + host_min_quantum), false); + if (!wait_for(usec - ((usec % host_min_quantum) + host_min_quantum), alert)) #else // Wait on multiple of min quantum for large durations to avoid overloading low thread cpus - wait_for(usec - (usec % host_min_quantum), false); + if (!wait_for(usec - (usec % host_min_quantum), alert)) #endif + { + // Notified + return; + } } // TODO: Determine best value for yield delay else if (usec >= host_min_quantum / 2) @@ -2504,7 +2518,7 @@ void thread_ctrl::wait_for_accurate(u64 usec) break; } - usec = (until - current).count(); + usec = std::chrono::duration_cast(until - current).count(); } } diff --git a/Utilities/Thread.h b/Utilities/Thread.h index a64d64cb995b..83ab8dd56cd4 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -260,11 +260,11 @@ class thread_ctrl final // Read current state, possibly executing some tasks static thread_state state(); - // Wait once with timeout. Infinite value is -1. - static void wait_for(u64 usec, bool alert = true); + // Wait once with timeout. Infinite value is -1. (returns false if known to be notified) + static bool wait_for(u64 usec, bool alert = true); // Waiting with accurate timeout - static void wait_for_accurate(u64 usec); + static void wait_for_accurate(u64 usec, bool alert = false); // Wait. static inline void wait() diff --git a/rpcs3/Emu/Cell/Modules/cellAudio.cpp b/rpcs3/Emu/Cell/Modules/cellAudio.cpp index dfc3d6ea244f..a65d506c2191 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudio.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAudio.cpp @@ -613,9 +613,10 @@ void cell_audio_thread::advance(u64 timestamp) lock.unlock(); + lv2_obj::notify_all_t notify; + for (u32 i = 0; i < queue_count; i++) { - lv2_obj::notify_all_t notify; queues[i]->send(event_sources[i], CELL_AUDIO_EVENT_MIX, 0, event_data3[i]); } } @@ -777,7 +778,7 @@ void cell_audio_thread::operator()() const s64 audio_period_alignment_delta = (timestamp - m_start_time) % cfg.audio_block_period; if (audio_period_alignment_delta > cfg.period_comparison_margin) { - thread_ctrl::wait_for(audio_period_alignment_delta - cfg.period_comparison_margin); + thread_ctrl::wait_for_accurate(audio_period_alignment_delta - cfg.period_comparison_margin, false); } if (cfg.buffering_enabled) @@ -831,7 +832,7 @@ void cell_audio_thread::operator()() if (time_left > cfg.period_comparison_margin) { - thread_ctrl::wait_for(get_thread_wait_delay(time_left)); + thread_ctrl::wait_for_accurate(get_thread_wait_delay(time_left), false); continue; } } @@ -904,7 +905,7 @@ void cell_audio_thread::operator()() const s64 time_left = m_dynamic_period - time_since_last_period; if (time_left > cfg.period_comparison_margin) { - thread_ctrl::wait_for(get_thread_wait_delay(time_left)); + thread_ctrl::wait_for_accurate(get_thread_wait_delay(time_left), false); continue; } diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 457a789554aa..cabbd8a12ed7 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -1644,7 +1644,7 @@ void lv2_obj::cleanup() void lv2_obj::schedule_all(u64 current_time) { - usz notify_later_idx = 0; + usz notify_later_idx = std::basic_string_view{g_to_notify, 4}.find_first_of(std::add_pointer_t{}); if (!g_pending && g_scheduler_ready) { @@ -1663,7 +1663,7 @@ void lv2_obj::schedule_all(u64 current_time) continue; } - if (notify_later_idx == std::size(g_to_notify)) + if (notify_later_idx >= std::size(g_to_notify)) { // Out of notification slots, notify locally (resizable container is not worth it) target->state.notify_one(cpu_flag::signal + cpu_flag::suspend); @@ -1697,7 +1697,7 @@ void lv2_obj::schedule_all(u64 current_time) ensure(!target->state.test_and_set(cpu_flag::notify)); // Otherwise notify it to wake itself - if (notify_later_idx == std::size(g_to_notify)) + if (notify_later_idx >= std::size(g_to_notify)) { // Out of notification slots, notify locally (resizable container is not worth it) target->state.notify_one(cpu_flag::notify); diff --git a/rpcs3/Emu/Cell/lv2/sys_timer.cpp b/rpcs3/Emu/Cell/lv2/sys_timer.cpp index 4816a4ef3f1c..a6503eae141e 100644 --- a/rpcs3/Emu/Cell/lv2/sys_timer.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_timer.cpp @@ -114,6 +114,7 @@ lv2_timer_thread::lv2_timer_thread() void lv2_timer_thread::operator()() { u64 sleep_time = 0; + u64 old_now = umax; while (true) { @@ -123,7 +124,7 @@ void lv2_timer_thread::operator()() sleep_time = std::min(sleep_time, u64{umax} / 100) * 100 / g_cfg.core.clocks_scale; } - thread_ctrl::wait_for(sleep_time); + thread_ctrl::wait_for_accurate(utils::sub_saturate(sleep_time, get_guest_system_time() - old_now)); if (thread_ctrl::state() == thread_state::aborting) { @@ -138,7 +139,7 @@ void lv2_timer_thread::operator()() continue; } - const u64 _now = get_guest_system_time(); + old_now = get_guest_system_time(); reader_lock lock(mutex); @@ -146,7 +147,7 @@ void lv2_timer_thread::operator()() { while (lv2_obj::check(timer)) { - if (const u64 advised_sleep_time = timer->check(_now)) + if (const u64 advised_sleep_time = timer->check(old_now)) { if (sleep_time > advised_sleep_time) {