From 13ed9438fb47d62663fb1ef367baac1a567b25b3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 4 Feb 2020 11:23:12 -0400 Subject: Common: Implement a basic SpinLock class --- src/common/CMakeLists.txt | 2 ++ src/common/spin_lock.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/common/spin_lock.h | 20 ++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 src/common/spin_lock.cpp create mode 100644 src/common/spin_lock.h (limited to 'src/common') diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 0a3e2f4d1..c8bf80372 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -143,6 +143,8 @@ add_library(common STATIC scm_rev.cpp scm_rev.h scope_exit.h + spin_lock.cpp + spin_lock.h string_util.cpp string_util.h swap.h diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp new file mode 100644 index 000000000..8077b78d2 --- /dev/null +++ b/src/common/spin_lock.cpp @@ -0,0 +1,46 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/spin_lock.h" + +#if _MSC_VER +#include +#if _M_AMD64 +#define __x86_64__ 1 +#endif +#if _M_ARM64 +#define __aarch64__ 1 +#endif +#else +#if __x86_64__ +#include +#endif +#endif + +namespace { + +void thread_pause() { +#if __x86_64__ + _mm_pause(); +#elif __aarch64__ && _MSC_VER + __yield(); +#elif __aarch64__ + asm("yield"); +#endif +} + +} // namespace + +namespace Common { + +void SpinLock::lock() { + while (lck.test_and_set(std::memory_order_acquire)) + thread_pause(); +} + +void SpinLock::unlock() { + lck.clear(std::memory_order_release); +} + +} // namespace Common diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h new file mode 100644 index 000000000..cbc67b6c8 --- /dev/null +++ b/src/common/spin_lock.h @@ -0,0 +1,20 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +namespace Common { + +class SpinLock { +public: + void lock(); + void unlock(); + +private: + std::atomic_flag lck = ATOMIC_FLAG_INIT; +}; + +} // namespace Common -- cgit v1.2.3 From bc266a9d98f38f6fd1006f1ca52bd57e6a7f37d3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 4 Feb 2020 15:06:23 -0400 Subject: Common: Implement a basic Fiber class. --- src/common/CMakeLists.txt | 2 + src/common/fiber.cpp | 147 ++++++++++++++++++++++++++++++++++++++++++++++ src/common/fiber.h | 55 +++++++++++++++++ 3 files changed, 204 insertions(+) create mode 100644 src/common/fiber.cpp create mode 100644 src/common/fiber.h (limited to 'src/common') diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index c8bf80372..554d6e253 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -110,6 +110,8 @@ add_library(common STATIC common_types.h dynamic_library.cpp dynamic_library.h + fiber.cpp + fiber.h file_util.cpp file_util.h hash.h diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp new file mode 100644 index 000000000..eb59f1aa9 --- /dev/null +++ b/src/common/fiber.cpp @@ -0,0 +1,147 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/fiber.h" + +namespace Common { + +#ifdef _MSC_VER +#include + +struct Fiber::FiberImpl { + LPVOID handle = nullptr; +}; + +void Fiber::_start([[maybe_unused]] void* parameter) { + guard.lock(); + if (previous_fiber) { + previous_fiber->guard.unlock(); + previous_fiber = nullptr; + } + entry_point(start_parameter); +} + +static void __stdcall FiberStartFunc(LPVOID lpFiberParameter) +{ + auto fiber = static_cast(lpFiberParameter); + fiber->_start(nullptr); +} + +Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) + : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { + impl = std::make_unique(); + impl->handle = CreateFiber(0, &FiberStartFunc, this); +} + +Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { + impl = std::make_unique(); +} + +Fiber::~Fiber() { + // Make sure the Fiber is not being used + guard.lock(); + guard.unlock(); + DeleteFiber(impl->handle); +} + +void Fiber::Exit() { + if (!is_thread_fiber) { + return; + } + ConvertFiberToThread(); + guard.unlock(); +} + +void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { + to->guard.lock(); + to->previous_fiber = from; + SwitchToFiber(to->impl->handle); + auto previous_fiber = from->previous_fiber; + if (previous_fiber) { + previous_fiber->guard.unlock(); + previous_fiber.reset(); + } +} + +std::shared_ptr Fiber::ThreadToFiber() { + std::shared_ptr fiber = std::shared_ptr{new Fiber()}; + fiber->guard.lock(); + fiber->impl->handle = ConvertThreadToFiber(NULL); + fiber->is_thread_fiber = true; + return fiber; +} + +#else + +#include + +constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB + +struct Fiber::FiberImpl { + boost::context::detail::fcontext_t context; + std::array stack; +}; + +void Fiber::_start(void* parameter) { + guard.lock(); + boost::context::detail::transfer_t* transfer = static_cast(parameter); + if (previous_fiber) { + previous_fiber->impl->context = transfer->fctx; + previous_fiber->guard.unlock(); + previous_fiber = nullptr; + } + entry_point(start_parameter); +} + +static void 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{} { + impl = std::make_unique(); + auto start_func = std::bind(&Fiber::start, this); + impl->context = + boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), &start_func); +} + +Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { + impl = std::make_unique(); +} + +Fiber::~Fiber() { + // Make sure the Fiber is not being used + guard.lock(); + guard.unlock(); +} + +void Fiber::Exit() { + if (!is_thread_fiber) { + return; + } + guard.unlock(); +} + +void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { + to->guard.lock(); + to->previous_fiber = from; + auto transfer = boost::context::detail::jump_fcontext(to->impl.context, nullptr); + auto previous_fiber = from->previous_fiber; + if (previous_fiber) { + previous_fiber->impl->context = transfer.fctx; + previous_fiber->guard.unlock(); + previous_fiber.reset(); + } +} + +std::shared_ptr Fiber::ThreadToFiber() { + std::shared_ptr fiber = std::shared_ptr{new Fiber()}; + fiber->is_thread_fiber = true; + return fiber; +} + +#endif +} // namespace Common diff --git a/src/common/fiber.h b/src/common/fiber.h new file mode 100644 index 000000000..ab44905cf --- /dev/null +++ b/src/common/fiber.h @@ -0,0 +1,55 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "common/spin_lock.h" + +namespace Common { + +class Fiber { +public: + Fiber(std::function&& entry_point_func, void* start_parameter); + ~Fiber(); + + Fiber(const Fiber&) = delete; + Fiber& operator=(const Fiber&) = delete; + + Fiber(Fiber&&) = default; + Fiber& operator=(Fiber&&) = default; + + /// Yields control from Fiber 'from' to Fiber 'to' + /// Fiber 'from' must be the currently running fiber. + static void YieldTo(std::shared_ptr from, std::shared_ptr to); + static std::shared_ptr ThreadToFiber(); + + /// Only call from main thread's fiber + void Exit(); + + /// Used internally but required to be public, Shall not be used + void _start(void* parameter); + + /// Changes the start parameter of the fiber. Has no effect if the fiber already started + void SetStartParameter(void* new_parameter) { + start_parameter = new_parameter; + } + +private: + Fiber(); + + struct FiberImpl; + + SpinLock guard; + std::function entry_point; + void* start_parameter; + std::shared_ptr previous_fiber; + std::unique_ptr impl; + bool is_thread_fiber{}; +}; + +} // namespace Common -- cgit v1.2.3 From 8d0e3c542258cc50081af93aa85e0e3cbf8900c3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 5 Feb 2020 14:13:16 -0400 Subject: Tests: Add tests for fibers and refactor/fix Fiber class --- src/common/fiber.cpp | 32 ++++++++++++++++---------------- src/common/fiber.h | 19 ++++++++++++++++--- 2 files changed, 32 insertions(+), 19 deletions(-) (limited to 'src/common') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index eb59f1aa9..a2c0401c4 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -3,18 +3,21 @@ // Refer to the license.txt file included. #include "common/fiber.h" +#ifdef _MSC_VER +#include +#else +#include +#endif namespace Common { #ifdef _MSC_VER -#include struct Fiber::FiberImpl { LPVOID handle = nullptr; }; -void Fiber::_start([[maybe_unused]] void* parameter) { - guard.lock(); +void Fiber::start() { if (previous_fiber) { previous_fiber->guard.unlock(); previous_fiber = nullptr; @@ -22,10 +25,10 @@ void Fiber::_start([[maybe_unused]] void* parameter) { entry_point(start_parameter); } -static void __stdcall FiberStartFunc(LPVOID lpFiberParameter) +void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { - auto fiber = static_cast(lpFiberParameter); - fiber->_start(nullptr); + auto fiber = static_cast(fiber_parameter); + fiber->start(); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) @@ -74,30 +77,26 @@ std::shared_ptr Fiber::ThreadToFiber() { #else -#include - constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB -struct Fiber::FiberImpl { - boost::context::detail::fcontext_t context; +struct alignas(64) Fiber::FiberImpl { std::array stack; + boost::context::detail::fcontext_t context; }; -void Fiber::_start(void* parameter) { - guard.lock(); - boost::context::detail::transfer_t* transfer = static_cast(parameter); +void Fiber::start(boost::context::detail::transfer_t& transfer) { if (previous_fiber) { - previous_fiber->impl->context = transfer->fctx; + previous_fiber->impl->context = transfer.fctx; previous_fiber->guard.unlock(); previous_fiber = nullptr; } entry_point(start_parameter); } -static void FiberStartFunc(boost::context::detail::transfer_t transfer) +void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { auto fiber = static_cast(transfer.data); - fiber->_start(&transfer); + fiber->start(transfer); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) @@ -139,6 +138,7 @@ void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { std::shared_ptr Fiber::ThreadToFiber() { std::shared_ptr fiber = std::shared_ptr{new Fiber()}; + fiber->guard.lock(); fiber->is_thread_fiber = true; return fiber; } diff --git a/src/common/fiber.h b/src/common/fiber.h index ab44905cf..812d6644a 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -10,6 +10,12 @@ #include "common/common_types.h" #include "common/spin_lock.h" +#ifndef _MSC_VER +namespace boost::context::detail { + struct transfer_t; +} +#endif + namespace Common { class Fiber { @@ -31,9 +37,6 @@ public: /// Only call from main thread's fiber void Exit(); - /// Used internally but required to be public, Shall not be used - void _start(void* parameter); - /// Changes the start parameter of the fiber. Has no effect if the fiber already started void SetStartParameter(void* new_parameter) { start_parameter = new_parameter; @@ -42,6 +45,16 @@ public: private: Fiber(); +#ifdef _MSC_VER + void start(); + static void FiberStartFunc(void* fiber_parameter); +#else + void start(boost::context::detail::transfer_t& transfer); + static void FiberStartFunc(boost::context::detail::transfer_t transfer); +#endif + + + struct FiberImpl; SpinLock guard; -- cgit v1.2.3 From be320a9e10fda32a984b12cdfe3aaf09cc67b39a Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 5 Feb 2020 15:48:20 -0400 Subject: Common: Polish Fiber class, add comments, asserts and more tests. --- src/common/fiber.cpp | 55 +++++++++++++++++++++++++++++------------------- src/common/fiber.h | 14 ++++++++++-- src/common/spin_lock.cpp | 7 ++++++ src/common/spin_lock.h | 1 + 4 files changed, 53 insertions(+), 24 deletions(-) (limited to 'src/common') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index a2c0401c4..a88a30ced 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/assert.h" #include "common/fiber.h" #ifdef _MSC_VER #include @@ -18,11 +19,11 @@ struct Fiber::FiberImpl { }; void Fiber::start() { - if (previous_fiber) { - previous_fiber->guard.unlock(); - previous_fiber = nullptr; - } + ASSERT(previous_fiber != nullptr); + previous_fiber->guard.unlock(); + previous_fiber.reset(); entry_point(start_parameter); + UNREACHABLE(); } void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) @@ -43,12 +44,16 @@ Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { Fiber::~Fiber() { // Make sure the Fiber is not being used - guard.lock(); - guard.unlock(); + bool locked = guard.try_lock(); + ASSERT_MSG(locked, "Destroying a fiber that's still running"); + if (locked) { + guard.unlock(); + } DeleteFiber(impl->handle); } void Fiber::Exit() { + ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); if (!is_thread_fiber) { return; } @@ -57,14 +62,15 @@ void Fiber::Exit() { } void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { + ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); + ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; SwitchToFiber(to->impl->handle); auto previous_fiber = from->previous_fiber; - if (previous_fiber) { - previous_fiber->guard.unlock(); - previous_fiber.reset(); - } + ASSERT(previous_fiber != nullptr); + previous_fiber->guard.unlock(); + previous_fiber.reset(); } std::shared_ptr Fiber::ThreadToFiber() { @@ -85,12 +91,12 @@ struct alignas(64) Fiber::FiberImpl { }; void Fiber::start(boost::context::detail::transfer_t& transfer) { - if (previous_fiber) { - previous_fiber->impl->context = transfer.fctx; - previous_fiber->guard.unlock(); - previous_fiber = nullptr; - } + ASSERT(previous_fiber != nullptr); + previous_fiber->impl->context = transfer.fctx; + previous_fiber->guard.unlock(); + previous_fiber.reset(); entry_point(start_parameter); + UNREACHABLE(); } void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) @@ -113,11 +119,15 @@ Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { Fiber::~Fiber() { // Make sure the Fiber is not being used - guard.lock(); - guard.unlock(); + bool locked = guard.try_lock(); + ASSERT_MSG(locked, "Destroying a fiber that's still running"); + if (locked) { + guard.unlock(); + } } void Fiber::Exit() { + ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); if (!is_thread_fiber) { return; } @@ -125,15 +135,16 @@ void Fiber::Exit() { } void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { + ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); + ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; auto transfer = boost::context::detail::jump_fcontext(to->impl.context, nullptr); auto previous_fiber = from->previous_fiber; - if (previous_fiber) { - previous_fiber->impl->context = transfer.fctx; - previous_fiber->guard.unlock(); - previous_fiber.reset(); - } + ASSERT(previous_fiber != nullptr); + previous_fiber->impl->context = transfer.fctx; + previous_fiber->guard.unlock(); + previous_fiber.reset(); } std::shared_ptr Fiber::ThreadToFiber() { diff --git a/src/common/fiber.h b/src/common/fiber.h index 812d6644a..89a01fdd8 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -18,6 +18,18 @@ namespace boost::context::detail { namespace Common { +/** + * Fiber class + * a fiber is a userspace thread with it's own context. They can be used to + * implement coroutines, emulated threading systems and certain asynchronous + * patterns. + * + * This class implements fibers at a low level, thus allowing greater freedom + * to implement such patterns. This fiber class is 'threadsafe' only one fiber + * can be running at a time and threads will be locked while trying to yield to + * a running fiber until it yields. WARNING exchanging two running fibers between + * threads will cause a deadlock. + */ class Fiber { public: Fiber(std::function&& entry_point_func, void* start_parameter); @@ -53,8 +65,6 @@ private: static void FiberStartFunc(boost::context::detail::transfer_t transfer); #endif - - struct FiberImpl; SpinLock guard; diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp index 8077b78d2..82a1d39ff 100644 --- a/src/common/spin_lock.cpp +++ b/src/common/spin_lock.cpp @@ -43,4 +43,11 @@ void SpinLock::unlock() { lck.clear(std::memory_order_release); } +bool SpinLock::try_lock() { + if (lck.test_and_set(std::memory_order_acquire)) { + return false; + } + return true; +} + } // namespace Common diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h index cbc67b6c8..70282a961 100644 --- a/src/common/spin_lock.h +++ b/src/common/spin_lock.h @@ -12,6 +12,7 @@ class SpinLock { public: void lock(); void unlock(); + bool try_lock(); private: std::atomic_flag lck = ATOMIC_FLAG_INIT; -- cgit v1.2.3 From 0f8e5a146563d1f245f8f62cb931dc1e0b55de2f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 8 Feb 2020 12:48:57 -0400 Subject: Tests: Add base tests to host timing --- src/common/thread.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/common') diff --git a/src/common/thread.h b/src/common/thread.h index 2fc071685..127cc7e23 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -9,6 +9,7 @@ #include #include #include +#include "common/common_types.h" namespace Common { @@ -28,8 +29,7 @@ public: is_set = false; } - template - bool WaitFor(const std::chrono::duration& time) { + bool WaitFor(const std::chrono::nanoseconds& time) { std::unique_lock lk{mutex}; if (!condvar.wait_for(lk, time, [this] { return is_set; })) return false; -- cgit v1.2.3 From 234b5ff6a999d7d69cdcdf214e0c3984cdab11cf 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/CMakeLists.txt | 4 ++ src/common/wall_clock.cpp | 90 ++++++++++++++++++++++++++++ src/common/wall_clock.h | 40 +++++++++++++ src/common/x64/cpu_detect.cpp | 33 +++++++++++ src/common/x64/cpu_detect.h | 12 ++++ src/common/x64/native_clock.cpp | 128 ++++++++++++++++++++++++++++++++++++++++ src/common/x64/native_clock.h | 41 +++++++++++++ 7 files changed, 348 insertions(+) create mode 100644 src/common/wall_clock.cpp create mode 100644 src/common/wall_clock.h create mode 100644 src/common/x64/native_clock.cpp create mode 100644 src/common/x64/native_clock.h (limited to 'src/common') diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 554d6e253..aacea0ab7 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -167,6 +167,8 @@ add_library(common STATIC vector_math.h virtual_buffer.cpp virtual_buffer.h + wall_clock.cpp + wall_clock.h web_result.h zstd_compression.cpp zstd_compression.h @@ -177,6 +179,8 @@ if(ARCHITECTURE_x86_64) PRIVATE x64/cpu_detect.cpp x64/cpu_detect.h + x64/native_clock.cpp + x64/native_clock.h x64/xbyak_abi.h x64/xbyak_util.h ) diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp new file mode 100644 index 000000000..eabbba9da --- /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 diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h new file mode 100644 index 000000000..6f763d74b --- /dev/null +++ b/src/common/wall_clock.h @@ -0,0 +1,40 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "common/common_types.h" + +namespace Common { + +class WallClock { +public: + virtual std::chrono::nanoseconds GetTimeNS() = 0; + virtual std::chrono::microseconds GetTimeUS() = 0; + virtual std::chrono::milliseconds GetTimeMS() = 0; + virtual u64 GetClockCycles() = 0; + virtual u64 GetCPUCycles() = 0; + + /// Tells if the wall clock, uses the host CPU's hardware clock + bool IsNative() const { + return is_native; + } + +protected: + WallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, bool is_native) + : emulated_cpu_frequency{emulated_cpu_frequency}, + emulated_clock_frequency{emulated_clock_frequency}, is_native{is_native} {} + + u64 emulated_cpu_frequency; + u64 emulated_clock_frequency; + +private: + bool is_native; +}; + +WallClock* CreateBestMatchingClock(u32 emulated_cpu_frequency, u32 emulated_clock_frequency); + +} // namespace Common diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index c9349a6b4..d767c544c 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp @@ -62,6 +62,17 @@ static CPUCaps Detect() { std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int)); std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int)); std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int)); + if (cpu_id[1] == 0x756e6547 && cpu_id[2] == 0x6c65746e && cpu_id[3] == 0x49656e69) + caps.manufacturer = Manufacturer::Intel; + else if (cpu_id[1] == 0x68747541 && cpu_id[2] == 0x444d4163 && cpu_id[3] == 0x69746e65) + caps.manufacturer = Manufacturer::AMD; + else if (cpu_id[1] == 0x6f677948 && cpu_id[2] == 0x656e6975 && cpu_id[3] == 0x6e65476e) + caps.manufacturer = Manufacturer::Hygon; + else + caps.manufacturer = Manufacturer::Unknown; + + u32 family = {}; + u32 model = {}; __cpuid(cpu_id, 0x80000000); @@ -73,6 +84,14 @@ static CPUCaps Detect() { // Detect family and other miscellaneous features if (max_std_fn >= 1) { __cpuid(cpu_id, 0x00000001); + family = (cpu_id[0] >> 8) & 0xf; + model = (cpu_id[0] >> 4) & 0xf; + if (family == 0xf) { + family += (cpu_id[0] >> 20) & 0xff; + } + if (family >= 6) { + model += ((cpu_id[0] >> 16) & 0xf) << 4; + } if ((cpu_id[3] >> 25) & 1) caps.sse = true; @@ -130,6 +149,20 @@ static CPUCaps Detect() { caps.fma4 = true; } + if (max_ex_fn >= 0x80000007) { + __cpuid(cpu_id, 0x80000007); + if (cpu_id[3] & (1 << 8)) { + caps.invariant_tsc = true; + } + } + + if (max_std_fn >= 0x16) { + __cpuid(cpu_id, 0x16); + caps.base_frequency = cpu_id[0]; + caps.max_frequency = cpu_id[1]; + caps.bus_frequency = cpu_id[2]; + } + return caps; } diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index 20f2ba234..f0676fa5e 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h @@ -6,8 +6,16 @@ namespace Common { +enum class Manufacturer : u32 { + Intel = 0, + AMD = 1, + Hygon = 2, + Unknown = 3, +}; + /// x86/x64 CPU capabilities that may be detected by this module struct CPUCaps { + Manufacturer manufacturer; char cpu_string[0x21]; char brand_string[0x41]; bool sse; @@ -24,6 +32,10 @@ struct CPUCaps { bool fma; bool fma4; bool aes; + bool invariant_tsc; + u32 base_frequency; + u32 max_frequency; + u32 bus_frequency; }; /** diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp new file mode 100644 index 000000000..c799111fd --- /dev/null +++ b/src/common/x64/native_clock.cpp @@ -0,0 +1,128 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include + +#ifdef _MSC_VER +#include +#else +#include +#endif + +#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 + _mm_mfence(); + const u64 tscStart = __rdtsc(); + const auto startTime = std::chrono::high_resolution_clock::now(); + // wait roughly 3 seconds + while (true) { + auto milli = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - startTime); + if (milli.count() >= 3000) + break; + std::this_thread::sleep_for(milli_10); + } + const auto endTime = std::chrono::high_resolution_clock::now(); + _mm_mfence(); + const u64 tscEnd = __rdtsc(); + // calculate difference + 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); + return tsc_freq; +} + +namespace X64 { +NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, + u64 rtsc_frequency) + : WallClock(emulated_cpu_frequency, emulated_clock_frequency, true), rtsc_frequency{ + rtsc_frequency} { + _mm_mfence(); + last_measure = __rdtsc(); + accumulated_ticks = 0U; +} + +u64 NativeClock::GetRTSC() { + rtsc_serialize.lock(); + _mm_mfence(); + const u64 current_measure = __rdtsc(); + u64 diff = current_measure - last_measure; + diff = diff & ~static_cast(static_cast(diff) >> 63); // max(diff, 0) + if (current_measure > last_measure) { + last_measure = current_measure; + } + accumulated_ticks += diff; + rtsc_serialize.unlock(); + return accumulated_ticks; +} + +std::chrono::nanoseconds NativeClock::GetTimeNS() { + const u64 rtsc_value = GetRTSC(); + return std::chrono::nanoseconds{umuldiv64(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)}; +} + +std::chrono::milliseconds NativeClock::GetTimeMS() { + const u64 rtsc_value = GetRTSC(); + return std::chrono::milliseconds{umuldiv64(rtsc_value, 1000, rtsc_frequency)}; +} + +u64 NativeClock::GetClockCycles() { + const u64 rtsc_value = GetRTSC(); + return umuldiv64(rtsc_value, emulated_clock_frequency, rtsc_frequency); +} + +u64 NativeClock::GetCPUCycles() { + const u64 rtsc_value = GetRTSC(); + return umuldiv64(rtsc_value, emulated_cpu_frequency, rtsc_frequency); +} + +} // namespace X64 + +} // namespace Common diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h new file mode 100644 index 000000000..b58cf9f5a --- /dev/null +++ b/src/common/x64/native_clock.h @@ -0,0 +1,41 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "common/spin_lock.h" +#include "common/wall_clock.h" + +namespace Common { + +namespace X64 { +class NativeClock : public WallClock { +public: + NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, u64 rtsc_frequency); + + std::chrono::nanoseconds GetTimeNS() override; + + std::chrono::microseconds GetTimeUS() override; + + std::chrono::milliseconds GetTimeMS() override; + + u64 GetClockCycles() override; + + u64 GetCPUCycles() override; + +private: + u64 GetRTSC(); + + SpinLock rtsc_serialize{}; + u64 last_measure{}; + u64 accumulated_ticks{}; + u64 rtsc_frequency; +}; +} // namespace X64 + +u64 EstimateRDTSCFrequency(); + +} // namespace Common -- cgit v1.2.3 From e3524d114246a9221c766bdf1992777b208cbd67 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 ++++++----------------------------------- 5 files changed, 49 insertions(+), 49 deletions(-) (limited to 'src/common') diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp index 32bf56730..7e77588db 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 a3be2a2cb..503cd2d0c 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 eabbba9da..8f5e17fa4 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 6f763d74b..fc34429bb 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 c799111fd..26d4d0ba6 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 -- cgit v1.2.3 From 03e4f5dac436fe361834e6b9918983e9c4787acb Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 13:18:23 -0400 Subject: Common: Correct fcontext fibers. --- src/common/fiber.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/common') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index a88a30ced..e91d86dbe 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -12,6 +12,7 @@ namespace Common { + #ifdef _MSC_VER struct Fiber::FiberImpl { @@ -82,7 +83,6 @@ std::shared_ptr Fiber::ThreadToFiber() { } #else - constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB struct alignas(64) Fiber::FiberImpl { @@ -108,9 +108,8 @@ void Fiber::FiberStartFunc(boost::context::detail::transfer_t 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{} { impl = std::make_unique(); - auto start_func = std::bind(&Fiber::start, this); - impl->context = - boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), &start_func); + impl->context = boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), + FiberStartFunc); } Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { @@ -139,7 +138,7 @@ void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; - auto transfer = boost::context::detail::jump_fcontext(to->impl.context, nullptr); + auto transfer = boost::context::detail::jump_fcontext(to->impl->context, nullptr); auto previous_fiber = from->previous_fiber; ASSERT(previous_fiber != nullptr); previous_fiber->impl->context = transfer.fctx; -- cgit v1.2.3 From 1bd706344e2381e11245b2f0bdc291429e46c634 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 ++-- 4 files changed, 21 insertions(+), 18 deletions(-) (limited to 'src/common') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index e91d86dbe..a46be73c1 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 89a01fdd8..b530bf4d2 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 8f5e17fa4..e6161c72c 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 fc34429bb..ed284cf50 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 -- cgit v1.2.3 From 3398f701eeac63f3cfcf193f3e9c1ee2f06edb08 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 14:21:23 -0400 Subject: Common: Make MinGW build use Windows Fibers instead of fcontext_t --- src/common/fiber.cpp | 4 ++-- src/common/fiber.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/common') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index a46be73c1..050c93acb 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -4,7 +4,7 @@ #include "common/assert.h" #include "common/fiber.h" -#ifdef _MSC_VER +#if defined(_WIN32) || defined(WIN32) #include #else #include @@ -12,7 +12,7 @@ namespace Common { -#ifdef _MSC_VER +#if defined(_WIN32) || defined(WIN32) struct Fiber::FiberImpl { LPVOID handle = nullptr; diff --git a/src/common/fiber.h b/src/common/fiber.h index b530bf4d2..598fe7daa 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -10,7 +10,7 @@ #include "common/common_types.h" #include "common/spin_lock.h" -#ifndef _MSC_VER +#if !defined(_WIN32) && !defined(WIN32) namespace boost::context::detail { struct transfer_t; } @@ -57,7 +57,7 @@ public: private: Fiber(); -#ifdef _MSC_VER +#if defined(_WIN32) || defined(WIN32) void start(); static void FiberStartFunc(void* fiber_parameter); #else -- cgit v1.2.3 From 1f7dd36499786d373b143a4437d4c32e077a32aa Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 10 Feb 2020 14:45:08 -0400 Subject: Common/Tests: Address Feedback --- src/common/fiber.cpp | 5 ++--- src/common/fiber.h | 8 ++++---- src/common/spin_lock.cpp | 3 ++- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/common') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 050c93acb..1220eddf0 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -32,13 +32,12 @@ void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) - : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, - previous_fiber{} { + : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { impl = std::make_unique(); impl->handle = CreateFiber(0, &FiberStartFunc, this); } -Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { +Fiber::Fiber() { impl = std::make_unique(); } diff --git a/src/common/fiber.h b/src/common/fiber.h index 598fe7daa..7e3b130a4 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -67,10 +67,10 @@ private: struct FiberImpl; - SpinLock guard; - std::function entry_point; - void* start_parameter; - std::shared_ptr previous_fiber; + SpinLock guard{}; + std::function entry_point{}; + void* start_parameter{}; + std::shared_ptr previous_fiber{}; std::unique_ptr impl; bool is_thread_fiber{}; }; diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp index 82a1d39ff..c7b46aac6 100644 --- a/src/common/spin_lock.cpp +++ b/src/common/spin_lock.cpp @@ -35,8 +35,9 @@ void thread_pause() { namespace Common { void SpinLock::lock() { - while (lck.test_and_set(std::memory_order_acquire)) + while (lck.test_and_set(std::memory_order_acquire)) { thread_pause(); + } } void SpinLock::unlock() { -- cgit v1.2.3 From 49a7e0984a1210832b8be24433a95711c7ce029b 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 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src/common') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 1220eddf0..e9c0946b6 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 e6161c72c..d4d35f4e7 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: -- cgit v1.2.3 From 7d2b1a6ec4a1c0daea0bac83a83c85f263609224 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 26 Feb 2020 14:39:27 -0400 Subject: Common/Fiber: Correct f_context based Fibers. --- src/common/fiber.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/common') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index e9c0946b6..3ef820c62 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -81,10 +81,10 @@ std::shared_ptr Fiber::ThreadToFiber() { } #else -constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB +constexpr std::size_t default_stack_size = 1024 * 1024; // 4MB -struct alignas(64) Fiber::FiberImpl { - std::array stack; +struct Fiber::FiberImpl { + alignas(64) std::array stack; boost::context::detail::fcontext_t context; }; @@ -106,8 +106,10 @@ Fiber::Fiber(std::function&& entry_point_func, void* start_paramete : 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); + void* stack_start = + static_cast(static_cast(impl->stack.data()) + default_stack_size); + impl->context = + boost::context::detail::make_fcontext(stack_start, impl->stack.size(), FiberStartFunc); } Fiber::Fiber() { @@ -136,7 +138,7 @@ void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; - auto transfer = boost::context::detail::jump_fcontext(to->impl->context, nullptr); + auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get()); auto previous_fiber = from->previous_fiber; ASSERT(previous_fiber != nullptr); previous_fiber->impl->context = transfer.fctx; -- cgit v1.2.3 From 41013381d69f952f78b85de3ce226c1499d889b6 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 26 Feb 2020 17:34:23 -0400 Subject: Common/Fiber: Additional corrections to f_context. --- src/common/fiber.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/common') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 3ef820c62..e4ecc73df 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -81,7 +81,7 @@ std::shared_ptr Fiber::ThreadToFiber() { } #else -constexpr std::size_t default_stack_size = 1024 * 1024; // 4MB +constexpr std::size_t default_stack_size = 1024 * 1024; // 1MB struct Fiber::FiberImpl { alignas(64) std::array stack; @@ -106,10 +106,10 @@ Fiber::Fiber(std::function&& entry_point_func, void* start_paramete : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} { impl = std::make_unique(); - void* stack_start = - static_cast(static_cast(impl->stack.data()) + default_stack_size); + u8* stack_limit = impl->stack.data(); + u8* stack_base = stack_limit + default_stack_size; impl->context = - boost::context::detail::make_fcontext(stack_start, impl->stack.size(), FiberStartFunc); + boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc); } Fiber::Fiber() { -- cgit v1.2.3 From 137d862d9b275209b3d62a413396a15e9e14b4b4 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 27 Feb 2020 16:32:47 -0400 Subject: Common/Fiber: Implement Rewinding. --- src/common/fiber.cpp | 32 ++++++++++++++++++++++++++++++-- src/common/fiber.h | 8 ++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) (limited to 'src/common') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index e4ecc73df..f61479e13 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -12,10 +12,13 @@ namespace Common { +constexpr std::size_t default_stack_size = 256 * 1024; // 256kb + #if defined(_WIN32) || defined(WIN32) struct Fiber::FiberImpl { LPVOID handle = nullptr; + LPVOID rewind_handle = nullptr; }; void Fiber::start() { @@ -26,15 +29,29 @@ void Fiber::start() { UNREACHABLE(); } +void Fiber::onRewind() { + ASSERT(impl->handle != nullptr); + DeleteFiber(impl->handle); + impl->handle = impl->rewind_handle; + impl->rewind_handle = nullptr; + rewind_point(rewind_parameter); + UNREACHABLE(); +} + void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { auto fiber = static_cast(fiber_parameter); fiber->start(); } +void __stdcall Fiber::RewindStartFunc(void* fiber_parameter) { + auto fiber = static_cast(fiber_parameter); + fiber->onRewind(); +} + Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { impl = std::make_unique(); - impl->handle = CreateFiber(0, &FiberStartFunc, this); + impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this); } Fiber::Fiber() { @@ -60,6 +77,18 @@ void Fiber::Exit() { guard.unlock(); } +void Fiber::SetRewindPoint(std::function&& rewind_func, void* start_parameter) { + rewind_point = std::move(rewind_func); + rewind_parameter = start_parameter; +} + +void Fiber::Rewind() { + ASSERT(rewind_point); + ASSERT(impl->rewind_handle == nullptr); + impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this); + SwitchToFiber(impl->rewind_handle); +} + void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); ASSERT_MSG(to != nullptr, "Next fiber is null!"); @@ -81,7 +110,6 @@ std::shared_ptr Fiber::ThreadToFiber() { } #else -constexpr std::size_t default_stack_size = 1024 * 1024; // 1MB struct Fiber::FiberImpl { alignas(64) std::array stack; diff --git a/src/common/fiber.h b/src/common/fiber.h index 7e3b130a4..a710df257 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -46,6 +46,10 @@ public: static void YieldTo(std::shared_ptr from, std::shared_ptr to); static std::shared_ptr ThreadToFiber(); + void SetRewindPoint(std::function&& rewind_func, void* start_parameter); + + void Rewind(); + /// Only call from main thread's fiber void Exit(); @@ -58,8 +62,10 @@ private: Fiber(); #if defined(_WIN32) || defined(WIN32) + void onRewind(); void start(); static void FiberStartFunc(void* fiber_parameter); + static void RewindStartFunc(void* fiber_parameter); #else void start(boost::context::detail::transfer_t& transfer); static void FiberStartFunc(boost::context::detail::transfer_t transfer); @@ -69,6 +75,8 @@ private: SpinLock guard{}; std::function entry_point{}; + std::function rewind_point{}; + void* rewind_parameter{}; void* start_parameter{}; std::shared_ptr previous_fiber{}; std::unique_ptr impl; -- cgit v1.2.3 From 18f54f74862322d5a9360cbdc3541b6e3f15dce6 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 6 Mar 2020 11:24:08 -0400 Subject: Common/Fiber: Document fiber interexchange. --- src/common/fiber.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/common') diff --git a/src/common/fiber.h b/src/common/fiber.h index a710df257..3bbd506b5 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -28,7 +28,10 @@ namespace Common { * to implement such patterns. This fiber class is 'threadsafe' only one fiber * can be running at a time and threads will be locked while trying to yield to * a running fiber until it yields. WARNING exchanging two running fibers between - * threads will cause a deadlock. + * threads will cause a deadlock. In order to prevent a deadlock, each thread should + * have an intermediary fiber, you switch to the intermediary fiber of the current + * thread and then from it switch to the expected fiber. This way you can exchange + * 2 fibers within 2 different threads. */ class Fiber { public: -- cgit v1.2.3 From 59ce6e6d06e5ce8628f96bb247a342dec356fe43 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 12 Mar 2020 20:10:51 -0400 Subject: Common/uint128: Correct MSVC Compilation in old versions. --- src/common/uint128.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/common') diff --git a/src/common/uint128.cpp b/src/common/uint128.cpp index 7e77588db..16bf7c828 100644 --- a/src/common/uint128.cpp +++ b/src/common/uint128.cpp @@ -19,7 +19,11 @@ u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) { u128 r{}; r[0] = _umul128(a, b, &r[1]); u64 remainder; +#if _MSC_VER < 1923 + return udiv128(r[1], r[0], d, &remainder); +#else return _udiv128(r[1], r[0], d, &remainder); +#endif } #else -- cgit v1.2.3 From b6655aa2e492e326e319b09e832c1612bf27acf4 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 1 Apr 2020 09:19:10 -0400 Subject: Common/Fiber: Implement Rewind on Boost Context. --- src/common/fiber.cpp | 39 +++++++++++++++++++++++++++++++++++++-- src/common/fiber.h | 2 ++ 2 files changed, 39 insertions(+), 2 deletions(-) (limited to 'src/common') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index f61479e13..6ea314d75 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -113,7 +113,11 @@ std::shared_ptr Fiber::ThreadToFiber() { struct Fiber::FiberImpl { alignas(64) std::array stack; + u8* stack_limit; + alignas(64) std::array rewind_stack; + u8* rewind_stack_limit; boost::context::detail::fcontext_t context; + boost::context::detail::fcontext_t rewind_context; }; void Fiber::start(boost::context::detail::transfer_t& transfer) { @@ -125,21 +129,43 @@ void Fiber::start(boost::context::detail::transfer_t& transfer) { UNREACHABLE(); } +void Fiber::onRewind(boost::context::detail::transfer_t& [[maybe_unused]] transfer) { + ASSERT(impl->context != nullptr); + impl->context = impl->rewind_context; + impl->rewind_context = nullptr; + u8* tmp = impl->stack_limit; + impl->stack_limit = impl->rewind_stack_limit; + impl->rewind_stack_limit = tmp; + rewind_point(rewind_parameter); + UNREACHABLE(); +} + void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { auto fiber = static_cast(transfer.data); fiber->start(transfer); } +void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) { + auto fiber = static_cast(transfer.data); + fiber->onRewind(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{} { impl = std::make_unique(); - u8* stack_limit = impl->stack.data(); - u8* stack_base = stack_limit + default_stack_size; + impl->stack_limit = impl->stack.data(); + impl->rewind_stack_limit = impl->rewind_stack.data(); + u8* stack_base = impl->stack_limit + default_stack_size; impl->context = boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc); } +void Fiber::SetRewindPoint(std::function&& rewind_func, void* start_parameter) { + rewind_point = std::move(rewind_func); + rewind_parameter = start_parameter; +} + Fiber::Fiber() { impl = std::make_unique(); } @@ -161,6 +187,15 @@ void Fiber::Exit() { guard.unlock(); } +void Fiber::Rewind() { + ASSERT(rewind_point); + ASSERT(impl->rewind_context == nullptr); + u8* stack_base = impl->rewind_stack_limit + default_stack_size; + impl->rewind_context = + boost::context::detail::make_fcontext(stack_base, impl->stack.size(), RewindStartFunc); + boost::context::detail::jump_fcontext(impl->rewind_context, this); +} + void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); ASSERT_MSG(to != nullptr, "Next fiber is null!"); diff --git a/src/common/fiber.h b/src/common/fiber.h index 3bbd506b5..cab7bc4b5 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -70,8 +70,10 @@ private: static void FiberStartFunc(void* fiber_parameter); static void RewindStartFunc(void* fiber_parameter); #else + void onRewind(boost::context::detail::transfer_t& transfer); void start(boost::context::detail::transfer_t& transfer); static void FiberStartFunc(boost::context::detail::transfer_t transfer); + static void RewindStartFunc(boost::context::detail::transfer_t transfer); #endif struct FiberImpl; -- cgit v1.2.3 From e77ee67bfacf9a0d3b9e7cd164531a2be158adc9 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 13 May 2020 13:49:36 -0400 Subject: Common/Fiber: Address Feedback and Correct Memory leaks. --- src/common/fiber.cpp | 58 +++++++++++++++++++++++++++++----------------------- src/common/fiber.h | 17 +++++++-------- 2 files changed, 41 insertions(+), 34 deletions(-) (limited to 'src/common') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 6ea314d75..f97ad433b 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -21,7 +21,7 @@ struct Fiber::FiberImpl { LPVOID rewind_handle = nullptr; }; -void Fiber::start() { +void Fiber::Start() { ASSERT(previous_fiber != nullptr); previous_fiber->guard.unlock(); previous_fiber.reset(); @@ -29,7 +29,7 @@ void Fiber::start() { UNREACHABLE(); } -void Fiber::onRewind() { +void Fiber::OnRewind() { ASSERT(impl->handle != nullptr); DeleteFiber(impl->handle); impl->handle = impl->rewind_handle; @@ -38,14 +38,14 @@ void Fiber::onRewind() { UNREACHABLE(); } -void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) { +void Fiber::FiberStartFunc(void* fiber_parameter) { auto fiber = static_cast(fiber_parameter); - fiber->start(); + fiber->Start(); } -void __stdcall Fiber::RewindStartFunc(void* fiber_parameter) { +void Fiber::RewindStartFunc(void* fiber_parameter) { auto fiber = static_cast(fiber_parameter); - fiber->onRewind(); + fiber->OnRewind(); } Fiber::Fiber(std::function&& entry_point_func, void* start_parameter) @@ -59,8 +59,11 @@ Fiber::Fiber() { } Fiber::~Fiber() { + if (released) { + return; + } // Make sure the Fiber is not being used - bool locked = guard.try_lock(); + const bool locked = guard.try_lock(); ASSERT_MSG(locked, "Destroying a fiber that's still running"); if (locked) { guard.unlock(); @@ -75,6 +78,7 @@ void Fiber::Exit() { } ConvertFiberToThread(); guard.unlock(); + released = true; } void Fiber::SetRewindPoint(std::function&& rewind_func, void* start_parameter) { @@ -89,22 +93,21 @@ void Fiber::Rewind() { SwitchToFiber(impl->rewind_handle); } -void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { +void Fiber::YieldTo(std::shared_ptr& from, std::shared_ptr& to) { ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; SwitchToFiber(to->impl->handle); - auto previous_fiber = from->previous_fiber; - ASSERT(previous_fiber != nullptr); - previous_fiber->guard.unlock(); - previous_fiber.reset(); + ASSERT(from->previous_fiber != nullptr); + from->previous_fiber->guard.unlock(); + from->previous_fiber.reset(); } std::shared_ptr Fiber::ThreadToFiber() { std::shared_ptr fiber = std::shared_ptr{new Fiber()}; fiber->guard.lock(); - fiber->impl->handle = ConvertThreadToFiber(NULL); + fiber->impl->handle = ConvertThreadToFiber(nullptr); fiber->is_thread_fiber = true; return fiber; } @@ -120,7 +123,7 @@ struct Fiber::FiberImpl { boost::context::detail::fcontext_t rewind_context; }; -void Fiber::start(boost::context::detail::transfer_t& transfer) { +void Fiber::Start(boost::context::detail::transfer_t& transfer) { ASSERT(previous_fiber != nullptr); previous_fiber->impl->context = transfer.fctx; previous_fiber->guard.unlock(); @@ -129,7 +132,7 @@ void Fiber::start(boost::context::detail::transfer_t& transfer) { UNREACHABLE(); } -void Fiber::onRewind(boost::context::detail::transfer_t& [[maybe_unused]] transfer) { +void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transfer) { ASSERT(impl->context != nullptr); impl->context = impl->rewind_context; impl->rewind_context = nullptr; @@ -142,17 +145,16 @@ void Fiber::onRewind(boost::context::detail::transfer_t& [[maybe_unused]] transf void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) { auto fiber = static_cast(transfer.data); - fiber->start(transfer); + fiber->Start(transfer); } void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) { auto fiber = static_cast(transfer.data); - fiber->onRewind(transfer); + fiber->OnRewind(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{} { + : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} { impl = std::make_unique(); impl->stack_limit = impl->stack.data(); impl->rewind_stack_limit = impl->rewind_stack.data(); @@ -171,8 +173,11 @@ Fiber::Fiber() { } Fiber::~Fiber() { + if (released) { + return; + } // Make sure the Fiber is not being used - bool locked = guard.try_lock(); + const bool locked = guard.try_lock(); ASSERT_MSG(locked, "Destroying a fiber that's still running"); if (locked) { guard.unlock(); @@ -180,11 +185,13 @@ Fiber::~Fiber() { } void Fiber::Exit() { + ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); if (!is_thread_fiber) { return; } guard.unlock(); + released = true; } void Fiber::Rewind() { @@ -196,17 +203,16 @@ void Fiber::Rewind() { boost::context::detail::jump_fcontext(impl->rewind_context, this); } -void Fiber::YieldTo(std::shared_ptr from, std::shared_ptr to) { +void Fiber::YieldTo(std::shared_ptr& from, std::shared_ptr& to) { ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get()); - auto previous_fiber = from->previous_fiber; - ASSERT(previous_fiber != nullptr); - previous_fiber->impl->context = transfer.fctx; - previous_fiber->guard.unlock(); - previous_fiber.reset(); + ASSERT(from->previous_fiber != nullptr); + from->previous_fiber->impl->context = transfer.fctx; + from->previous_fiber->guard.unlock(); + from->previous_fiber.reset(); } std::shared_ptr Fiber::ThreadToFiber() { diff --git a/src/common/fiber.h b/src/common/fiber.h index cab7bc4b5..dafc1100e 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -46,7 +46,7 @@ public: /// Yields control from Fiber 'from' to Fiber 'to' /// Fiber 'from' must be the currently running fiber. - static void YieldTo(std::shared_ptr from, std::shared_ptr to); + static void YieldTo(std::shared_ptr& from, std::shared_ptr& to); static std::shared_ptr ThreadToFiber(); void SetRewindPoint(std::function&& rewind_func, void* start_parameter); @@ -65,13 +65,13 @@ private: Fiber(); #if defined(_WIN32) || defined(WIN32) - void onRewind(); - void start(); + void OnRewind(); + void Start(); static void FiberStartFunc(void* fiber_parameter); static void RewindStartFunc(void* fiber_parameter); #else - void onRewind(boost::context::detail::transfer_t& transfer); - void start(boost::context::detail::transfer_t& transfer); + void OnRewind(boost::context::detail::transfer_t& transfer); + void Start(boost::context::detail::transfer_t& transfer); static void FiberStartFunc(boost::context::detail::transfer_t transfer); static void RewindStartFunc(boost::context::detail::transfer_t transfer); #endif @@ -79,13 +79,14 @@ private: struct FiberImpl; SpinLock guard{}; - std::function entry_point{}; - std::function rewind_point{}; + std::function entry_point; + std::function rewind_point; void* rewind_parameter{}; void* start_parameter{}; - std::shared_ptr previous_fiber{}; + std::shared_ptr previous_fiber; std::unique_ptr impl; bool is_thread_fiber{}; + bool released{}; }; } // namespace Common -- cgit v1.2.3 From d6474b4aca7f054d00df350c716709475ef0f49b Mon Sep 17 00:00:00 2001 From: Morph Date: Sat, 16 May 2020 07:24:57 -0400 Subject: common/cpu_detect: Add AVX512 detection --- src/common/x64/cpu_detect.cpp | 5 +++++ src/common/x64/cpu_detect.h | 1 + 2 files changed, 6 insertions(+) (limited to 'src/common') diff --git a/src/common/x64/cpu_detect.cpp b/src/common/x64/cpu_detect.cpp index c9349a6b4..f35dcb498 100644 --- a/src/common/x64/cpu_detect.cpp +++ b/src/common/x64/cpu_detect.cpp @@ -110,6 +110,11 @@ static CPUCaps Detect() { caps.bmi1 = true; if ((cpu_id[1] >> 8) & 1) caps.bmi2 = true; + // Checks for AVX512F, AVX512CD, AVX512VL, AVX512DQ, AVX512BW (Intel Skylake-X/SP) + if ((cpu_id[1] >> 16) & 1 && (cpu_id[1] >> 28) & 1 && (cpu_id[1] >> 31) & 1 && + (cpu_id[1] >> 17) & 1 && (cpu_id[1] >> 30) & 1) { + caps.avx512 = caps.avx2; + } } } diff --git a/src/common/x64/cpu_detect.h b/src/common/x64/cpu_detect.h index 20f2ba234..7606c3f7b 100644 --- a/src/common/x64/cpu_detect.h +++ b/src/common/x64/cpu_detect.h @@ -19,6 +19,7 @@ struct CPUCaps { bool lzcnt; bool avx; bool avx2; + bool avx512; bool bmi1; bool bmi2; bool fma; -- cgit v1.2.3 From 97ba520434cceb42af3b17a59c731dd734e9108f Mon Sep 17 00:00:00 2001 From: Morph Date: Sat, 16 May 2020 07:25:13 -0400 Subject: common/telemetry: Add AVX512 to telemetry --- src/common/telemetry.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/common') diff --git a/src/common/telemetry.cpp b/src/common/telemetry.cpp index 200c6489a..16d42facd 100644 --- a/src/common/telemetry.cpp +++ b/src/common/telemetry.cpp @@ -60,6 +60,7 @@ void AppendCPUInfo(FieldCollection& fc) { fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AES", Common::GetCPUCaps().aes); fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX", Common::GetCPUCaps().avx); fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX2", Common::GetCPUCaps().avx2); + fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_AVX512", Common::GetCPUCaps().avx512); fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI1", Common::GetCPUCaps().bmi1); fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_BMI2", Common::GetCPUCaps().bmi2); fc.AddField(FieldType::UserSystem, "CPU_Extension_x64_FMA", Common::GetCPUCaps().fma); -- cgit v1.2.3 From 7b893c7963a57bf41f5dad7dd1709985971ce291 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 19 Jun 2020 19:46:43 -0400 Subject: Common: Fix non-conan build --- src/common/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/common') diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index aacea0ab7..3cc17d0e9 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -187,6 +187,7 @@ if(ARCHITECTURE_x86_64) endif() create_target_directory_groups(common) +find_package(Boost 1.71 COMPONENTS context headers REQUIRED) -target_link_libraries(common PUBLIC Boost::boost fmt::fmt microprofile) +target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile) target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak) -- cgit v1.2.3 From e31425df3877636c098ec7426ebd2067920715cb Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 24 Feb 2020 22:04:12 -0400 Subject: General: Recover Prometheus project from harddrive failure This commit: Implements CPU Interrupts, Replaces Cycle Timing for Host Timing, Reworks the Kernel's Scheduler, Introduce Idle State and Suspended State, Recreates the bootmanager, Initializes Multicore system. --- src/common/thread.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/common') diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 0cd2d10bf..c9684aed9 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -70,6 +70,12 @@ void SetCurrentThreadName(const char* name) { } #endif +#if defined(_WIN32) +void SetCurrentThreadName(const char* name) { + // Do Nothing on MingW +} +#endif + #endif } // namespace Common -- cgit v1.2.3 From 18dcb0934217628711c5b1d22fd6d7635e683e3f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 25 Feb 2020 12:28:55 -0400 Subject: HostTiming: Pause the hardware clock on pause. --- src/common/wall_clock.cpp | 4 ++++ src/common/wall_clock.h | 2 ++ src/common/x64/native_clock.cpp | 7 +++++++ src/common/x64/native_clock.h | 2 ++ 4 files changed, 15 insertions(+) (limited to 'src/common') diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index d4d35f4e7..a46db6bbf 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -53,6 +53,10 @@ public: return Common::Divide128On32(temporary, 1000000000).first; } + void Pause(bool is_paused) override { + // Do nothing in this clock type. + } + private: base_time_point start_time; }; diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index ed284cf50..367d72134 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h @@ -28,6 +28,8 @@ public: /// Returns current wall time in emulated cpu cycles virtual u64 GetCPUCycles() = 0; + virtual void Pause(bool is_paused) = 0; + /// Tells if the wall clock, uses the host CPU's hardware clock bool IsNative() const { return is_native; diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 26d4d0ba6..926f92ff8 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -65,6 +65,13 @@ u64 NativeClock::GetRTSC() { return accumulated_ticks; } +void NativeClock::Pause(bool is_paused) { + if (!is_paused) { + _mm_mfence(); + last_measure = __rdtsc(); + } +} + std::chrono::nanoseconds NativeClock::GetTimeNS() { const u64 rtsc_value = GetRTSC(); return std::chrono::nanoseconds{MultiplyAndDivide64(rtsc_value, 1000000000, rtsc_frequency)}; diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index b58cf9f5a..3851f8fc2 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h @@ -26,6 +26,8 @@ public: u64 GetCPUCycles() override; + void Pause(bool is_paused) override; + private: u64 GetRTSC(); -- cgit v1.2.3 From cd1c38be8d15d3caf52f566a9e8dc20504c61068 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 7 Mar 2020 18:59:42 -0400 Subject: ARM/Memory: Correct Exclusive Monitor and Implement Exclusive Memory Writes. --- src/common/CMakeLists.txt | 2 ++ src/common/atomic_ops.cpp | 70 +++++++++++++++++++++++++++++++++++++++++++++++ src/common/atomic_ops.h | 17 ++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 src/common/atomic_ops.cpp create mode 100644 src/common/atomic_ops.h (limited to 'src/common') diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 3cc17d0e9..d120c8d3d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -98,6 +98,8 @@ add_library(common STATIC algorithm.h alignment.h assert.h + atomic_ops.cpp + atomic_ops.h detached_tasks.cpp detached_tasks.h bit_field.h diff --git a/src/common/atomic_ops.cpp b/src/common/atomic_ops.cpp new file mode 100644 index 000000000..65cdfb4fd --- /dev/null +++ b/src/common/atomic_ops.cpp @@ -0,0 +1,70 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include + +#include "common/atomic_ops.h" + +#if _MSC_VER +#include +#endif + +namespace Common { + +#if _MSC_VER + +bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { + u8 result = _InterlockedCompareExchange8((char*)pointer, value, expected); + return result == expected; +} + +bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { + u16 result = _InterlockedCompareExchange16((short*)pointer, value, expected); + return result == expected; +} + +bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { + u32 result = _InterlockedCompareExchange((long*)pointer, value, expected); + return result == expected; +} + +bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { + u64 result = _InterlockedCompareExchange64((__int64*)pointer, value, expected); + return result == expected; +} + +bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { + return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0], (__int64*)expected.data()) != 0; +} + + +#else + +bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { + return __sync_bool_compare_and_swap (pointer, value, expected); +} + +bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { + return __sync_bool_compare_and_swap (pointer, value, expected); +} + +bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { + return __sync_bool_compare_and_swap (pointer, value, expected); +} + +bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { + return __sync_bool_compare_and_swap (pointer, value, expected); +} + +bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { + unsigned __int128 value_a; + unsigned __int128 expected_a; + std::memcpy(&value_a, value.data(), sizeof(u128)); + std::memcpy(&expected_a, expected.data(), sizeof(u128)); + return __sync_bool_compare_and_swap ((unsigned __int128*)pointer, value_a, expected_a); +} + +#endif + +} // namespace Common diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h new file mode 100644 index 000000000..22cb3a402 --- /dev/null +++ b/src/common/atomic_ops.h @@ -0,0 +1,17 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +namespace Common { + +bool AtomicCompareAndSwap(u8 volatile * pointer, u8 value, u8 expected); +bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected); +bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected); +bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected); +bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected); + +} // namespace Common -- cgit v1.2.3 From 534466754f381e90f5f6475a0c02031242a5c256 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 21 Mar 2020 12:23:13 -0400 Subject: X64 Clock: Reduce accuracy to be less or equal to guest accuracy. --- src/common/x64/native_clock.cpp | 3 ++- src/common/x64/native_clock.h | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src/common') diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 926f92ff8..f1bc60fd2 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -62,7 +62,8 @@ u64 NativeClock::GetRTSC() { } accumulated_ticks += diff; rtsc_serialize.unlock(); - return accumulated_ticks; + /// The clock cannot be more precise than the guest timer, remove the lower bits + return accumulated_ticks & inaccuracy_mask; } void NativeClock::Pause(bool is_paused) { diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index 3851f8fc2..e853094d2 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h @@ -31,6 +31,11 @@ public: private: u64 GetRTSC(); + /// value used to reduce the native clocks accuracy as some apss rely on + /// undefined behavior where the level of accuracy in the clock shouldn't + /// be higher. + static constexpr u64 inaccuracy_mask = ~(0x100 - 1); + SpinLock rtsc_serialize{}; u64 last_measure{}; u64 accumulated_ticks{}; -- cgit v1.2.3 From 528b19a84287167d7699465e495b196d216b99db Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 5 Apr 2020 09:48:53 -0400 Subject: General: Tune the priority of main emulation threads so they have higher priority than less important helper threads. --- src/common/thread.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/common/thread.h | 9 +++++++++ 2 files changed, 55 insertions(+) (limited to 'src/common') diff --git a/src/common/thread.cpp b/src/common/thread.cpp index c9684aed9..33c8437f5 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -25,6 +25,52 @@ namespace Common { +#ifdef _WIN32 + +void SetCurrentThreadPriority(ThreadPriority new_priority) { + auto handle = GetCurrentThread(); + int windows_priority = 0; + switch (new_priority) { + case ThreadPriority::Low: + windows_priority = THREAD_PRIORITY_BELOW_NORMAL; + break; + case ThreadPriority::Normal: + windows_priority = THREAD_PRIORITY_NORMAL; + break; + case ThreadPriority::High: + windows_priority = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case ThreadPriority::VeryHigh: + windows_priority = THREAD_PRIORITY_HIGHEST; + break; + default: + windows_priority = THREAD_PRIORITY_NORMAL; + break; + } + SetThreadPriority(handle, windows_priority); +} + +#else + +void SetCurrentThreadPriority(ThreadPriority new_priority) { + pthread_t this_thread = pthread_self(); + + s32 max_prio = sched_get_priority_max(SCHED_OTHER); + s32 min_prio = sched_get_priority_min(SCHED_OTHER); + u32 level = static_cast(new_priority) + 1; + + struct sched_param params; + if (max_prio > min_prio) { + params.sched_priority = min_prio + ((max_prio - min_prio) * level) / 4; + } else { + params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4; + } + + pthread_setschedparam(this_thread, SCHED_OTHER, ¶ms); +} + +#endif + #ifdef _MSC_VER // Sets the debugger-visible name of the current thread. diff --git a/src/common/thread.h b/src/common/thread.h index 127cc7e23..52b359413 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -86,6 +86,15 @@ private: std::size_t generation = 0; // Incremented once each time the barrier is used }; +enum class ThreadPriority : u32 { + Low = 0, + Normal = 1, + High = 2, + VeryHigh = 3, +}; + +void SetCurrentThreadPriority(ThreadPriority new_priority); + void SetCurrentThreadName(const char* name); } // namespace Common -- cgit v1.2.3 From 467d43570e10b98fa33067352d35fe62ceb3cb9e Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 8 May 2020 18:53:13 -0400 Subject: Clang Format. --- src/common/atomic_ops.cpp | 14 +++++++------- src/common/atomic_ops.h | 2 +- src/common/thread.cpp | 30 +++++++++++++++--------------- 3 files changed, 23 insertions(+), 23 deletions(-) (limited to 'src/common') diff --git a/src/common/atomic_ops.cpp b/src/common/atomic_ops.cpp index 65cdfb4fd..6b2236114 100644 --- a/src/common/atomic_ops.cpp +++ b/src/common/atomic_ops.cpp @@ -35,26 +35,26 @@ bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { } bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { - return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0], (__int64*)expected.data()) != 0; + return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0], + (__int64*)expected.data()) != 0; } - #else bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { - return __sync_bool_compare_and_swap (pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, value, expected); } bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { - return __sync_bool_compare_and_swap (pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, value, expected); } bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { - return __sync_bool_compare_and_swap (pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, value, expected); } bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { - return __sync_bool_compare_and_swap (pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, value, expected); } bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { @@ -62,7 +62,7 @@ bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { unsigned __int128 expected_a; std::memcpy(&value_a, value.data(), sizeof(u128)); std::memcpy(&expected_a, expected.data(), sizeof(u128)); - return __sync_bool_compare_and_swap ((unsigned __int128*)pointer, value_a, expected_a); + return __sync_bool_compare_and_swap((unsigned __int128*)pointer, value_a, expected_a); } #endif diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h index 22cb3a402..e6181d521 100644 --- a/src/common/atomic_ops.h +++ b/src/common/atomic_ops.h @@ -8,7 +8,7 @@ namespace Common { -bool AtomicCompareAndSwap(u8 volatile * pointer, u8 value, u8 expected); +bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected); bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected); bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected); bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected); diff --git a/src/common/thread.cpp b/src/common/thread.cpp index 33c8437f5..8e5935e6a 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -31,21 +31,21 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) { auto handle = GetCurrentThread(); int windows_priority = 0; switch (new_priority) { - case ThreadPriority::Low: - windows_priority = THREAD_PRIORITY_BELOW_NORMAL; - break; - case ThreadPriority::Normal: - windows_priority = THREAD_PRIORITY_NORMAL; - break; - case ThreadPriority::High: - windows_priority = THREAD_PRIORITY_ABOVE_NORMAL; - break; - case ThreadPriority::VeryHigh: - windows_priority = THREAD_PRIORITY_HIGHEST; - break; - default: - windows_priority = THREAD_PRIORITY_NORMAL; - break; + case ThreadPriority::Low: + windows_priority = THREAD_PRIORITY_BELOW_NORMAL; + break; + case ThreadPriority::Normal: + windows_priority = THREAD_PRIORITY_NORMAL; + break; + case ThreadPriority::High: + windows_priority = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case ThreadPriority::VeryHigh: + windows_priority = THREAD_PRIORITY_HIGHEST; + break; + default: + windows_priority = THREAD_PRIORITY_NORMAL; + break; } SetThreadPriority(handle, windows_priority); } -- cgit v1.2.3 From 7b1804dab40fabfbf73157cd5ce76793de1375ee Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 14 May 2020 14:44:03 -0400 Subject: Common/AtomicOps: Correct GCC Intrinsic argument ordering. --- src/common/atomic_ops.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/common') diff --git a/src/common/atomic_ops.cpp b/src/common/atomic_ops.cpp index 6b2236114..1098e21ff 100644 --- a/src/common/atomic_ops.cpp +++ b/src/common/atomic_ops.cpp @@ -42,19 +42,19 @@ bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { #else bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) { - return __sync_bool_compare_and_swap(pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, expected, value); } bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) { - return __sync_bool_compare_and_swap(pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, expected, value); } bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) { - return __sync_bool_compare_and_swap(pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, expected, value); } bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) { - return __sync_bool_compare_and_swap(pointer, value, expected); + return __sync_bool_compare_and_swap(pointer, expected, value); } bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { @@ -62,7 +62,7 @@ bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) { unsigned __int128 expected_a; std::memcpy(&value_a, value.data(), sizeof(u128)); std::memcpy(&expected_a, expected.data(), sizeof(u128)); - return __sync_bool_compare_and_swap((unsigned __int128*)pointer, value_a, expected_a); + return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a); } #endif -- cgit v1.2.3 From 31651523968312433ebd267a74e86689107a7023 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 18 May 2020 13:08:53 -0400 Subject: Common/NativeClockx86: Reduce native clock accuracy further. --- src/common/x64/native_clock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/common') diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index e853094d2..891a3bbfd 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h @@ -34,7 +34,7 @@ private: /// value used to reduce the native clocks accuracy as some apss rely on /// undefined behavior where the level of accuracy in the clock shouldn't /// be higher. - static constexpr u64 inaccuracy_mask = ~(0x100 - 1); + static constexpr u64 inaccuracy_mask = ~(0x400 - 1); SpinLock rtsc_serialize{}; u64 last_measure{}; -- cgit v1.2.3 From 7fd7d05838b88e9dd63a7329e29ea355669a5f18 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 29 May 2020 17:37:37 -0400 Subject: Common/Kernel: Corrections and small bug fixing. --- src/common/wall_clock.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'src/common') diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index a46db6bbf..3afbdb898 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -68,12 +68,7 @@ std::unique_ptr CreateBestMatchingClock(u32 emulated_cpu_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(); - } + rtsc_frequency = EstimateRDTSCFrequency(); } if (rtsc_frequency == 0) { return std::make_unique(emulated_cpu_frequency, -- cgit v1.2.3 From 2f8947583f2f0af4058600243d6c1d244e3c4890 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 27 Jun 2020 18:20:06 -0400 Subject: Core/Common: Address Feedback. --- src/common/fiber.cpp | 10 +++------- src/common/spin_lock.cpp | 6 +++--- src/common/spin_lock.h | 5 +++++ src/common/x64/native_clock.cpp | 4 ++-- 4 files changed, 13 insertions(+), 12 deletions(-) (limited to 'src/common') diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index f97ad433b..1c1d09ccb 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -54,9 +54,7 @@ Fiber::Fiber(std::function&& entry_point_func, void* start_paramete impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this); } -Fiber::Fiber() { - impl = std::make_unique(); -} +Fiber::Fiber() : impl{std::make_unique()} {} Fiber::~Fiber() { if (released) { @@ -116,8 +114,8 @@ std::shared_ptr Fiber::ThreadToFiber() { struct Fiber::FiberImpl { alignas(64) std::array stack; - u8* stack_limit; alignas(64) std::array rewind_stack; + u8* stack_limit; u8* rewind_stack_limit; boost::context::detail::fcontext_t context; boost::context::detail::fcontext_t rewind_context; @@ -168,9 +166,7 @@ void Fiber::SetRewindPoint(std::function&& rewind_func, void* start rewind_parameter = start_parameter; } -Fiber::Fiber() { - impl = std::make_unique(); -} +Fiber::Fiber() : impl{std::make_unique()} {} Fiber::~Fiber() { if (released) { diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp index c7b46aac6..c1524220f 100644 --- a/src/common/spin_lock.cpp +++ b/src/common/spin_lock.cpp @@ -20,7 +20,7 @@ namespace { -void thread_pause() { +void ThreadPause() { #if __x86_64__ _mm_pause(); #elif __aarch64__ && _MSC_VER @@ -30,13 +30,13 @@ void thread_pause() { #endif } -} // namespace +} // Anonymous namespace namespace Common { void SpinLock::lock() { while (lck.test_and_set(std::memory_order_acquire)) { - thread_pause(); + ThreadPause(); } } diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h index 70282a961..1df5528c4 100644 --- a/src/common/spin_lock.h +++ b/src/common/spin_lock.h @@ -8,6 +8,11 @@ namespace Common { +/** + * SpinLock class + * a lock similar to mutex that forces a thread to spin wait instead calling the + * supervisor. Should be used on short sequences of code. + */ class SpinLock { public: void lock(); diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index f1bc60fd2..424b39b1f 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #ifdef _MSC_VER @@ -52,7 +53,7 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequenc } u64 NativeClock::GetRTSC() { - rtsc_serialize.lock(); + std::scoped_lock scope{rtsc_serialize}; _mm_mfence(); const u64 current_measure = __rdtsc(); u64 diff = current_measure - last_measure; @@ -61,7 +62,6 @@ u64 NativeClock::GetRTSC() { last_measure = current_measure; } accumulated_ticks += diff; - rtsc_serialize.unlock(); /// The clock cannot be more precise than the guest timer, remove the lower bits return accumulated_ticks & inaccuracy_mask; } -- cgit v1.2.3 From 765e37aa356d9398ee8d4b4eaac2fd35b065aa38 Mon Sep 17 00:00:00 2001 From: John Galt Date: Mon, 29 Jun 2020 06:49:22 -0400 Subject: cmake: fix fmt linking On gcc/ld, and clang/lld, fmt::v6 symbols are excluded, so linking fails. This fixes the issue. Note: This was included in the FindBoost changes I shared with BlinkHawk, however only they were merged. I'm not sure if it was missed, or if there was an issue with this part of the change. --- src/common/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/common') diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index d120c8d3d..1808f7e3f 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -191,5 +191,5 @@ endif() create_target_directory_groups(common) find_package(Boost 1.71 COMPONENTS context headers REQUIRED) -target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile) +target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt microprofile) target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak) -- cgit v1.2.3 From e6085ea35f0b2579a1df40ee4e24d02e47ee85bc Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Mon, 29 Jun 2020 22:39:31 +0000 Subject: common: add sysconf() fallback src/common/memory_detect.cpp:15:10: fatal error: 'sys/sysinfo.h' file not found #include ^~~~~~~~~~~~~~~ --- src/common/memory_detect.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'src/common') diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp index 3fdc309a2..e981b9a2a 100644 --- a/src/common/memory_detect.cpp +++ b/src/common/memory_detect.cpp @@ -9,10 +9,12 @@ // clang-format on #else #include -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__FreeBSD__) #include -#else +#elif defined(__linux__) #include +#else +#include #endif #endif @@ -42,11 +44,22 @@ static MemoryInfo Detect() { sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, NULL, 0); mem_info.TotalPhysicalMemory = ramsize; mem_info.TotalSwapMemory = vmusage.xsu_total; -#else +#elif defined(__FreeBSD__) + u_long physmem, swap_total; + std::size_t sizeof_u_long = sizeof(u_long); + // sysctlbyname(const char *, void *, size_t *, const void *, size_t); + sysctlbyname("hw.physmem", &physmem, &sizeof_u_long, NULL, 0); + sysctlbyname("vm.swap_total", &swap_total, &sizeof_u_long, NULL, 0); + mem_info.TotalPhysicalMemory = physmem; + mem_info.TotalSwapMemory = swap_total; +#elif defined(__linux__) struct sysinfo meminfo; sysinfo(&meminfo); mem_info.TotalPhysicalMemory = meminfo.totalram; mem_info.TotalSwapMemory = meminfo.totalswap; +#else + mem_info.TotalPhysicalMemory = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE); + mem_info.TotalSwapMemory = 0; #endif return mem_info; -- cgit v1.2.3 From 3b1683a152610b2da161ba3084e2dd5942127303 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 30 Jun 2020 22:55:47 +0000 Subject: common: switch to nullptr for sysctl's empty new value --- src/common/memory_detect.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/common') diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp index e981b9a2a..8cff6ec37 100644 --- a/src/common/memory_detect.cpp +++ b/src/common/memory_detect.cpp @@ -40,16 +40,16 @@ static MemoryInfo Detect() { // hw and vm are defined in sysctl.h // https://github.com/apple/darwin-xnu/blob/master/bsd/sys/sysctl.h#L471 // sysctlbyname(const char *, void *, size_t *, void *, size_t); - sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, NULL, 0); - sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, NULL, 0); + sysctlbyname("hw.memsize", &ramsize, &sizeof_ramsize, nullptr, 0); + sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, nullptr, 0); mem_info.TotalPhysicalMemory = ramsize; mem_info.TotalSwapMemory = vmusage.xsu_total; #elif defined(__FreeBSD__) u_long physmem, swap_total; std::size_t sizeof_u_long = sizeof(u_long); // sysctlbyname(const char *, void *, size_t *, const void *, size_t); - sysctlbyname("hw.physmem", &physmem, &sizeof_u_long, NULL, 0); - sysctlbyname("vm.swap_total", &swap_total, &sizeof_u_long, NULL, 0); + sysctlbyname("hw.physmem", &physmem, &sizeof_u_long, nullptr, 0); + sysctlbyname("vm.swap_total", &swap_total, &sizeof_u_long, nullptr, 0); mem_info.TotalPhysicalMemory = physmem; mem_info.TotalSwapMemory = swap_total; #elif defined(__linux__) -- cgit v1.2.3 From 98fcd3ba5d24d65e91f89f7c9c4f3f60e29e080f Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 2 Jul 2020 22:53:12 -0400 Subject: Revert "cmake: fix fmt linking" --- src/common/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/common') diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 1808f7e3f..d120c8d3d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -191,5 +191,5 @@ endif() create_target_directory_groups(common) find_package(Boost 1.71 COMPONENTS context headers REQUIRED) -target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt microprofile) +target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile) target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak) -- cgit v1.2.3