diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index abca753eac..f0c72eb71b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -409,6 +409,8 @@ add_library(core STATIC hle/service/am/button_poller.h hle/service/am/display_layer_manager.cpp hle/service/am/display_layer_manager.h + hle/service/am/event_observer.cpp + hle/service/am/event_observer.h hle/service/am/frontend/applet_cabinet.cpp hle/service/am/frontend/applet_cabinet.h hle/service/am/frontend/applet_controller.cpp diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index ecd0f60162..c6ff8cf67d 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -3,6 +3,7 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/button_poller.h" +#include "core/hle/service/am/event_observer.h" #include "core/hle/service/am/service/all_system_applet_proxies_service.h" #include "core/hle/service/am/service/application_proxy_service.h" #include "core/hle/service/server_manager.h" @@ -11,6 +12,7 @@ namespace Service::AM { void LoopProcess(Core::System& system) { ButtonPoller button_poller(system); + EventObserver event_observer(system); auto server_manager = std::make_unique(system); diff --git a/src/core/hle/service/am/event_observer.cpp b/src/core/hle/service/am/event_observer.cpp new file mode 100644 index 0000000000..87ef85afa6 --- /dev/null +++ b/src/core/hle/service/am/event_observer.cpp @@ -0,0 +1,156 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/event_observer.h" + +namespace Service::AM { + +enum class UserDataTag : u32 { + WakeupEvent, + AppletProcess, +}; + +EventObserver::EventObserver(Core::System& system) + : m_system(system), m_context(system, "am:EventObserver"), m_wakeup_event(m_context), + m_wakeup_holder(m_wakeup_event.GetHandle()) { + m_wakeup_holder.SetUserData(static_cast(UserDataTag::WakeupEvent)); + m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait)); + m_thread = std::thread([&] { this->ThreadFunc(); }); +} + +EventObserver::~EventObserver() { + // Signal thread and wait for processing to finish. + m_stop_source.request_stop(); + m_wakeup_event.Signal(); + m_thread.join(); + + // Free remaining owned sessions. + auto it = m_process_holder_list.begin(); + while (it != m_process_holder_list.end()) { + // Get the holder. + auto* const holder = std::addressof(*it); + + // Remove from the list. + it = m_process_holder_list.erase(it); + + // Free the holder. + delete holder; + } +} + +void EventObserver::TrackAppletProcess(Applet& applet) { + // Don't observe dummy processes. + if (!applet.process->IsInitialized()) { + return; + } + + // Allocate new holder. + auto* holder = new ProcessHolder(applet, *applet.process); + holder->SetUserData(static_cast(UserDataTag::AppletProcess)); + + // Insert into list. + { + std::scoped_lock lk{m_lock}; + m_process_holder_list.push_back(*holder); + holder->LinkToMultiWait(std::addressof(m_deferred_wait_list)); + } + + // Signal wakeup. + m_wakeup_event.Signal(); +} + +void EventObserver::RequestUpdate() { + m_wakeup_event.Signal(); +} + +void EventObserver::LinkDeferred() { + std::scoped_lock lk{m_lock}; + m_multi_wait.MoveAll(std::addressof(m_deferred_wait_list)); +} + +MultiWaitHolder* EventObserver::WaitSignaled() { + while (true) { + this->LinkDeferred(); + + // If we're done, return before we start waiting. + if (m_stop_source.stop_requested()) { + return nullptr; + } + + auto* selected = m_multi_wait.WaitAny(m_system.Kernel()); + if (selected != std::addressof(m_wakeup_holder)) { + // Unlink the process. + selected->UnlinkFromMultiWait(); + } + + return selected; + } +} + +void EventObserver::Process(MultiWaitHolder* holder) { + switch (static_cast(holder->GetUserData())) { + case UserDataTag::WakeupEvent: + this->OnWakeupEvent(holder); + break; + case UserDataTag::AppletProcess: + this->OnProcessEvent(static_cast(holder)); + break; + default: + UNREACHABLE(); + } +} + +void EventObserver::OnWakeupEvent(MultiWaitHolder* holder) { + m_wakeup_event.Clear(); + + // Perform recalculation. + // TODO +} + +void EventObserver::OnProcessEvent(ProcessHolder* holder) { + // Check process state. + auto& process = holder->GetProcess(); + + { + std::scoped_lock lk{m_lock}; + if (process.IsTerminated()) { + // Destroy the holder. + this->DestroyAppletProcessHolderLocked(holder); + } else { + // Reset signaled state. + process.ResetSignal(); + + // Relink wakeup event. + holder->LinkToMultiWait(std::addressof(m_deferred_wait_list)); + } + } + + // Perform recalculation. + // TODO +} + +void EventObserver::DestroyAppletProcessHolderLocked(ProcessHolder* holder) { + // Remove from owned list. + m_process_holder_list.erase(m_process_holder_list.iterator_to(*holder)); + + // Destroy and free. + delete holder; +} + +void EventObserver::ThreadFunc() { + Common::SetCurrentThreadName("am:EventObserver"); + + while (true) { + auto* signaled_holder = this->WaitSignaled(); + if (!signaled_holder) { + break; + } + + this->Process(signaled_holder); + } +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/event_observer.h b/src/core/hle/service/am/event_observer.h new file mode 100644 index 0000000000..67504fcc67 --- /dev/null +++ b/src/core/hle/service/am/event_observer.h @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/polyfill_thread.h" +#include "common/thread.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/os/event.h" +#include "core/hle/service/os/multi_wait.h" + +namespace Core { +class System; +} + +namespace Service::AM { + +struct Applet; +class ProcessHolder; + +class EventObserver { +public: + explicit EventObserver(Core::System& system); + ~EventObserver(); + + void TrackAppletProcess(Applet& applet); + void RequestUpdate(); + +private: + void LinkDeferred(); + MultiWaitHolder* WaitSignaled(); + void Process(MultiWaitHolder* holder); + bool WaitAndProcessImpl(); + void LoopProcess(); + +private: + void OnWakeupEvent(MultiWaitHolder* holder); + void OnProcessEvent(ProcessHolder* holder); + +private: + void DestroyAppletProcessHolderLocked(ProcessHolder* holder); + +private: + void ThreadFunc(); + +private: + // System reference and context. + Core::System& m_system; + KernelHelpers::ServiceContext m_context; + + // Guest event handle to wake up the event loop processor. + Event m_wakeup_event; + MultiWaitHolder m_wakeup_holder; + + // Mutex to protect remaining members. + std::mutex m_lock{}; + + // List of owned process holders. + Common::IntrusiveListBaseTraits::ListType m_process_holder_list; + + // Multi-wait objects for new tasks. + MultiWait m_multi_wait; + MultiWait m_deferred_wait_list; + + // Processing thread. + std::thread m_thread{}; + std::stop_source m_stop_source{}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/process_holder.h b/src/core/hle/service/am/process_holder.h index 7e7fa5d240..3a9b81dfb4 100644 --- a/src/core/hle/service/am/process_holder.h +++ b/src/core/hle/service/am/process_holder.h @@ -13,7 +13,7 @@ namespace Service::AM { struct Applet; -class ProcessHolder : public MultiWaitHolder { +class ProcessHolder : public MultiWaitHolder, public Common::IntrusiveListBaseNode { public: explicit ProcessHolder(Applet& applet, Process& process); ~ProcessHolder();