From 9b1e9da18a86eea41ab2deb33a0b4066c308829e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 4 Apr 2025 14:57:13 +0200 Subject: [PATCH] Simplify AsyncIO part 1: Remove ThreadEventQueue.h --- CMakeLists.txt | 1 - Core/Core.vcxproj | 1 - Core/Core.vcxproj.filters | 3 - Core/HW/AsyncIOManager.h | 190 ++++++++++++++++++++++++- Core/ThreadEventQueue.h | 207 ---------------------------- UWP/CoreUWP/CoreUWP.vcxproj | 3 +- UWP/CoreUWP/CoreUWP.vcxproj.filters | 1 - 7 files changed, 186 insertions(+), 220 deletions(-) delete mode 100644 Core/ThreadEventQueue.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 042303f79a..af9bd79746 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2057,7 +2057,6 @@ add_library(${CoreLibName} ${CoreLinkType} Core/LuaContext.h Core/RetroAchievements.h Core/RetroAchievements.cpp - Core/ThreadEventQueue.h Core/TiltEventProcessor.h Core/TiltEventProcessor.cpp Core/WebServer.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index b8160f184e..39b6694b3b 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -1473,7 +1473,6 @@ - diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 4ef4ec1600..ddf9bac3ec 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1737,9 +1737,6 @@ FileSystems - - Core - HW diff --git a/Core/HW/AsyncIOManager.h b/Core/HW/AsyncIOManager.h index e1f03527ab..2baab81de1 100644 --- a/Core/HW/AsyncIOManager.h +++ b/Core/HW/AsyncIOManager.h @@ -17,15 +17,16 @@ #pragma once +#include +#include +#include #include #include -#include #include "Core/Core.h" -#include "Core/ThreadEventQueue.h" -class NoBase { -}; +#include "Core/System.h" +#include "Core/CoreTiming.h" enum AsyncIOEventType { IO_EVENT_INVALID, @@ -78,7 +79,186 @@ struct AsyncIOResult { u32 invalidateAddr; }; -typedef ThreadEventQueue IOThreadEventQueue; +template +struct ThreadEventQueue { + virtual ~ThreadEventQueue() {} + + void SetThreadEnabled(bool threadEnabled) { + threadEnabled_ = threadEnabled; + } + + bool ThreadEnabled() { + return threadEnabled_; + } + + void ScheduleEvent(Event ev) { + if (threadEnabled_) { + std::lock_guard guard(eventsLock_); + events_.push_back(ev); + eventsWait_.notify_one(); + } else { + events_.push_back(ev); + } + + if (!threadEnabled_) { + RunEventsUntil(0); + } + } + + bool HasEvents() { + if (threadEnabled_) { + std::lock_guard guard(eventsLock_); + return !events_.empty(); + } else { + return !events_.empty(); + } + } + + void NotifyDrain() { + if (threadEnabled_) { + std::lock_guard guard(eventsLock_); + eventsDrain_.notify_one(); + } + } + + Event GetNextEvent() { + if (threadEnabled_) { + std::lock_guard guard(eventsLock_); + if (events_.empty()) { + NotifyDrain(); + return EVENT_INVALID; + } + + Event ev = events_.front(); + events_.pop_front(); + return ev; + } else { + if (events_.empty()) { + return EVENT_INVALID; + } + Event ev = events_.front(); + events_.pop_front(); + return ev; + } + } + + void RunEventsUntil(u64 globalticks) { + if (!threadEnabled_) { + do { + for (Event ev = GetNextEvent(); EventType(ev) != EVENT_INVALID; ev = GetNextEvent()) { + ProcessEventIfApplicable(ev, globalticks); + } + } while (CoreTiming::GetTicks() < globalticks); + return; + } + + std::unique_lock guard(eventsLock_); + eventsRunning_ = true; + eventsHaveRun_ = true; + do { + while (events_.empty() && !ShouldExitEventLoop()) { + eventsWait_.wait(guard); + } + // Quit the loop if the queue is drained and coreState has tripped, or threading is disabled. + if (events_.empty()) { + break; + } + + for (Event ev = GetNextEvent(); EventType(ev) != EVENT_INVALID; ev = GetNextEvent()) { + guard.unlock(); + ProcessEventIfApplicable(ev, globalticks); + guard.lock(); + } + } while (CoreTiming::GetTicks() < globalticks); + + // This will force the waiter to check coreState, even if we didn't actually drain. + NotifyDrain(); + eventsRunning_ = false; + } + + void SyncBeginFrame() { + if (threadEnabled_) { + std::lock_guard guard(eventsLock_); + eventsHaveRun_ = false; + } else { + eventsHaveRun_ = false; + } + } + + inline bool ShouldSyncThread(bool force) { + if (!HasEvents()) + return false; + if (coreState != CORE_RUNNING_CPU && !force) + return false; + + // Don't run if it's not running, but wait for startup. + if (!eventsRunning_) { + if (eventsHaveRun_ || coreState == CORE_BOOT_ERROR || coreState == CORE_RUNTIME_ERROR || coreState == CORE_POWERDOWN) { + return false; + } + } + + return true; + } + + // Force ignores coreState. + void SyncThread(bool force = false) { + if (!threadEnabled_) { + return; + } + + std::unique_lock guard(eventsLock_); + // While processing the last event, HasEvents() will be false even while not done. + // So we schedule a nothing event and wait for that to finish. + ScheduleEvent(EVENT_SYNC); + while (ShouldSyncThread(force)) { + eventsDrain_.wait(guard); + } + } + + void FinishEventLoop() { + if (!threadEnabled_) { + return; + } + + std::lock_guard guard(eventsLock_); + // Don't schedule a finish if it's not even running. + if (eventsRunning_) { + ScheduleEvent(EVENT_FINISH); + } + } + +protected: + virtual void ProcessEvent(Event ev) = 0; + virtual bool ShouldExitEventLoop() = 0; + + inline void ProcessEventIfApplicable(Event &ev, u64 &globalticks) { + switch (EventType(ev)) { + case EVENT_FINISH: + // Stop waiting. + globalticks = 0; + break; + + case EVENT_SYNC: + // Nothing special to do, this event it just to wait on, see SyncThread. + break; + + default: + ProcessEvent(ev); + } + } + +private: + bool threadEnabled_ = false; + bool eventsRunning_ = false; + bool eventsHaveRun_ = false; + std::deque events_; + std::recursive_mutex eventsLock_; // TODO: Should really make this non-recursive - condition_variable_any is dangerous + std::condition_variable_any eventsWait_; + std::condition_variable_any eventsDrain_; +}; + +typedef ThreadEventQueue IOThreadEventQueue; class AsyncIOManager : public IOThreadEventQueue { public: void DoState(PointerWrap &p); diff --git a/Core/ThreadEventQueue.h b/Core/ThreadEventQueue.h deleted file mode 100644 index 7b88d1a897..0000000000 --- a/Core/ThreadEventQueue.h +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (c) 2013- PPSSPP Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official git repository and contact information can be found at -// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - -#pragma once - -#include -#include -#include - -#include "Core/System.h" -#include "Core/CoreTiming.h" - -template -struct ThreadEventQueue : public B { - ThreadEventQueue() : threadEnabled_(false), eventsRunning_(false), eventsHaveRun_(false) { - } - virtual ~ThreadEventQueue() { - } - - void SetThreadEnabled(bool threadEnabled) { - threadEnabled_ = threadEnabled; - } - - bool ThreadEnabled() { - return threadEnabled_; - } - - void ScheduleEvent(Event ev) { - if (threadEnabled_) { - std::lock_guard guard(eventsLock_); - events_.push_back(ev); - eventsWait_.notify_one(); - } else { - events_.push_back(ev); - } - - if (!threadEnabled_) { - RunEventsUntil(0); - } - } - - bool HasEvents() { - if (threadEnabled_) { - std::lock_guard guard(eventsLock_); - return !events_.empty(); - } else { - return !events_.empty(); - } - } - - void NotifyDrain() { - if (threadEnabled_) { - std::lock_guard guard(eventsLock_); - eventsDrain_.notify_one(); - } - } - - Event GetNextEvent() { - if (threadEnabled_) { - std::lock_guard guard(eventsLock_); - if (events_.empty()) { - NotifyDrain(); - return EVENT_INVALID; - } - - Event ev = events_.front(); - events_.pop_front(); - return ev; - } else { - if (events_.empty()) { - return EVENT_INVALID; - } - Event ev = events_.front(); - events_.pop_front(); - return ev; - } - } - - void RunEventsUntil(u64 globalticks) { - if (!threadEnabled_) { - do { - for (Event ev = GetNextEvent(); EventType(ev) != EVENT_INVALID; ev = GetNextEvent()) { - ProcessEventIfApplicable(ev, globalticks); - } - } while (CoreTiming::GetTicks() < globalticks); - return; - } - - std::unique_lock guard(eventsLock_); - eventsRunning_ = true; - eventsHaveRun_ = true; - do { - while (events_.empty() && !ShouldExitEventLoop()) { - eventsWait_.wait(guard); - } - // Quit the loop if the queue is drained and coreState has tripped, or threading is disabled. - if (events_.empty()) { - break; - } - - for (Event ev = GetNextEvent(); EventType(ev) != EVENT_INVALID; ev = GetNextEvent()) { - guard.unlock(); - ProcessEventIfApplicable(ev, globalticks); - guard.lock(); - } - } while (CoreTiming::GetTicks() < globalticks); - - // This will force the waiter to check coreState, even if we didn't actually drain. - NotifyDrain(); - eventsRunning_ = false; - } - - void SyncBeginFrame() { - if (threadEnabled_) { - std::lock_guard guard(eventsLock_); - eventsHaveRun_ = false; - } else { - eventsHaveRun_ = false; - } - } - - inline bool ShouldSyncThread(bool force) { - if (!HasEvents()) - return false; - if (coreState != CORE_RUNNING_CPU && !force) - return false; - - // Don't run if it's not running, but wait for startup. - if (!eventsRunning_) { - if (eventsHaveRun_ || coreState == CORE_BOOT_ERROR || coreState == CORE_RUNTIME_ERROR || coreState == CORE_POWERDOWN) { - return false; - } - } - - return true; - } - - // Force ignores coreState. - void SyncThread(bool force = false) { - if (!threadEnabled_) { - return; - } - - std::unique_lock guard(eventsLock_); - // While processing the last event, HasEvents() will be false even while not done. - // So we schedule a nothing event and wait for that to finish. - ScheduleEvent(EVENT_SYNC); - while (ShouldSyncThread(force)) { - eventsDrain_.wait(guard); - } - } - - void FinishEventLoop() { - if (!threadEnabled_) { - return; - } - - std::lock_guard guard(eventsLock_); - // Don't schedule a finish if it's not even running. - if (eventsRunning_) { - ScheduleEvent(EVENT_FINISH); - } - } - -protected: - virtual void ProcessEvent(Event ev) = 0; - virtual bool ShouldExitEventLoop() = 0; - - inline void ProcessEventIfApplicable(Event &ev, u64 &globalticks) { - switch (EventType(ev)) { - case EVENT_FINISH: - // Stop waiting. - globalticks = 0; - break; - - case EVENT_SYNC: - // Nothing special to do, this event it just to wait on, see SyncThread. - break; - - default: - ProcessEvent(ev); - } - } - -private: - bool threadEnabled_; - bool eventsRunning_; - bool eventsHaveRun_; - std::deque events_; - std::recursive_mutex eventsLock_; // TODO: Should really make this non-recursive - condition_variable_any is dangerous - std::condition_variable_any eventsWait_; - std::condition_variable_any eventsDrain_; -}; diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index b68c89d8b7..03cfb098a0 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -332,7 +332,6 @@ - @@ -1049,4 +1048,4 @@ - \ No newline at end of file + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index ad84f6d35f..63011547e3 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -1267,7 +1267,6 @@ - MIPS\ARM