diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index b99a903ba3..8693063685 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -38,6 +38,9 @@ #include #include #endif +#ifdef __linux__ +#include +#endif #include "sync.h" #include "Log.h" @@ -1719,6 +1722,14 @@ void thread_base::initialize(bool(*wait_cb)(const void*)) #elif !defined(_WIN32) pthread_setname_np(pthread_self(), m_name.get().substr(0, 15).c_str()); #endif + +#ifdef __linux__ + m_timer = timerfd_create(CLOCK_MONOTONIC, 0); + if (m_timer == -1) + { + LOG_ERROR(GENERAL, "Linux timer allocation failed, use wait_unlock() only"); + } +#endif } void thread_base::notify_abort() noexcept @@ -1734,6 +1745,13 @@ bool thread_base::finalize(int) noexcept // Report pending errors error_code::error_report(0, 0, 0, 0); +#ifdef __linux__ + if (m_timer != -1) + { + close(m_timer); + } +#endif + #ifdef _WIN32 ULONG64 cycles{}; QueryThreadCycleTime(GetCurrentThread(), &cycles); @@ -1781,6 +1799,23 @@ void thread_ctrl::_wait_for(u64 usec, bool alert /* true */) { auto _this = g_tls_this_thread; +#ifdef __linux__ + if (!alert && _this->m_timer != -1 && usec > 0 && usec <= 1000) + { + struct itimerspec timeout; + u64 missed; + u64 nsec = usec * 1000ull; + + timeout.it_value.tv_nsec = (nsec % 1000000000ull); + timeout.it_value.tv_sec = nsec / 1000000000ull; + timeout.it_interval.tv_sec = 0; + timeout.it_interval.tv_nsec = 0; + timerfd_settime(_this->m_timer, 0, &timeout, NULL); + read(_this->m_timer, &missed, sizeof(missed)); + return; + } +#endif + std::unique_lock lock(_this->m_mutex, std::defer_lock); while (true) diff --git a/Utilities/Thread.h b/Utilities/Thread.h index 9302f3b6d2..f54a64fd5a 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -118,6 +118,11 @@ class thread_base using native_entry = void*(*)(void* arg); #endif +#ifdef __linux__ + // Linux thread timer + int m_timer = -1; +#endif + // Thread handle (platform-specific) atomic_t m_thread{0}; diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index 4e3fdd9abf..f7f16f441f 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -241,14 +241,6 @@ public: // Now scale the result usec = (std::min(usec, max_usec) * g_cfg.core.clocks_scale) / 100; -#ifdef __linux__ - // TODO: Confirm whether Apple or any BSD can benefit from this as well - constexpr u32 host_min_quantum = 50; -#else - // Host scheduler quantum for windows (worst case) - // NOTE: On ps3 this function has very high accuracy - constexpr u32 host_min_quantum = 500; -#endif extern u64 get_system_time(); u64 passed = 0; @@ -258,6 +250,14 @@ public: while (usec >= passed) { remaining = usec - passed; +#ifdef __linux__ + // NOTE: Assumption that timer initialization has succeeded + u64 host_min_quantum = is_usleep && remaining <= 1000 ? 16 : 50; +#else + // Host scheduler quantum for windows (worst case) + // NOTE: On ps3 this function has very high accuracy + constexpr u64 host_min_quantum = 500; +#endif if (g_cfg.core.sleep_timers_accuracy < (is_usleep ? sleep_timers_accuracy_level::_usleep : sleep_timers_accuracy_level::_all_timers)) {