summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/atomic_ops.cpp70
-rw-r--r--src/common/atomic_ops.h17
-rw-r--r--src/common/fiber.cpp10
-rw-r--r--src/common/spin_lock.cpp6
-rw-r--r--src/common/spin_lock.h5
-rw-r--r--src/common/thread.cpp52
-rw-r--r--src/common/thread.h9
-rw-r--r--src/common/wall_clock.cpp11
-rw-r--r--src/common/wall_clock.h2
-rw-r--r--src/common/x64/native_clock.cpp14
-rw-r--r--src/common/x64/native_clock.h7
12 files changed, 186 insertions, 19 deletions
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..1098e21ff
--- /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 <cstring>
+
+#include "common/atomic_ops.h"
+
+#if _MSC_VER
+#include <intrin.h>
+#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, expected, value);
+}
+
+bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 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, expected, value);
+}
+
+bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) {
+ return __sync_bool_compare_and_swap(pointer, expected, value);
+}
+
+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, expected_a, value_a);
+}
+
+#endif
+
+} // namespace Common
diff --git a/src/common/atomic_ops.h b/src/common/atomic_ops.h
new file mode 100644
index 000000000..e6181d521
--- /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
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<void(void*)>&& entry_point_func, void* start_paramete
impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
}
-Fiber::Fiber() {
- impl = std::make_unique<FiberImpl>();
-}
+Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
Fiber::~Fiber() {
if (released) {
@@ -116,8 +114,8 @@ std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
struct Fiber::FiberImpl {
alignas(64) std::array<u8, default_stack_size> stack;
- u8* stack_limit;
alignas(64) std::array<u8, default_stack_size> 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<void(void*)>&& rewind_func, void* start
rewind_parameter = start_parameter;
}
-Fiber::Fiber() {
- impl = std::make_unique<FiberImpl>();
-}
+Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
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/thread.cpp b/src/common/thread.cpp
index 0cd2d10bf..8e5935e6a 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<u32>(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, &params);
+}
+
+#endif
+
#ifdef _MSC_VER
// Sets the debugger-visible name of the current thread.
@@ -70,6 +116,12 @@ void SetCurrentThreadName(const char* name) {
}
#endif
+#if defined(_WIN32)
+void SetCurrentThreadName(const char* name) {
+ // Do Nothing on MingW
+}
+#endif
+
#endif
} // namespace Common
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
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index d4d35f4e7..3afbdb898 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;
};
@@ -64,12 +68,7 @@ std::unique_ptr<WallClock> 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<u64>(caps.base_frequency) * 1000000U;
- }
- if (rtsc_frequency == 0) {
- rtsc_frequency = EstimateRDTSCFrequency();
- }
+ rtsc_frequency = EstimateRDTSCFrequency();
}
if (rtsc_frequency == 0) {
return std::make_unique<StandardWallClock>(emulated_cpu_frequency,
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..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 <chrono>
+#include <mutex>
#include <thread>
#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,8 +62,15 @@ u64 NativeClock::GetRTSC() {
last_measure = current_measure;
}
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) {
+ if (!is_paused) {
+ _mm_mfence();
+ last_measure = __rdtsc();
+ }
}
std::chrono::nanoseconds NativeClock::GetTimeNS() {
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
index b58cf9f5a..891a3bbfd 100644
--- a/src/common/x64/native_clock.h
+++ b/src/common/x64/native_clock.h
@@ -26,9 +26,16 @@ public:
u64 GetCPUCycles() override;
+ void Pause(bool is_paused) override;
+
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 = ~(0x400 - 1);
+
SpinLock rtsc_serialize{};
u64 last_measure{};
u64 accumulated_ticks{};