summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/stream.cpp2
-rw-r--r--src/core/core_timing.cpp34
-rw-r--r--src/core/core_timing.h23
-rw-r--r--src/core/hle/kernel/thread.cpp6
-rw-r--r--src/tests/core/core_timing.cpp20
5 files changed, 25 insertions, 60 deletions
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 11481a776..426e38b45 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -101,7 +101,7 @@ void Stream::PlayNextBuffer() {
101 101
102 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); 102 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
103 103
104 core_timing.ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); 104 core_timing.ScheduleEvent(GetBufferReleaseCycles(*active_buffer), release_event, {});
105} 105}
106 106
107void Stream::ReleaseActiveBuffer() { 107void Stream::ReleaseActiveBuffer() {
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 41adb2302..a58f7b131 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -56,12 +56,12 @@ void CoreTiming::Initialize() {
56} 56}
57 57
58void CoreTiming::Shutdown() { 58void CoreTiming::Shutdown() {
59 MoveEvents();
60 ClearPendingEvents(); 59 ClearPendingEvents();
61 UnregisterAllEvents(); 60 UnregisterAllEvents();
62} 61}
63 62
64EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) { 63EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) {
64 std::lock_guard guard{inner_mutex};
65 // check for existing type with same name. 65 // check for existing type with same name.
66 // we want event type names to remain unique so that we can use them for serialization. 66 // we want event type names to remain unique so that we can use them for serialization.
67 ASSERT_MSG(event_types.find(name) == event_types.end(), 67 ASSERT_MSG(event_types.find(name) == event_types.end(),
@@ -82,6 +82,7 @@ void CoreTiming::UnregisterAllEvents() {
82 82
83void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { 83void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
84 ASSERT(event_type != nullptr); 84 ASSERT(event_type != nullptr);
85 std::lock_guard guard{inner_mutex};
85 const s64 timeout = GetTicks() + cycles_into_future; 86 const s64 timeout = GetTicks() + cycles_into_future;
86 87
87 // If this event needs to be scheduled before the next advance(), force one early 88 // If this event needs to be scheduled before the next advance(), force one early
@@ -93,12 +94,8 @@ void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_ty
93 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 94 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
94} 95}
95 96
96void CoreTiming::ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
97 u64 userdata) {
98 ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type});
99}
100
101void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) { 97void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
98 std::lock_guard guard{inner_mutex};
102 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { 99 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
103 return e.type == event_type && e.userdata == userdata; 100 return e.type == event_type && e.userdata == userdata;
104 }); 101 });
@@ -110,10 +107,6 @@ void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
110 } 107 }
111} 108}
112 109
113void CoreTiming::UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) {
114 unschedule_queue.Push(std::make_pair(event_type, userdata));
115}
116
117u64 CoreTiming::GetTicks() const { 110u64 CoreTiming::GetTicks() const {
118 u64 ticks = static_cast<u64>(global_timer); 111 u64 ticks = static_cast<u64>(global_timer);
119 if (!is_global_timer_sane) { 112 if (!is_global_timer_sane) {
@@ -135,6 +128,7 @@ void CoreTiming::ClearPendingEvents() {
135} 128}
136 129
137void CoreTiming::RemoveEvent(const EventType* event_type) { 130void CoreTiming::RemoveEvent(const EventType* event_type) {
131 std::lock_guard guard{inner_mutex};
138 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), 132 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
139 [&](const Event& e) { return e.type == event_type; }); 133 [&](const Event& e) { return e.type == event_type; });
140 134
@@ -145,11 +139,6 @@ void CoreTiming::RemoveEvent(const EventType* event_type) {
145 } 139 }
146} 140}
147 141
148void CoreTiming::RemoveNormalAndThreadsafeEvent(const EventType* event_type) {
149 MoveEvents();
150 RemoveEvent(event_type);
151}
152
153void CoreTiming::ForceExceptionCheck(s64 cycles) { 142void CoreTiming::ForceExceptionCheck(s64 cycles) {
154 cycles = std::max<s64>(0, cycles); 143 cycles = std::max<s64>(0, cycles);
155 if (downcount <= cycles) { 144 if (downcount <= cycles) {
@@ -162,19 +151,8 @@ void CoreTiming::ForceExceptionCheck(s64 cycles) {
162 downcount = static_cast<int>(cycles); 151 downcount = static_cast<int>(cycles);
163} 152}
164 153
165void CoreTiming::MoveEvents() {
166 for (Event ev; ts_queue.Pop(ev);) {
167 ev.fifo_order = event_fifo_id++;
168 event_queue.emplace_back(std::move(ev));
169 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
170 }
171}
172
173void CoreTiming::Advance() { 154void CoreTiming::Advance() {
174 MoveEvents(); 155 std::unique_lock<std::mutex> guard(inner_mutex);
175 for (std::pair<const EventType*, u64> ev; unschedule_queue.Pop(ev);) {
176 UnscheduleEvent(ev.first, ev.second);
177 }
178 156
179 const int cycles_executed = slice_length - downcount; 157 const int cycles_executed = slice_length - downcount;
180 global_timer += cycles_executed; 158 global_timer += cycles_executed;
@@ -186,7 +164,9 @@ void CoreTiming::Advance() {
186 Event evt = std::move(event_queue.front()); 164 Event evt = std::move(event_queue.front());
187 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 165 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
188 event_queue.pop_back(); 166 event_queue.pop_back();
167 inner_mutex.unlock();
189 evt.type->callback(evt.userdata, global_timer - evt.time); 168 evt.type->callback(evt.userdata, global_timer - evt.time);
169 inner_mutex.lock();
190 } 170 }
191 171
192 is_global_timer_sane = false; 172 is_global_timer_sane = false;
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 9d2efde37..161c7007d 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -6,6 +6,7 @@
6 6
7#include <chrono> 7#include <chrono>
8#include <functional> 8#include <functional>
9#include <mutex>
9#include <string> 10#include <string>
10#include <unordered_map> 11#include <unordered_map>
11#include <vector> 12#include <vector>
@@ -67,7 +68,7 @@ public:
67 /// 68 ///
68 EventType* RegisterEvent(const std::string& name, TimedCallback callback); 69 EventType* RegisterEvent(const std::string& name, TimedCallback callback);
69 70
70 /// Unregisters all registered events thus far. 71 /// Unregisters all registered events thus far. Note: not thread unsafe
71 void UnregisterAllEvents(); 72 void UnregisterAllEvents();
72 73
73 /// After the first Advance, the slice lengths and the downcount will be reduced whenever an 74 /// After the first Advance, the slice lengths and the downcount will be reduced whenever an
@@ -76,20 +77,10 @@ public:
76 /// Scheduling from a callback will not update the downcount until the Advance() completes. 77 /// Scheduling from a callback will not update the downcount until the Advance() completes.
77 void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0); 78 void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
78 79
79 /// This is to be called when outside of hle threads, such as the graphics thread, wants to
80 /// schedule things to be executed on the main thread.
81 ///
82 /// @note This doesn't change slice_length and thus events scheduled by this might be
83 /// called with a delay of up to MAX_SLICE_LENGTH
84 void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
85 u64 userdata = 0);
86
87 void UnscheduleEvent(const EventType* event_type, u64 userdata); 80 void UnscheduleEvent(const EventType* event_type, u64 userdata);
88 void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata);
89 81
90 /// We only permit one event of each type in the queue at a time. 82 /// We only permit one event of each type in the queue at a time.
91 void RemoveEvent(const EventType* event_type); 83 void RemoveEvent(const EventType* event_type);
92 void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
93 84
94 void ForceExceptionCheck(s64 cycles); 85 void ForceExceptionCheck(s64 cycles);
95 86
@@ -120,7 +111,6 @@ private:
120 111
121 /// Clear all pending events. This should ONLY be done on exit. 112 /// Clear all pending events. This should ONLY be done on exit.
122 void ClearPendingEvents(); 113 void ClearPendingEvents();
123 void MoveEvents();
124 114
125 s64 global_timer = 0; 115 s64 global_timer = 0;
126 s64 idled_cycles = 0; 116 s64 idled_cycles = 0;
@@ -143,14 +133,9 @@ private:
143 // remain stable regardless of rehashes/resizing. 133 // remain stable regardless of rehashes/resizing.
144 std::unordered_map<std::string, EventType> event_types; 134 std::unordered_map<std::string, EventType> event_types;
145 135
146 // The queue for storing the events from other threads threadsafe until they will be added
147 // to the event_queue by the emu thread
148 Common::MPSCQueue<Event> ts_queue;
149
150 // The queue for unscheduling the events from other threads threadsafe
151 Common::MPSCQueue<std::pair<const EventType*, u64>> unschedule_queue;
152
153 EventType* ev_lost = nullptr; 136 EventType* ev_lost = nullptr;
137
138 std::mutex inner_mutex;
154}; 139};
155 140
156} // namespace Core::Timing 141} // namespace Core::Timing
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index c73a40977..a055a5002 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -76,13 +76,13 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
76 // This function might be called from any thread so we have to be cautious and use the 76 // This function might be called from any thread so we have to be cautious and use the
77 // thread-safe version of ScheduleEvent. 77 // thread-safe version of ScheduleEvent.
78 const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); 78 const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
79 Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe( 79 Core::System::GetInstance().CoreTiming().ScheduleEvent(
80 cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle); 80 cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle);
81} 81}
82 82
83void Thread::CancelWakeupTimer() { 83void Thread::CancelWakeupTimer() {
84 Core::System::GetInstance().CoreTiming().UnscheduleEventThreadsafe( 84 Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
85 kernel.ThreadWakeupCallbackEventType(), callback_handle); 85 callback_handle);
86} 86}
87 87
88static std::optional<s32> GetNextProcessorId(u64 mask) { 88static std::optional<s32> GetNextProcessorId(u64 mask) {
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 340d6a272..f8be8fd19 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -99,24 +99,24 @@ TEST_CASE("CoreTiming[Threadsave]", "[core]") {
99 core_timing.Advance(); 99 core_timing.Advance();
100 100
101 // D -> B -> C -> A -> E 101 // D -> B -> C -> A -> E
102 core_timing.ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]); 102 core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
103 // Manually force since ScheduleEventThreadsafe doesn't call it 103 // Manually force since ScheduleEvent doesn't call it
104 core_timing.ForceExceptionCheck(1000); 104 core_timing.ForceExceptionCheck(1000);
105 REQUIRE(1000 == core_timing.GetDowncount()); 105 REQUIRE(1000 == core_timing.GetDowncount());
106 core_timing.ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]); 106 core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]);
107 // Manually force since ScheduleEventThreadsafe doesn't call it 107 // Manually force since ScheduleEvent doesn't call it
108 core_timing.ForceExceptionCheck(500); 108 core_timing.ForceExceptionCheck(500);
109 REQUIRE(500 == core_timing.GetDowncount()); 109 REQUIRE(500 == core_timing.GetDowncount());
110 core_timing.ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]); 110 core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]);
111 // Manually force since ScheduleEventThreadsafe doesn't call it 111 // Manually force since ScheduleEvent doesn't call it
112 core_timing.ForceExceptionCheck(800); 112 core_timing.ForceExceptionCheck(800);
113 REQUIRE(500 == core_timing.GetDowncount()); 113 REQUIRE(500 == core_timing.GetDowncount());
114 core_timing.ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]); 114 core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]);
115 // Manually force since ScheduleEventThreadsafe doesn't call it 115 // Manually force since ScheduleEvent doesn't call it
116 core_timing.ForceExceptionCheck(100); 116 core_timing.ForceExceptionCheck(100);
117 REQUIRE(100 == core_timing.GetDowncount()); 117 REQUIRE(100 == core_timing.GetDowncount());
118 core_timing.ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]); 118 core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]);
119 // Manually force since ScheduleEventThreadsafe doesn't call it 119 // Manually force since ScheduleEvent doesn't call it
120 core_timing.ForceExceptionCheck(1200); 120 core_timing.ForceExceptionCheck(1200);
121 REQUIRE(100 == core_timing.GetDowncount()); 121 REQUIRE(100 == core_timing.GetDowncount());
122 122