core_timing: Use better reference tracking for EventType. (#3159)
* core_timing: Use better reference tracking for EventType. - Moves ownership of the event to the caller, ensuring we don't fire events for destroyed objects. - Removes need for unique names - we won't be using this for save states anyways.
This commit is contained in:
		| @@ -37,7 +37,7 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format fo | ||||
|     : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)}, | ||||
|       sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} { | ||||
|  | ||||
|     release_event = core_timing.RegisterEvent( | ||||
|     release_event = Core::Timing::CreateEvent( | ||||
|         name, [this](u64 userdata, s64 cycles_late) { ReleaseActiveBuffer(); }); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -98,18 +98,19 @@ private: | ||||
|     /// Gets the number of core cycles when the specified buffer will be released | ||||
|     s64 GetBufferReleaseCycles(const Buffer& buffer) const; | ||||
|  | ||||
|     u32 sample_rate;                          ///< Sample rate of the stream | ||||
|     Format format;                            ///< Format of the stream | ||||
|     float game_volume = 1.0f;                 ///< The volume the game currently has set | ||||
|     ReleaseCallback release_callback;         ///< Buffer release callback for the stream | ||||
|     State state{State::Stopped};              ///< Playback state of the stream | ||||
|     Core::Timing::EventType* release_event{}; ///< Core timing release event for the stream | ||||
|     BufferPtr active_buffer;                  ///< Actively playing buffer in the stream | ||||
|     std::queue<BufferPtr> queued_buffers;     ///< Buffers queued to be played in the stream | ||||
|     std::queue<BufferPtr> released_buffers;   ///< Buffers recently released from the stream | ||||
|     SinkStream& sink_stream;                  ///< Output sink for the stream | ||||
|     Core::Timing::CoreTiming& core_timing;    ///< Core timing instance. | ||||
|     std::string name;                         ///< Name of the stream, must be unique | ||||
|     u32 sample_rate;                  ///< Sample rate of the stream | ||||
|     Format format;                    ///< Format of the stream | ||||
|     float game_volume = 1.0f;         ///< The volume the game currently has set | ||||
|     ReleaseCallback release_callback; ///< Buffer release callback for the stream | ||||
|     State state{State::Stopped};      ///< Playback state of the stream | ||||
|     std::shared_ptr<Core::Timing::EventType> | ||||
|         release_event;                      ///< Core timing release event for the stream | ||||
|     BufferPtr active_buffer;                ///< Actively playing buffer in the stream | ||||
|     std::queue<BufferPtr> queued_buffers;   ///< Buffers queued to be played in the stream | ||||
|     std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream | ||||
|     SinkStream& sink_stream;                ///< Output sink for the stream | ||||
|     Core::Timing::CoreTiming& core_timing;  ///< Core timing instance. | ||||
|     std::string name;                       ///< Name of the stream, must be unique | ||||
| }; | ||||
|  | ||||
| using StreamPtr = std::shared_ptr<Stream>; | ||||
|   | ||||
| @@ -17,11 +17,15 @@ namespace Core::Timing { | ||||
|  | ||||
| constexpr int MAX_SLICE_LENGTH = 10000; | ||||
|  | ||||
| std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { | ||||
|     return std::make_shared<EventType>(std::move(callback), std::move(name)); | ||||
| } | ||||
|  | ||||
| struct CoreTiming::Event { | ||||
|     s64 time; | ||||
|     u64 fifo_order; | ||||
|     u64 userdata; | ||||
|     const EventType* type; | ||||
|     std::weak_ptr<EventType> type; | ||||
|  | ||||
|     // Sort by time, unless the times are the same, in which case sort by | ||||
|     // the order added to the queue | ||||
| @@ -54,36 +58,15 @@ void CoreTiming::Initialize() { | ||||
|     event_fifo_id = 0; | ||||
|  | ||||
|     const auto empty_timed_callback = [](u64, s64) {}; | ||||
|     ev_lost = RegisterEvent("_lost_event", empty_timed_callback); | ||||
|     ev_lost = CreateEvent("_lost_event", empty_timed_callback); | ||||
| } | ||||
|  | ||||
| void CoreTiming::Shutdown() { | ||||
|     ClearPendingEvents(); | ||||
|     UnregisterAllEvents(); | ||||
| } | ||||
|  | ||||
| EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) { | ||||
|     std::lock_guard guard{inner_mutex}; | ||||
|     // check for existing type with same name. | ||||
|     // we want event type names to remain unique so that we can use them for serialization. | ||||
|     ASSERT_MSG(event_types.find(name) == event_types.end(), | ||||
|                "CoreTiming Event \"{}\" is already registered. Events should only be registered " | ||||
|                "during Init to avoid breaking save states.", | ||||
|                name.c_str()); | ||||
|  | ||||
|     auto info = event_types.emplace(name, EventType{callback, nullptr}); | ||||
|     EventType* event_type = &info.first->second; | ||||
|     event_type->name = &info.first->first; | ||||
|     return event_type; | ||||
| } | ||||
|  | ||||
| void CoreTiming::UnregisterAllEvents() { | ||||
|     ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending"); | ||||
|     event_types.clear(); | ||||
| } | ||||
|  | ||||
| void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { | ||||
|     ASSERT(event_type != nullptr); | ||||
| void CoreTiming::ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type, | ||||
|                                u64 userdata) { | ||||
|     std::lock_guard guard{inner_mutex}; | ||||
|     const s64 timeout = GetTicks() + cycles_into_future; | ||||
|  | ||||
| @@ -93,13 +76,15 @@ void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_ty | ||||
|     } | ||||
|  | ||||
|     event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); | ||||
|  | ||||
|     std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||||
| } | ||||
|  | ||||
| void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) { | ||||
| void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { | ||||
|     std::lock_guard guard{inner_mutex}; | ||||
|  | ||||
|     const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | ||||
|         return e.type == event_type && e.userdata == userdata; | ||||
|         return e.type.lock().get() == event_type.get() && e.userdata == userdata; | ||||
|     }); | ||||
|  | ||||
|     // Removing random items breaks the invariant so we have to re-establish it. | ||||
| @@ -130,10 +115,12 @@ void CoreTiming::ClearPendingEvents() { | ||||
|     event_queue.clear(); | ||||
| } | ||||
|  | ||||
| void CoreTiming::RemoveEvent(const EventType* event_type) { | ||||
| void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { | ||||
|     std::lock_guard guard{inner_mutex}; | ||||
|     const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), | ||||
|                                     [&](const Event& e) { return e.type == event_type; }); | ||||
|  | ||||
|     const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | ||||
|         return e.type.lock().get() == event_type.get(); | ||||
|     }); | ||||
|  | ||||
|     // Removing random items breaks the invariant so we have to re-establish it. | ||||
|     if (itr != event_queue.end()) { | ||||
| @@ -181,7 +168,11 @@ void CoreTiming::Advance() { | ||||
|         std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||||
|         event_queue.pop_back(); | ||||
|         inner_mutex.unlock(); | ||||
|         evt.type->callback(evt.userdata, global_timer - evt.time); | ||||
|  | ||||
|         if (auto event_type{evt.type.lock()}) { | ||||
|             event_type->callback(evt.userdata, global_timer - evt.time); | ||||
|         } | ||||
|  | ||||
|         inner_mutex.lock(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -6,11 +6,12 @@ | ||||
|  | ||||
| #include <chrono> | ||||
| #include <functional> | ||||
| #include <memory> | ||||
| #include <mutex> | ||||
| #include <optional> | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "common/threadsafe_queue.h" | ||||
|  | ||||
| @@ -21,10 +22,13 @@ using TimedCallback = std::function<void(u64 userdata, s64 cycles_late)>; | ||||
|  | ||||
| /// Contains the characteristics of a particular event. | ||||
| struct EventType { | ||||
|     EventType(TimedCallback&& callback, std::string&& name) | ||||
|         : callback{std::move(callback)}, name{std::move(name)} {} | ||||
|  | ||||
|     /// The event's callback function. | ||||
|     TimedCallback callback; | ||||
|     /// A pointer to the name of the event. | ||||
|     const std::string* name; | ||||
|     const std::string name; | ||||
| }; | ||||
|  | ||||
| /** | ||||
| @@ -57,31 +61,17 @@ public: | ||||
|     /// Tears down all timing related functionality. | ||||
|     void Shutdown(); | ||||
|  | ||||
|     /// Registers a core timing event with the given name and callback. | ||||
|     /// | ||||
|     /// @param name     The name of the core timing event to register. | ||||
|     /// @param callback The callback to execute for the event. | ||||
|     /// | ||||
|     /// @returns An EventType instance representing the registered event. | ||||
|     /// | ||||
|     /// @pre The name of the event being registered must be unique among all | ||||
|     ///      registered events. | ||||
|     /// | ||||
|     EventType* RegisterEvent(const std::string& name, TimedCallback callback); | ||||
|  | ||||
|     /// Unregisters all registered events thus far. Note: not thread unsafe | ||||
|     void UnregisterAllEvents(); | ||||
|  | ||||
|     /// After the first Advance, the slice lengths and the downcount will be reduced whenever an | ||||
|     /// event is scheduled earlier than the current values. | ||||
|     /// | ||||
|     /// Scheduling from a callback will not update the downcount until the Advance() completes. | ||||
|     void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0); | ||||
|     void ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type, | ||||
|                        u64 userdata = 0); | ||||
|  | ||||
|     void UnscheduleEvent(const EventType* event_type, u64 userdata); | ||||
|     void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); | ||||
|  | ||||
|     /// We only permit one event of each type in the queue at a time. | ||||
|     void RemoveEvent(const EventType* event_type); | ||||
|     void RemoveEvent(const std::shared_ptr<EventType>& event_type); | ||||
|  | ||||
|     void ForceExceptionCheck(s64 cycles); | ||||
|  | ||||
| @@ -148,13 +138,18 @@ private: | ||||
|     std::vector<Event> event_queue; | ||||
|     u64 event_fifo_id = 0; | ||||
|  | ||||
|     // Stores each element separately as a linked list node so pointers to elements | ||||
|     // remain stable regardless of rehashes/resizing. | ||||
|     std::unordered_map<std::string, EventType> event_types; | ||||
|  | ||||
|     EventType* ev_lost = nullptr; | ||||
|     std::shared_ptr<EventType> ev_lost; | ||||
|  | ||||
|     std::mutex inner_mutex; | ||||
| }; | ||||
|  | ||||
| /// Creates a core timing event with the given name and callback. | ||||
| /// | ||||
| /// @param name     The name of the core timing event to create. | ||||
| /// @param callback The callback to execute for the event. | ||||
| /// | ||||
| /// @returns An EventType instance representing the created event. | ||||
| /// | ||||
| std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback); | ||||
|  | ||||
| } // namespace Core::Timing | ||||
|   | ||||
| @@ -11,13 +11,12 @@ | ||||
| namespace Core::Hardware { | ||||
|  | ||||
| InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { | ||||
|     gpu_interrupt_event = | ||||
|         system.CoreTiming().RegisterEvent("GPUInterrupt", [this](u64 message, s64) { | ||||
|             auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); | ||||
|             const u32 syncpt = static_cast<u32>(message >> 32); | ||||
|             const u32 value = static_cast<u32>(message); | ||||
|             nvdrv->SignalGPUInterruptSyncpt(syncpt, value); | ||||
|         }); | ||||
|     gpu_interrupt_event = Core::Timing::CreateEvent("GPUInterrupt", [this](u64 message, s64) { | ||||
|         auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); | ||||
|         const u32 syncpt = static_cast<u32>(message >> 32); | ||||
|         const u32 value = static_cast<u32>(message); | ||||
|         nvdrv->SignalGPUInterruptSyncpt(syncpt, value); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| InterruptManager::~InterruptManager() = default; | ||||
|   | ||||
| @@ -4,6 +4,8 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <memory> | ||||
|  | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace Core { | ||||
| @@ -25,7 +27,7 @@ public: | ||||
|  | ||||
| private: | ||||
|     Core::System& system; | ||||
|     Core::Timing::EventType* gpu_interrupt_event{}; | ||||
|     std::shared_ptr<Core::Timing::EventType> gpu_interrupt_event; | ||||
| }; | ||||
|  | ||||
| } // namespace Core::Hardware | ||||
|   | ||||
| @@ -139,12 +139,12 @@ struct KernelCore::Impl { | ||||
|  | ||||
|     void InitializeThreads() { | ||||
|         thread_wakeup_event_type = | ||||
|             system.CoreTiming().RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); | ||||
|             Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback); | ||||
|     } | ||||
|  | ||||
|     void InitializePreemption() { | ||||
|         preemption_event = system.CoreTiming().RegisterEvent( | ||||
|             "PreemptionCallback", [this](u64 userdata, s64 cycles_late) { | ||||
|         preemption_event = | ||||
|             Core::Timing::CreateEvent("PreemptionCallback", [this](u64 userdata, s64 cycles_late) { | ||||
|                 global_scheduler.PreemptThreads(); | ||||
|                 s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); | ||||
|                 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | ||||
| @@ -166,8 +166,9 @@ struct KernelCore::Impl { | ||||
|  | ||||
|     std::shared_ptr<ResourceLimit> system_resource_limit; | ||||
|  | ||||
|     Core::Timing::EventType* thread_wakeup_event_type = nullptr; | ||||
|     Core::Timing::EventType* preemption_event = nullptr; | ||||
|     std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type; | ||||
|     std::shared_ptr<Core::Timing::EventType> preemption_event; | ||||
|  | ||||
|     // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, | ||||
|     // allowing us to simply use a pool index or similar. | ||||
|     Kernel::HandleTable thread_wakeup_callback_handle_table; | ||||
| @@ -269,7 +270,7 @@ u64 KernelCore::CreateNewUserProcessID() { | ||||
|     return impl->next_user_process_id++; | ||||
| } | ||||
|  | ||||
| Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const { | ||||
| const std::shared_ptr<Core::Timing::EventType>& KernelCore::ThreadWakeupCallbackEventType() const { | ||||
|     return impl->thread_wakeup_event_type; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -113,7 +113,7 @@ private: | ||||
|     u64 CreateNewThreadID(); | ||||
|  | ||||
|     /// Retrieves the event type used for thread wakeup callbacks. | ||||
|     Core::Timing::EventType* ThreadWakeupCallbackEventType() const; | ||||
|     const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const; | ||||
|  | ||||
|     /// Provides a reference to the thread wakeup callback handle table. | ||||
|     Kernel::HandleTable& ThreadWakeupCallbackHandleTable(); | ||||
|   | ||||
| @@ -77,15 +77,14 @@ IAppletResource::IAppletResource(Core::System& system) | ||||
|     GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); | ||||
|  | ||||
|     // Register update callbacks | ||||
|     auto& core_timing = system.CoreTiming(); | ||||
|     pad_update_event = | ||||
|         core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { | ||||
|         Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { | ||||
|             UpdateControllers(userdata, cycles_late); | ||||
|         }); | ||||
|  | ||||
|     // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) | ||||
|  | ||||
|     core_timing.ScheduleEvent(pad_update_ticks, pad_update_event); | ||||
|     system.CoreTiming().ScheduleEvent(pad_update_ticks, pad_update_event); | ||||
|  | ||||
|     ReloadInputDevices(); | ||||
| } | ||||
|   | ||||
| @@ -69,7 +69,7 @@ private: | ||||
|  | ||||
|     std::shared_ptr<Kernel::SharedMemory> shared_mem; | ||||
|  | ||||
|     Core::Timing::EventType* pad_update_event; | ||||
|     std::shared_ptr<Core::Timing::EventType> pad_update_event; | ||||
|     Core::System& system; | ||||
|  | ||||
|     std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> | ||||
|   | ||||
| @@ -37,8 +37,8 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) { | ||||
|     displays.emplace_back(4, "Null", system); | ||||
|  | ||||
|     // Schedule the screen composition events | ||||
|     composition_event = system.CoreTiming().RegisterEvent( | ||||
|         "ScreenComposition", [this](u64 userdata, s64 cycles_late) { | ||||
|     composition_event = | ||||
|         Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) { | ||||
|             Compose(); | ||||
|             const auto ticks = | ||||
|                 Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); | ||||
|   | ||||
| @@ -103,7 +103,7 @@ private: | ||||
|     u32 swap_interval = 1; | ||||
|  | ||||
|     /// Event that handles screen composition. | ||||
|     Core::Timing::EventType* composition_event; | ||||
|     std::shared_ptr<Core::Timing::EventType> composition_event; | ||||
|  | ||||
|     Core::System& system; | ||||
| }; | ||||
|   | ||||
| @@ -186,7 +186,7 @@ CheatEngine::~CheatEngine() { | ||||
| } | ||||
|  | ||||
| void CheatEngine::Initialize() { | ||||
|     event = core_timing.RegisterEvent( | ||||
|     event = Core::Timing::CreateEvent( | ||||
|         "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), | ||||
|         [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); | ||||
|     core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <atomic> | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| #include "common/common_types.h" | ||||
| #include "core/memory/dmnt_cheat_types.h" | ||||
| @@ -78,7 +79,7 @@ private: | ||||
|     std::vector<CheatEntry> cheats; | ||||
|     std::atomic_bool is_pending_reload{false}; | ||||
|  | ||||
|     Core::Timing::EventType* event{}; | ||||
|     std::shared_ptr<Core::Timing::EventType> event; | ||||
|     Core::Timing::CoreTiming& core_timing; | ||||
|     Core::System& system; | ||||
| }; | ||||
|   | ||||
| @@ -54,7 +54,7 @@ void MemoryWriteWidth(u32 width, VAddr addr, u64 value) { | ||||
| } // Anonymous namespace | ||||
|  | ||||
| Freezer::Freezer(Core::Timing::CoreTiming& core_timing) : core_timing(core_timing) { | ||||
|     event = core_timing.RegisterEvent( | ||||
|     event = Core::Timing::CreateEvent( | ||||
|         "MemoryFreezer::FrameCallback", | ||||
|         [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); | ||||
|     core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <atomic> | ||||
| #include <memory> | ||||
| #include <mutex> | ||||
| #include <optional> | ||||
| #include <vector> | ||||
| @@ -75,7 +76,7 @@ private: | ||||
|     mutable std::mutex entries_mutex; | ||||
|     std::vector<Entry> entries; | ||||
|  | ||||
|     Core::Timing::EventType* event; | ||||
|     std::shared_ptr<Core::Timing::EventType> event; | ||||
|     Core::Timing::CoreTiming& core_timing; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,9 @@ | ||||
| #include <array> | ||||
| #include <bitset> | ||||
| #include <cstdlib> | ||||
| #include <memory> | ||||
| #include <string> | ||||
|  | ||||
| #include "common/file_util.h" | ||||
| #include "core/core.h" | ||||
| #include "core/core_timing.h" | ||||
| @@ -65,11 +67,16 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") { | ||||
|     ScopeInit guard; | ||||
|     auto& core_timing = guard.core_timing; | ||||
|  | ||||
|     Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>); | ||||
|     Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>); | ||||
|     Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>); | ||||
|     Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>); | ||||
|     Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>); | ||||
|     std::shared_ptr<Core::Timing::EventType> cb_a = | ||||
|         Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); | ||||
|     std::shared_ptr<Core::Timing::EventType> cb_b = | ||||
|         Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>); | ||||
|     std::shared_ptr<Core::Timing::EventType> cb_c = | ||||
|         Core::Timing::CreateEvent("callbackC", CallbackTemplate<2>); | ||||
|     std::shared_ptr<Core::Timing::EventType> cb_d = | ||||
|         Core::Timing::CreateEvent("callbackD", CallbackTemplate<3>); | ||||
|     std::shared_ptr<Core::Timing::EventType> cb_e = | ||||
|         Core::Timing::CreateEvent("callbackE", CallbackTemplate<4>); | ||||
|  | ||||
|     // Enter slice 0 | ||||
|     core_timing.ResetRun(); | ||||
| @@ -99,8 +106,8 @@ TEST_CASE("CoreTiming[FairSharing]", "[core]") { | ||||
|     ScopeInit guard; | ||||
|     auto& core_timing = guard.core_timing; | ||||
|  | ||||
|     Core::Timing::EventType* empty_callback = | ||||
|         core_timing.RegisterEvent("empty_callback", EmptyCallback); | ||||
|     std::shared_ptr<Core::Timing::EventType> empty_callback = | ||||
|         Core::Timing::CreateEvent("empty_callback", EmptyCallback); | ||||
|  | ||||
|     callbacks_done = 0; | ||||
|     u64 MAX_CALLBACKS = 10; | ||||
| @@ -133,8 +140,10 @@ TEST_CASE("Core::Timing[PredictableLateness]", "[core]") { | ||||
|     ScopeInit guard; | ||||
|     auto& core_timing = guard.core_timing; | ||||
|  | ||||
|     Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>); | ||||
|     Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>); | ||||
|     std::shared_ptr<Core::Timing::EventType> cb_a = | ||||
|         Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); | ||||
|     std::shared_ptr<Core::Timing::EventType> cb_b = | ||||
|         Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>); | ||||
|  | ||||
|     // Enter slice 0 | ||||
|     core_timing.ResetRun(); | ||||
| @@ -145,60 +154,3 @@ TEST_CASE("Core::Timing[PredictableLateness]", "[core]") { | ||||
|     AdvanceAndCheck(core_timing, 0, 0, 10, -10); // (100 - 10) | ||||
|     AdvanceAndCheck(core_timing, 1, 1, 50, -50); | ||||
| } | ||||
|  | ||||
| namespace ChainSchedulingTest { | ||||
| static int reschedules = 0; | ||||
|  | ||||
| static void RescheduleCallback(Core::Timing::CoreTiming& core_timing, u64 userdata, | ||||
|                                s64 cycles_late) { | ||||
|     --reschedules; | ||||
|     REQUIRE(reschedules >= 0); | ||||
|     REQUIRE(lateness == cycles_late); | ||||
|  | ||||
|     if (reschedules > 0) { | ||||
|         core_timing.ScheduleEvent(1000, reinterpret_cast<Core::Timing::EventType*>(userdata), | ||||
|                                   userdata); | ||||
|     } | ||||
| } | ||||
| } // namespace ChainSchedulingTest | ||||
|  | ||||
| TEST_CASE("CoreTiming[ChainScheduling]", "[core]") { | ||||
|     using namespace ChainSchedulingTest; | ||||
|  | ||||
|     ScopeInit guard; | ||||
|     auto& core_timing = guard.core_timing; | ||||
|  | ||||
|     Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>); | ||||
|     Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>); | ||||
|     Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>); | ||||
|     Core::Timing::EventType* cb_rs = core_timing.RegisterEvent( | ||||
|         "callbackReschedule", [&core_timing](u64 userdata, s64 cycles_late) { | ||||
|             RescheduleCallback(core_timing, userdata, cycles_late); | ||||
|         }); | ||||
|  | ||||
|     // Enter slice 0 | ||||
|     core_timing.ResetRun(); | ||||
|  | ||||
|     core_timing.ScheduleEvent(800, cb_a, CB_IDS[0]); | ||||
|     core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]); | ||||
|     core_timing.ScheduleEvent(2200, cb_c, CB_IDS[2]); | ||||
|     core_timing.ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs)); | ||||
|     REQUIRE(800 == core_timing.GetDowncount()); | ||||
|  | ||||
|     reschedules = 3; | ||||
|     AdvanceAndCheck(core_timing, 0, 0); // cb_a | ||||
|     AdvanceAndCheck(core_timing, 1, 1); // cb_b, cb_rs | ||||
|     REQUIRE(2 == reschedules); | ||||
|  | ||||
|     core_timing.AddTicks(core_timing.GetDowncount()); | ||||
|     core_timing.Advance(); // cb_rs | ||||
|     core_timing.SwitchContext(3); | ||||
|     REQUIRE(1 == reschedules); | ||||
|     REQUIRE(200 == core_timing.GetDowncount()); | ||||
|  | ||||
|     AdvanceAndCheck(core_timing, 2, 3); // cb_c | ||||
|  | ||||
|     core_timing.AddTicks(core_timing.GetDowncount()); | ||||
|     core_timing.Advance(); // cb_rs | ||||
|     REQUIRE(0 == reschedules); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 bunnei
					bunnei