From ee32067b10b242aebed3aa831ba3b8c77a9e0904 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 9 Feb 2020 16:53:22 -0400 Subject: Common: Implement WallClock Interface and implement a native clock for x64 --- src/common/wall_clock.cpp | 90 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/common/wall_clock.cpp (limited to 'src/common/wall_clock.cpp') diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp new file mode 100644 index 0000000000..eabbba9da6 --- /dev/null +++ b/src/common/wall_clock.cpp @@ -0,0 +1,90 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/uint128.h" +#include "common/wall_clock.h" + +#ifdef ARCHITECTURE_x86_64 +#include "common/x64/cpu_detect.h" +#include "common/x64/native_clock.h" +#endif + +namespace Common { + +using base_timer = std::chrono::steady_clock; +using base_time_point = std::chrono::time_point; + +class StandardWallClock : public WallClock { +public: + StandardWallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency) + : WallClock(emulated_cpu_frequency, emulated_clock_frequency, false) { + start_time = base_timer::now(); + } + + std::chrono::nanoseconds GetTimeNS() override { + base_time_point current = base_timer::now(); + auto elapsed = current - start_time; + return std::chrono::duration_cast(elapsed); + } + + std::chrono::microseconds GetTimeUS() override { + base_time_point current = base_timer::now(); + auto elapsed = current - start_time; + return std::chrono::duration_cast(elapsed); + } + + std::chrono::milliseconds GetTimeMS() override { + base_time_point current = base_timer::now(); + auto elapsed = current - start_time; + return std::chrono::duration_cast(elapsed); + } + + u64 GetClockCycles() override { + std::chrono::nanoseconds time_now = GetTimeNS(); + const u128 temporal = Common::Multiply64Into128(time_now.count(), emulated_clock_frequency); + return Common::Divide128On32(temporal, 1000000000).first; + } + + u64 GetCPUCycles() override { + std::chrono::nanoseconds time_now = GetTimeNS(); + const u128 temporal = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency); + return Common::Divide128On32(temporal, 1000000000).first; + } + +private: + base_time_point start_time; +}; + +#ifdef ARCHITECTURE_x86_64 + +WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { + const auto& caps = GetCPUCaps(); + u64 rtsc_frequency = 0; + if (caps.invariant_tsc) { + if (caps.base_frequency != 0) { + rtsc_frequency = static_cast(caps.base_frequency) * 1000000U; + } + if (rtsc_frequency == 0) { + rtsc_frequency = EstimateRDTSCFrequency(); + } + } + if (rtsc_frequency == 0) { + return static_cast( + new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency)); + } else { + return static_cast( + new X64::NativeClock(emulated_cpu_frequency, emulated_clock_frequency, rtsc_frequency)); + } +} + +#else + +WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { + return static_cast( + new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency)); +} + +#endif + +} // namespace Common -- cgit v1.2.3 From 0c789667d9e7ac0f88ad98dd513ded523b35b27a Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 11:20:40 -0400 Subject: Common: Refactor & Document Wall clock. --- src/common/uint128.cpp | 22 +++++++++++++++++++ src/common/uint128.h | 3 +++ src/common/wall_clock.cpp | 13 +++++------- src/common/wall_clock.h | 13 +++++++++++- src/common/x64/native_clock.cpp | 47 ++++++----------------------------------- src/core/host_timing.cpp | 3 +-- 6 files changed, 50 insertions(+), 51 deletions(-) (limited to 'src/common/wall_clock.cpp') diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp index 32bf56730f..7e77588db1 100644 --- a/src/common/uint128.cpp +++ b/src/common/uint128.cpp @@ -6,12 +6,34 @@ #include #pragma intrinsic(_umul128) +#pragma intrinsic(_udiv128) #endif #include #include "common/uint128.h" namespace Common { +#ifdef _MSC_VER + +u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) { + u128 r{}; + r[0] = _umul128(a, b, &r[1]); + u64 remainder; + return _udiv128(r[1], r[0], d, &remainder); +} + +#else + +u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) { + const u64 diva = a / d; + const u64 moda = a % d; + const u64 divb = b / d; + const u64 modb = b % d; + return diva * b + moda * divb + moda * modb / d; +} + +#endif + u128 Multiply64Into128(u64 a, u64 b) { u128 result; #ifdef _MSC_VER diff --git a/src/common/uint128.h b/src/common/uint128.h index a3be2a2cba..503cd2d0c6 100644 --- a/src/common/uint128.h +++ b/src/common/uint128.h @@ -9,6 +9,9 @@ namespace Common { +// This function multiplies 2 u64 values and divides it by a u64 value. +u64 MultiplyAndDivide64(u64 a, u64 b, u64 d); + // This function multiplies 2 u64 values and produces a u128 value; u128 Multiply64Into128(u64 a, u64 b); diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index eabbba9da6..8f5e17fa4d 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -58,7 +58,7 @@ private: #ifdef ARCHITECTURE_x86_64 -WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { +std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { const auto& caps = GetCPUCaps(); u64 rtsc_frequency = 0; if (caps.invariant_tsc) { @@ -70,19 +70,16 @@ WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_cloc } } if (rtsc_frequency == 0) { - return static_cast( - new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency)); + return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency); } else { - return static_cast( - new X64::NativeClock(emulated_cpu_frequency, emulated_clock_frequency, rtsc_frequency)); + return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency, rtsc_frequency); } } #else -WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { - return static_cast( - new StandardWallClock(emulated_cpu_frequency, emulated_clock_frequency)); +std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { + return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency); } #endif diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index 6f763d74ba..fc34429bbe 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "common/common_types.h" @@ -12,10 +13,20 @@ namespace Common { class WallClock { public: + + /// Returns current wall time in nanoseconds virtual std::chrono::nanoseconds GetTimeNS() = 0; + + /// Returns current wall time in microseconds virtual std::chrono::microseconds GetTimeUS() = 0; + + /// Returns current wall time in milliseconds virtual std::chrono::milliseconds GetTimeMS() = 0; + + /// Returns current wall time in emulated clock cycles virtual u64 GetClockCycles() = 0; + + /// Returns current wall time in emulated cpu cycles virtual u64 GetCPUCycles() = 0; /// Tells if the wall clock, uses the host CPU's hardware clock @@ -35,6 +46,6 @@ private: bool is_native; }; -WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency); +std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency); } // namespace Common diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index c799111fdd..26d4d0ba68 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -11,44 +11,11 @@ #include #endif +#include "common/uint128.h" #include "common/x64/native_clock.h" namespace Common { -#ifdef _MSC_VER - -namespace { - -struct uint128 { - u64 low; - u64 high; -}; - -u64 umuldiv64(u64 a, u64 b, u64 d) { - uint128 r{}; - r.low = _umul128(a, b, &r.high); - u64 remainder; - return _udiv128(r.high, r.low, d, &remainder); -} - -} // namespace - -#else - -namespace { - -u64 umuldiv64(u64 a, u64 b, u64 d) { - const u64 diva = a / d; - const u64 moda = a % d; - const u64 divb = b / d; - const u64 modb = b % d; - return diva * b + moda * divb + moda * modb / d; -} - -} // namespace - -#endif - u64 EstimateRDTSCFrequency() { const auto milli_10 = std::chrono::milliseconds{10}; // get current time @@ -70,7 +37,7 @@ u64 EstimateRDTSCFrequency() { const u64 timer_diff = std::chrono::duration_cast(endTime - startTime).count(); const u64 tsc_diff = tscEnd - tscStart; - const u64 tsc_freq = umuldiv64(tsc_diff, 1000000000ULL, timer_diff); + const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff); return tsc_freq; } @@ -100,27 +67,27 @@ u64 NativeClock::GetRTSC() { std::chrono::nanoseconds NativeClock::GetTimeNS() { const u64 rtsc_value = GetRTSC(); - return std::chrono::nanoseconds{umuldiv64(rtsc_value, 1000000000, rtsc_frequency)}; + return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)}; } std::chrono::microseconds NativeClock::GetTimeUS() { const u64 rtsc_value = GetRTSC(); - return std::chrono::microseconds{umuldiv64(rtsc_value, 1000000, rtsc_frequency)}; + return std::chrono::microseconds{MultiplyAndDivide64(rtsc_value, 1000000, rtsc_frequency)}; } std::chrono::milliseconds NativeClock::GetTimeMS() { const u64 rtsc_value = GetRTSC(); - return std::chrono::milliseconds{umuldiv64(rtsc_value, 1000, rtsc_frequency)}; + return std::chrono::milliseconds{MultiplyAndDivide64(rtsc_value, 1000, rtsc_frequency)}; } u64 NativeClock::GetClockCycles() { const u64 rtsc_value = GetRTSC(); - return umuldiv64(rtsc_value, emulated_clock_frequency, rtsc_frequency); + return MultiplyAndDivide64(rtsc_value, emulated_clock_frequency, rtsc_frequency); } u64 NativeClock::GetCPUCycles() { const u64 rtsc_value = GetRTSC(); - return umuldiv64(rtsc_value, emulated_cpu_frequency, rtsc_frequency); + return MultiplyAndDivide64(rtsc_value, emulated_cpu_frequency, rtsc_frequency); } } // namespace X64 diff --git a/src/core/host_timing.cpp b/src/core/host_timing.cpp index ef9977b760..4ccf7c6c1c 100644 --- a/src/core/host_timing.cpp +++ b/src/core/host_timing.cpp @@ -36,8 +36,7 @@ struct CoreTiming::Event { }; CoreTiming::CoreTiming() { - Common::WallClock* wall = Common::CreateBestMatchingClock(Core::Timing::BASE_CLOCK_RATE, Core::Timing::CNTFREQ); - clock = std::unique_ptr(wall); + clock = Common::CreateBestMatchingClock(Core::Timing::BASE_CLOCK_RATE, Core::Timing::CNTFREQ); } CoreTiming::~CoreTiming() = default; -- cgit v1.2.3 From 09d2ffa66300d4774436fd8b91c63ea0d7fc2607 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 13:33:13 -0400 Subject: Common/Tests: Clang Format. --- src/common/fiber.cpp | 21 ++++++++++----------- src/common/fiber.h | 2 +- src/common/wall_clock.cpp | 12 ++++++++---- src/common/wall_clock.h | 4 ++-- src/core/host_timing.cpp | 8 +++++--- src/core/host_timing.h | 2 +- src/tests/common/fibers.cpp | 23 ++++++++++++++--------- 7 files changed, 41 insertions(+), 31 deletions(-) (limited to 'src/common/wall_clock.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index e91d86dbea..a46be73c10 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -12,7 +12,6 @@ namespace Common { - #ifdef _MSC_VER struct Fiber::FiberImpl { @@ -27,14 +26,14 @@ void Fiber::start() { UNREACHABLE(); } -void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) -{ - auto fiber = static_cast(fiber_parameter); - fiber->start(); +void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { + auto fiber = static_cast(fiber_parameter); + fiber->start(); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) - : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { + : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, + previous_fiber{} { impl = std::make_unique(); impl->handle = CreateFiber(0, &FiberStartFunc, this); } @@ -99,14 +98,14 @@ void Fiber::start(boost::context::detail::transfer_t& transfer) { UNREACHABLE(); } -void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) -{ - auto fiber = static_cast(transfer.data); - fiber->start(transfer); +void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { + auto fiber = static_cast(transfer.data); + fiber->start(transfer); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) - : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { + : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, + previous_fiber{} { impl = std::make_unique(); impl->context = boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), FiberStartFunc); diff --git a/src/common/fiber.h b/src/common/fiber.h index 89a01fdd8e..b530bf4d2c 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -12,7 +12,7 @@ #ifndef _MSC_VER namespace boost::context::detail { - struct transfer_t; +struct transfer_t; } #endif diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index 8f5e17fa4d..e6161c72ce 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -58,7 +58,8 @@ private: #ifdef ARCHITECTURE_x86_64 -std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { +std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, + u32 emulated_clock_frequency) { const auto& caps = GetCPUCaps(); u64 rtsc_frequency = 0; if (caps.invariant_tsc) { @@ -70,15 +71,18 @@ std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u } } if (rtsc_frequency == 0) { - return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency); + return std::make_unique(emulated_cpu_frequency, + emulated_clock_frequency); } else { - return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency, rtsc_frequency); + return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency, + rtsc_frequency); } } #else -std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency) { +std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, + u32 emulated_clock_frequency) { return std::make_unique(emulated_cpu_frequency, emulated_clock_frequency); } diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index fc34429bbe..ed284cf502 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h @@ -13,7 +13,6 @@ namespace Common { class WallClock { public: - /// Returns current wall time in nanoseconds virtual std::chrono::nanoseconds GetTimeNS() = 0; @@ -46,6 +45,7 @@ private: bool is_native; }; -std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency); +std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_frequency, + u32 emulated_clock_frequency); } // namespace Common diff --git a/src/core/host_timing.cpp b/src/core/host_timing.cpp index 4ccf7c6c1c..c734a118e4 100644 --- a/src/core/host_timing.cpp +++ b/src/core/host_timing.cpp @@ -72,7 +72,8 @@ void CoreTiming::SyncPause(bool is_paused) { } Pause(is_paused); event.Set(); - while (paused_set != is_paused); + while (paused_set != is_paused) + ; } bool CoreTiming::IsRunning() { @@ -158,7 +159,8 @@ void CoreTiming::Advance() { } if (!event_queue.empty()) { - std::chrono::nanoseconds next_time = std::chrono::nanoseconds(event_queue.front().time - global_timer); + std::chrono::nanoseconds next_time = + std::chrono::nanoseconds(event_queue.front().time - global_timer); basic_lock.unlock(); event.WaitFor(next_time); } else { @@ -181,4 +183,4 @@ std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { return clock->GetTimeUS(); } -} // namespace Core::Timing +} // namespace Core::HostTiming diff --git a/src/core/host_timing.h b/src/core/host_timing.h index f04a150eeb..15a150904a 100644 --- a/src/core/host_timing.h +++ b/src/core/host_timing.h @@ -145,4 +145,4 @@ private: /// std::shared_ptr CreateEvent(std::string name, TimedCallback&& callback); -} // namespace Core::Timing +} // namespace Core::HostTiming diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp index 358393a192..d63194dd4c 100644 --- a/src/tests/common/fibers.cpp +++ b/src/tests/common/fibers.cpp @@ -92,7 +92,8 @@ public: void DoWork1() { trap2 = false; - while (trap.load()); + while (trap.load()) + ; for (u32 i = 0; i < 12000; i++) { value1 += i; } @@ -105,7 +106,8 @@ public: } void DoWork2() { - while (trap2.load()); + while (trap2.load()) + ; value2 = 2000; trap = false; Fiber::YieldTo(fiber2, fiber1); @@ -197,9 +199,12 @@ static void ThreadStart2_2(u32 id, TestControl2& test_control) { TEST_CASE("Fibers::InterExchange", "[common]") { TestControl2 test_control{}; test_control.thread_fibers.resize(2, nullptr); - test_control.fiber1 = std::make_shared(std::function{WorkControl2_1}, &test_control); - test_control.fiber2 = std::make_shared(std::function{WorkControl2_2}, &test_control); - test_control.fiber3 = std::make_shared(std::function{WorkControl2_3}, &test_control); + test_control.fiber1 = + std::make_shared(std::function{WorkControl2_1}, &test_control); + test_control.fiber2 = + std::make_shared(std::function{WorkControl2_2}, &test_control); + test_control.fiber3 = + std::make_shared(std::function{WorkControl2_3}, &test_control); std::thread thread1(ThreadStart2_1, 0, std::ref(test_control)); std::thread thread2(ThreadStart2_2, 1, std::ref(test_control)); thread1.join(); @@ -291,8 +296,10 @@ static void ThreadStart3(u32 id, TestControl3& test_control) { TEST_CASE("Fibers::StartRace", "[common]") { TestControl3 test_control{}; test_control.thread_fibers.resize(2, nullptr); - test_control.fiber1 = std::make_shared(std::function{WorkControl3_1}, &test_control); - test_control.fiber2 = std::make_shared(std::function{WorkControl3_2}, &test_control); + test_control.fiber1 = + std::make_shared(std::function{WorkControl3_1}, &test_control); + test_control.fiber2 = + std::make_shared(std::function{WorkControl3_2}, &test_control); std::thread thread1(ThreadStart3, 0, std::ref(test_control)); std::thread thread2(ThreadStart3, 1, std::ref(test_control)); thread1.join(); @@ -302,6 +309,4 @@ TEST_CASE("Fibers::StartRace", "[common]") { REQUIRE(test_control.value3 == 1); } - - } // namespace Common -- cgit v1.2.3 From e7f5b1f1d3afa2295e0506df03cadc8d9ec8477a Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 15:02:04 -0400 Subject: Core/HostTiming: Allow events to be advanced manually. --- src/common/fiber.cpp | 2 +- src/common/wall_clock.cpp | 9 +++---- src/core/host_timing.cpp | 61 ++++++++++++++++++++++++++++------------------- src/core/host_timing.h | 6 ++++- 4 files changed, 47 insertions(+), 31 deletions(-) (limited to 'src/common/wall_clock.cpp') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 1220eddf05..e9c0946b6a 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -110,7 +110,7 @@ Fiber::Fiber(std::function&& entry_point_func, void* start_paramete FiberStartFunc); } -Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { +Fiber::Fiber() { impl = std::make_unique(); } diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index e6161c72ce..d4d35f4e7a 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -42,14 +42,15 @@ public: u64 GetClockCycles() override { std::chrono::nanoseconds time_now = GetTimeNS(); - const u128 temporal = Common::Multiply64Into128(time_now.count(), emulated_clock_frequency); - return Common::Divide128On32(temporal, 1000000000).first; + const u128 temporary = + Common::Multiply64Into128(time_now.count(), emulated_clock_frequency); + return Common::Divide128On32(temporary, 1000000000).first; } u64 GetCPUCycles() override { std::chrono::nanoseconds time_now = GetTimeNS(); - const u128 temporal = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency); - return Common::Divide128On32(temporal, 1000000000).first; + const u128 temporary = Common::Multiply64Into128(time_now.count(), emulated_cpu_frequency); + return Common::Divide128On32(temporary, 1000000000).first; } private: diff --git a/src/core/host_timing.cpp b/src/core/host_timing.cpp index be80d9f8ed..5d35a96b14 100644 --- a/src/core/host_timing.cpp +++ b/src/core/host_timing.cpp @@ -42,7 +42,7 @@ CoreTiming::CoreTiming() { CoreTiming::~CoreTiming() = default; void CoreTiming::ThreadEntry(CoreTiming& instance) { - instance.Advance(); + instance.ThreadLoop(); } void CoreTiming::Initialize() { @@ -137,38 +137,49 @@ void CoreTiming::RemoveEvent(const std::shared_ptr& event_type) { basic_lock.unlock(); } -void CoreTiming::Advance() { - has_started = true; - while (!shutting_down) { - while (!paused) { - paused_set = false; - basic_lock.lock(); - global_timer = GetGlobalTimeNs().count(); +std::optional CoreTiming::Advance() { + advance_lock.lock(); + basic_lock.lock(); + global_timer = GetGlobalTimeNs().count(); - while (!event_queue.empty() && event_queue.front().time <= global_timer) { - Event evt = std::move(event_queue.front()); - std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); - event_queue.pop_back(); - basic_lock.unlock(); + while (!event_queue.empty() && event_queue.front().time <= global_timer) { + Event evt = std::move(event_queue.front()); + std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); + event_queue.pop_back(); + basic_lock.unlock(); - if (auto event_type{evt.type.lock()}) { - event_type->callback(evt.userdata, global_timer - evt.time); - } + if (auto event_type{evt.type.lock()}) { + event_type->callback(evt.userdata, global_timer - evt.time); + } - basic_lock.lock(); - } + basic_lock.lock(); + } - if (!event_queue.empty()) { - std::chrono::nanoseconds next_time = - std::chrono::nanoseconds(event_queue.front().time - global_timer); - basic_lock.unlock(); - event.WaitFor(next_time); + if (!event_queue.empty()) { + const u64 next_time = event_queue.front().time - global_timer; + basic_lock.unlock(); + advance_lock.unlock(); + return next_time; + } else { + basic_lock.unlock(); + advance_lock.unlock(); + return std::nullopt; + } +} + +void CoreTiming::ThreadLoop() { + has_started = true; + while (!shutting_down) { + while (!paused) { + paused_set = false; + const auto next_time = Advance(); + if (next_time) { + std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time); + event.WaitFor(next_time_ns); } else { - basic_lock.unlock(); wait_set = true; event.Wait(); } - wait_set = false; } paused_set = true; diff --git a/src/core/host_timing.h b/src/core/host_timing.h index 679fcf491a..cd44b308cb 100644 --- a/src/core/host_timing.h +++ b/src/core/host_timing.h @@ -103,6 +103,9 @@ public: /// Returns current time in nanoseconds. std::chrono::nanoseconds GetGlobalTimeNs() const; + /// Checks for events manually and returns time in nanoseconds for next event, threadsafe. + std::optional Advance(); + private: struct Event; @@ -110,7 +113,7 @@ private: void ClearPendingEvents(); static void ThreadEntry(CoreTiming& instance); - void Advance(); + void ThreadLoop(); std::unique_ptr clock; @@ -128,6 +131,7 @@ private: std::shared_ptr ev_lost; Common::Event event{}; Common::SpinLock basic_lock{}; + Common::SpinLock advance_lock{}; std::unique_ptr timer_thread; std::atomic paused{}; std::atomic paused_set{}; -- cgit v1.2.3