diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 16016947bc..325e4ea00d 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -1622,7 +1622,7 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe error_code sending_error = not_an_error(CELL_EBUSY); // If we fail due to being busy, wait a bit and try again. - for (; static_cast(sending_error) == CELL_EBUSY; thread_ctrl::wait_for(1000)) + for (u64 sleep_until = get_system_time(); static_cast(sending_error) == CELL_EBUSY; thread_ctrl::wait_until(&sleep_until, 1000)) { sending_error = send_event(); @@ -2414,6 +2414,34 @@ void thread_ctrl::wait_for(u64 usec, [[maybe_unused]] bool alert /* true */) list.wait(atomic_wait_timeout{usec <= 0xffff'ffff'ffff'ffff / 1000 ? usec * 1000 : 0xffff'ffff'ffff'ffff}); } + +void thread_ctrl::wait_until(u64* wait_time, u64 add_time, u64 min_wait, bool update_to_current_time) +{ + *wait_time = utils::add_saturate(*wait_time, add_time); + + // TODO: Implement proper support for "waiting until" inside atomic wait engine + const u64 current_time = get_system_time(); + + if (current_time > *wait_time) + { + if (update_to_current_time) + { + *wait_time = current_time + (add_time - (current_time - *wait_time) % add_time); + } + else if (!min_wait) + { + return; + } + } + + if (min_wait) + { + *wait_time = std::max(*wait_time, utils::add_saturate(current_time, min_wait)); + } + + wait_for(*wait_time - current_time); +} + void thread_ctrl::wait_for_accurate(u64 usec) { if (!usec) diff --git a/Utilities/Thread.h b/Utilities/Thread.h index df45fda70d..1aab60481f 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -276,6 +276,9 @@ public: // Wait once with timeout. Infinite value is -1. static void wait_for(u64 usec, bool alert = true); + // Wait once with time point, add_time is added to the time point. + static void wait_until(u64* wait_time, u64 add_time = 0, u64 min_wait = 0, bool update_to_current_time = true); + // Waiting with accurate timeout static void wait_for_accurate(u64 usec); diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index e05c87c39a..e4ed28fbcd 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -1206,10 +1206,11 @@ public: void operator()() { bool was_paused = false; + u64 sleep_until = get_system_time(); for (u32 i = 1; thread_ctrl::state() != thread_state::aborting; i++) { - thread_ctrl::wait_for(1'000'000); + thread_ctrl::wait_until(&sleep_until, 1'000'000); const bool is_paused = Emu.IsPaused(); diff --git a/rpcs3/Emu/GDB.cpp b/rpcs3/Emu/GDB.cpp index 4f28f1c8d7..16e681f28f 100644 --- a/rpcs3/Emu/GDB.cpp +++ b/rpcs3/Emu/GDB.cpp @@ -894,7 +894,7 @@ void gdb_thread::operator()() { start_server(); - while (server_socket != -1 && thread_ctrl::state() != thread_state::aborting) + for (u64 sleep_until = get_system_time(); server_socket != -1 && thread_ctrl::state() != thread_state::aborting;) { sockaddr_in client; socklen_t client_len = sizeof(client); @@ -904,7 +904,7 @@ void gdb_thread::operator()() { if (check_errno_again()) { - thread_ctrl::wait_for(5000); + thread_ctrl::wait_until(&sleep_until, 5000); continue; } diff --git a/rpcs3/Emu/perf_monitor.cpp b/rpcs3/Emu/perf_monitor.cpp index 360b563bae..5f0c6ad84b 100644 --- a/rpcs3/Emu/perf_monitor.cpp +++ b/rpcs3/Emu/perf_monitor.cpp @@ -20,10 +20,11 @@ void perf_monitor::operator()() u64 last_pause_time = umax; std::vector per_core_usage; + std::string msg; - while (thread_ctrl::state() != thread_state::aborting) + for (u64 sleep_until = get_system_time(); thread_ctrl::state() != thread_state::aborting;) { - thread_ctrl::wait_for(update_interval_us); + thread_ctrl::wait_until(&sleep_until, update_interval_us); elapsed_us += update_interval_us; if (thread_ctrl::state() == thread_state::aborting) @@ -61,14 +62,15 @@ void perf_monitor::operator()() logged_pause++; } - std::string msg = fmt::format("CPU Usage: Total: %.1f%%", total_usage); + msg.clear(); + fmt::append(msg, "CPU Usage: Total: %.1f%%", total_usage); if (!per_core_usage.empty()) { fmt::append(msg, ", Cores:"); } - for (size_t i = 0; i < per_core_usage.size(); i++) + for (usz i = 0; i < per_core_usage.size(); i++) { fmt::append(msg, "%s %.1f%%", i > 0 ? "," : "", per_core_usage[i]); } diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index 94e058534b..33c80a8d4d 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -175,7 +175,9 @@ void progress_dialog_server::operator()() usz time_left_queue_idx = 0; // Update progress - while (!g_system_progress_stopping && thread_ctrl::state() != thread_state::aborting) + for (u64 sleep_until = get_system_time(), sleep_for = 500; + !g_system_progress_stopping && thread_ctrl::state() != thread_state::aborting; + thread_ctrl::wait_until(&sleep_until, std::exchange(sleep_for, 500))) { const auto& [text_new, ftotal_new, fdone_new, ftotal_bits_new, fknown_bits_new, ptotal_new, pdone_new] = get_state(); @@ -236,7 +238,7 @@ void progress_dialog_server::operator()() } } - thread_ctrl::wait_for(10000); + sleep_for = 10000; continue; } @@ -365,7 +367,7 @@ void progress_dialog_server::operator()() break; } - thread_ctrl::wait_for(10'000); + sleep_for = 10'000; wait_no_update_count++; }