diff options
| author | 2019-06-30 12:54:00 -0400 | |
|---|---|---|
| committer | 2019-06-30 12:54:00 -0400 | |
| commit | d992909636269cde90cf6cb3749ccffcff9a6c56 (patch) | |
| tree | 9dd14057dab85c1ba171df75f2112697cdc85cb6 /src | |
| parent | Merge pull request #2653 from FearlessTobi/revert-2474-patch-1 (diff) | |
| parent | Core_Timing: Make core_timing threadsafe by default. (diff) | |
| download | yuzu-d992909636269cde90cf6cb3749ccffcff9a6c56.tar.gz yuzu-d992909636269cde90cf6cb3749ccffcff9a6c56.tar.xz yuzu-d992909636269cde90cf6cb3749ccffcff9a6c56.zip | |
Merge pull request #2583 from FernandoS27/core-timing-safe
Core_Timing: Make core_timing threadsafe by default.
Diffstat (limited to 'src')
| -rw-r--r-- | src/audio_core/stream.cpp | 2 | ||||
| -rw-r--r-- | src/core/core_timing.cpp | 34 | ||||
| -rw-r--r-- | src/core/core_timing.h | 23 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 6 | ||||
| -rw-r--r-- | src/tests/core/core_timing.cpp | 20 |
5 files changed, 25 insertions, 60 deletions
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index 982c7af2f..6a5f53a57 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp | |||
| @@ -105,7 +105,7 @@ void Stream::PlayNextBuffer() { | |||
| 105 | 105 | ||
| 106 | sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); | 106 | sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); |
| 107 | 107 | ||
| 108 | core_timing.ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); | 108 | core_timing.ScheduleEvent(GetBufferReleaseCycles(*active_buffer), release_event, {}); |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | void Stream::ReleaseActiveBuffer() { | 111 | void 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 | ||
| 58 | void CoreTiming::Shutdown() { | 58 | void CoreTiming::Shutdown() { |
| 59 | MoveEvents(); | ||
| 60 | ClearPendingEvents(); | 59 | ClearPendingEvents(); |
| 61 | UnregisterAllEvents(); | 60 | UnregisterAllEvents(); |
| 62 | } | 61 | } |
| 63 | 62 | ||
| 64 | EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) { | 63 | EventType* 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 | ||
| 83 | void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { | 83 | void 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 | ||
| 96 | void 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 | |||
| 101 | void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) { | 97 | void 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 | ||
| 113 | void CoreTiming::UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) { | ||
| 114 | unschedule_queue.Push(std::make_pair(event_type, userdata)); | ||
| 115 | } | ||
| 116 | |||
| 117 | u64 CoreTiming::GetTicks() const { | 110 | u64 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 | ||
| 137 | void CoreTiming::RemoveEvent(const EventType* event_type) { | 130 | void 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 | ||
| 148 | void CoreTiming::RemoveNormalAndThreadsafeEvent(const EventType* event_type) { | ||
| 149 | MoveEvents(); | ||
| 150 | RemoveEvent(event_type); | ||
| 151 | } | ||
| 152 | |||
| 153 | void CoreTiming::ForceExceptionCheck(s64 cycles) { | 142 | void 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 | ||
| 165 | void 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 | |||
| 173 | void CoreTiming::Advance() { | 154 | void 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 | ||
| 83 | void Thread::CancelWakeupTimer() { | 83 | void 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 | ||
| 88 | static std::optional<s32> GetNextProcessorId(u64 mask) { | 88 | static 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 | ||