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
98 algorithm.h 98 algorithm.h
99 alignment.h 99 alignment.h
100 assert.h 100 assert.h
101 atomic_ops.cpp
102 atomic_ops.h
101 detached_tasks.cpp 103 detached_tasks.cpp
102 detached_tasks.h 104 detached_tasks.h
103 bit_field.h 105 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 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6
7#include "common/atomic_ops.h"
8
9#if _MSC_VER
10#include <intrin.h>
11#endif
12
13namespace Common {
14
15#if _MSC_VER
16
17bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) {
18 u8 result = _InterlockedCompareExchange8((char*)pointer, value, expected);
19 return result == expected;
20}
21
22bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) {
23 u16 result = _InterlockedCompareExchange16((short*)pointer, value, expected);
24 return result == expected;
25}
26
27bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) {
28 u32 result = _InterlockedCompareExchange((long*)pointer, value, expected);
29 return result == expected;
30}
31
32bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) {
33 u64 result = _InterlockedCompareExchange64((__int64*)pointer, value, expected);
34 return result == expected;
35}
36
37bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) {
38 return _InterlockedCompareExchange128((__int64*)pointer, value[1], value[0],
39 (__int64*)expected.data()) != 0;
40}
41
42#else
43
44bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected) {
45 return __sync_bool_compare_and_swap(pointer, expected, value);
46}
47
48bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected) {
49 return __sync_bool_compare_and_swap(pointer, expected, value);
50}
51
52bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected) {
53 return __sync_bool_compare_and_swap(pointer, expected, value);
54}
55
56bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected) {
57 return __sync_bool_compare_and_swap(pointer, expected, value);
58}
59
60bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected) {
61 unsigned __int128 value_a;
62 unsigned __int128 expected_a;
63 std::memcpy(&value_a, value.data(), sizeof(u128));
64 std::memcpy(&expected_a, expected.data(), sizeof(u128));
65 return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
66}
67
68#endif
69
70} // 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 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9namespace Common {
10
11bool AtomicCompareAndSwap(u8 volatile* pointer, u8 value, u8 expected);
12bool AtomicCompareAndSwap(u16 volatile* pointer, u16 value, u16 expected);
13bool AtomicCompareAndSwap(u32 volatile* pointer, u32 value, u32 expected);
14bool AtomicCompareAndSwap(u64 volatile* pointer, u64 value, u64 expected);
15bool AtomicCompareAndSwap(u64 volatile* pointer, u128 value, u128 expected);
16
17} // 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
54 impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this); 54 impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
55} 55}
56 56
57Fiber::Fiber() { 57Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
58 impl = std::make_unique<FiberImpl>();
59}
60 58
61Fiber::~Fiber() { 59Fiber::~Fiber() {
62 if (released) { 60 if (released) {
@@ -116,8 +114,8 @@ std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
116 114
117struct Fiber::FiberImpl { 115struct Fiber::FiberImpl {
118 alignas(64) std::array<u8, default_stack_size> stack; 116 alignas(64) std::array<u8, default_stack_size> stack;
119 u8* stack_limit;
120 alignas(64) std::array<u8, default_stack_size> rewind_stack; 117 alignas(64) std::array<u8, default_stack_size> rewind_stack;
118 u8* stack_limit;
121 u8* rewind_stack_limit; 119 u8* rewind_stack_limit;
122 boost::context::detail::fcontext_t context; 120 boost::context::detail::fcontext_t context;
123 boost::context::detail::fcontext_t rewind_context; 121 boost::context::detail::fcontext_t rewind_context;
@@ -168,9 +166,7 @@ void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start
168 rewind_parameter = start_parameter; 166 rewind_parameter = start_parameter;
169} 167}
170 168
171Fiber::Fiber() { 169Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
172 impl = std::make_unique<FiberImpl>();
173}
174 170
175Fiber::~Fiber() { 171Fiber::~Fiber() {
176 if (released) { 172 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 @@
20 20
21namespace { 21namespace {
22 22
23void thread_pause() { 23void ThreadPause() {
24#if __x86_64__ 24#if __x86_64__
25 _mm_pause(); 25 _mm_pause();
26#elif __aarch64__ && _MSC_VER 26#elif __aarch64__ && _MSC_VER
@@ -30,13 +30,13 @@ void thread_pause() {
30#endif 30#endif
31} 31}
32 32
33} // namespace 33} // Anonymous namespace
34 34
35namespace Common { 35namespace Common {
36 36
37void SpinLock::lock() { 37void SpinLock::lock() {
38 while (lck.test_and_set(std::memory_order_acquire)) { 38 while (lck.test_and_set(std::memory_order_acquire)) {
39 thread_pause(); 39 ThreadPause();
40 } 40 }
41} 41}
42 42
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 @@
8 8
9namespace Common { 9namespace Common {
10 10
11/**
12 * SpinLock class
13 * a lock similar to mutex that forces a thread to spin wait instead calling the
14 * supervisor. Should be used on short sequences of code.
15 */
11class SpinLock { 16class SpinLock {
12public: 17public:
13 void lock(); 18 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 @@
25 25
26namespace Common { 26namespace Common {
27 27
28#ifdef _WIN32
29
30void SetCurrentThreadPriority(ThreadPriority new_priority) {
31 auto handle = GetCurrentThread();
32 int windows_priority = 0;
33 switch (new_priority) {
34 case ThreadPriority::Low:
35 windows_priority = THREAD_PRIORITY_BELOW_NORMAL;
36 break;
37 case ThreadPriority::Normal:
38 windows_priority = THREAD_PRIORITY_NORMAL;
39 break;
40 case ThreadPriority::High:
41 windows_priority = THREAD_PRIORITY_ABOVE_NORMAL;
42 break;
43 case ThreadPriority::VeryHigh:
44 windows_priority = THREAD_PRIORITY_HIGHEST;
45 break;
46 default:
47 windows_priority = THREAD_PRIORITY_NORMAL;
48 break;
49 }
50 SetThreadPriority(handle, windows_priority);
51}
52
53#else
54
55void SetCurrentThreadPriority(ThreadPriority new_priority) {
56 pthread_t this_thread = pthread_self();
57
58 s32 max_prio = sched_get_priority_max(SCHED_OTHER);
59 s32 min_prio = sched_get_priority_min(SCHED_OTHER);
60 u32 level = static_cast<u32>(new_priority) + 1;
61
62 struct sched_param params;
63 if (max_prio > min_prio) {
64 params.sched_priority = min_prio + ((max_prio - min_prio) * level) / 4;
65 } else {
66 params.sched_priority = min_prio - ((min_prio - max_prio) * level) / 4;
67 }
68
69 pthread_setschedparam(this_thread, SCHED_OTHER, &params);
70}
71
72#endif
73
28#ifdef _MSC_VER 74#ifdef _MSC_VER
29 75
30// Sets the debugger-visible name of the current thread. 76// Sets the debugger-visible name of the current thread.
@@ -70,6 +116,12 @@ void SetCurrentThreadName(const char* name) {
70} 116}
71#endif 117#endif
72 118
119#if defined(_WIN32)
120void SetCurrentThreadName(const char* name) {
121 // Do Nothing on MingW
122}
123#endif
124
73#endif 125#endif
74 126
75} // namespace Common 127} // 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:
86 std::size_t generation = 0; // Incremented once each time the barrier is used 86 std::size_t generation = 0; // Incremented once each time the barrier is used
87}; 87};
88 88
89enum class ThreadPriority : u32 {
90 Low = 0,
91 Normal = 1,
92 High = 2,
93 VeryHigh = 3,
94};
95
96void SetCurrentThreadPriority(ThreadPriority new_priority);
97
89void SetCurrentThreadName(const char* name); 98void SetCurrentThreadName(const char* name);
90 99
91} // namespace Common 100} // 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:
53 return Common::Divide128On32(temporary, 1000000000).first; 53 return Common::Divide128On32(temporary, 1000000000).first;
54 } 54 }
55 55
56 void Pause(bool is_paused) override {
57 // Do nothing in this clock type.
58 }
59
56private: 60private:
57 base_time_point start_time; 61 base_time_point start_time;
58}; 62};
@@ -64,12 +68,7 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency,
64 const auto& caps = GetCPUCaps(); 68 const auto& caps = GetCPUCaps();
65 u64 rtsc_frequency = 0; 69 u64 rtsc_frequency = 0;
66 if (caps.invariant_tsc) { 70 if (caps.invariant_tsc) {
67 if (caps.base_frequency != 0) { 71 rtsc_frequency = EstimateRDTSCFrequency();
68 rtsc_frequency = static_cast<u64>(caps.base_frequency) * 1000000U;
69 }
70 if (rtsc_frequency == 0) {
71 rtsc_frequency = EstimateRDTSCFrequency();
72 }
73 } 72 }
74 if (rtsc_frequency == 0) { 73 if (rtsc_frequency == 0) {
75 return std::make_unique<StandardWallClock>(emulated_cpu_frequency, 74 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:
28 /// Returns current wall time in emulated cpu cycles 28 /// Returns current wall time in emulated cpu cycles
29 virtual u64 GetCPUCycles() = 0; 29 virtual u64 GetCPUCycles() = 0;
30 30
31 virtual void Pause(bool is_paused) = 0;
32
31 /// Tells if the wall clock, uses the host CPU's hardware clock 33 /// Tells if the wall clock, uses the host CPU's hardware clock
32 bool IsNative() const { 34 bool IsNative() const {
33 return is_native; 35 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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono> 5#include <chrono>
6#include <mutex>
6#include <thread> 7#include <thread>
7 8
8#ifdef _MSC_VER 9#ifdef _MSC_VER
@@ -52,7 +53,7 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequenc
52} 53}
53 54
54u64 NativeClock::GetRTSC() { 55u64 NativeClock::GetRTSC() {
55 rtsc_serialize.lock(); 56 std::scoped_lock scope{rtsc_serialize};
56 _mm_mfence(); 57 _mm_mfence();
57 const u64 current_measure = __rdtsc(); 58 const u64 current_measure = __rdtsc();
58 u64 diff = current_measure - last_measure; 59 u64 diff = current_measure - last_measure;
@@ -61,8 +62,15 @@ u64 NativeClock::GetRTSC() {
61 last_measure = current_measure; 62 last_measure = current_measure;
62 } 63 }
63 accumulated_ticks += diff; 64 accumulated_ticks += diff;
64 rtsc_serialize.unlock(); 65 /// The clock cannot be more precise than the guest timer, remove the lower bits
65 return accumulated_ticks; 66 return accumulated_ticks & inaccuracy_mask;
67}
68
69void NativeClock::Pause(bool is_paused) {
70 if (!is_paused) {
71 _mm_mfence();
72 last_measure = __rdtsc();
73 }
66} 74}
67 75
68std::chrono::nanoseconds NativeClock::GetTimeNS() { 76std::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:
26 26
27 u64 GetCPUCycles() override; 27 u64 GetCPUCycles() override;
28 28
29 void Pause(bool is_paused) override;
30
29private: 31private:
30 u64 GetRTSC(); 32 u64 GetRTSC();
31 33
34 /// value used to reduce the native clocks accuracy as some apss rely on
35 /// undefined behavior where the level of accuracy in the clock shouldn't
36 /// be higher.
37 static constexpr u64 inaccuracy_mask = ~(0x400 - 1);
38
32 SpinLock rtsc_serialize{}; 39 SpinLock rtsc_serialize{};
33 u64 last_measure{}; 40 u64 last_measure{};
34 u64 accumulated_ticks{}; 41 u64 accumulated_ticks{};